[sandbox] Introduce BoundedSize
A BoundedSize is just a regular size_t when the sandbox is disabled. However, when the sandbox is enabled, a BoundedLength is guaranteed to be in the range [0, kMaxSafeBufferSizeForSandbox]. This is (currently) achieved by storing the length shifted to the left, then right-shifting it when loading it. This guarantees that the top bits are zero. BoundedSizes are used to ensure safe access to variable-sized buffers, in particular ArrayBuffers and their views, located inside the sandbox. If a full size_t is used to represent their size, it may allow an attacker to "reach out of" the sandbox address space by setting the length to a very large value. A BoundedSize prevents this. Bug: chromium:1360375 Change-Id: I0579693db528af96c41eeaa64bd3ed71266aacd9 Cq-Include-Trybots: luci.v8.try.triggered:v8_linux64_no_sandbox_dbg_ng_triggered Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3876823 Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Samuel Groß <saelo@chromium.org> Cr-Commit-Position: refs/heads/main@{#83631}
This commit is contained in:
parent
325853cf50
commit
e9775165b6
@ -2071,6 +2071,8 @@ filegroup(
|
||||
"src/sandbox/sandbox.h",
|
||||
"src/sandbox/sandboxed-pointer-inl.h",
|
||||
"src/sandbox/sandboxed-pointer.h",
|
||||
"src/sandbox/bounded-size-inl.h",
|
||||
"src/sandbox/bounded-size.h",
|
||||
"src/base/sanitizer/asan.h",
|
||||
"src/base/sanitizer/lsan-page-allocator.cc",
|
||||
"src/base/sanitizer/lsan-page-allocator.h",
|
||||
|
2
BUILD.gn
2
BUILD.gn
@ -3456,6 +3456,8 @@ v8_header_set("v8_internal_headers") {
|
||||
"src/roots/roots.h",
|
||||
"src/runtime/runtime-utils.h",
|
||||
"src/runtime/runtime.h",
|
||||
"src/sandbox/bounded-size-inl.h",
|
||||
"src/sandbox/bounded-size.h",
|
||||
"src/sandbox/external-pointer-inl.h",
|
||||
"src/sandbox/external-pointer-table-inl.h",
|
||||
"src/sandbox/external-pointer-table.h",
|
||||
|
@ -182,7 +182,7 @@ constexpr size_t kSandboxSizeLog2 = 37; // 128 GB
|
||||
#else
|
||||
// Everywhere else use a 1TB sandbox.
|
||||
constexpr size_t kSandboxSizeLog2 = 40; // 1 TB
|
||||
#endif // V8_OS_ANDROID
|
||||
#endif // V8_TARGET_OS_ANDROID
|
||||
constexpr size_t kSandboxSize = 1ULL << kSandboxSizeLog2;
|
||||
|
||||
// Required alignment of the sandbox. For simplicity, we require the
|
||||
@ -223,6 +223,21 @@ static_assert(kSandboxMinimumReservationSize > kPtrComprCageReservationSize,
|
||||
"The minimum reservation size for a sandbox must be larger than "
|
||||
"the pointer compression cage contained within it.");
|
||||
|
||||
// The maximum buffer size allowed inside the sandbox. This is mostly dependent
|
||||
// on the size of the guard regions around the sandbox: an attacker must not be
|
||||
// able to construct a buffer that appears larger than the guard regions and
|
||||
// thereby "reach out of" the sandbox.
|
||||
constexpr size_t kMaxSafeBufferSizeForSandbox = 32ULL * GB - 1;
|
||||
static_assert(kMaxSafeBufferSizeForSandbox <= kSandboxGuardRegionSize,
|
||||
"The maximum allowed buffer size must not be larger than the "
|
||||
"sandbox's guard regions");
|
||||
|
||||
constexpr size_t kBoundedSizeShift = 29;
|
||||
static_assert(1ULL << (64 - kBoundedSizeShift) ==
|
||||
kMaxSafeBufferSizeForSandbox + 1,
|
||||
"The maximum size of a BoundedSize must be synchronized with the "
|
||||
"kMaxSafeBufferSizeForSandbox");
|
||||
|
||||
#endif // V8_ENABLE_SANDBOX
|
||||
|
||||
#ifdef V8_COMPRESS_POINTERS
|
||||
|
@ -64,8 +64,8 @@ TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::AllocateEmptyOnHeapBuffer(
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kBitFieldOffset,
|
||||
Int32Constant(bitfield_value));
|
||||
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kByteLengthOffset,
|
||||
UintPtrConstant(0));
|
||||
StoreBoundedSizeToObject(buffer, JSArrayBuffer::kRawByteLengthOffset,
|
||||
UintPtrConstant(0));
|
||||
StoreSandboxedPointerToObject(buffer, JSArrayBuffer::kBackingStoreOffset,
|
||||
EmptyBackingStoreBufferConstant());
|
||||
StoreObjectFieldNoWriteBarrier(buffer, JSArrayBuffer::kExtensionOffset,
|
||||
@ -141,7 +141,7 @@ TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
|
||||
// Default to zero if the {receiver}s buffer was detached.
|
||||
TNode<UintPtrT> byte_length = Select<UintPtrT>(
|
||||
IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
|
||||
[=] { return LoadJSArrayBufferViewRawByteLength(receiver_array); });
|
||||
[=] { return LoadJSArrayBufferViewByteLength(receiver_array); });
|
||||
Return(ChangeUintPtrToTagged(byte_length));
|
||||
}
|
||||
}
|
||||
|
@ -3291,16 +3291,6 @@ void MacroAssembler::RecordWriteField(Register object, int offset,
|
||||
Bind(&done);
|
||||
}
|
||||
|
||||
void TurboAssembler::EncodeSandboxedPointer(const Register& value) {
|
||||
ASM_CODE_COMMENT(this);
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
Sub(value, value, kPtrComprCageBaseRegister);
|
||||
Mov(value, Operand(value, LSL, kSandboxedPointerShift));
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TurboAssembler::DecodeSandboxedPointer(const Register& value) {
|
||||
ASM_CODE_COMMENT(this);
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
@ -3313,19 +3303,27 @@ void TurboAssembler::DecodeSandboxedPointer(const Register& value) {
|
||||
|
||||
void TurboAssembler::LoadSandboxedPointerField(
|
||||
const Register& destination, const MemOperand& field_operand) {
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
ASM_CODE_COMMENT(this);
|
||||
Ldr(destination, field_operand);
|
||||
DecodeSandboxedPointer(destination);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TurboAssembler::StoreSandboxedPointerField(
|
||||
const Register& value, const MemOperand& dst_field_operand) {
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
ASM_CODE_COMMENT(this);
|
||||
UseScratchRegisterScope temps(this);
|
||||
Register scratch = temps.AcquireX();
|
||||
Mov(scratch, value);
|
||||
EncodeSandboxedPointer(scratch);
|
||||
Sub(scratch, value, kPtrComprCageBaseRegister);
|
||||
Mov(scratch, Operand(scratch, LSL, kSandboxedPointerShift));
|
||||
Str(scratch, dst_field_operand);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TurboAssembler::LoadExternalPointerField(Register destination,
|
||||
|
@ -1454,13 +1454,9 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
|
||||
// Transform a SandboxedPointer from/to its encoded form, which is used when
|
||||
// the pointer is stored on the heap and ensures that the pointer will always
|
||||
// point into the sandbox.
|
||||
void EncodeSandboxedPointer(const Register& value);
|
||||
void DecodeSandboxedPointer(const Register& value);
|
||||
|
||||
// Load and decode a SandboxedPointer from the heap.
|
||||
void LoadSandboxedPointerField(const Register& destination,
|
||||
const MemOperand& field_operand);
|
||||
// Encode and store a SandboxedPointer to the heap.
|
||||
void StoreSandboxedPointerField(const Register& value,
|
||||
const MemOperand& dst_field_operand);
|
||||
|
||||
|
@ -1552,6 +1552,33 @@ TNode<RawPtrT> CodeStubAssembler::EmptyBackingStoreBufferConstant() {
|
||||
#endif // V8_ENABLE_SANDBOX
|
||||
}
|
||||
|
||||
TNode<UintPtrT> CodeStubAssembler::LoadBoundedSizeFromObject(
|
||||
TNode<HeapObject> object, TNode<IntPtrT> field_offset) {
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
TNode<Uint64T> raw_value = LoadObjectField<Uint64T>(object, field_offset);
|
||||
TNode<Uint64T> shift_amount = Uint64Constant(kBoundedSizeShift);
|
||||
TNode<Uint64T> decoded_value = Word64Shr(raw_value, shift_amount);
|
||||
return ReinterpretCast<UintPtrT>(decoded_value);
|
||||
#else
|
||||
return LoadObjectField<UintPtrT>(object, field_offset);
|
||||
#endif // V8_ENABLE_SANDBOX
|
||||
}
|
||||
|
||||
void CodeStubAssembler::StoreBoundedSizeToObject(TNode<HeapObject> object,
|
||||
TNode<IntPtrT> offset,
|
||||
TNode<UintPtrT> value) {
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
CSA_DCHECK(this, UintPtrLessThan(
|
||||
value, IntPtrConstant(kMaxSafeBufferSizeForSandbox)));
|
||||
TNode<Uint64T> raw_value = ReinterpretCast<Uint64T>(value);
|
||||
TNode<Uint64T> shift_amount = Uint64Constant(kBoundedSizeShift);
|
||||
TNode<Uint64T> encoded_value = Word64Shl(raw_value, shift_amount);
|
||||
StoreObjectFieldNoWriteBarrier<Uint64T>(object, offset, encoded_value);
|
||||
#else
|
||||
StoreObjectFieldNoWriteBarrier<UintPtrT>(object, offset, value);
|
||||
#endif // V8_ENABLE_SANDBOX
|
||||
}
|
||||
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
TNode<RawPtrT> CodeStubAssembler::ExternalPointerTableAddress(
|
||||
ExternalPointerTag tag) {
|
||||
@ -14299,6 +14326,18 @@ void CodeStubAssembler::ThrowIfArrayBufferViewBufferIsDetached(
|
||||
ThrowIfArrayBufferIsDetached(context, buffer, method_name);
|
||||
}
|
||||
|
||||
TNode<UintPtrT> CodeStubAssembler::LoadJSArrayBufferByteLength(
|
||||
TNode<JSArrayBuffer> array_buffer) {
|
||||
return LoadBoundedSizeFromObject(array_buffer,
|
||||
JSArrayBuffer::kRawByteLengthOffset);
|
||||
}
|
||||
|
||||
TNode<UintPtrT> CodeStubAssembler::LoadJSArrayBufferMaxByteLength(
|
||||
TNode<JSArrayBuffer> array_buffer) {
|
||||
return LoadBoundedSizeFromObject(array_buffer,
|
||||
JSArrayBuffer::kRawMaxByteLengthOffset);
|
||||
}
|
||||
|
||||
TNode<RawPtrT> CodeStubAssembler::LoadJSArrayBufferBackingStorePtr(
|
||||
TNode<JSArrayBuffer> array_buffer) {
|
||||
return LoadSandboxedPointerFromObject(array_buffer,
|
||||
@ -14311,16 +14350,38 @@ TNode<JSArrayBuffer> CodeStubAssembler::LoadJSArrayBufferViewBuffer(
|
||||
JSArrayBufferView::kBufferOffset);
|
||||
}
|
||||
|
||||
TNode<UintPtrT> CodeStubAssembler::LoadJSArrayBufferViewRawByteLength(
|
||||
TNode<UintPtrT> CodeStubAssembler::LoadJSArrayBufferViewByteLength(
|
||||
TNode<JSArrayBufferView> array_buffer_view) {
|
||||
return LoadObjectField<UintPtrT>(array_buffer_view,
|
||||
JSArrayBufferView::kByteLengthOffset);
|
||||
return LoadBoundedSizeFromObject(array_buffer_view,
|
||||
JSArrayBufferView::kRawByteLengthOffset);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::StoreJSArrayBufferViewByteLength(
|
||||
TNode<JSArrayBufferView> array_buffer_view, TNode<UintPtrT> value) {
|
||||
StoreBoundedSizeToObject(array_buffer_view,
|
||||
JSArrayBufferView::kRawByteLengthOffset, value);
|
||||
}
|
||||
|
||||
TNode<UintPtrT> CodeStubAssembler::LoadJSArrayBufferViewByteOffset(
|
||||
TNode<JSArrayBufferView> array_buffer_view) {
|
||||
return LoadObjectField<UintPtrT>(array_buffer_view,
|
||||
JSArrayBufferView::kByteOffsetOffset);
|
||||
return LoadBoundedSizeFromObject(array_buffer_view,
|
||||
JSArrayBufferView::kRawByteOffsetOffset);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::StoreJSArrayBufferViewByteOffset(
|
||||
TNode<JSArrayBufferView> array_buffer_view, TNode<UintPtrT> value) {
|
||||
StoreBoundedSizeToObject(array_buffer_view,
|
||||
JSArrayBufferView::kRawByteOffsetOffset, value);
|
||||
}
|
||||
|
||||
TNode<UintPtrT> CodeStubAssembler::LoadJSTypedArrayLength(
|
||||
TNode<JSTypedArray> typed_array) {
|
||||
return LoadBoundedSizeFromObject(typed_array, JSTypedArray::kRawLengthOffset);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::StoreJSTypedArrayLength(TNode<JSTypedArray> typed_array,
|
||||
TNode<UintPtrT> value) {
|
||||
StoreBoundedSizeToObject(typed_array, JSTypedArray::kRawLengthOffset, value);
|
||||
}
|
||||
|
||||
TNode<UintPtrT> CodeStubAssembler::LoadJSTypedArrayLengthAndCheckDetached(
|
||||
@ -14418,7 +14479,7 @@ CodeStubAssembler::LoadVariableLengthJSArrayBufferViewByteLength(
|
||||
// Check if the backing RAB has shrunk so that the buffer is out of
|
||||
// bounds.
|
||||
TNode<UintPtrT> array_byte_length =
|
||||
LoadJSArrayBufferViewRawByteLength(array);
|
||||
LoadJSArrayBufferViewByteLength(array);
|
||||
GotoIfNot(UintPtrGreaterThanOrEqual(
|
||||
buffer_byte_length,
|
||||
UintPtrAdd(array_byte_offset, array_byte_length)),
|
||||
@ -14463,7 +14524,7 @@ void CodeStubAssembler::IsJSArrayBufferViewDetachedOrOutOfBounds(
|
||||
// Check if the backing RAB has shrunk so that the buffer is out of
|
||||
// bounds.
|
||||
TNode<UintPtrT> array_byte_length =
|
||||
LoadJSArrayBufferViewRawByteLength(array_buffer_view);
|
||||
LoadJSArrayBufferViewByteLength(array_buffer_view);
|
||||
Branch(UintPtrGreaterThanOrEqual(
|
||||
buffer_byte_length,
|
||||
UintPtrAdd(array_byte_offset, array_byte_length)),
|
||||
|
@ -1087,10 +1087,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
void GotoIfForceSlowPath(Label* if_true);
|
||||
|
||||
//
|
||||
// Caged pointer related functionality.
|
||||
// Sandboxed pointer related functionality.
|
||||
//
|
||||
|
||||
// Load a caged pointer value from an object.
|
||||
// Load a sandboxed pointer value from an object.
|
||||
TNode<RawPtrT> LoadSandboxedPointerFromObject(TNode<HeapObject> object,
|
||||
int offset) {
|
||||
return LoadSandboxedPointerFromObject(object, IntPtrConstant(offset));
|
||||
@ -1099,7 +1099,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<RawPtrT> LoadSandboxedPointerFromObject(TNode<HeapObject> object,
|
||||
TNode<IntPtrT> offset);
|
||||
|
||||
// Stored a caged pointer value to an object.
|
||||
// Stored a sandboxed pointer value to an object.
|
||||
void StoreSandboxedPointerToObject(TNode<HeapObject> object, int offset,
|
||||
TNode<RawPtrT> pointer) {
|
||||
StoreSandboxedPointerToObject(object, IntPtrConstant(offset), pointer);
|
||||
@ -1111,6 +1111,27 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
|
||||
TNode<RawPtrT> EmptyBackingStoreBufferConstant();
|
||||
|
||||
//
|
||||
// Bounded size related functionality.
|
||||
//
|
||||
|
||||
// Load a bounded size value from an object.
|
||||
TNode<UintPtrT> LoadBoundedSizeFromObject(TNode<HeapObject> object,
|
||||
int offset) {
|
||||
return LoadBoundedSizeFromObject(object, IntPtrConstant(offset));
|
||||
}
|
||||
|
||||
TNode<UintPtrT> LoadBoundedSizeFromObject(TNode<HeapObject> object,
|
||||
TNode<IntPtrT> offset);
|
||||
|
||||
// Stored a bounded size value to an object.
|
||||
void StoreBoundedSizeToObject(TNode<HeapObject> object, int offset,
|
||||
TNode<UintPtrT> value) {
|
||||
StoreBoundedSizeToObject(object, IntPtrConstant(offset), value);
|
||||
}
|
||||
|
||||
void StoreBoundedSizeToObject(TNode<HeapObject> object, TNode<IntPtrT> offset,
|
||||
TNode<UintPtrT> value);
|
||||
//
|
||||
// ExternalPointerT-related functionality.
|
||||
//
|
||||
@ -3757,6 +3778,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<BoolT> IsSideEffectFreeDebuggingActive();
|
||||
|
||||
// JSArrayBuffer helpers
|
||||
TNode<UintPtrT> LoadJSArrayBufferByteLength(
|
||||
TNode<JSArrayBuffer> array_buffer);
|
||||
TNode<UintPtrT> LoadJSArrayBufferMaxByteLength(
|
||||
TNode<JSArrayBuffer> array_buffer);
|
||||
TNode<RawPtrT> LoadJSArrayBufferBackingStorePtr(
|
||||
TNode<JSArrayBuffer> array_buffer);
|
||||
void ThrowIfArrayBufferIsDetached(TNode<Context> context,
|
||||
@ -3766,16 +3791,22 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
// JSArrayBufferView helpers
|
||||
TNode<JSArrayBuffer> LoadJSArrayBufferViewBuffer(
|
||||
TNode<JSArrayBufferView> array_buffer_view);
|
||||
TNode<UintPtrT> LoadJSArrayBufferViewRawByteLength(
|
||||
TNode<UintPtrT> LoadJSArrayBufferViewByteLength(
|
||||
TNode<JSArrayBufferView> array_buffer_view);
|
||||
|
||||
void StoreJSArrayBufferViewByteLength(
|
||||
TNode<JSArrayBufferView> array_buffer_view, TNode<UintPtrT> value);
|
||||
TNode<UintPtrT> LoadJSArrayBufferViewByteOffset(
|
||||
TNode<JSArrayBufferView> array_buffer_view);
|
||||
void StoreJSArrayBufferViewByteOffset(
|
||||
TNode<JSArrayBufferView> array_buffer_view, TNode<UintPtrT> value);
|
||||
void ThrowIfArrayBufferViewBufferIsDetached(
|
||||
TNode<Context> context, TNode<JSArrayBufferView> array_buffer_view,
|
||||
const char* method_name);
|
||||
|
||||
// JSTypedArray helpers
|
||||
TNode<UintPtrT> LoadJSTypedArrayLength(TNode<JSTypedArray> typed_array);
|
||||
void StoreJSTypedArrayLength(TNode<JSTypedArray> typed_array,
|
||||
TNode<UintPtrT> value);
|
||||
TNode<UintPtrT> LoadJSTypedArrayLengthAndCheckDetached(
|
||||
TNode<JSTypedArray> typed_array, Label* detached);
|
||||
// Helper for length tracking JSTypedArrays and JSTypedArrays backed by
|
||||
|
@ -240,7 +240,7 @@ class MachineType {
|
||||
}
|
||||
constexpr static MachineType SandboxedPointer() {
|
||||
return MachineType(MachineRepresentation::kSandboxedPointer,
|
||||
MachineSemantic::kNone);
|
||||
MachineSemantic::kInt64);
|
||||
}
|
||||
constexpr static MachineType Bool() {
|
||||
return MachineType(MachineRepresentation::kBit, MachineSemantic::kBool);
|
||||
|
@ -376,39 +376,48 @@ FieldAccess AccessBuilder::ForJSArrayBufferViewBuffer() {
|
||||
// static
|
||||
FieldAccess AccessBuilder::ForJSArrayBufferViewByteLength() {
|
||||
FieldAccess access = {kTaggedBase,
|
||||
JSArrayBufferView::kByteLengthOffset,
|
||||
JSArrayBufferView::kRawByteLengthOffset,
|
||||
MaybeHandle<Name>(),
|
||||
MaybeHandle<Map>(),
|
||||
TypeCache::Get()->kJSArrayBufferViewByteLengthType,
|
||||
MachineType::UintPtr(),
|
||||
kNoWriteBarrier,
|
||||
"JSArrayBufferViewByteLength"};
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
access.is_bounded_size_access = true;
|
||||
#endif
|
||||
return access;
|
||||
}
|
||||
|
||||
// static
|
||||
FieldAccess AccessBuilder::ForJSArrayBufferViewByteOffset() {
|
||||
FieldAccess access = {kTaggedBase,
|
||||
JSArrayBufferView::kByteOffsetOffset,
|
||||
JSArrayBufferView::kRawByteOffsetOffset,
|
||||
MaybeHandle<Name>(),
|
||||
MaybeHandle<Map>(),
|
||||
TypeCache::Get()->kJSArrayBufferViewByteOffsetType,
|
||||
MachineType::UintPtr(),
|
||||
kNoWriteBarrier,
|
||||
"JSArrayBufferViewByteOffset"};
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
access.is_bounded_size_access = true;
|
||||
#endif
|
||||
return access;
|
||||
}
|
||||
|
||||
// static
|
||||
FieldAccess AccessBuilder::ForJSTypedArrayLength() {
|
||||
FieldAccess access = {kTaggedBase,
|
||||
JSTypedArray::kLengthOffset,
|
||||
JSTypedArray::kRawLengthOffset,
|
||||
MaybeHandle<Name>(),
|
||||
MaybeHandle<Map>(),
|
||||
TypeCache::Get()->kJSTypedArrayLengthType,
|
||||
MachineType::UintPtr(),
|
||||
kNoWriteBarrier,
|
||||
"JSTypedArrayLength"};
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
access.is_bounded_size_access = true;
|
||||
#endif
|
||||
return access;
|
||||
}
|
||||
|
||||
|
@ -339,7 +339,7 @@ ArchOpcode GetLoadOpcode(LoadRepresentation load_rep) {
|
||||
break;
|
||||
case MachineRepresentation::kSimd256: // Fall through.
|
||||
case MachineRepresentation::kNone: // Fall through.
|
||||
case MachineRepresentation::kMapWord:
|
||||
case MachineRepresentation::kMapWord: // Fall through.
|
||||
UNREACHABLE();
|
||||
}
|
||||
return opcode;
|
||||
@ -377,7 +377,7 @@ ArchOpcode GetStoreOpcode(StoreRepresentation store_rep) {
|
||||
return kX64Movdqu;
|
||||
case MachineRepresentation::kSimd256: // Fall through.
|
||||
case MachineRepresentation::kNone: // Fall through.
|
||||
case MachineRepresentation::kMapWord:
|
||||
case MachineRepresentation::kMapWord: // Fall through.
|
||||
UNREACHABLE();
|
||||
}
|
||||
UNREACHABLE();
|
||||
|
@ -616,8 +616,11 @@ void ReduceNode(const Operator* op, EscapeAnalysisTracker::Scope* current,
|
||||
Node* value = current->ValueInput(1);
|
||||
const VirtualObject* vobject = current->GetVirtualObject(object);
|
||||
Variable var;
|
||||
// BoundedSize fields cannot currently be materialized by the deoptimizer,
|
||||
// so we must not dematerialze them.
|
||||
if (vobject && !vobject->HasEscaped() &&
|
||||
vobject->FieldAt(OffsetOfFieldAccess(op)).To(&var)) {
|
||||
vobject->FieldAt(OffsetOfFieldAccess(op)).To(&var) &&
|
||||
!FieldAccessOf(op).is_bounded_size_access) {
|
||||
current->Set(var, value);
|
||||
current->MarkForDeletion();
|
||||
} else {
|
||||
|
@ -492,6 +492,27 @@ Reduction MemoryLowering::ReduceLoadExternalPointerField(Node* node) {
|
||||
return Changed(node);
|
||||
}
|
||||
|
||||
Reduction MemoryLowering::ReduceLoadBoundedSize(Node* node) {
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
const Operator* load_op =
|
||||
!machine()->UnalignedLoadSupported(MachineRepresentation::kWord64)
|
||||
? machine()->UnalignedLoad(MachineType::Uint64())
|
||||
: machine()->Load(MachineType::Uint64());
|
||||
NodeProperties::ChangeOp(node, load_op);
|
||||
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
Node* control = NodeProperties::GetControlInput(node);
|
||||
__ InitializeEffectControl(effect, control);
|
||||
|
||||
Node* raw_value = __ AddNode(graph()->CloneNode(node));
|
||||
Node* shift_amount = __ IntPtrConstant(kBoundedSizeShift);
|
||||
Node* decoded_size = __ Word64Shr(raw_value, shift_amount);
|
||||
return Replace(decoded_size);
|
||||
#else
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
Reduction MemoryLowering::ReduceLoadMap(Node* node) {
|
||||
#ifdef V8_MAP_PACKING
|
||||
NodeProperties::ChangeOp(node, machine()->Load(MachineType::AnyTagged()));
|
||||
@ -524,6 +545,10 @@ Reduction MemoryLowering::ReduceLoadField(Node* node) {
|
||||
return ReduceLoadExternalPointerField(node);
|
||||
}
|
||||
|
||||
if (access.is_bounded_size_access) {
|
||||
return ReduceLoadBoundedSize(node);
|
||||
}
|
||||
|
||||
NodeProperties::ChangeOp(node, machine()->Load(type));
|
||||
|
||||
return Changed(node);
|
||||
@ -574,6 +599,8 @@ Reduction MemoryLowering::ReduceStoreField(Node* node,
|
||||
DCHECK(!access.type.Is(Type::ExternalPointer()));
|
||||
// SandboxedPointers are not currently stored by optimized code.
|
||||
DCHECK(!access.type.Is(Type::SandboxedPointer()));
|
||||
// Bounded size fields are not currently stored by optimized code.
|
||||
DCHECK(!access.is_bounded_size_access);
|
||||
MachineType machine_type = access.machine_type;
|
||||
Node* object = node->InputAt(0);
|
||||
Node* value = node->InputAt(1);
|
||||
|
@ -109,6 +109,7 @@ class MemoryLowering final : public Reducer {
|
||||
AllocationState const* state,
|
||||
WriteBarrierKind);
|
||||
Reduction ReduceLoadExternalPointerField(Node* node);
|
||||
Reduction ReduceLoadBoundedSize(Node* node);
|
||||
Reduction ReduceLoadMap(Node* node);
|
||||
Node* ComputeIndex(ElementAccess const& access, Node* node);
|
||||
void EnsureAllocateOperator();
|
||||
|
@ -87,6 +87,12 @@ struct FieldAccess {
|
||||
// initializing a newly
|
||||
// allocated object or part
|
||||
// of a map transition.
|
||||
bool is_bounded_size_access = false; // Whether this field is stored as a
|
||||
// bounded size field. In that case,
|
||||
// the size is shifted to the left to
|
||||
// guarantee that the value is at most
|
||||
// kMaxSafeBufferSizeForSandbox after
|
||||
// decoding.
|
||||
|
||||
FieldAccess()
|
||||
: base_is_tagged(kTaggedBase),
|
||||
|
@ -28,11 +28,19 @@ RELEASE_ACQUIRE_ACCESSORS(JSTypedArray, base_pointer, Object,
|
||||
kBasePointerOffset)
|
||||
|
||||
size_t JSArrayBuffer::byte_length() const {
|
||||
return ReadField<size_t>(kByteLengthOffset);
|
||||
return ReadBoundedSizeField(kRawByteLengthOffset);
|
||||
}
|
||||
|
||||
void JSArrayBuffer::set_byte_length(size_t value) {
|
||||
WriteField<size_t>(kByteLengthOffset, value);
|
||||
WriteBoundedSizeField(kRawByteLengthOffset, value);
|
||||
}
|
||||
|
||||
size_t JSArrayBuffer::max_byte_length() const {
|
||||
return ReadBoundedSizeField(kRawMaxByteLengthOffset);
|
||||
}
|
||||
|
||||
void JSArrayBuffer::set_max_byte_length(size_t value) {
|
||||
WriteBoundedSizeField(kRawMaxByteLengthOffset, value);
|
||||
}
|
||||
|
||||
DEF_GETTER(JSArrayBuffer, backing_store, void*) {
|
||||
@ -169,19 +177,19 @@ bool JSArrayBuffer::IsEmpty() const {
|
||||
}
|
||||
|
||||
size_t JSArrayBufferView::byte_offset() const {
|
||||
return ReadField<size_t>(kByteOffsetOffset);
|
||||
return ReadBoundedSizeField(kRawByteOffsetOffset);
|
||||
}
|
||||
|
||||
void JSArrayBufferView::set_byte_offset(size_t value) {
|
||||
WriteField<size_t>(kByteOffsetOffset, value);
|
||||
WriteBoundedSizeField(kRawByteOffsetOffset, value);
|
||||
}
|
||||
|
||||
size_t JSArrayBufferView::byte_length() const {
|
||||
return ReadField<size_t>(kByteLengthOffset);
|
||||
return ReadBoundedSizeField(kRawByteLengthOffset);
|
||||
}
|
||||
|
||||
void JSArrayBufferView::set_byte_length(size_t value) {
|
||||
WriteField<size_t>(kByteLengthOffset, value);
|
||||
WriteBoundedSizeField(kRawByteLengthOffset, value);
|
||||
}
|
||||
|
||||
bool JSArrayBufferView::WasDetached() const {
|
||||
@ -250,15 +258,15 @@ inline void JSTypedArray::ForFixedTypedArray(ExternalArrayType array_type,
|
||||
size_t JSTypedArray::length() const {
|
||||
DCHECK(!is_length_tracking());
|
||||
DCHECK(!is_backed_by_rab());
|
||||
return ReadField<size_t>(kLengthOffset);
|
||||
return ReadBoundedSizeField(kRawLengthOffset);
|
||||
}
|
||||
|
||||
size_t JSTypedArray::LengthUnchecked() const {
|
||||
return ReadField<size_t>(kLengthOffset);
|
||||
return ReadBoundedSizeField(kRawLengthOffset);
|
||||
}
|
||||
|
||||
void JSTypedArray::set_length(size_t value) {
|
||||
WriteField<size_t>(kLengthOffset, value);
|
||||
WriteBoundedSizeField(kRawLengthOffset, value);
|
||||
}
|
||||
|
||||
DEF_GETTER(JSTypedArray, external_pointer, Address) {
|
||||
|
@ -28,7 +28,9 @@ class JSArrayBuffer
|
||||
// On 32-bit architectures we limit this to 2GiB, so that
|
||||
// we can continue to use CheckBounds with the Unsigned31
|
||||
// restriction for the length.
|
||||
#if V8_HOST_ARCH_32_BIT
|
||||
#if V8_ENABLE_SANDBOX
|
||||
static constexpr size_t kMaxByteLength = kMaxSafeBufferSizeForSandbox;
|
||||
#elif V8_HOST_ARCH_32_BIT
|
||||
static constexpr size_t kMaxByteLength = kMaxInt;
|
||||
#else
|
||||
static constexpr size_t kMaxByteLength = kMaxSafeInteger;
|
||||
@ -37,6 +39,9 @@ class JSArrayBuffer
|
||||
// [byte_length]: length in bytes
|
||||
DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)
|
||||
|
||||
// [max_byte_length]: maximum length in bytes
|
||||
DECL_PRIMITIVE_ACCESSORS(max_byte_length, size_t)
|
||||
|
||||
// [backing_store]: backing memory for this array
|
||||
// It should not be assumed that this will be nullptr for empty ArrayBuffers.
|
||||
DECL_GETTER(backing_store, void*)
|
||||
@ -257,10 +262,10 @@ class JSArrayBufferView
|
||||
DECL_BOOLEAN_ACCESSORS(is_backed_by_rab)
|
||||
inline bool IsVariableLength() const;
|
||||
|
||||
static constexpr int kEndOfTaggedFieldsOffset = kByteOffsetOffset;
|
||||
static constexpr int kEndOfTaggedFieldsOffset = kRawByteOffsetOffset;
|
||||
|
||||
static_assert(IsAligned(kByteOffsetOffset, kUIntptrSize));
|
||||
static_assert(IsAligned(kByteLengthOffset, kUIntptrSize));
|
||||
static_assert(IsAligned(kRawByteOffsetOffset, kUIntptrSize));
|
||||
static_assert(IsAligned(kRawByteLengthOffset, kUIntptrSize));
|
||||
|
||||
TQ_OBJECT_CONSTRUCTORS(JSArrayBufferView)
|
||||
};
|
||||
@ -271,6 +276,7 @@ class JSTypedArray
|
||||
// TODO(v8:4153): This should be equal to JSArrayBuffer::kMaxByteLength
|
||||
// eventually.
|
||||
static constexpr size_t kMaxLength = v8::TypedArray::kMaxLength;
|
||||
static_assert(kMaxLength <= JSArrayBuffer::kMaxByteLength);
|
||||
|
||||
// [length]: length of typed array in elements.
|
||||
DECL_PRIMITIVE_GETTER(length, size_t)
|
||||
|
@ -12,8 +12,10 @@ bitfield struct JSArrayBufferFlags extends uint32 {
|
||||
}
|
||||
|
||||
extern class JSArrayBuffer extends JSObjectWithEmbedderSlots {
|
||||
byte_length: uintptr;
|
||||
max_byte_length: uintptr;
|
||||
// A BoundedSize if the sandbox is enabled
|
||||
raw_byte_length: uintptr;
|
||||
// A BoundedSize if the sandbox is enabled
|
||||
raw_max_byte_length: uintptr;
|
||||
// A SandboxedPtr if the sandbox is enabled
|
||||
backing_store: RawPtr;
|
||||
extension: RawPtr;
|
||||
@ -23,6 +25,11 @@ extern class JSArrayBuffer extends JSObjectWithEmbedderSlots {
|
||||
@ifnot(TAGGED_SIZE_8_BYTES) optional_padding: void;
|
||||
}
|
||||
|
||||
extern operator '.byte_length' macro LoadJSArrayBufferByteLength(JSArrayBuffer):
|
||||
uintptr;
|
||||
extern operator '.max_byte_length' macro LoadJSArrayBufferMaxByteLength(
|
||||
JSArrayBuffer): uintptr;
|
||||
|
||||
extern operator '.backing_store_ptr' macro LoadJSArrayBufferBackingStorePtr(
|
||||
JSArrayBuffer): RawPtr;
|
||||
|
||||
@ -55,14 +62,25 @@ bitfield struct JSArrayBufferViewFlags extends uint32 {
|
||||
@abstract
|
||||
extern class JSArrayBufferView extends JSObjectWithEmbedderSlots {
|
||||
buffer: JSArrayBuffer;
|
||||
byte_offset: uintptr;
|
||||
byte_length: uintptr;
|
||||
// A BoundedSize if the sandbox is enabled
|
||||
raw_byte_offset: uintptr;
|
||||
// A BoundedSize if the sandbox is enabled
|
||||
raw_byte_length: uintptr;
|
||||
bit_field: JSArrayBufferViewFlags;
|
||||
// Pads header size to be a multiple of kTaggedSize.
|
||||
@if(TAGGED_SIZE_8_BYTES) optional_padding: uint32;
|
||||
@ifnot(TAGGED_SIZE_8_BYTES) optional_padding: void;
|
||||
}
|
||||
|
||||
extern operator '.byte_offset' macro LoadJSArrayBufferViewByteOffset(
|
||||
JSArrayBufferView): uintptr;
|
||||
extern operator '.byte_offset=' macro StoreJSArrayBufferViewByteOffset(
|
||||
JSArrayBufferView, uintptr): void;
|
||||
extern operator '.byte_length' macro LoadJSArrayBufferViewByteLength(
|
||||
JSArrayBufferView): uintptr;
|
||||
extern operator '.byte_length=' macro StoreJSArrayBufferViewByteLength(
|
||||
JSArrayBufferView, uintptr): void;
|
||||
|
||||
@export
|
||||
macro IsVariableLengthJSArrayBufferView(array: JSArrayBufferView): bool {
|
||||
return array.bit_field.is_length_tracking || array.bit_field.is_backed_by_rab;
|
||||
@ -88,12 +106,17 @@ macro LoadJSArrayBufferViewByteLength(
|
||||
}
|
||||
|
||||
extern class JSTypedArray extends JSArrayBufferView {
|
||||
length: uintptr;
|
||||
// A BoundedSize if the sandbox is enabled
|
||||
raw_length: uintptr;
|
||||
// A SandboxedPtr if the sandbox is enabled
|
||||
external_pointer: RawPtr;
|
||||
base_pointer: ByteArray|Smi;
|
||||
}
|
||||
|
||||
extern operator '.length' macro LoadJSTypedArrayLength(JSTypedArray): uintptr;
|
||||
extern operator '.length=' macro StoreJSTypedArrayLength(
|
||||
JSTypedArray, uintptr): void;
|
||||
|
||||
@export
|
||||
macro IsOnHeapTypedArray(array: JSTypedArray): bool {
|
||||
// See JSTypedArray::is_on_heap()
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "src/objects/tagged-impl-inl.h"
|
||||
#include "src/objects/tagged-index.h"
|
||||
#include "src/objects/templates.h"
|
||||
#include "src/sandbox/bounded-size-inl.h"
|
||||
#include "src/sandbox/external-pointer-inl.h"
|
||||
#include "src/sandbox/sandboxed-pointer-inl.h"
|
||||
|
||||
@ -663,6 +664,14 @@ void Object::WriteSandboxedPointerField(size_t offset, Isolate* isolate,
|
||||
PtrComprCageBase(isolate), value);
|
||||
}
|
||||
|
||||
size_t Object::ReadBoundedSizeField(size_t offset) const {
|
||||
return i::ReadBoundedSizeField(field_address(offset));
|
||||
}
|
||||
|
||||
void Object::WriteBoundedSizeField(size_t offset, size_t value) {
|
||||
i::WriteBoundedSizeField(field_address(offset), value);
|
||||
}
|
||||
|
||||
template <ExternalPointerTag tag>
|
||||
void Object::InitExternalPointerField(size_t offset, Isolate* isolate,
|
||||
Address value) {
|
||||
|
@ -743,6 +743,12 @@ class Object : public TaggedImpl<HeapObjectReferenceType::STRONG, Address> {
|
||||
inline void WriteSandboxedPointerField(size_t offset, Isolate* isolate,
|
||||
Address value);
|
||||
|
||||
//
|
||||
// BoundedSize field accessors.
|
||||
//
|
||||
inline size_t ReadBoundedSizeField(size_t offset) const;
|
||||
inline void WriteBoundedSizeField(size_t offset, size_t value);
|
||||
|
||||
//
|
||||
// ExternalPointer_t field accessors.
|
||||
//
|
||||
|
36
src/sandbox/bounded-size-inl.h
Normal file
36
src/sandbox/bounded-size-inl.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2022 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_BOUNDED_SIZE_INL_H_
|
||||
#define V8_SANDBOX_BOUNDED_SIZE_INL_H_
|
||||
|
||||
#include "include/v8-internal.h"
|
||||
#include "src/common/ptr-compr-inl.h"
|
||||
#include "src/sandbox/sandbox.h"
|
||||
#include "src/sandbox/sandboxed-pointer.h"
|
||||
|
||||
namespace v8::internal {
|
||||
|
||||
V8_INLINE size_t ReadBoundedSizeField(Address field_address) {
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
size_t raw_value = base::ReadUnalignedValue<size_t>(field_address);
|
||||
return raw_value >> kBoundedSizeShift;
|
||||
#else
|
||||
return ReadMaybeUnalignedValue<size_t>(field_address);
|
||||
#endif
|
||||
}
|
||||
|
||||
V8_INLINE void WriteBoundedSizeField(Address field_address, size_t value) {
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
DCHECK_LE(value, kMaxSafeBufferSizeForSandbox);
|
||||
size_t raw_value = value << kBoundedSizeShift;
|
||||
base::WriteUnalignedValue<size_t>(field_address, raw_value);
|
||||
#else
|
||||
WriteMaybeUnalignedValue<size_t>(field_address, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace v8::internal
|
||||
|
||||
#endif // V8_SANDBOX_BOUNDED_SIZE_INL_H_
|
28
src/sandbox/bounded-size.h
Normal file
28
src/sandbox/bounded-size.h
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2022 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_BOUNDED_SIZE_H_
|
||||
#define V8_SANDBOX_BOUNDED_SIZE_H_
|
||||
|
||||
#include "src/common/globals.h"
|
||||
|
||||
namespace v8::internal {
|
||||
|
||||
//
|
||||
// BoundedSize accessors.
|
||||
//
|
||||
// A BoundedSize is just a regular size_t when the sandbox is disabled.
|
||||
// However, when the sandbox is enabled, a BoundedLength is guaranteed to be in
|
||||
// the range [0, kMaxSafeBufferSizeForSandbox]. This property is required to
|
||||
// ensure safe access to variable-sized buffers, in particular ArrayBuffers and
|
||||
// their views, located inside the sandbox.
|
||||
//
|
||||
|
||||
V8_INLINE size_t ReadBoundedLengthField(Address field_address);
|
||||
|
||||
V8_INLINE void WriteBoundedLengthField(Address field_address, size_t value);
|
||||
|
||||
} // namespace v8::internal
|
||||
|
||||
#endif // V8_SANDBOX_BOUNDED_SIZE_H_
|
@ -277,13 +277,13 @@ extras_accessors = [
|
||||
'FixedArray, data, uintptr_t, kHeaderSize',
|
||||
'BytecodeArray, data, uintptr_t, kHeaderSize',
|
||||
'JSArrayBuffer, backing_store, uintptr_t, kBackingStoreOffset',
|
||||
'JSArrayBuffer, byte_length, size_t, kByteLengthOffset',
|
||||
'JSArrayBufferView, byte_length, size_t, kByteLengthOffset',
|
||||
'JSArrayBufferView, byte_offset, size_t, kByteOffsetOffset',
|
||||
'JSArrayBuffer, byte_length, size_t, kRawByteLengthOffset',
|
||||
'JSArrayBufferView, byte_length, size_t, kRawByteLengthOffset',
|
||||
'JSArrayBufferView, byte_offset, size_t, kRawByteOffsetOffset',
|
||||
'JSDate, value, Object, kValueOffset',
|
||||
'JSRegExp, source, Object, kSourceOffset',
|
||||
'JSTypedArray, external_pointer, uintptr_t, kExternalPointerOffset',
|
||||
'JSTypedArray, length, Object, kLengthOffset',
|
||||
'JSTypedArray, length, Object, kRawLengthOffset',
|
||||
'Map, instance_size_in_words, char, kInstanceSizeInWordsOffset',
|
||||
'Map, inobject_properties_start_or_constructor_function_index, char, kInobjectPropertiesStartOrConstructorFunctionIndexOffset',
|
||||
'Map, instance_type, uint16_t, kInstanceTypeOffset',
|
||||
@ -319,7 +319,7 @@ extras_accessors = [
|
||||
'ThinString, actual, String, kActualOffset',
|
||||
'Symbol, name, Object, kDescriptionOffset',
|
||||
'FixedArrayBase, length, SMI, kLengthOffset',
|
||||
];
|
||||
]
|
||||
|
||||
#
|
||||
# The following is a whitelist of classes we expect to find when scanning the
|
||||
|
Loading…
Reference in New Issue
Block a user