[arm64] Implement list of allowed return addresses in the deoptimizer
When CFI is enabled this adds a check against this list whenever a new return address must be set in a deoptimized frame, as a mitigation for ROP attacks. The list is known at linking time so that its content and the pointer to it can be stored in a read-only memory section. The check is performed in the signing function, which is no longer generic, as well as when setting the current pc of the frame. Since the pc is now only signed when setting the caller's pc, there is no need for ReplaceContext anymore. Bug: v8:10026 Change-Id: I5e85a62b94722051716fdeba476db383c702a318 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2287490 Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Commit-Queue: Rodolph Perfetta <rodolph.perfetta@arm.com> Cr-Commit-Position: refs/heads/master@{#69478}
This commit is contained in:
parent
ad8f2f6fd7
commit
929dd3748e
14
BUILD.gn
14
BUILD.gn
@ -342,6 +342,13 @@ if (v8_enable_shared_ro_heap && v8_enable_pointer_compression) {
|
||||
"Sharing read-only heap with pointer compression is only supported on Linux or Android")
|
||||
}
|
||||
|
||||
assert(!v8_use_multi_snapshots || !v8_control_flow_integrity,
|
||||
"Control-flow integrity does not support multisnapshots")
|
||||
|
||||
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")
|
||||
|
||||
@ -1668,6 +1675,9 @@ v8_source_set("v8_snapshot") {
|
||||
public = []
|
||||
|
||||
sources = [ "src/init/setup-isolate-deserialize.cc" ]
|
||||
if (v8_control_flow_integrity) {
|
||||
sources += [ "src/deoptimizer/deoptimizer-cfi-builtins.cc" ]
|
||||
}
|
||||
if (emit_builtins_as_inline_asm) {
|
||||
deps += [ ":asm_to_inline_asm_default" ]
|
||||
sources += [ "$target_gen_dir/embedded.cc" ]
|
||||
@ -4449,6 +4459,10 @@ if (current_toolchain == v8_snapshot_toolchain) {
|
||||
"src/snapshot/snapshot-empty.cc",
|
||||
]
|
||||
|
||||
if (v8_control_flow_integrity) {
|
||||
sources += [ "src/deoptimizer/deoptimizer-cfi-empty.cc" ]
|
||||
}
|
||||
|
||||
configs = [ ":internal_config" ]
|
||||
|
||||
deps = [
|
||||
|
@ -308,14 +308,9 @@ Float32 RegisterValues::GetFloatRegister(unsigned n) const {
|
||||
}
|
||||
|
||||
void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
|
||||
// TODO(v8:10026): check that the pointer is still in the list of allowed
|
||||
// builtins.
|
||||
Address new_context =
|
||||
static_cast<Address>(GetTop()) + offset + kPCOnStackSize;
|
||||
uint64_t old_context = GetTop() + GetFrameSize();
|
||||
PointerAuthentication::ReplaceContext(reinterpret_cast<Address*>(&value),
|
||||
old_context, new_context);
|
||||
|
||||
value = PointerAuthentication::SignAndCheckPC(value, new_context);
|
||||
SetFrameSlot(offset, value);
|
||||
}
|
||||
|
||||
@ -329,9 +324,11 @@ void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
|
||||
}
|
||||
|
||||
void FrameDescription::SetPc(intptr_t pc) {
|
||||
// TODO(v8:10026): we should only accept a specific list of allowed builtins
|
||||
// here.
|
||||
pc_ = PointerAuthentication::SignPCWithSP(pc, GetTop());
|
||||
if (ENABLE_CONTROL_FLOW_INTEGRITY_BOOL) {
|
||||
CHECK(
|
||||
Deoptimizer::IsValidReturnAddress(PointerAuthentication::StripPAC(pc)));
|
||||
}
|
||||
pc_ = pc;
|
||||
}
|
||||
|
||||
#undef __
|
||||
|
47
src/deoptimizer/deoptimizer-cfi-builtins.cc
Normal file
47
src/deoptimizer/deoptimizer-cfi-builtins.cc
Normal file
@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/deoptimizer/deoptimizer.h"
|
||||
|
||||
extern "C" {
|
||||
void Builtins_InterpreterEnterBytecodeAdvance();
|
||||
void Builtins_InterpreterEnterBytecodeDispatch();
|
||||
void Builtins_ContinueToCodeStubBuiltinWithResult();
|
||||
void Builtins_ContinueToCodeStubBuiltin();
|
||||
void Builtins_ContinueToJavaScriptBuiltinWithResult();
|
||||
void Builtins_ContinueToJavaScriptBuiltin();
|
||||
void arguments_adaptor_deopt_addr();
|
||||
void construct_stub_create_deopt_addr();
|
||||
void construct_stub_invoke_deopt_addr();
|
||||
typedef void (*function_ptr)();
|
||||
}
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// List of allowed builtin addresses that we can return to in the deoptimizer.
|
||||
constexpr function_ptr builtins[] = {
|
||||
&Builtins_InterpreterEnterBytecodeAdvance,
|
||||
&Builtins_InterpreterEnterBytecodeDispatch,
|
||||
&Builtins_ContinueToCodeStubBuiltinWithResult,
|
||||
&Builtins_ContinueToCodeStubBuiltin,
|
||||
&Builtins_ContinueToJavaScriptBuiltinWithResult,
|
||||
&Builtins_ContinueToJavaScriptBuiltin,
|
||||
&construct_stub_create_deopt_addr,
|
||||
&construct_stub_invoke_deopt_addr,
|
||||
&arguments_adaptor_deopt_addr,
|
||||
};
|
||||
|
||||
bool Deoptimizer::IsValidReturnAddress(Address address) {
|
||||
for (function_ptr builtin : builtins) {
|
||||
if (address == FUNCTION_ADDR(builtin)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
14
src/deoptimizer/deoptimizer-cfi-empty.cc
Normal file
14
src/deoptimizer/deoptimizer-cfi-empty.cc
Normal file
@ -0,0 +1,14 @@
|
||||
// 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.
|
||||
|
||||
#include "src/deoptimizer/deoptimizer.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// Dummy implementation when building mksnapshot.
|
||||
bool Deoptimizer::IsValidReturnAddress(Address address) { return false; }
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -62,7 +62,16 @@ class FrameWriter {
|
||||
}
|
||||
}
|
||||
|
||||
void PushCallerPc(intptr_t pc) {
|
||||
// There is no check against the allowed addresses for bottommost frames, as
|
||||
// the caller's pc could be anything. The caller's pc pushed here should never
|
||||
// be re-signed.
|
||||
void PushBottommostCallerPc(intptr_t pc) {
|
||||
top_offset_ -= kPCOnStackSize;
|
||||
frame_->SetFrameSlot(top_offset_, pc);
|
||||
DebugPrintOutputPc(pc, "bottommost caller's pc\n");
|
||||
}
|
||||
|
||||
void PushApprovedCallerPc(intptr_t pc) {
|
||||
top_offset_ -= kPCOnStackSize;
|
||||
frame_->SetCallerPc(top_offset_, pc);
|
||||
DebugPrintOutputPc(pc, "caller's pc\n");
|
||||
@ -111,6 +120,8 @@ class FrameWriter {
|
||||
|
||||
unsigned top_offset() const { return top_offset_; }
|
||||
|
||||
FrameDescription* frame() { return frame_; }
|
||||
|
||||
private:
|
||||
void PushValue(intptr_t value) {
|
||||
CHECK_GE(top_offset_, 0);
|
||||
@ -755,13 +766,6 @@ void Deoptimizer::DoComputeOutputFrames() {
|
||||
caller_fp_ = Memory<intptr_t>(fp_address);
|
||||
caller_pc_ =
|
||||
Memory<intptr_t>(fp_address + CommonFrameConstants::kCallerPCOffset);
|
||||
// Sign caller_pc_ with caller_frame_top_ to be consistent with everything
|
||||
// else here.
|
||||
uint64_t sp = stack_fp_ + StandardFrameConstants::kCallerSPOffset;
|
||||
// TODO(v8:10026): avoid replacing a signed pointer.
|
||||
PointerAuthentication::ReplaceContext(
|
||||
reinterpret_cast<Address*>(&caller_pc_), sp, caller_frame_top_);
|
||||
|
||||
input_frame_context_ = Memory<intptr_t>(
|
||||
fp_address + CommonFrameConstants::kContextOrFrameTypeOffset);
|
||||
|
||||
@ -970,9 +974,11 @@ void Deoptimizer::DoComputeInterpretedFrame(TranslatedFrame* translated_frame,
|
||||
// input frame. For all subsequent output frames, it can be read from the
|
||||
// previous one. This frame's pc can be computed from the non-optimized
|
||||
// function code and AST id of the bailout.
|
||||
const intptr_t caller_pc =
|
||||
is_bottommost ? caller_pc_ : output_[frame_index - 1]->GetPc();
|
||||
frame_writer.PushCallerPc(caller_pc);
|
||||
if (is_bottommost) {
|
||||
frame_writer.PushBottommostCallerPc(caller_pc_);
|
||||
} else {
|
||||
frame_writer.PushApprovedCallerPc(output_[frame_index - 1]->GetPc());
|
||||
}
|
||||
|
||||
// The caller's frame pointer for the bottommost output frame is the same
|
||||
// as in the input frame. For all subsequent output frames, it can be
|
||||
@ -1124,8 +1130,17 @@ void Deoptimizer::DoComputeInterpretedFrame(TranslatedFrame* translated_frame,
|
||||
!goto_catch_handler
|
||||
? builtins->builtin(Builtins::kInterpreterEnterBytecodeAdvance)
|
||||
: builtins->builtin(Builtins::kInterpreterEnterBytecodeDispatch);
|
||||
output_frame->SetPc(
|
||||
static_cast<intptr_t>(dispatch_builtin.InstructionStart()));
|
||||
if (is_topmost) {
|
||||
// Only the pc of the topmost frame needs to be signed since it is
|
||||
// authenticated at the end of GenerateDeoptimizationEntries.
|
||||
const intptr_t top_most_pc = PointerAuthentication::SignAndCheckPC(
|
||||
static_cast<intptr_t>(dispatch_builtin.InstructionStart()),
|
||||
frame_writer.frame()->GetTop());
|
||||
output_frame->SetPc(top_most_pc);
|
||||
} else {
|
||||
output_frame->SetPc(
|
||||
static_cast<intptr_t>(dispatch_builtin.InstructionStart()));
|
||||
}
|
||||
|
||||
// Update constant pool.
|
||||
if (FLAG_enable_embedded_constant_pool) {
|
||||
@ -1202,9 +1217,11 @@ void Deoptimizer::DoComputeArgumentsAdaptorFrame(
|
||||
frame_writer.top_offset());
|
||||
|
||||
// Read caller's PC from the previous frame.
|
||||
const intptr_t caller_pc =
|
||||
is_bottommost ? caller_pc_ : output_[frame_index - 1]->GetPc();
|
||||
frame_writer.PushCallerPc(caller_pc);
|
||||
if (is_bottommost) {
|
||||
frame_writer.PushBottommostCallerPc(caller_pc_);
|
||||
} else {
|
||||
frame_writer.PushApprovedCallerPc(output_[frame_index - 1]->GetPc());
|
||||
}
|
||||
|
||||
// Read caller's FP from the previous frame, and set this frame's FP.
|
||||
const intptr_t caller_fp =
|
||||
@ -1315,7 +1332,7 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
|
||||
|
||||
// Read caller's PC from the previous frame.
|
||||
const intptr_t caller_pc = output_[frame_index - 1]->GetPc();
|
||||
frame_writer.PushCallerPc(caller_pc);
|
||||
frame_writer.PushApprovedCallerPc(caller_pc);
|
||||
|
||||
// Read caller's FP from the previous frame, and set this frame's FP.
|
||||
const intptr_t caller_fp = output_[frame_index - 1]->GetFp();
|
||||
@ -1383,7 +1400,14 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
|
||||
? isolate_->heap()->construct_stub_create_deopt_pc_offset().value()
|
||||
: isolate_->heap()->construct_stub_invoke_deopt_pc_offset().value();
|
||||
intptr_t pc_value = static_cast<intptr_t>(start + pc_offset);
|
||||
output_frame->SetPc(pc_value);
|
||||
if (is_topmost) {
|
||||
// Only the pc of the topmost frame needs to be signed since it is
|
||||
// authenticated at the end of GenerateDeoptimizationEntries.
|
||||
output_frame->SetPc(PointerAuthentication::SignAndCheckPC(
|
||||
pc_value, frame_writer.frame()->GetTop()));
|
||||
} else {
|
||||
output_frame->SetPc(pc_value);
|
||||
}
|
||||
|
||||
// Update constant pool.
|
||||
if (FLAG_enable_embedded_constant_pool) {
|
||||
@ -1692,9 +1716,11 @@ void Deoptimizer::DoComputeBuiltinContinuation(
|
||||
output_frame->SetRegister(kContextRegister.code(), value);
|
||||
|
||||
// Set caller's PC (JSFunction continuation).
|
||||
const intptr_t caller_pc =
|
||||
is_bottommost ? caller_pc_ : output_[frame_index - 1]->GetPc();
|
||||
frame_writer.PushCallerPc(caller_pc);
|
||||
if (is_bottommost) {
|
||||
frame_writer.PushBottommostCallerPc(caller_pc_);
|
||||
} else {
|
||||
frame_writer.PushApprovedCallerPc(output_[frame_index - 1]->GetPc());
|
||||
}
|
||||
|
||||
// Read caller's FP from the previous frame, and set this frame's FP.
|
||||
const intptr_t caller_fp =
|
||||
@ -1804,8 +1830,17 @@ void Deoptimizer::DoComputeBuiltinContinuation(
|
||||
Code continue_to_builtin =
|
||||
isolate()->builtins()->builtin(TrampolineForBuiltinContinuation(
|
||||
mode, frame_info.frame_has_result_stack_slot()));
|
||||
output_frame->SetPc(
|
||||
static_cast<intptr_t>(continue_to_builtin.InstructionStart()));
|
||||
if (is_topmost) {
|
||||
// Only the pc of the topmost frame needs to be signed since it is
|
||||
// authenticated at the end of GenerateDeoptimizationEntries.
|
||||
const intptr_t top_most_pc = PointerAuthentication::SignAndCheckPC(
|
||||
static_cast<intptr_t>(continue_to_builtin.InstructionStart()),
|
||||
frame_writer.frame()->GetTop());
|
||||
output_frame->SetPc(top_most_pc);
|
||||
} else {
|
||||
output_frame->SetPc(
|
||||
static_cast<intptr_t>(continue_to_builtin.InstructionStart()));
|
||||
}
|
||||
|
||||
Code continuation =
|
||||
isolate()->builtins()->builtin(Builtins::kNotifyDeoptimized);
|
||||
|
@ -482,6 +482,13 @@ class Deoptimizer : public Malloced {
|
||||
// refer to that code.
|
||||
static void DeoptimizeMarkedCode(Isolate* isolate);
|
||||
|
||||
// Check the given address against a list of allowed addresses, to prevent a
|
||||
// potential attacker from using the frame creation process in the
|
||||
// deoptimizer, in particular the signing process, to gain control over the
|
||||
// program.
|
||||
// When building mksnapshot, always return false.
|
||||
static bool IsValidReturnAddress(Address address);
|
||||
|
||||
~Deoptimizer();
|
||||
|
||||
void MaterializeHeapObjects();
|
||||
|
@ -60,24 +60,6 @@ V8_INLINE Address PointerAuthentication::StripPAC(Address pc) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Sign {pc} using {sp}.
|
||||
V8_INLINE Address PointerAuthentication::SignPCWithSP(Address pc, Address sp) {
|
||||
#ifdef USE_SIMULATOR
|
||||
return Simulator::AddPAC(pc, sp, Simulator::kPACKeyIB,
|
||||
Simulator::kInstructionPointer);
|
||||
#else
|
||||
asm volatile(
|
||||
" mov x17, %[pc]\n"
|
||||
" mov x16, %[sp]\n"
|
||||
" pacib1716\n"
|
||||
" mov %[pc], x17\n"
|
||||
: [pc] "+r"(pc)
|
||||
: [sp] "r"(sp)
|
||||
: "x16", "x17");
|
||||
return pc;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Authenticate the address stored in {pc_address} and replace it with
|
||||
// {new_pc}, after signing it. {offset_from_sp} is the offset between
|
||||
// {pc_address} and the pointer used as a context for signing.
|
||||
@ -113,43 +95,27 @@ V8_INLINE void PointerAuthentication::ReplacePC(Address* pc_address,
|
||||
*pc_address = new_pc;
|
||||
}
|
||||
|
||||
// Authenticate the address stored in {pc_address} based on {old_context} and
|
||||
// replace it with the same address signed with {new_context} instead.
|
||||
V8_INLINE void PointerAuthentication::ReplaceContext(Address* pc_address,
|
||||
Address old_context,
|
||||
Address new_context) {
|
||||
uint64_t old_signed_pc = static_cast<uint64_t>(*pc_address);
|
||||
uint64_t new_pc;
|
||||
|
||||
// Sign {pc} using {sp}.
|
||||
V8_INLINE Address PointerAuthentication::SignAndCheckPC(Address pc,
|
||||
Address sp) {
|
||||
#ifdef USE_SIMULATOR
|
||||
uint64_t auth_pc =
|
||||
Simulator::AuthPAC(old_signed_pc, old_context, Simulator::kPACKeyIB,
|
||||
Simulator::kInstructionPointer);
|
||||
uint64_t raw_pc =
|
||||
Simulator::StripPAC(auth_pc, Simulator::kInstructionPointer);
|
||||
// Verify that the old address is authenticated.
|
||||
CHECK_EQ(raw_pc, auth_pc);
|
||||
new_pc = Simulator::AddPAC(raw_pc, new_context, Simulator::kPACKeyIB,
|
||||
Simulator::kInstructionPointer);
|
||||
pc = Simulator::AddPAC(pc, sp, Simulator::kPACKeyIB,
|
||||
Simulator::kInstructionPointer);
|
||||
CHECK(Deoptimizer::IsValidReturnAddress(PointerAuthentication::StripPAC(pc)));
|
||||
return pc;
|
||||
#else
|
||||
// Only store newly signed address after we have verified that the old
|
||||
// address is authenticated.
|
||||
asm volatile(
|
||||
" mov x17, %[old_pc]\n"
|
||||
" mov x16, %[old_ctx]\n"
|
||||
" autib1716\n"
|
||||
" mov x16, %[new_ctx]\n"
|
||||
" pacib1716\n"
|
||||
" mov %[new_pc], x17\n"
|
||||
" mov x17, %[old_pc]\n"
|
||||
" mov x16, %[old_ctx]\n"
|
||||
" autib1716\n"
|
||||
" ldr xzr, [x17]\n"
|
||||
: [new_pc] "=&r"(new_pc)
|
||||
: [old_pc] "r"(old_signed_pc), [old_ctx] "r"(old_context),
|
||||
[new_ctx] "r"(new_context)
|
||||
: "x16", "x17");
|
||||
" mov x17, %[pc]\n"
|
||||
" mov x16, %[sp]\n"
|
||||
" pacib1716\n"
|
||||
" mov %[pc], x17\n"
|
||||
: [pc] "+r"(pc)
|
||||
: [sp] "r"(sp)
|
||||
: "x16", "x17");
|
||||
CHECK(Deoptimizer::IsValidReturnAddress(PointerAuthentication::StripPAC(pc)));
|
||||
return pc;
|
||||
#endif
|
||||
*pc_address = new_pc;
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
@ -3324,6 +3324,8 @@ void Isolate::CreateAndSetEmbeddedBlob() {
|
||||
|
||||
PrepareBuiltinSourcePositionMap();
|
||||
|
||||
PrepareBuiltinLabelInfoMap();
|
||||
|
||||
// If a sticky blob has been set, we reuse it.
|
||||
if (StickyEmbeddedBlobCode() != nullptr) {
|
||||
CHECK_EQ(embedded_blob_code(), StickyEmbeddedBlobCode());
|
||||
@ -4198,6 +4200,15 @@ void Isolate::PrepareBuiltinSourcePositionMap() {
|
||||
}
|
||||
}
|
||||
|
||||
void Isolate::PrepareBuiltinLabelInfoMap() {
|
||||
if (embedded_file_writer_ != nullptr) {
|
||||
embedded_file_writer_->PrepareBuiltinLabelInfoMap(
|
||||
heap()->construct_stub_create_deopt_pc_offset().value(),
|
||||
heap()->construct_stub_invoke_deopt_pc_offset().value(),
|
||||
heap()->arguments_adaptor_deopt_pc_offset().value());
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(V8_OS_WIN64)
|
||||
void Isolate::SetBuiltinUnwindData(
|
||||
int builtin_index,
|
||||
|
@ -1466,6 +1466,10 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
|
||||
// annotate the builtin blob with debugging information.
|
||||
void PrepareBuiltinSourcePositionMap();
|
||||
|
||||
// Store the position of the labels that will be used in the list of allowed
|
||||
// return addresses.
|
||||
void PrepareBuiltinLabelInfoMap();
|
||||
|
||||
#if defined(V8_OS_WIN64)
|
||||
void SetBuiltinUnwindData(
|
||||
int builtin_index,
|
||||
|
@ -27,12 +27,6 @@ V8_INLINE Address PointerAuthentication::AuthenticatePC(
|
||||
// Return {pc} unmodified.
|
||||
V8_INLINE Address PointerAuthentication::StripPAC(Address pc) { return pc; }
|
||||
|
||||
// Return {pc} unmodified.
|
||||
V8_INLINE Address PointerAuthentication::SignPCWithSP(Address pc, Address sp) {
|
||||
USE(sp);
|
||||
return pc;
|
||||
}
|
||||
|
||||
// Store {new_pc} to {pc_address} without signing.
|
||||
V8_INLINE void PointerAuthentication::ReplacePC(Address* pc_address,
|
||||
Address new_pc,
|
||||
@ -41,13 +35,11 @@ V8_INLINE void PointerAuthentication::ReplacePC(Address* pc_address,
|
||||
*pc_address = new_pc;
|
||||
}
|
||||
|
||||
// Do nothing.
|
||||
V8_INLINE void PointerAuthentication::ReplaceContext(Address* pc_address,
|
||||
Address old_context,
|
||||
Address new_context) {
|
||||
USE(pc_address);
|
||||
USE(old_context);
|
||||
USE(new_context);
|
||||
// Return {pc} unmodified.
|
||||
V8_INLINE Address PointerAuthentication::SignAndCheckPC(Address pc,
|
||||
Address sp) {
|
||||
USE(sp);
|
||||
return pc;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "include/v8.h"
|
||||
#include "src/base/macros.h"
|
||||
#include "src/common/globals.h"
|
||||
#include "src/deoptimizer/deoptimizer.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -27,10 +28,6 @@ class PointerAuthentication : public AllStatic {
|
||||
// When CFI is not enabled, return {pc} unmodified.
|
||||
V8_INLINE static Address StripPAC(Address pc);
|
||||
|
||||
// When CFI is enabled, sign {pc} using {sp} and return the signed value.
|
||||
// When CFI is not enabled, return {pc} unmodified.
|
||||
V8_INLINE static Address SignPCWithSP(Address pc, Address sp);
|
||||
|
||||
// When CFI is enabled, authenticate the address stored in {pc_address} and
|
||||
// replace it with {new_pc}, after signing it. {offset_from_sp} is the offset
|
||||
// between {pc_address} and the pointer used as a context for signing.
|
||||
@ -38,12 +35,10 @@ class PointerAuthentication : public AllStatic {
|
||||
V8_INLINE static void ReplacePC(Address* pc_address, Address new_pc,
|
||||
int offset_from_sp);
|
||||
|
||||
// When CFI is enabled, authenticate the address stored in {pc_address} based
|
||||
// on {old_context} and replace it with the same address signed with
|
||||
// {new_context} instead.
|
||||
// When CFI is not enabled, do nothing.
|
||||
V8_INLINE static void ReplaceContext(Address* pc_address, Address old_context,
|
||||
Address new_context);
|
||||
// When CFI is enabled, sign {pc} using {sp}, check the address and return the
|
||||
// signed value. When CFI is not enabled, return {pc} unmodified. This method
|
||||
// only applies in the deoptimizer.
|
||||
V8_INLINE static Address SignAndCheckPC(Address pc, Address sp);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -4,9 +4,11 @@
|
||||
|
||||
#include "src/snapshot/embedded/embedded-file-writer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "src/codegen/source-position-table.h"
|
||||
#include "src/flags/flags.h" // For ENABLE_CONTROL_FLOW_INTEGRITY_BOOL
|
||||
#include "src/objects/code-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -34,7 +36,6 @@ void EmbeddedFileWriter::WriteBuiltin(PlatformEmbeddedFileWriterBase* w,
|
||||
w->DeclareFunctionBegin(builtin_symbol.begin(),
|
||||
blob->InstructionSizeOfBuiltin(builtin_id));
|
||||
const std::vector<byte>& current_positions = source_positions_[builtin_id];
|
||||
|
||||
// The code below interleaves bytes of assembly code for the builtin
|
||||
// function with source positions at the appropriate offsets.
|
||||
Vector<const byte> vpos(current_positions.data(), current_positions.size());
|
||||
@ -45,24 +46,42 @@ void EmbeddedFileWriter::WriteBuiltin(PlatformEmbeddedFileWriterBase* w,
|
||||
CHECK(positions.done()); // Release builds must not contain debug infos.
|
||||
#endif
|
||||
|
||||
// Some builtins (ArgumentsAdaptorTrampoline and JSConstructStubGeneric) have
|
||||
// entry points located in the middle of them, we need to store their
|
||||
// addresses since they are part of the list of allowed return addresses in
|
||||
// the deoptimizer.
|
||||
const std::vector<LabelInfo>& current_labels = label_info_[builtin_id];
|
||||
auto label = current_labels.begin();
|
||||
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(
|
||||
blob->InstructionStartOfBuiltin(builtin_id));
|
||||
uint32_t size = blob->PaddedInstructionSizeOfBuiltin(builtin_id);
|
||||
uint32_t i = 0;
|
||||
uint32_t next_offset =
|
||||
uint32_t next_source_pos_offset =
|
||||
static_cast<uint32_t>(positions.done() ? size : positions.code_offset());
|
||||
uint32_t next_label_offset = static_cast<uint32_t>(
|
||||
(label == current_labels.end()) ? size : label->offset);
|
||||
uint32_t next_offset = 0;
|
||||
while (i < size) {
|
||||
if (i == next_offset) {
|
||||
if (i == next_source_pos_offset) {
|
||||
// Write source directive.
|
||||
w->SourceInfo(positions.source_position().ExternalFileId(),
|
||||
GetExternallyCompiledFilename(
|
||||
positions.source_position().ExternalFileId()),
|
||||
positions.source_position().ExternalLine());
|
||||
positions.Advance();
|
||||
next_offset = static_cast<uint32_t>(
|
||||
next_source_pos_offset = static_cast<uint32_t>(
|
||||
positions.done() ? size : positions.code_offset());
|
||||
CHECK_GE(next_source_pos_offset, i);
|
||||
}
|
||||
CHECK_GE(next_offset, i);
|
||||
if (i == next_label_offset) {
|
||||
WriteBuiltinLabels(w, label->name);
|
||||
label++;
|
||||
next_label_offset = static_cast<uint32_t>(
|
||||
(label == current_labels.end()) ? size : label->offset);
|
||||
CHECK_GE(next_label_offset, i);
|
||||
}
|
||||
next_offset = std::min(next_source_pos_offset, next_label_offset);
|
||||
WriteBinaryContentsAsInlineAssembly(w, data + i, next_offset - i);
|
||||
i = next_offset;
|
||||
}
|
||||
@ -70,6 +89,15 @@ void EmbeddedFileWriter::WriteBuiltin(PlatformEmbeddedFileWriterBase* w,
|
||||
w->DeclareFunctionEnd(builtin_symbol.begin());
|
||||
}
|
||||
|
||||
void EmbeddedFileWriter::WriteBuiltinLabels(PlatformEmbeddedFileWriterBase* w,
|
||||
std::string name) const {
|
||||
if (ENABLE_CONTROL_FLOW_INTEGRITY_BOOL) {
|
||||
w->DeclareSymbolGlobal(name.c_str());
|
||||
}
|
||||
|
||||
w->DeclareLabel(name.c_str());
|
||||
}
|
||||
|
||||
void EmbeddedFileWriter::WriteFileEpilogue(PlatformEmbeddedFileWriterBase* w,
|
||||
const i::EmbeddedData* blob) const {
|
||||
{
|
||||
@ -237,5 +265,15 @@ void EmbeddedFileWriter::PrepareBuiltinSourcePositionMap(Builtins* builtins) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedFileWriter::PrepareBuiltinLabelInfoMap(
|
||||
int create_offset, int invoke_offset, int arguments_adaptor_offset) {
|
||||
label_info_[Builtins::kJSConstructStubGeneric].push_back(
|
||||
{create_offset, "construct_stub_create_deopt_addr"});
|
||||
label_info_[Builtins::kJSConstructStubGeneric].push_back(
|
||||
{invoke_offset, "construct_stub_invoke_deopt_addr"});
|
||||
label_info_[Builtins::kArgumentsAdaptorTrampoline].push_back(
|
||||
{arguments_adaptor_offset, "arguments_adaptor_deopt_addr"});
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -23,6 +23,11 @@ namespace internal {
|
||||
|
||||
static constexpr char kDefaultEmbeddedVariant[] = "Default";
|
||||
|
||||
struct LabelInfo {
|
||||
int offset;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// Detailed source-code information about builtins can only be obtained by
|
||||
// registration on the isolate during compilation.
|
||||
class EmbeddedFileWriterInterface {
|
||||
@ -36,6 +41,9 @@ class EmbeddedFileWriterInterface {
|
||||
// compiled builtin Code objects with trampolines.
|
||||
virtual void PrepareBuiltinSourcePositionMap(Builtins* builtins) = 0;
|
||||
|
||||
virtual void PrepareBuiltinLabelInfoMap(int create_offset, int invoke_offset,
|
||||
int arguments_adaptor_offset) = 0;
|
||||
|
||||
#if defined(V8_OS_WIN64)
|
||||
virtual void SetBuiltinUnwindData(
|
||||
int builtin_index,
|
||||
@ -60,6 +68,9 @@ class EmbeddedFileWriter : public EmbeddedFileWriterInterface {
|
||||
|
||||
void PrepareBuiltinSourcePositionMap(Builtins* builtins) override;
|
||||
|
||||
void PrepareBuiltinLabelInfoMap(int create_offset, int invoke_create,
|
||||
int arguments_adaptor_offset) override;
|
||||
|
||||
#if defined(V8_OS_WIN64)
|
||||
void SetBuiltinUnwindData(
|
||||
int builtin_index,
|
||||
@ -172,6 +183,9 @@ class EmbeddedFileWriter : public EmbeddedFileWriterInterface {
|
||||
void WriteBuiltin(PlatformEmbeddedFileWriterBase* w,
|
||||
const i::EmbeddedData* blob, const int builtin_id) const;
|
||||
|
||||
void WriteBuiltinLabels(PlatformEmbeddedFileWriterBase* w,
|
||||
std::string name) const;
|
||||
|
||||
void WriteInstructionStreams(PlatformEmbeddedFileWriterBase* w,
|
||||
const i::EmbeddedData* blob) const {
|
||||
w->Comment("The embedded blob data starts here. It contains the builtin");
|
||||
@ -210,6 +224,7 @@ class EmbeddedFileWriter : public EmbeddedFileWriterInterface {
|
||||
|
||||
private:
|
||||
std::vector<byte> source_positions_[Builtins::builtin_count];
|
||||
std::vector<LabelInfo> label_info_[Builtins::builtin_count];
|
||||
|
||||
#if defined(V8_OS_WIN64)
|
||||
win64_unwindinfo::BuiltinUnwindInfo unwind_infos_[Builtins::builtin_count];
|
||||
|
@ -73,7 +73,6 @@ void PlatformEmbeddedFileWriterAIX::Comment(const char* string) {
|
||||
}
|
||||
|
||||
void PlatformEmbeddedFileWriterAIX::DeclareLabel(const char* name) {
|
||||
DeclareSymbolGlobal(name);
|
||||
fprintf(fp_, "%s:\n", name);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ class PlatformEmbeddedFileWriterAIX : public PlatformEmbeddedFileWriterBase {
|
||||
void DeclareUint32(const char* name, uint32_t value) override;
|
||||
void DeclarePointerToSymbol(const char* name, const char* target) override;
|
||||
|
||||
void DeclareSymbolGlobal(const char* name) override;
|
||||
void DeclareLabel(const char* name) override;
|
||||
|
||||
void SourceInfo(int fileid, const char* filename, int line) override;
|
||||
@ -47,9 +48,6 @@ class PlatformEmbeddedFileWriterAIX : public PlatformEmbeddedFileWriterBase {
|
||||
|
||||
DataDirective ByteChunkDataDirective() const override;
|
||||
|
||||
private:
|
||||
void DeclareSymbolGlobal(const char* name);
|
||||
|
||||
private:
|
||||
const EmbeddedTargetArch target_arch_;
|
||||
const EmbeddedTargetOs target_os_;
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include <cstdio> // For FILE.
|
||||
#include <memory>
|
||||
|
||||
#include "src/flags/flags.h" // For ENABLE_CONTROL_FLOW_INTEGRITY_BOOL
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
@ -60,6 +62,7 @@ class PlatformEmbeddedFileWriterBase {
|
||||
virtual void DeclareUint32(const char* name, uint32_t value) = 0;
|
||||
virtual void DeclarePointerToSymbol(const char* name, const char* target) = 0;
|
||||
|
||||
virtual void DeclareSymbolGlobal(const char* name) = 0;
|
||||
virtual void DeclareLabel(const char* name) = 0;
|
||||
|
||||
virtual void SourceInfo(int fileid, const char* filename, int line) = 0;
|
||||
|
@ -67,6 +67,9 @@ void PlatformEmbeddedFileWriterGeneric::DeclarePointerToSymbol(
|
||||
|
||||
void PlatformEmbeddedFileWriterGeneric::DeclareSymbolGlobal(const char* name) {
|
||||
fprintf(fp_, ".global %s%s\n", SYMBOL_PREFIX, name);
|
||||
// These symbols are not visible outside of the final binary, this allows for
|
||||
// reduced binary size, and less work for the dynamic linker.
|
||||
fprintf(fp_, ".hidden %s\n", name);
|
||||
}
|
||||
|
||||
void PlatformEmbeddedFileWriterGeneric::AlignToCodeAlignment() {
|
||||
@ -97,6 +100,10 @@ void PlatformEmbeddedFileWriterGeneric::SourceInfo(int fileid,
|
||||
|
||||
void PlatformEmbeddedFileWriterGeneric::DeclareFunctionBegin(const char* name,
|
||||
uint32_t size) {
|
||||
if (ENABLE_CONTROL_FLOW_INTEGRITY_BOOL) {
|
||||
DeclareSymbolGlobal(name);
|
||||
}
|
||||
|
||||
DeclareLabel(name);
|
||||
|
||||
if (target_arch_ == EmbeddedTargetArch::kArm ||
|
||||
|
@ -33,6 +33,7 @@ class PlatformEmbeddedFileWriterGeneric
|
||||
void DeclareUint32(const char* name, uint32_t value) override;
|
||||
void DeclarePointerToSymbol(const char* name, const char* target) override;
|
||||
|
||||
void DeclareSymbolGlobal(const char* name) override;
|
||||
void DeclareLabel(const char* name) override;
|
||||
|
||||
void SourceInfo(int fileid, const char* filename, int line) override;
|
||||
@ -49,9 +50,6 @@ class PlatformEmbeddedFileWriterGeneric
|
||||
|
||||
DataDirective ByteChunkDataDirective() const override;
|
||||
|
||||
private:
|
||||
void DeclareSymbolGlobal(const char* name);
|
||||
|
||||
private:
|
||||
const EmbeddedTargetArch target_arch_;
|
||||
const EmbeddedTargetOs target_os_;
|
||||
|
@ -81,6 +81,10 @@ void PlatformEmbeddedFileWriterMac::SourceInfo(int fileid, const char* filename,
|
||||
// TODO(mmarchini): investigate emitting size annotations for OS X
|
||||
void PlatformEmbeddedFileWriterMac::DeclareFunctionBegin(const char* name,
|
||||
uint32_t size) {
|
||||
if (ENABLE_CONTROL_FLOW_INTEGRITY_BOOL) {
|
||||
DeclareSymbolGlobal(name);
|
||||
}
|
||||
|
||||
DeclareLabel(name);
|
||||
|
||||
// TODO(mvstanton): Investigate the proper incantations to mark the label as
|
||||
|
@ -31,6 +31,7 @@ class PlatformEmbeddedFileWriterMac : public PlatformEmbeddedFileWriterBase {
|
||||
void DeclareUint32(const char* name, uint32_t value) override;
|
||||
void DeclarePointerToSymbol(const char* name, const char* target) override;
|
||||
|
||||
void DeclareSymbolGlobal(const char* name) override;
|
||||
void DeclareLabel(const char* name) override;
|
||||
|
||||
void SourceInfo(int fileid, const char* filename, int line) override;
|
||||
@ -45,9 +46,6 @@ class PlatformEmbeddedFileWriterMac : public PlatformEmbeddedFileWriterBase {
|
||||
|
||||
int IndentedDataDirective(DataDirective directive) override;
|
||||
|
||||
private:
|
||||
void DeclareSymbolGlobal(const char* name);
|
||||
|
||||
private:
|
||||
const EmbeddedTargetArch target_arch_;
|
||||
const EmbeddedTargetOs target_os_;
|
||||
|
@ -502,6 +502,10 @@ void PlatformEmbeddedFileWriterWin::SourceInfo(int fileid, const char* filename,
|
||||
// TODO(mmarchini): investigate emitting size annotations for Windows
|
||||
void PlatformEmbeddedFileWriterWin::DeclareFunctionBegin(const char* name,
|
||||
uint32_t size) {
|
||||
if (ENABLE_CONTROL_FLOW_INTEGRITY_BOOL) {
|
||||
DeclareSymbolGlobal(name);
|
||||
}
|
||||
|
||||
if (target_arch_ == EmbeddedTargetArch::kArm64) {
|
||||
fprintf(fp_, "%s%s FUNCTION\n", SYMBOL_PREFIX, name);
|
||||
|
||||
|
@ -30,6 +30,7 @@ class PlatformEmbeddedFileWriterWin : public PlatformEmbeddedFileWriterBase {
|
||||
void DeclareUint32(const char* name, uint32_t value) override;
|
||||
void DeclarePointerToSymbol(const char* name, const char* target) override;
|
||||
|
||||
void DeclareSymbolGlobal(const char* name) override;
|
||||
void DeclareLabel(const char* name) override;
|
||||
|
||||
void SourceInfo(int fileid, const char* filename, int line) override;
|
||||
@ -65,7 +66,6 @@ class PlatformEmbeddedFileWriterWin : public PlatformEmbeddedFileWriterBase {
|
||||
const void* unwind_infos) override;
|
||||
|
||||
private:
|
||||
void DeclareSymbolGlobal(const char* name);
|
||||
const char* DirectiveAsString(DataDirective directive);
|
||||
|
||||
private:
|
||||
|
@ -3,7 +3,6 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "include/v8.h"
|
||||
|
||||
#include "src/api/api-inl.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/execution/isolate.h"
|
||||
@ -40,8 +39,25 @@ TEST(Unwind_BadState_Fail) {
|
||||
}
|
||||
|
||||
void StorePc(uintptr_t stack[], int index, uintptr_t pc) {
|
||||
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
|
||||
Address sp = reinterpret_cast<Address>(&stack[index]) + kSystemPointerSize;
|
||||
stack[index] = PointerAuthentication::SignPCWithSP(pc, sp);
|
||||
#ifdef USE_SIMULATOR
|
||||
stack[index] = Simulator::AddPAC(pc, sp, Simulator::kPACKeyIB,
|
||||
Simulator::kInstructionPointer);
|
||||
#else
|
||||
asm volatile(
|
||||
" mov x17, %[pc]\n"
|
||||
" mov x16, %[sp]\n"
|
||||
" pacib1716\n"
|
||||
" mov %[pc], x17\n"
|
||||
: [pc] "+r"(pc)
|
||||
: [sp] "r"(sp)
|
||||
: "x16", "x17");
|
||||
stack[index] = pc;
|
||||
#endif // USE_SIMULATOR
|
||||
#else
|
||||
stack[index] = pc;
|
||||
#endif // V8_ENABLE_CONTROL_FLOW_INTEGRITY
|
||||
}
|
||||
|
||||
TEST(Unwind_BuiltinPCInMiddle_Success) {
|
||||
|
Loading…
Reference in New Issue
Block a user