[arm64] Use B instruction key for return address signing

The C++ code uses the A instruction key for return address signing,
which is the default for Clang and GCC when the -mbranch-protection
option is used (although this can be configured to use the B key).

Using the B key for JS means that it's not possible to use an A key
signing gadget to replace a return address signed with the B key and
vice-versa. This should offer a degree of separation from the C++ side.

Bug: v8:10026
Change-Id: Ia9dcc7ae7096c96b4a271efbe25fc02940f6fc8e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2242953
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Commit-Queue: Georgia Kouveli <georgia.kouveli@arm.com>
Cr-Commit-Position: refs/heads/master@{#68360}
This commit is contained in:
Georgia Kouveli 2020-06-12 16:51:31 +01:00 committed by Commit Bot
parent 637f04f66a
commit c65c1c10a5
14 changed files with 105 additions and 93 deletions

View File

@ -1162,10 +1162,10 @@ void Assembler::cls(const Register& rd, const Register& rn) {
DataProcessing1Source(rd, rn, CLS);
}
void Assembler::pacia1716() { Emit(PACIA1716); }
void Assembler::autia1716() { Emit(AUTIA1716); }
void Assembler::paciasp() { Emit(PACIASP); }
void Assembler::autiasp() { Emit(AUTIASP); }
void Assembler::pacib1716() { Emit(PACIB1716); }
void Assembler::autib1716() { Emit(AUTIB1716); }
void Assembler::pacibsp() { Emit(PACIBSP); }
void Assembler::autibsp() { Emit(AUTIBSP); }
void Assembler::bti(BranchTargetIdentifier id) {
SystemHint op;
@ -1183,9 +1183,9 @@ void Assembler::bti(BranchTargetIdentifier id) {
op = BTI_jc;
break;
case BranchTargetIdentifier::kNone:
case BranchTargetIdentifier::kPaciasp:
case BranchTargetIdentifier::kPacibsp:
// We always want to generate a BTI instruction here, so disallow
// skipping its generation or generating a PACIASP instead.
// skipping its generation or generating a PACIBSP instead.
UNREACHABLE();
}
hint(op);

View File

@ -780,21 +780,21 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
void clz(const Register& rd, const Register& rn);
void cls(const Register& rd, const Register& rn);
// Pointer Authentication Code for Instruction address, using key A, with
// Pointer Authentication Code for Instruction address, using key B, with
// address in x17 and modifier in x16 [Armv8.3].
void pacia1716();
void pacib1716();
// Pointer Authentication Code for Instruction address, using key A, with
// Pointer Authentication Code for Instruction address, using key B, with
// address in LR and modifier in SP [Armv8.3].
void paciasp();
void pacibsp();
// Authenticate Instruction address, using key A, with address in x17 and
// Authenticate Instruction address, using key B, with address in x17 and
// modifier in x16 [Armv8.3].
void autia1716();
void autib1716();
// Authenticate Instruction address, using key A, with address in LR and
// Authenticate Instruction address, using key B, with address in LR and
// modifier in SP [Armv8.3].
void autiasp();
void autibsp();
// Memory instructions.

View File

@ -412,9 +412,9 @@ enum class BranchTargetIdentifier {
// Emit a "BTI jc" instruction, which is a combination of "BTI j" and "BTI c".
kBtiJumpCall,
// Emit a PACIASP instruction, which acts like a "BTI c" or a "BTI jc", based
// on the value of SCTLR_EL1.BT0.
kPaciasp
// Emit a PACIBSP instruction, which acts like a "BTI c" or a "BTI jc",
// based on the value of SCTLR_EL1.BT0.
kPacibsp
};
enum BarrierDomain {
@ -793,10 +793,10 @@ enum SystemPAuthOp : uint32_t {
SystemPAuthFixed = 0xD503211F,
SystemPAuthFMask = 0xFFFFFD1F,
SystemPAuthMask = 0xFFFFFFFF,
PACIA1716 = SystemPAuthFixed | 0x00000100,
AUTIA1716 = SystemPAuthFixed | 0x00000180,
PACIASP = SystemPAuthFixed | 0x00000320,
AUTIASP = SystemPAuthFixed | 0x000003A0
PACIB1716 = SystemPAuthFixed | 0x00000140,
AUTIB1716 = SystemPAuthFixed | 0x000001C0,
PACIBSP = SystemPAuthFixed | 0x00000360,
AUTIBSP = SystemPAuthFixed | 0x000003E0
};
// Any load or store (including pair).

View File

@ -318,8 +318,8 @@ void TurboAssembler::Bind(Label* label, BranchTargetIdentifier id) {
// instructions between the bind and the target identifier instruction.
InstructionAccurateScope scope(this, 1);
bind(label);
if (id == BranchTargetIdentifier::kPaciasp) {
paciasp();
if (id == BranchTargetIdentifier::kPacibsp) {
pacibsp();
} else {
bti(id);
}
@ -1136,7 +1136,7 @@ void TurboAssembler::Push(const CPURegister& src0, const CPURegister& src1,
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
if (lr_mode == kSignLR) {
Paciasp();
Pacibsp();
}
#endif
@ -1153,7 +1153,7 @@ void TurboAssembler::Push(const Register& src0, const VRegister& src1) {
DCHECK_IMPLIES((lr_mode == kDontStoreLR), ((src0 != lr) && (src1 != lr)));
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
if (lr_mode == kSignLR) {
Paciasp();
Pacibsp();
}
#endif
@ -1188,7 +1188,7 @@ void TurboAssembler::Pop(const CPURegister& dst0, const CPURegister& dst1,
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
if (lr_mode == kAuthLR) {
Autiasp();
Autibsp();
}
#endif
}
@ -1199,7 +1199,7 @@ void TurboAssembler::Poke(const CPURegister& src, const Operand& offset) {
DCHECK_IMPLIES((lr_mode == kDontStoreLR), (src != lr));
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
if (lr_mode == kSignLR) {
Paciasp();
Pacibsp();
}
#endif
@ -1228,7 +1228,7 @@ void TurboAssembler::Peek(const CPURegister& dst, const Operand& offset) {
DCHECK_IMPLIES((lr_mode == kDontLoadLR), (dst != lr));
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
if (lr_mode == kAuthLR) {
Autiasp();
Autibsp();
}
#endif
}
@ -1238,7 +1238,7 @@ void TurboAssembler::PushCPURegList(CPURegList registers) {
DCHECK_IMPLIES((lr_mode == kDontStoreLR), !registers.IncludesAliasOf(lr));
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
if (lr_mode == kSignLR && registers.IncludesAliasOf(lr)) {
Paciasp();
Pacibsp();
}
#endif
@ -1280,7 +1280,7 @@ void TurboAssembler::PopCPURegList(CPURegList registers) {
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
if (lr_mode == kAuthLR && contains_lr) {
Autiasp();
Autibsp();
}
#endif
}

View File

@ -1197,7 +1197,7 @@ void MacroAssembler::PeekPair(const CPURegister& dst1, const CPURegister& dst2,
void MacroAssembler::PushCalleeSavedRegisters() {
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
Paciasp();
Pacibsp();
#endif
{
@ -1249,7 +1249,7 @@ void MacroAssembler::PopCalleeSavedRegisters() {
}
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
Autiasp();
Autibsp();
#endif
}
@ -1971,7 +1971,7 @@ void TurboAssembler::StoreReturnAddressAndCall(Register target) {
Adr(x17, &return_location);
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
Add(x16, sp, kSystemPointerSize);
Pacia1716();
Pacib1716();
#endif
Poke(x17, 0);
@ -3248,7 +3248,7 @@ void TurboAssembler::RestoreFPAndLR() {
// We can load the return address directly into x17.
Add(x16, fp, StandardFrameConstants::kCallerSPOffset);
Ldp(fp, x17, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
Autia1716();
Autib1716();
Mov(lr, x17);
#else
Ldp(fp, lr, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
@ -3261,7 +3261,7 @@ void TurboAssembler::StoreReturnAddressInWasmExitFrame(Label* return_location) {
Adr(x17, return_location);
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
Add(x16, fp, WasmExitFrameConstants::kCallingPCOffset + kSystemPointerSize);
Pacia1716();
Pacib1716();
#endif
Str(x17, MemOperand(fp, WasmExitFrameConstants::kCallingPCOffset));
}

View File

@ -503,13 +503,13 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
void Cbnz(const Register& rt, Label* label);
void Cbz(const Register& rt, Label* label);
void Paciasp() {
void Pacibsp() {
DCHECK(allow_macro_instructions_);
paciasp();
pacibsp();
}
void Autiasp() {
void Autibsp() {
DCHECK(allow_macro_instructions_);
autiasp();
autibsp();
}
// The 1716 pac and aut instructions encourage people to use x16 and x17
@ -519,7 +519,7 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
// Register temp = temps.AcquireX(); // temp will be x16
// __ Mov(x17, ptr);
// __ Mov(x16, modifier); // Will override temp!
// __ Pacia1716();
// __ Pacib1716();
//
// To work around this issue, you must exclude x16 and x17 from the scratch
// register list. You may need to replace them with other registers:
@ -529,18 +529,18 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
// temps.Include(x10, x11);
// __ Mov(x17, ptr);
// __ Mov(x16, modifier);
// __ Pacia1716();
void Pacia1716() {
// __ Pacib1716();
void Pacib1716() {
DCHECK(allow_macro_instructions_);
DCHECK(!TmpList()->IncludesAliasOf(x16));
DCHECK(!TmpList()->IncludesAliasOf(x17));
pacia1716();
pacib1716();
}
void Autia1716() {
void Autib1716() {
DCHECK(allow_macro_instructions_);
DCHECK(!TmpList()->IncludesAliasOf(x16));
DCHECK(!TmpList()->IncludesAliasOf(x17));
autia1716();
autib1716();
}
inline void Dmb(BarrierDomain domain, BarrierType type);

View File

@ -295,7 +295,7 @@ void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm,
FrameDescription::continuation_offset()));
__ Ldr(lr, MemOperand(last_output_frame, FrameDescription::pc_offset()));
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
__ Autiasp();
__ Autibsp();
#endif
__ Br(continuation);
}

View File

@ -1423,10 +1423,10 @@ void DisassemblingDecoder::VisitFPFixedPointConvert(Instruction* instr) {
// clang-format off
#define PAUTH_SYSTEM_MNEMONICS(V) \
V(PACIA1716, "pacia1716") \
V(AUTIA1716, "autia1716") \
V(PACIASP, "paciasp") \
V(AUTIASP, "autiasp")
V(PACIB1716, "pacib1716") \
V(AUTIB1716, "autib1716") \
V(PACIBSP, "pacibsp") \
V(AUTIBSP, "autibsp")
// clang-format on
void DisassemblingDecoder::VisitSystem(Instruction* instr) {

View File

@ -10,8 +10,8 @@ namespace v8 {
namespace internal {
// Randomly generated example key for simulating only.
const Simulator::PACKey Simulator::kPACKeyIA = {0xc31718727de20f71,
0xab9fd4e14b2fec51, 0};
const Simulator::PACKey Simulator::kPACKeyIB = {0xeebb163b474e04c8,
0x5267ac6fc280fb7c, 1};
namespace {

View File

@ -10,11 +10,6 @@
#include "src/common/globals.h"
#include "src/execution/arm64/simulator-arm64.h"
// TODO(v8:10026): Replace hints with instruction aliases, when supported.
#define AUTIA1716 "hint #12"
#define PACIA1716 "hint #8"
#define XPACLRI "hint #7"
namespace v8 {
namespace internal {
@ -31,13 +26,13 @@ V8_INLINE Address PointerAuthentication::AuthenticatePC(
uint64_t sp = reinterpret_cast<uint64_t>(pc_address) + offset_from_sp;
uint64_t pc = reinterpret_cast<uint64_t>(*pc_address);
#ifdef USE_SIMULATOR
pc = Simulator::AuthPAC(pc, sp, Simulator::kPACKeyIA,
pc = Simulator::AuthPAC(pc, sp, Simulator::kPACKeyIB,
Simulator::kInstructionPointer);
#else
asm volatile(
" mov x17, %[pc]\n"
" mov x16, %[stack_ptr]\n"
" " AUTIA1716 "\n"
" autib1716\n"
" ldr xzr, [x17]\n"
" mov %[pc], x17\n"
: [pc] "+r"(pc)
@ -55,7 +50,7 @@ V8_INLINE Address PointerAuthentication::StripPAC(Address pc) {
asm volatile(
" mov x16, lr\n"
" mov lr, %[pc]\n"
" " XPACLRI "\n"
" xpaclri\n"
" mov %[pc], lr\n"
" mov lr, x16\n"
: [pc] "+r"(pc)
@ -68,13 +63,13 @@ V8_INLINE Address PointerAuthentication::StripPAC(Address pc) {
// Sign {pc} using {sp}.
V8_INLINE Address PointerAuthentication::SignPCWithSP(Address pc, Address sp) {
#ifdef USE_SIMULATOR
return Simulator::AddPAC(pc, sp, Simulator::kPACKeyIA,
return Simulator::AddPAC(pc, sp, Simulator::kPACKeyIB,
Simulator::kInstructionPointer);
#else
asm volatile(
" mov x17, %[pc]\n"
" mov x16, %[sp]\n"
" " PACIA1716 "\n"
" pacib1716\n"
" mov %[pc], x17\n"
: [pc] "+r"(pc)
: [sp] "r"(sp)
@ -92,13 +87,13 @@ V8_INLINE void PointerAuthentication::ReplacePC(Address* pc_address,
uint64_t sp = reinterpret_cast<uint64_t>(pc_address) + offset_from_sp;
uint64_t old_pc = reinterpret_cast<uint64_t>(*pc_address);
#ifdef USE_SIMULATOR
uint64_t auth_old_pc = Simulator::AuthPAC(old_pc, sp, Simulator::kPACKeyIA,
uint64_t auth_old_pc = Simulator::AuthPAC(old_pc, sp, Simulator::kPACKeyIB,
Simulator::kInstructionPointer);
uint64_t raw_old_pc =
Simulator::StripPAC(old_pc, Simulator::kInstructionPointer);
// Verify that the old address is authenticated.
CHECK_EQ(auth_old_pc, raw_old_pc);
new_pc = Simulator::AddPAC(new_pc, sp, Simulator::kPACKeyIA,
new_pc = Simulator::AddPAC(new_pc, sp, Simulator::kPACKeyIB,
Simulator::kInstructionPointer);
#else
// Only store newly signed address after we have verified that the old
@ -106,10 +101,10 @@ V8_INLINE void PointerAuthentication::ReplacePC(Address* pc_address,
asm volatile(
" mov x17, %[new_pc]\n"
" mov x16, %[sp]\n"
" " PACIA1716 "\n"
" pacib1716\n"
" mov %[new_pc], x17\n"
" mov x17, %[old_pc]\n"
" " AUTIA1716 "\n"
" autib1716\n"
" ldr xzr, [x17]\n"
: [new_pc] "+&r"(new_pc)
: [sp] "r"(sp), [old_pc] "r"(old_pc)
@ -127,13 +122,13 @@ V8_INLINE void PointerAuthentication::ReplaceContext(Address* pc_address,
uint64_t new_pc;
#ifdef USE_SIMULATOR
uint64_t auth_pc =
Simulator::AuthPAC(old_signed_pc, old_context, Simulator::kPACKeyIA,
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::kPACKeyIA,
new_pc = Simulator::AddPAC(raw_pc, new_context, Simulator::kPACKeyIB,
Simulator::kInstructionPointer);
#else
// Only store newly signed address after we have verified that the old
@ -141,13 +136,13 @@ V8_INLINE void PointerAuthentication::ReplaceContext(Address* pc_address,
asm volatile(
" mov x17, %[old_pc]\n"
" mov x16, %[old_ctx]\n"
" " AUTIA1716 "\n"
" autib1716\n"
" mov x16, %[new_ctx]\n"
" " PACIA1716 "\n"
" pacib1716\n"
" mov %[new_pc], x17\n"
" mov x17, %[old_pc]\n"
" mov x16, %[old_ctx]\n"
" " AUTIA1716 "\n"
" autib1716\n"
" ldr xzr, [x17]\n"
: [new_pc] "=&r"(new_pc)
: [old_pc] "r"(old_signed_pc), [old_ctx] "r"(old_context),

View File

@ -3128,8 +3128,8 @@ bool Simulator::FPProcessNaNs(Instruction* instr) {
// clang-format off
#define PAUTH_SYSTEM_MODES(V) \
V(A1716, 17, xreg(16), kPACKeyIA) \
V(ASP, 30, xreg(31, Reg31IsStackPointer), kPACKeyIA)
V(B1716, 17, xreg(16), kPACKeyIB) \
V(BSP, 30, xreg(31, Reg31IsStackPointer), kPACKeyIB)
// clang-format on
void Simulator::VisitSystem(Instruction* instr) {
@ -3137,7 +3137,7 @@ void Simulator::VisitSystem(Instruction* instr) {
// range of immediates instead of indicating a different instruction. This
// makes the decoding tricky.
if (instr->Mask(SystemPAuthFMask) == SystemPAuthFixed) {
// The BType check for PACIASP happens in CheckBType().
// The BType check for PACIBSP happens in CheckBType().
switch (instr->Mask(SystemPAuthMask)) {
#define DEFINE_PAUTH_FUNCS(SUFFIX, DST, MOD, KEY) \
case PACI##SUFFIX: \

View File

@ -828,8 +828,8 @@ class Simulator : public DecoderVisitor, public SimulatorBase {
void CheckBTypeForPAuth() {
DCHECK(pc_->IsPAuth());
Instr instr = pc_->Mask(SystemPAuthMask);
// Only PACI[AB]SP allowed here, but we don't currently support PACIBSP.
CHECK_EQ(instr, PACIASP);
// Only PACI[AB]SP allowed here, and we only support PACIBSP.
CHECK(instr == PACIBSP);
// Check BType allows PACI[AB]SP instructions.
switch (btype()) {
case BranchFromGuardedNotToIP:
@ -837,7 +837,7 @@ class Simulator : public DecoderVisitor, public SimulatorBase {
// here to be set. This makes PACI[AB]SP behave like "BTI c",
// disallowing its execution when BTYPE is BranchFromGuardedNotToIP
// (0b11).
FATAL("Executing PACIASP with wrong BType.");
FATAL("Executing PACIBSP with wrong BType.");
case BranchFromUnguardedOrToIP:
case BranchAndLink:
break;
@ -1397,7 +1397,7 @@ class Simulator : public DecoderVisitor, public SimulatorBase {
int number;
};
static const PACKey kPACKeyIA;
static const PACKey kPACKeyIB;
// Current implementation is that all pointers are tagged.
static bool HasTBI(uint64_t ptr, PointerType type) {

View File

@ -1878,24 +1878,41 @@ TEST(branch_to_reg) {
static void BtiHelper(Register ipreg) {
SETUP();
Label jump_target, jump_call_target, call_target, done;
Label jump_target, jump_call_target, call_target, test_pacibsp,
pacibsp_target, done;
START();
UseScratchRegisterScope temps(&masm);
temps.Exclude(ipreg);
__ Adr(x0, &jump_target);
__ Br(x0);
__ Nop();
__ Bind(&jump_target, BranchTargetIdentifier::kBtiJump);
__ Adr(x0, &call_target);
__ Blr(x0);
__ Adr(ipreg, &jump_call_target);
__ Blr(ipreg);
__ Adr(lr, &test_pacibsp); // Make Ret return to test_pacibsp.
__ Br(ipreg);
__ Bind(&test_pacibsp, BranchTargetIdentifier::kNone);
__ Adr(ipreg, &pacibsp_target);
__ Blr(ipreg);
__ Adr(lr, &done); // Make Ret return to done label.
__ Br(ipreg);
__ Bind(&call_target, BranchTargetIdentifier::kBtiCall);
__ Ret();
__ Bind(&jump_call_target, BranchTargetIdentifier::kBtiJumpCall);
__ Ret();
__ Bind(&pacibsp_target, BranchTargetIdentifier::kPacibsp);
__ Autibsp();
__ Ret();
__ Bind(&done);
END();
@ -11763,12 +11780,12 @@ TEST(system_msr) {
CHECK_EQUAL_64(0, x10);
}
TEST(system_pauth_a) {
TEST(system_pauth_b) {
SETUP();
START();
// Exclude x16 and x17 from the scratch register list so we can use
// Pac/Autia1716 safely.
// Pac/Autib1716 safely.
UseScratchRegisterScope temps(&masm);
temps.Exclude(x16, x17);
temps.Include(x10, x11);
@ -11782,29 +11799,29 @@ TEST(system_pauth_a) {
// Generate PACs using the 3 system instructions.
__ Mov(x17, 0x0000000012345678);
__ Pacia1716();
__ Pacib1716();
__ Mov(x0, x17);
__ Mov(lr, 0x0000000012345678);
__ Paciasp();
__ Pacibsp();
__ Mov(x2, lr);
// Authenticate the pointers above.
__ Mov(x17, x0);
__ Autia1716();
__ Autib1716();
__ Mov(x3, x17);
__ Mov(lr, x2);
__ Autiasp();
__ Autibsp();
__ Mov(x5, lr);
// Attempt to authenticate incorrect pointers.
__ Mov(x17, x2);
__ Autia1716();
__ Autib1716();
__ Mov(x6, x17);
__ Mov(lr, x0);
__ Autiasp();
__ Autibsp();
__ Mov(x8, lr);
// Restore stack pointer.
@ -11830,8 +11847,8 @@ TEST(system_pauth_a) {
CHECK_EQUAL_64(0x0000000012345678, x5);
// Pointers corrupted after failing to authenticate.
CHECK_EQUAL_64(0x0020000012345678, x6);
CHECK_EQUAL_64(0x0020000012345678, x8);
CHECK_EQUAL_64(0x0040000012345678, x6);
CHECK_EQUAL_64(0x0040000012345678, x8);
#endif // USE_SIMULATOR
}

View File

@ -1919,10 +1919,10 @@ TEST_(bti) {
TEST(system_pauth) {
SET_UP_ASM();
COMPARE(pacia1716(), "pacia1716");
COMPARE(paciasp(), "paciasp");
COMPARE(autia1716(), "autia1716");
COMPARE(autiasp(), "autiasp");
COMPARE(pacib1716(), "pacib1716");
COMPARE(pacibsp(), "pacibsp");
COMPARE(autib1716(), "autib1716");
COMPARE(autibsp(), "autibsp");
CLEANUP();
}