summaryrefslogtreecommitdiffstats
path: root/chromium/v8/src/sandbox/code-pointer-table.h
blob: d51235d3907ae960670fd54d3e1fe3386de26ebd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_SANDBOX_CODE_POINTER_TABLE_H_
#define V8_SANDBOX_CODE_POINTER_TABLE_H_

#include "include/v8config.h"
#include "src/base/atomicops.h"
#include "src/base/memory.h"
#include "src/base/platform/mutex.h"
#include "src/common/globals.h"
#include "src/sandbox/external-entity-table.h"

#ifdef V8_COMPRESS_POINTERS

namespace v8 {
namespace internal {

class Isolate;
class Counters;

/**
 * The entries of a CodePointerTable.
 *
 * Each entry contains a (compressed) pointer to a Code object as well as a raw
 * pointer to the Code's entrypoint.
 */
struct CodePointerTableEntry {
  // Make this entry a code pointer entry for the given code object and
  // entrypoint.
  inline void MakeCodePointerEntry(Address code, Address entrypoint);

  // Load code entrypoint pointer stored in this entry.
  // This entry must be a code pointer entry.
  inline Address GetEntrypoint() const;

  // Store the given code entrypoint pointer in this entry.
  // This entry must be a code pointer entry.
  inline void SetEntrypoint(Address value);

  // Load the code object pointer stored in this entry.
  // This entry must be a code pointer entry.
  inline Address GetCodeObject() const;

  // Store the given code object pointer in this entry.
  // This entry must be a code pointer entry.
  inline void SetCodeObject(Address value);

  // Make this entry a freelist entry, containing the index of the next entry
  // on the freelist.
  inline void MakeFreelistEntry(uint32_t next_entry_index);

  // Returns true if this entry is a freelist entry.
  inline bool IsFreelistEntry() const;

  // Get the index of the next entry on the freelist. This method may be
  // called even when the entry is not a freelist entry. However, the result
  // is only valid if this is a freelist entry. This behaviour is required
  // for efficient entry allocation, see TryAllocateEntryFromFreelist.
  inline uint32_t GetNextFreelistEntryIndex() const;

  // Mark this entry as alive during garbage collection.
  inline void Mark();

  // Unmark this entry during sweeping.
  inline void Unmark();

  // Test whether this entry is currently marked as alive.
  inline bool IsMarked() const;

 private:
  friend class CodePointerTable;

  // Freelist entries contain the index of the next free entry in their lower 32
  // bits and this tag in the upper 32 bits.
  static constexpr Address kFreeEntryTag = 0xffffffffULL << 32;

  // The marking bit is stored in the code_ field, see below.
  static constexpr Address kMarkingBit = 1;

  std::atomic<Address> entrypoint_;
  // The pointer to the Code object also contains the marking bit: since this is
  // a tagged pointer to a V8 HeapObject, we know that it will be 4-byte aligned
  // and that the LSB should always be set. We therefore use the LSB as marking
  // bit. In this way:
  //  - When loading the pointer, we only need to perform an unconditional OR 1
  //  to get the correctly tagged pointer
  //  - When storing the pointer we don't need to do anything since the tagged
  //  pointer will automatically be marked
  std::atomic<Address> code_;
};

static_assert(sizeof(CodePointerTableEntry) == kCodePointerTableEntrySize);

/**
 * A table containing pointers to code.
 *
 * When the sandbox is enabled, a code pointer table (CPT) can be used to ensure
 * basic control-flow integrity in the absence of special hardware support (such
 * as landing pad instructions): by referencing code through an index into a
 * CPT, and ensuring that only valid code entrypoints are stored inside the
 * table, it is then guaranteed that any indirect control-flow transfer ends up
 * on a valid entrypoint as long as an attacker is still confined to the
 * sandbox.
 */
class V8_EXPORT_PRIVATE CodePointerTable
    : public ExternalEntityTable<CodePointerTableEntry,
                                 kCodePointerTableReservationSize> {
 public:
  // Size of a CodePointerTable, for layout computation in IsolateData.
  static int constexpr kSize = 2 * kSystemPointerSize;
  static_assert(kMaxCodePointers == kMaxCapacity);

  CodePointerTable() = default;
  CodePointerTable(const CodePointerTable&) = delete;
  CodePointerTable& operator=(const CodePointerTable&) = delete;

  // The Spaces used by a CodePointerTable.
  struct Space
      : public ExternalEntityTable<CodePointerTableEntry,
                                   kCodePointerTableReservationSize>::Space {
   private:
    friend class CodePointerTable;
  };

  //
  // This method is atomic and can be called from background threads.
  inline Address GetEntrypoint(CodePointerHandle handle) const;

  // Retrieves the code object of the entry referenced by the given handle.
  //
  // This method is atomic and can be called from background threads.
  inline Address GetCodeObject(CodePointerHandle handle) const;

  // Sets the entrypoint of the entry referenced by the given handle.
  //
  // This method is atomic and can be called from background threads.
  inline void SetEntrypoint(CodePointerHandle handle, Address value);

  // Sets the code object of the entry referenced by the given handle.
  //
  // This method is atomic and can be called from background threads.
  inline void SetCodeObject(CodePointerHandle handle, Address value);

  // Allocates a new entry in the table. The caller must provide the initial
  // value and tag.
  //
  // This method is atomic and can be called from background threads.
  inline CodePointerHandle AllocateAndInitializeEntry(Space* space,
                                                      Address code,
                                                      Address entrypoint);

  // Marks the specified entry as alive.
  //
  // This method is atomic and can be called from background threads.
  inline void Mark(Space* space, CodePointerHandle handle);

  // Frees all unmarked entries in the given space.
  //
  // This method must only be called while mutator threads are stopped as it is
  // not safe to allocate table entries while a space is being swept.
  //
  // Returns the number of live entries after sweeping.
  uint32_t Sweep(Space* space, Counters* counters);

  // The base address of this table, for use in JIT compilers.
  Address base_address() const { return base(); }

 private:
  inline uint32_t HandleToIndex(CodePointerHandle handle) const;
  inline CodePointerHandle IndexToHandle(uint32_t index) const;
};

static_assert(sizeof(CodePointerTable) == CodePointerTable::kSize);

V8_EXPORT_PRIVATE CodePointerTable* GetProcessWideCodePointerTable();

}  // namespace internal
}  // namespace v8

#endif  // V8_COMPRESS_POINTERS

#endif  // V8_SANDBOX_CODE_POINTER_TABLE_H_