#ifndef INCLUDE_V8_SANDBOX_H_
#define INCLUDE_V8_SANDBOX_H_
#include <cstdint>
#include "v8-internal.h"
#include "v8config.h"
namespace v8 {
* A pointer tag used for wrapping and unwrapping `CppHeap` pointers as used
* with JS API wrapper objects that rely on `v8::Object::Wrap()` and
* `v8::Object::Unwrap()`.
*
* The CppHeapPointers use a range-based type checking scheme, where on access
* to a pointer, the actual type of the pointer is checked to be within a
* specified range of types. This allows supporting type hierarchies, where a
* type check for a supertype must succeed for any subtype.
*
* The tag is currently in practice limited to 15 bits since it needs to fit
* together with a marking bit into the unused parts of a pointer.
*/
enum class CppHeapPointerTag : uint16_t {
kFirstTag = 0,
kNullTag = 0,
* The lower type ids are reserved for the embedder to assign. For that, the
* main requirement is that all (transitive) child classes of a given parent
* class have type ids in the same range, and that there are no unrelated
* types in that range. For example, given the following type hierarchy:
*
* A F
* / \
* B E
* / \
* C D
*
* a potential type id assignment that satistifes these requirements is
* {C: 0, D: 1, B: 2, A: 3, E: 4, F: 5}. With that, the type check for type A
* would check for the range [0, 4], while the check for B would check range
* [0, 2], and for F it would simply check [5, 5].
*
* In addition, there is an option for performance tweaks: if the size of the
* type range corresponding to a supertype is a power of two and starts at a
* power of two (e.g. [0x100, 0x13f]), then the compiler can often optimize
* the type check to use even fewer instructions (essentially replace a AND +
* SUB with a single AND).
*/
kDefaultTag = 0x7000,
kZappedEntryTag = 0x7ffd,
kEvacuationEntryTag = 0x7ffe,
kFreeEntryTag = 0x7fff,
kLastTag = 0x7fff,
};
struct CppHeapPointerTagRange {
constexpr CppHeapPointerTagRange(CppHeapPointerTag lower,
CppHeapPointerTag upper)
: lower_bound(lower), upper_bound(upper) {}
CppHeapPointerTag lower_bound;
CppHeapPointerTag upper_bound;
bool CheckTagOf(uint64_t entry) {
uint32_t actual_tag = static_cast<uint16_t>(entry);
constexpr int kTagShift = internal::kCppHeapPointerTagShift;
uint32_t first_tag = static_cast<uint32_t>(lower_bound) << kTagShift;
uint32_t last_tag = (static_cast<uint32_t>(upper_bound) << kTagShift) + 1;
return actual_tag >= first_tag && actual_tag <= last_tag;
}
};
constexpr CppHeapPointerTagRange kAnyCppHeapPointer(
CppHeapPointerTag::kFirstTag, CppHeapPointerTag::kLastTag);
* Hardware support for the V8 Sandbox.
*
* This is an experimental feature that may change or be removed without
* further notice. Use at your own risk.
*/
class SandboxHardwareSupport {
public:
* Initialize sandbox hardware support. This needs to be called before
* creating any thread that might access sandbox memory since it sets up
* hardware permissions to the memory that will be inherited on clone.
*/
V8_EXPORT static void InitializeBeforeThreadCreation();
* Prepares the current thread for executing sandboxed code.
*
* This must be called on newly created threads before they execute any
* sandboxed code (in particular any JavaScript or WebAssembly code). It
* should not be invoked on threads that never execute sandboxed code,
* although it is fine to do so from a security point of view.
*/
V8_EXPORT static void PrepareCurrentThreadForHardwareSandboxing();
};
namespace internal {
#ifdef V8_COMPRESS_POINTERS
V8_INLINE static Address* GetCppHeapPointerTableBase(v8::Isolate* isolate) {
Address addr = reinterpret_cast<Address>(isolate) +
Internals::kIsolateCppHeapPointerTableOffset +
Internals::kExternalPointerTableBasePointerOffset;
return *reinterpret_cast<Address**>(addr);
}
#endif
template <typename T>
V8_INLINE static T* ReadCppHeapPointerField(v8::Isolate* isolate,
Address heap_object_ptr, int offset,
CppHeapPointerTagRange tag_range) {
#ifdef V8_COMPRESS_POINTERS
const CppHeapPointerHandle handle =
Internals::ReadRawField<CppHeapPointerHandle>(heap_object_ptr, offset);
const uint32_t index = handle >> kExternalPointerIndexShift;
const Address* table = GetCppHeapPointerTableBase(isolate);
const std::atomic<Address>* ptr =
reinterpret_cast<const std::atomic<Address>*>(&table[index]);
Address entry = std::atomic_load_explicit(ptr, std::memory_order_relaxed);
Address pointer = entry;
if (V8_LIKELY(tag_range.CheckTagOf(entry))) {
pointer = entry >> kCppHeapPointerPayloadShift;
} else {
pointer = 0;
}
return reinterpret_cast<T*>(pointer);
#else
return reinterpret_cast<T*>(
Internals::ReadRawField<Address>(heap_object_ptr, offset));
#endif
}
}
}
#endif