Commit b9eda863 authored by Igor Sheludko's avatar Igor Sheludko Committed by Commit Bot

[sandbox][x64] Add build flag for V8 heap sandbox feature

... and bottlenecks for C++, CSA, Torque, TurboFan and hand-written
assembly.

Bug: v8:10391
Change-Id: I62f8c6f9c934b2cd492e550b7c25f1078c2c6a71
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2134140
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67479}
parent aa9948f3
......@@ -221,6 +221,10 @@ declare_args() {
# Enable object names in cppgc for debug purposes.
cppgc_enable_object_names = false
# Enable V8 heap sandbox experimental feature.
# Sets -DV8_HEAP_SANDBOX.
v8_enable_heap_sandbox = ""
}
# Derived defaults.
......@@ -257,7 +261,9 @@ if (v8_enable_pointer_compression == "") {
if (v8_enable_fast_torque == "") {
v8_enable_fast_torque = v8_enable_fast_mksnapshot
}
if (v8_enable_heap_sandbox == "") {
v8_enable_heap_sandbox = false
}
if (v8_enable_single_generation == "") {
v8_enable_single_generation = v8_disable_write_barriers
}
......@@ -287,6 +293,9 @@ assert(
!v8_enable_pointer_compression || !v8_enable_shared_ro_heap,
"Pointer compression is not supported with shared read-only heap enabled")
assert(!v8_enable_heap_sandbox || v8_enable_pointer_compression,
"V8 Heap Sandbox requires pointer compression")
v8_random_seed = "314159265"
v8_toolset_for_shell = "host"
......@@ -402,6 +411,9 @@ config("v8_header_features") {
if (v8_enable_pointer_compression || v8_enable_31bit_smis_on_64bit_arch) {
defines += [ "V8_31BIT_SMIS_ON_64BIT_ARCH" ]
}
if (v8_enable_heap_sandbox) {
defines += [ "V8_HEAP_SANDBOX" ]
}
if (v8_deprecation_warnings) {
defines += [ "V8_DEPRECATION_WARNINGS" ]
}
......@@ -2238,6 +2250,8 @@ v8_source_set("v8_base_without_compiler") {
"src/common/assert-scope.cc",
"src/common/assert-scope.h",
"src/common/checks.h",
"src/common/external-pointer-inl.h",
"src/common/external-pointer.h",
"src/common/message-template.h",
"src/common/ptr-compr-inl.h",
"src/common/ptr-compr.h",
......
......@@ -147,6 +147,8 @@ type ObjectHashTable extends HashTable
extern class NumberDictionary extends HashTable;
type RawPtr generates 'TNode<RawPtrT>' constexpr 'void*';
type ExternalPointer
generates 'TNode<ExternalPointerT>' constexpr 'ExternalPointer_t';
extern class Code extends HeapObject;
type BuiltinPtr extends Smi generates 'TNode<BuiltinPtr>';
......
......@@ -9,6 +9,7 @@
#include "src/base/macros.h"
#include "src/codegen/bailout-reason.h"
#include "src/common/external-pointer.h"
#include "src/common/globals.h"
#include "src/common/message-template.h"
#include "src/compiler/code-assembler.h"
......@@ -1065,6 +1066,19 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// Works only with V8_ENABLE_FORCE_SLOW_PATH compile time flag. Nop otherwise.
void GotoIfForceSlowPath(Label* if_true);
// Convert external pointer from on-V8-heap representation to an actual
// external pointer value.
TNode<RawPtrT> DecodeExternalPointer(
TNode<ExternalPointerT> encoded_pointer) {
STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize);
TNode<RawPtrT> value = ReinterpretCast<RawPtrT>(encoded_pointer);
if (V8_HEAP_SANDBOX_BOOL) {
value = UncheckedCast<RawPtrT>(
WordXor(value, UintPtrConstant(kExternalPointerSalt)));
}
return value;
}
// Load value from current parent frame by given offset in bytes.
TNode<Object> LoadFromParentFrame(int offset);
......
......@@ -79,6 +79,12 @@ struct UintPtrT : WordT {
static constexpr MachineType kMachineType = MachineType::UintPtr();
};
struct ExternalPointerT : UntaggedT {
static const MachineRepresentation kMachineRepresentation =
MachineType::PointerRepresentation();
static constexpr MachineType kMachineType = MachineType::Pointer();
};
struct Float32T : UntaggedT {
static const MachineRepresentation kMachineRepresentation =
MachineRepresentation::kFloat32;
......
......@@ -14,6 +14,7 @@
#include "src/codegen/register-configuration.h"
#include "src/codegen/string-constants.h"
#include "src/codegen/x64/assembler-x64.h"
#include "src/common/external-pointer.h"
#include "src/common/globals.h"
#include "src/debug/debug.h"
#include "src/execution/frames-inl.h"
......@@ -341,6 +342,14 @@ void TurboAssembler::SaveRegisters(RegList registers) {
}
}
void TurboAssembler::LoadExternalPointerField(Register destination,
Operand field_operand) {
movq(destination, field_operand);
if (V8_HEAP_SANDBOX_BOOL) {
xorq(destination, Immediate(kExternalPointerSalt));
}
}
void TurboAssembler::RestoreRegisters(RegList registers) {
DCHECK_GT(NumRegs(registers), 0);
for (int i = Register::kNumRegisters - 1; i >= 0; --i) {
......
......@@ -677,6 +677,13 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
void DecompressTaggedPointer(Register destination, Register source);
void DecompressAnyTagged(Register destination, Operand field_operand);
// ---------------------------------------------------------------------------
// V8 Heap sandbox support
// Loads a field containing off-heap pointer and does necessary decoding
// if V8 heap sandbox is enabled.
void LoadExternalPointerField(Register destination, Operand field_operand);
protected:
static const int kSmiShift = kSmiTagSize + kSmiShiftSize;
......
// Copyright 2020 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_COMMON_EXTERNAL_POINTER_INL_H_
#define V8_COMMON_EXTERNAL_POINTER_INL_H_
#include "include/v8-internal.h"
#include "src/common/external-pointer.h"
#include "src/execution/isolate.h"
namespace v8 {
namespace internal {
V8_INLINE ExternalPointer_t EncodeExternalPointer(Isolate* isolate,
Address external_pointer) {
STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize);
if (!V8_HEAP_SANDBOX_BOOL) return external_pointer;
return external_pointer ^ kExternalPointerSalt;
}
V8_INLINE Address DecodeExternalPointer(const Isolate* isolate,
ExternalPointer_t encoded_pointer) {
STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize);
if (!V8_HEAP_SANDBOX_BOOL) return encoded_pointer;
return encoded_pointer ^ kExternalPointerSalt;
}
} // namespace internal
} // namespace v8
#endif // V8_COMMON_EXTERNAL_POINTER_INL_H_
// Copyright 2020 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_COMMON_EXTERNAL_POINTER_H_
#define V8_COMMON_EXTERNAL_POINTER_H_
#include "src/common/globals.h"
namespace v8 {
namespace internal {
// See v8:10391 for details about V8 heap sandbox.
constexpr uint32_t kExternalPointerSalt =
0x7fffffff & ~static_cast<uint32_t>(kHeapObjectTagMask);
static_assert(static_cast<int32_t>(kExternalPointerSalt) >= 0,
"Salt value must be positive for better assembly code");
// Convert external pointer value into encoded form suitable for being stored
// on V8 heap.
V8_INLINE ExternalPointer_t EncodeExternalPointer(Isolate* isolate,
Address external_pointer);
// Convert external pointer from on-V8-heap representation to an actual external
// pointer value.
V8_INLINE Address DecodeExternalPointer(const Isolate* isolate,
ExternalPointer_t encoded_pointer);
} // namespace internal
} // namespace v8
#endif // V8_COMMON_EXTERNAL_POINTER_H_
......@@ -279,6 +279,11 @@ constexpr int kPointerSizeLog2 = kSystemPointerSizeLog2;
STATIC_ASSERT(kPointerSize == (1 << kPointerSizeLog2));
#endif
// This type defines raw storage type for external (or off-V8 heap) pointers
// stored on V8 heap.
using ExternalPointer_t = Address;
constexpr int kExternalPointerSize = sizeof(ExternalPointer_t);
constexpr int kEmbedderDataSlotSize = kSystemPointerSize;
constexpr int kEmbedderDataSlotSizeInTaggedSlots =
......
......@@ -93,7 +93,8 @@ class BasicBlock;
V(WordEqual) \
V(WordSar) \
V(WordSarShiftOutZeros) \
V(WordShl)
V(WordShl) \
V(WordXor)
#define CHECKED_ASSEMBLER_MACH_BINOP_LIST(V) \
V(Int32AddWithOverflow) \
......
......@@ -5,6 +5,7 @@
#include "src/compiler/memory-lowering.h"
#include "src/codegen/interface-descriptors.h"
#include "src/common/external-pointer.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
......@@ -51,7 +52,7 @@ MemoryLowering::MemoryLowering(JSGraph* jsgraph, Zone* zone,
const char* function_debug_name)
: isolate_(jsgraph->isolate()),
zone_(zone),
graph_zone_(jsgraph->graph()->zone()),
graph_(jsgraph->graph()),
common_(jsgraph->common()),
machine_(jsgraph->machine()),
graph_assembler_(graph_assembler),
......@@ -60,6 +61,8 @@ MemoryLowering::MemoryLowering(JSGraph* jsgraph, Zone* zone,
write_barrier_assert_failed_(callback),
function_debug_name_(function_debug_name) {}
Zone* MemoryLowering::graph_zone() const { return graph()->zone(); }
Reduction MemoryLowering::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kAllocate:
......@@ -303,6 +306,29 @@ Reduction MemoryLowering::ReduceLoadElement(Node* node) {
return Changed(node);
}
Node* MemoryLowering::DecodeExternalPointer(Node* node) {
DCHECK(V8_HEAP_SANDBOX_BOOL);
DCHECK(node->opcode() == IrOpcode::kLoad ||
node->opcode() == IrOpcode::kPoisonedLoad);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
__ InitializeEffectControl(effect, control);
// Clone the load node and put it here.
// TODO(turbofan): consider adding GraphAssembler::Clone() suitable for
// cloning nodes from arbitrary locaions in effect/control chains.
Node* node_copy = __ AddNode(graph()->CloneNode(node));
// Uncomment this to generate a breakpoint for debugging purposes.
// __ DebugBreak();
// Decode loaded enternal pointer.
STATIC_ASSERT(kExternalPointerSize == kSystemPointerSize);
Node* salt = __ IntPtrConstant(kExternalPointerSalt);
Node* decoded_ptr = __ WordXor(node_copy, salt);
return decoded_ptr;
}
Reduction MemoryLowering::ReduceLoadField(Node* node) {
DCHECK_EQ(IrOpcode::kLoadField, node->opcode());
FieldAccess const& access = FieldAccessOf(node->op());
......@@ -314,6 +340,13 @@ Reduction MemoryLowering::ReduceLoadField(Node* node) {
} else {
NodeProperties::ChangeOp(node, machine()->Load(type));
}
if (V8_HEAP_SANDBOX_BOOL &&
access.type.Is(Type::SandboxedExternalPointer())) {
node = DecodeExternalPointer(node);
return Replace(node);
} else {
DCHECK(!access.type.Is(Type::SandboxedExternalPointer()));
}
return Changed(node);
}
......@@ -351,6 +384,10 @@ Reduction MemoryLowering::ReduceStoreField(Node* node,
AllocationState const* state) {
DCHECK_EQ(IrOpcode::kStoreField, node->opcode());
FieldAccess const& access = FieldAccessOf(node->op());
// External pointer must never be stored by optimized code.
DCHECK_IMPLIES(V8_HEAP_SANDBOX_BOOL,
!access.type.Is(Type::ExternalPointer()) &&
!access.type.Is(Type::SandboxedExternalPointer()));
Node* object = node->InputAt(0);
Node* value = node->InputAt(1);
WriteBarrierKind write_barrier_kind = ComputeWriteBarrierKind(
......
......@@ -107,13 +107,14 @@ class MemoryLowering final : public Reducer {
Node* value,
AllocationState const* state,
WriteBarrierKind);
Node* DecodeExternalPointer(Node* encoded_pointer);
Node* ComputeIndex(ElementAccess const& access, Node* node);
bool NeedsPoisoning(LoadSensitivity load_sensitivity) const;
Graph* graph() const;
Graph* graph() const { return graph_; }
Isolate* isolate() const { return isolate_; }
Zone* zone() const { return zone_; }
Zone* graph_zone() const { return graph_zone_; }
inline Zone* graph_zone() const;
CommonOperatorBuilder* common() const { return common_; }
MachineOperatorBuilder* machine() const { return machine_; }
JSGraphAssembler* gasm() const { return graph_assembler_; }
......@@ -121,7 +122,7 @@ class MemoryLowering final : public Reducer {
SetOncePointer<const Operator> allocate_operator_;
Isolate* isolate_;
Zone* zone_;
Zone* graph_zone_;
Graph* graph_;
CommonOperatorBuilder* common_;
MachineOperatorBuilder* machine_;
JSGraphAssembler* graph_assembler_;
......
......@@ -4,6 +4,7 @@
#include "src/compiler/memory-optimizer.h"
#include "src/base/logging.h"
#include "src/codegen/interface-descriptors.h"
#include "src/codegen/tick-counter.h"
#include "src/compiler/js-graph.h"
......@@ -321,8 +322,23 @@ void MemoryOptimizer::VisitLoadElement(Node* node,
void MemoryOptimizer::VisitLoadField(Node* node, AllocationState const* state) {
DCHECK_EQ(IrOpcode::kLoadField, node->opcode());
memory_lowering()->ReduceLoadField(node);
Reduction reduction = memory_lowering()->ReduceLoadField(node);
DCHECK(reduction.Changed());
// In case of replacement, the replacement graph should not require futher
// lowering, so we can proceed iterating the graph from the node uses.
EnqueueUses(node, state);
// Node can be replaced only when V8_HEAP_SANDBOX_BOOL is enabled and
// when loading an external pointer value.
DCHECK_IMPLIES(!V8_HEAP_SANDBOX_BOOL, reduction.replacement() == node);
if (V8_HEAP_SANDBOX_BOOL && reduction.replacement() != node) {
// Replace all uses of node and kill the node to make sure we don't leave
// dangling dead uses.
NodeProperties::ReplaceUses(node, reduction.replacement(),
graph_assembler_.effect(),
graph_assembler_.control());
node->Kill();
}
}
void MemoryOptimizer::VisitStoreElement(Node* node,
......
......@@ -963,7 +963,8 @@ class RepresentationSelector {
return MachineRepresentation::kFloat64;
} else if (type.Is(Type::BigInt()) && use.IsUsedAsWord64()) {
return MachineRepresentation::kWord64;
} else if (type.Is(Type::ExternalPointer())) {
} else if (type.Is(Type::ExternalPointer()) ||
type.Is(Type::SandboxedExternalPointer())) {
return MachineType::PointerRepresentation();
}
return MachineRepresentation::kTagged;
......
......@@ -112,7 +112,7 @@ namespace compiler {
V(Null, 1u << 7) \
V(Undefined, 1u << 8) \
V(Boolean, 1u << 9) \
V(Unsigned30, 1u << 10) \
V(Unsigned30, 1u << 10) \
V(MinusZero, 1u << 11) \
V(NaN, 1u << 12) \
V(Symbol, 1u << 13) \
......@@ -129,6 +129,9 @@ namespace compiler {
V(ExternalPointer, 1u << 25) \
V(Array, 1u << 26) \
V(BigInt, 1u << 27) \
/* TODO(v8:10391): Remove this type once all ExternalPointer usages are */ \
/* sandbox-ready. */ \
V(SandboxedExternalPointer, 1u << 28) \
\
V(Signed31, kUnsigned30 | kNegative31) \
V(Signed32, kSigned31 | kOtherUnsigned31 | \
......@@ -192,7 +195,8 @@ namespace compiler {
V(StringOrReceiver, kString | kReceiver) \
V(Unique, kBoolean | kUniqueName | kNull | \
kUndefined | kHole | kReceiver) \
V(Internal, kHole | kExternalPointer | kOtherInternal) \
V(Internal, kHole | kExternalPointer | \
kSandboxedExternalPointer | kOtherInternal) \
V(NonInternal, kPrimitive | kReceiver) \
V(NonBigInt, kNonBigIntPrimitive | kReceiver) \
V(NonNumber, kBigInt | kUnique | kString | kInternal) \
......
......@@ -134,6 +134,12 @@ struct MaybeBoolFlag {
#define COMPRESS_POINTERS_BOOL false
#endif
#ifdef V8_HEAP_SANDBOX
#define V8_HEAP_SANDBOX_BOOL true
#else
#define V8_HEAP_SANDBOX_BOOL false
#endif
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
#define ENABLE_CONTROL_FLOW_INTEGRITY_BOOL true
#else
......
......@@ -38,6 +38,7 @@ static const char* const UNINITIALIZED_TYPE_STRING = "Uninitialized";
static const char* const UNINITIALIZED_HEAP_OBJECT_TYPE_STRING =
"UninitializedHeapObject";
static const char* const RAWPTR_TYPE_STRING = "RawPtr";
static const char* const EXTERNALPTR_TYPE_STRING = "ExternalPointer";
static const char* const CONST_STRING_TYPE_STRING = "constexpr string";
static const char* const STRING_TYPE_STRING = "String";
static const char* const NUMBER_TYPE_STRING = "Number";
......
......@@ -27,7 +27,9 @@ TargetArchitecture::TargetArchitecture(bool force_32bit)
raw_ptr_size_(force_32bit ? sizeof(int32_t) : kSystemPointerSize),
smi_tag_and_shift_size_(
kSmiTagSize + (force_32bit ? SmiTagging<kApiInt32Size>::kSmiShiftSize
: kSmiShiftSize)) {}
: kSmiShiftSize)),
external_ptr_size_(force_32bit ? sizeof(int32_t) : kExternalPointerSize) {
}
} // namespace torque
} // namespace internal
......
......@@ -91,6 +91,7 @@ class TargetArchitecture : public ContextualClass<TargetArchitecture> {
static size_t TaggedSize() { return Get().tagged_size_; }
static size_t RawPtrSize() { return Get().raw_ptr_size_; }
static size_t ExternalPointerSize() { return Get().external_ptr_size_; }
static size_t MaxHeapAlignment() { return TaggedSize(); }
static bool ArePointersCompressed() { return TaggedSize() < RawPtrSize(); }
static int SmiTagAndShiftSize() { return Get().smi_tag_and_shift_size_; }
......@@ -99,6 +100,7 @@ class TargetArchitecture : public ContextualClass<TargetArchitecture> {
const size_t tagged_size_;
const size_t raw_ptr_size_;
const int smi_tag_and_shift_size_;
const size_t external_ptr_size_;
};
} // namespace torque
......
......@@ -182,6 +182,10 @@ class TypeOracle : public ContextualClass<TypeOracle> {
return Get().GetBuiltinType(RAWPTR_TYPE_STRING);
}
static const Type* GetExternalPointerType() {
return Get().GetBuiltinType(EXTERNALPTR_TYPE_STRING);
}
static const Type* GetMapType() {
return Get().GetBuiltinType(MAP_TYPE_STRING);
}
......
......@@ -815,6 +815,8 @@ size_t AbstractType::AlignmentLog2() const {
alignment = TargetArchitecture::TaggedSize();
} else if (this == TypeOracle::GetRawPtrType()) {
alignment = TargetArchitecture::RawPtrSize();
} else if (this == TypeOracle::GetExternalPointerType()) {
alignment = TargetArchitecture::ExternalPointerSize();
} else if (this == TypeOracle::GetVoidType()) {
alignment = 1;
} else if (this == TypeOracle::GetInt8Type()) {
......@@ -882,6 +884,9 @@ base::Optional<std::tuple<size_t, std::string>> SizeOf(const Type* type) {
} else if (type->IsSubtypeOf(TypeOracle::GetRawPtrType())) {
size = TargetArchitecture::RawPtrSize();
size_string = "kSystemPointerSize";
} else if (type->IsSubtypeOf(TypeOracle::GetExternalPointerType())) {
size = TargetArchitecture::ExternalPointerSize();
size_string = "kExternalPointerSize";
} else if (type->IsSubtypeOf(TypeOracle::GetVoidType())) {
size = 0;
size_string = "0";
......
......@@ -70,6 +70,8 @@ type bool generates 'TNode<BoolT>' constexpr 'bool';
type bint generates 'TNode<BInt>' constexpr 'BInt';
type string constexpr 'const char*';
type RawPtr generates 'TNode<RawPtrT>' constexpr 'void*';
type ExternalPointer
generates 'TNode<ExternalPointerT>' constexpr 'ExternalPointer_t';
type Code extends HeapObject generates 'TNode<Code>';
type BuiltinPtr extends Smi generates 'TNode<BuiltinPtr>';
type Context extends HeapObject generates 'TNode<Context>';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment