diff --git a/BUILD.gn b/BUILD.gn index 62ce950066..a10aa15f50 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -3115,6 +3115,7 @@ v8_source_set("v8_base_without_compiler") { "src/diagnostics/arm64/eh-frame-arm64.cc", "src/execution/arm64/frame-constants-arm64.cc", "src/execution/arm64/frame-constants-arm64.h", + "src/execution/arm64/pointer-auth-arm64.cc", "src/execution/arm64/simulator-arm64.cc", "src/execution/arm64/simulator-arm64.h", "src/execution/arm64/simulator-logic-arm64.cc", diff --git a/src/codegen/arm64/assembler-arm64.cc b/src/codegen/arm64/assembler-arm64.cc index 159e763ba2..2c91ff97a6 100644 --- a/src/codegen/arm64/assembler-arm64.cc +++ b/src/codegen/arm64/assembler-arm64.cc @@ -1166,6 +1166,11 @@ 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::ldp(const CPURegister& rt, const CPURegister& rt2, const MemOperand& src) { LoadStorePair(rt, rt2, src, LoadPairOpFor(rt, rt2)); diff --git a/src/codegen/arm64/assembler-arm64.h b/src/codegen/arm64/assembler-arm64.h index 6a6bf633c1..a9f20e7b2d 100644 --- a/src/codegen/arm64/assembler-arm64.h +++ b/src/codegen/arm64/assembler-arm64.h @@ -786,6 +786,22 @@ 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 + // address in x17 and modifier in x16 [Armv8.3]. + void pacia1716(); + + // Pointer Authentication Code for Instruction address, using key A, with + // address in LR and modifier in SP [Armv8.3]. + void paciasp(); + + // Authenticate Instruction address, using key A, with address in x17 and + // modifier in x16 [Armv8.3]. + void autia1716(); + + // Authenticate Instruction address, using key A, with address in LR and + // modifier in SP [Armv8.3]. + void autiasp(); + // Memory instructions. // Load integer or FP register. diff --git a/src/codegen/arm64/constants-arm64.h b/src/codegen/arm64/constants-arm64.h index a1e962452b..1ae0d0b7b4 100644 --- a/src/codegen/arm64/constants-arm64.h +++ b/src/codegen/arm64/constants-arm64.h @@ -130,6 +130,8 @@ const uint64_t kAddressTagMask = ((UINT64_C(1) << kAddressTagWidth) - 1) static_assert(kAddressTagMask == UINT64_C(0xff00000000000000), "AddressTagMask must represent most-significant eight bits."); +const uint64_t kTTBRMask = UINT64_C(1) << 55; + // AArch64 floating-point specifics. These match IEEE-754. const unsigned kDoubleMantissaBits = 52; const unsigned kDoubleExponentBits = 11; @@ -760,6 +762,16 @@ enum MemBarrierOp : uint32_t { ISB = MemBarrierFixed | 0x00000040 }; +enum SystemPAuthOp { + SystemPAuthFixed = 0xD503211F, + SystemPAuthFMask = 0xFFFFFD1F, + SystemPAuthMask = 0xFFFFFFFF, + PACIA1716 = SystemPAuthFixed | 0x00000100, + AUTIA1716 = SystemPAuthFixed | 0x00000180, + PACIASP = SystemPAuthFixed | 0x00000320, + AUTIASP = SystemPAuthFixed | 0x000003A0 +}; + // Any load or store (including pair). enum LoadStoreAnyOp : uint32_t { LoadStoreAnyFMask = 0x0a000000, diff --git a/src/codegen/arm64/macro-assembler-arm64.h b/src/codegen/arm64/macro-assembler-arm64.h index d4e9c3055b..e7a2036fbe 100644 --- a/src/codegen/arm64/macro-assembler-arm64.h +++ b/src/codegen/arm64/macro-assembler-arm64.h @@ -515,6 +515,46 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { void Cbnz(const Register& rt, Label* label); void Cbz(const Register& rt, Label* label); + void Paciasp() { + DCHECK(allow_macro_instructions_); + paciasp(); + } + void Autiasp() { + DCHECK(allow_macro_instructions_); + autiasp(); + } + + // The 1716 pac and aut instructions encourage people to use x16 and x17 + // directly, perhaps without realising that this is forbidden. For example: + // + // UseScratchRegisterScope temps(&masm); + // Register temp = temps.AcquireX(); // temp will be x16 + // __ Mov(x17, ptr); + // __ Mov(x16, modifier); // Will override temp! + // __ Pacia1716(); + // + // 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: + // + // UseScratchRegisterScope temps(&masm); + // temps.Exclude(x16, x17); + // temps.Include(x10, x11); + // __ Mov(x17, ptr); + // __ Mov(x16, modifier); + // __ Pacia1716(); + void Pacia1716() { + DCHECK(allow_macro_instructions_); + DCHECK(!TmpList()->IncludesAliasOf(x16)); + DCHECK(!TmpList()->IncludesAliasOf(x17)); + pacia1716(); + } + void Autia1716() { + DCHECK(allow_macro_instructions_); + DCHECK(!TmpList()->IncludesAliasOf(x16)); + DCHECK(!TmpList()->IncludesAliasOf(x17)); + autia1716(); + } + inline void Dmb(BarrierDomain domain, BarrierType type); inline void Dsb(BarrierDomain domain, BarrierType type); inline void Isb(); @@ -1997,6 +2037,26 @@ class UseScratchRegisterScope { Register AcquireSameSizeAs(const Register& reg); V8_EXPORT_PRIVATE VRegister AcquireSameSizeAs(const VRegister& reg); + void Include(const CPURegList& list) { available_->Combine(list); } + void Exclude(const CPURegList& list) { +#if DEBUG + CPURegList copy(list); + while (!copy.IsEmpty()) { + const CPURegister& reg = copy.PopHighestIndex(); + DCHECK(available_->IncludesAliasOf(reg)); + } +#endif + available_->Remove(list); + } + void Include(const Register& reg1, const Register& reg2) { + CPURegList list(reg1, reg2); + Include(list); + } + void Exclude(const Register& reg1, const Register& reg2) { + CPURegList list(reg1, reg2); + Exclude(list); + } + private: V8_EXPORT_PRIVATE static CPURegister AcquireNextAvailable( CPURegList* available); diff --git a/src/diagnostics/arm64/disasm-arm64.cc b/src/diagnostics/arm64/disasm-arm64.cc index e51986ee4c..7141cdf283 100644 --- a/src/diagnostics/arm64/disasm-arm64.cc +++ b/src/diagnostics/arm64/disasm-arm64.cc @@ -1417,14 +1417,33 @@ void DisassemblingDecoder::VisitFPFixedPointConvert(Instruction* instr) { Format(instr, mnemonic, form); } +// clang-format off +#define PAUTH_SYSTEM_MNEMONICS(V) \ + V(PACIA1716, "pacia1716") \ + V(AUTIA1716, "autia1716") \ + V(PACIASP, "paciasp") \ + V(AUTIASP, "autiasp") +// clang-format on + void DisassemblingDecoder::VisitSystem(Instruction* instr) { // Some system instructions hijack their Op and Cp fields to represent a // range of immediates instead of indicating a different instruction. This // makes the decoding tricky. const char* mnemonic = "unimplemented"; const char* form = "(System)"; + if (instr->Mask(SystemPAuthFMask) == SystemPAuthFixed) { + switch (instr->Mask(SystemPAuthMask)) { +#define PAUTH_CASE(NAME, MN) \ + case NAME: \ + mnemonic = MN; \ + form = NULL; \ + break; - if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { + PAUTH_SYSTEM_MNEMONICS(PAUTH_CASE) +#undef PAUTH_CASE +#undef PAUTH_SYSTEM_MNEMONICS + } + } else if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { switch (instr->Mask(SystemSysRegMask)) { case MRS: { mnemonic = "mrs"; diff --git a/src/execution/arm64/pointer-auth-arm64.cc b/src/execution/arm64/pointer-auth-arm64.cc new file mode 100644 index 0000000000..cb8ff2c740 --- /dev/null +++ b/src/execution/arm64/pointer-auth-arm64.cc @@ -0,0 +1,269 @@ +// Copyright 2019 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/execution/arm64/simulator-arm64.h" + +#if defined(USE_SIMULATOR) + +namespace v8 { +namespace internal { + +// Randomly generated example key for simulating only. +const Simulator::PACKey Simulator::kPACKeyIA = {0xc31718727de20f71, + 0xab9fd4e14b2fec51, 0}; + +namespace { + +uint64_t GetNibble(uint64_t in_data, int position) { + return (in_data >> position) & 0xf; +} + +uint64_t PACCellShuffle(uint64_t in_data) { + static int in_positions[16] = {52, 24, 44, 0, 28, 48, 4, 40, + 32, 12, 56, 20, 8, 36, 16, 60}; + uint64_t out_data = 0; + for (int i = 0; i < 16; ++i) { + out_data |= GetNibble(in_data, in_positions[i]) << (4 * i); + } + return out_data; +} + +uint64_t PACCellInvShuffle(uint64_t in_data) { + static int in_positions[16] = {12, 24, 48, 36, 56, 44, 4, 16, + 32, 52, 28, 8, 20, 0, 40, 60}; + uint64_t out_data = 0; + for (int i = 0; i < 16; ++i) { + out_data |= GetNibble(in_data, in_positions[i]) << (4 * i); + } + return out_data; +} + +uint64_t RotCell(uint64_t in_cell, int amount) { + DCHECK((amount >= 1) && (amount <= 3)); + + in_cell &= 0xf; + uint8_t temp = in_cell << 4 | in_cell; + return static_cast((temp >> (4 - amount)) & 0xf); +} + +uint64_t PACMult(uint64_t s_input) { + uint8_t t0; + uint8_t t1; + uint8_t t2; + uint8_t t3; + uint64_t s_output = 0; + + for (int i = 0; i < 4; ++i) { + uint8_t s12 = (s_input >> (4 * (i + 12))) & 0xf; + uint8_t s8 = (s_input >> (4 * (i + 8))) & 0xf; + uint8_t s4 = (s_input >> (4 * (i + 4))) & 0xf; + uint8_t s0 = (s_input >> (4 * (i + 0))) & 0xf; + + t0 = RotCell(s8, 1) ^ RotCell(s4, 2) ^ RotCell(s0, 1); + t1 = RotCell(s12, 1) ^ RotCell(s4, 1) ^ RotCell(s0, 2); + t2 = RotCell(s12, 2) ^ RotCell(s8, 1) ^ RotCell(s0, 1); + t3 = RotCell(s12, 1) ^ RotCell(s8, 2) ^ RotCell(s4, 1); + + s_output |= static_cast(t3) << (4 * (i + 0)); + s_output |= static_cast(t2) << (4 * (i + 4)); + s_output |= static_cast(t1) << (4 * (i + 8)); + s_output |= static_cast(t0) << (4 * (i + 12)); + } + return s_output; +} + +uint64_t PACSub(uint64_t t_input) { + uint64_t t_output = 0; + uint8_t substitutions[16] = {0xb, 0x6, 0x8, 0xf, 0xc, 0x0, 0x9, 0xe, + 0x3, 0x7, 0x4, 0x5, 0xd, 0x2, 0x1, 0xa}; + for (int i = 0; i < 16; ++i) { + unsigned index = ((t_input >> (4 * i)) & 0xf); + t_output |= static_cast(substitutions[index]) << (4 * i); + } + return t_output; +} + +uint64_t PACInvSub(uint64_t t_input) { + uint64_t t_output = 0; + uint8_t substitutions[16] = {0x5, 0xe, 0xd, 0x8, 0xa, 0xb, 0x1, 0x9, + 0x2, 0x6, 0xf, 0x0, 0x4, 0xc, 0x7, 0x3}; + for (int i = 0; i < 16; ++i) { + unsigned index = ((t_input >> (4 * i)) & 0xf); + t_output |= static_cast(substitutions[index]) << (4 * i); + } + return t_output; +} + +uint64_t TweakCellInvRot(uint64_t in_cell) { + uint64_t out_cell = 0; + out_cell |= (in_cell & 0x7) << 1; + out_cell |= (in_cell & 0x1) ^ ((in_cell >> 3) & 0x1); + return out_cell; +} + +uint64_t TweakInvShuffle(uint64_t in_data) { + uint64_t out_data = 0; + out_data |= TweakCellInvRot(in_data >> 48) << 0; + out_data |= ((in_data >> 52) & 0xf) << 4; + out_data |= ((in_data >> 20) & 0xff) << 8; + out_data |= ((in_data >> 0) & 0xff) << 16; + out_data |= TweakCellInvRot(in_data >> 8) << 24; + out_data |= ((in_data >> 12) & 0xf) << 28; + out_data |= TweakCellInvRot(in_data >> 28) << 32; + out_data |= TweakCellInvRot(in_data >> 60) << 36; + out_data |= TweakCellInvRot(in_data >> 56) << 40; + out_data |= TweakCellInvRot(in_data >> 16) << 44; + out_data |= ((in_data >> 32) & 0xfff) << 48; + out_data |= TweakCellInvRot(in_data >> 44) << 60; + return out_data; +} + +uint64_t TweakCellRot(uint64_t in_cell) { + uint64_t out_cell = 0; + out_cell |= ((in_cell & 0x1) ^ ((in_cell >> 1) & 0x1)) << 3; + out_cell |= (in_cell >> 0x1) & 0x7; + return out_cell; +} + +uint64_t TweakShuffle(uint64_t in_data) { + uint64_t out_data = 0; + out_data |= ((in_data >> 16) & 0xff) << 0; + out_data |= TweakCellRot(in_data >> 24) << 8; + out_data |= ((in_data >> 28) & 0xf) << 12; + out_data |= TweakCellRot(in_data >> 44) << 16; + out_data |= ((in_data >> 8) & 0xff) << 20; + out_data |= TweakCellRot(in_data >> 32) << 28; + out_data |= ((in_data >> 48) & 0xfff) << 32; + out_data |= TweakCellRot(in_data >> 60) << 44; + out_data |= TweakCellRot(in_data >> 0) << 48; + out_data |= ((in_data >> 4) & 0xf) << 52; + out_data |= TweakCellRot(in_data >> 40) << 56; + out_data |= TweakCellRot(in_data >> 36) << 60; + return out_data; +} + +} // namespace + +// For a description of QARMA see: +// The QARMA Block Cipher Family, Roberto Avanzi, Qualcomm Product Security +// Initiative. +// The pseudocode is available in ARM DDI 0487D.b, J1-6946. +uint64_t Simulator::ComputePAC(uint64_t data, uint64_t context, PACKey key) { + uint64_t key0 = key.high; + uint64_t key1 = key.low; + const uint64_t RC[5] = {0x0000000000000000, 0x13198a2e03707344, + 0xa4093822299f31d0, 0x082efa98ec4e6c89, + 0x452821e638d01377}; + const uint64_t Alpha = 0xc0ac29B7c97c50dd; + + uint64_t modk0 = ((key0 & 0x1) << 63) | ((key0 >> 2) << 1) | + ((key0 >> 63) ^ ((key0 >> 1) & 0x1)); + uint64_t running_mod = context; + uint64_t working_val = data ^ key0; + uint64_t round_key; + for (int i = 0; i < 5; ++i) { + round_key = key1 ^ running_mod; + working_val ^= round_key; + working_val ^= RC[i]; + if (i > 0) { + working_val = PACCellShuffle(working_val); + working_val = PACMult(working_val); + } + working_val = PACSub(working_val); + running_mod = TweakShuffle(running_mod); + } + + round_key = modk0 ^ running_mod; + working_val ^= round_key; + working_val = PACCellShuffle(working_val); + working_val = PACMult(working_val); + working_val = PACSub(working_val); + working_val = PACCellShuffle(working_val); + working_val = PACMult(working_val); + working_val ^= key1; + working_val = PACCellInvShuffle(working_val); + working_val = PACInvSub(working_val); + working_val = PACMult(working_val); + working_val = PACCellInvShuffle(working_val); + working_val ^= key0; + working_val ^= running_mod; + + for (int i = 0; i < 5; ++i) { + working_val = PACInvSub(working_val); + if (i < 4) { + working_val = PACMult(working_val); + working_val = PACCellInvShuffle(working_val); + } + running_mod = TweakInvShuffle(running_mod); + round_key = key1 ^ running_mod; + working_val ^= RC[4 - i]; + working_val ^= round_key; + working_val ^= Alpha; + } + + return working_val ^ modk0; +} + +// The TTBR is selected by bit 63 or 55 depending on TBI for pointers without +// codes, but is always 55 once a PAC code is added to a pointer. For this +// reason, it must be calculated at the call site. +uint64_t Simulator::CalculatePACMask(uint64_t ptr, PointerType type, int ttbr) { + int bottom_pac_bit = GetBottomPACBit(ptr, ttbr); + int top_pac_bit = GetTopPACBit(ptr, type); + return unsigned_bitextract_64(top_pac_bit, bottom_pac_bit, + 0xffffffffffffffff & ~kTTBRMask) + << bottom_pac_bit; +} + +uint64_t Simulator::AuthPAC(uint64_t ptr, uint64_t context, PACKey key, + PointerType type) { + DCHECK((key.number == 0) || (key.number == 1)); + + uint64_t pac_mask = CalculatePACMask(ptr, type, (ptr >> 55) & 1); + uint64_t original_ptr = + ((ptr & kTTBRMask) == 0) ? (ptr & ~pac_mask) : (ptr | pac_mask); + + uint64_t pac = ComputePAC(original_ptr, context, key); + + uint64_t error_code = 1 << key.number; + if ((pac & pac_mask) == (ptr & pac_mask)) { + return original_ptr; + } else { + int error_lsb = GetTopPACBit(ptr, type) - 2; + uint64_t error_mask = UINT64_C(0x3) << error_lsb; + return (original_ptr & ~error_mask) | (error_code << error_lsb); + } +} + +uint64_t Simulator::AddPAC(uint64_t ptr, uint64_t context, PACKey key, + PointerType type) { + int top_pac_bit = GetTopPACBit(ptr, type); + + DCHECK(HasTBI(ptr, type)); + int ttbr = (ptr >> 55) & 1; + uint64_t pac_mask = CalculatePACMask(ptr, type, ttbr); + uint64_t ext_ptr = (ttbr == 0) ? (ptr & ~pac_mask) : (ptr | pac_mask); + + uint64_t pac = ComputePAC(ext_ptr, context, key); + + // If the pointer isn't all zeroes or all ones in the PAC bitfield, corrupt + // the resulting code. + if (((ptr & (pac_mask | kTTBRMask)) != 0x0) && + ((~ptr & (pac_mask | kTTBRMask)) != 0x0)) { + pac ^= UINT64_C(1) << (top_pac_bit - 1); + } + + uint64_t ttbr_shifted = static_cast(ttbr) << 55; + return (pac & pac_mask) | ttbr_shifted | (ptr & ~pac_mask); +} + +uint64_t Simulator::StripPAC(uint64_t ptr, PointerType type) { + uint64_t pac_mask = CalculatePACMask(ptr, type, (ptr >> 55) & 1); + return ((ptr & kTTBRMask) == 0) ? (ptr & ~pac_mask) : (ptr | pac_mask); +} + +} // namespace internal +} // namespace v8 + +#endif // USE_SIMULATOR diff --git a/src/execution/arm64/simulator-arm64.cc b/src/execution/arm64/simulator-arm64.cc index 8618dd8551..713142b11d 100644 --- a/src/execution/arm64/simulator-arm64.cc +++ b/src/execution/arm64/simulator-arm64.cc @@ -3037,11 +3037,31 @@ bool Simulator::FPProcessNaNs(Instruction* instr) { return done; } +// clang-format off +#define PAUTH_SYSTEM_MODES(V) \ + V(A1716, 17, xreg(16), kPACKeyIA) \ + V(ASP, 30, xreg(31, Reg31IsStackPointer), kPACKeyIA) +// clang-format on + void Simulator::VisitSystem(Instruction* instr) { // Some system instructions hijack their Op and Cp fields to represent a // range of immediates instead of indicating a different instruction. This // makes the decoding tricky. - if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { + if (instr->Mask(SystemPAuthFMask) == SystemPAuthFixed) { + switch (instr->Mask(SystemPAuthMask)) { +#define DEFINE_PAUTH_FUNCS(SUFFIX, DST, MOD, KEY) \ + case PACI##SUFFIX: \ + set_xreg(DST, AddPAC(xreg(DST), MOD, KEY, kInstructionPointer)); \ + break; \ + case AUTI##SUFFIX: \ + set_xreg(DST, AuthPAC(xreg(DST), MOD, KEY, kInstructionPointer)); \ + break; + + PAUTH_SYSTEM_MODES(DEFINE_PAUTH_FUNCS) +#undef DEFINE_PAUTH_FUNCS +#undef PAUTH_SYSTEM_MODES + } + } else if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { switch (instr->Mask(SystemSysRegMask)) { case MRS: { switch (instr->ImmSystemRegister()) { diff --git a/src/execution/arm64/simulator-arm64.h b/src/execution/arm64/simulator-arm64.h index ca1cef61ae..4a493ec696 100644 --- a/src/execution/arm64/simulator-arm64.h +++ b/src/execution/arm64/simulator-arm64.h @@ -1273,6 +1273,45 @@ class Simulator : public DecoderVisitor, public SimulatorBase { static inline const char* VRegNameForCode(unsigned code); static inline int CodeFromName(const char* name); + enum PointerType { kDataPointer, kInstructionPointer }; + + struct PACKey { + uint64_t high; + uint64_t low; + int number; + }; + + static const PACKey kPACKeyIA; + + // Current implementation is that all pointers are tagged. + static bool HasTBI(uint64_t ptr, PointerType type) { + USE(ptr, type); + return true; + } + + // Current implementation uses 48-bit virtual addresses. + static int GetBottomPACBit(uint64_t ptr, int ttbr) { + USE(ptr, ttbr); + DCHECK((ttbr == 0) || (ttbr == 1)); + return 48; + } + + // The top PAC bit is 55 for the purposes of relative bit fields with TBI, + // however bit 55 is the TTBR bit regardless of TBI so isn't part of the PAC + // codes in pointers. + static int GetTopPACBit(uint64_t ptr, PointerType type) { + return HasTBI(ptr, type) ? 55 : 63; + } + + // Armv8.3 Pointer authentication helpers. + static uint64_t CalculatePACMask(uint64_t ptr, PointerType type, int ext_bit); + static uint64_t ComputePAC(uint64_t data, uint64_t context, PACKey key); + static uint64_t AuthPAC(uint64_t ptr, uint64_t context, PACKey key, + PointerType type); + static uint64_t AddPAC(uint64_t ptr, uint64_t context, PACKey key, + PointerType type); + static uint64_t StripPAC(uint64_t ptr, PointerType type); + protected: // Simulation helpers ------------------------------------ bool ConditionPassed(Condition cond) { diff --git a/test/cctest/BUILD.gn b/test/cctest/BUILD.gn index 32a766736f..7f09cc8420 100644 --- a/test/cctest/BUILD.gn +++ b/test/cctest/BUILD.gn @@ -313,6 +313,7 @@ v8_source_set("cctest_sources") { "test-javascript-arm64.cc", "test-js-arm64-variables.cc", "test-macro-assembler-arm64.cc", + "test-pointer-auth-arm64.cc", "test-poison-disasm-arm64.cc", "test-sync-primitives-arm64.cc", "test-utils-arm64.cc", diff --git a/test/cctest/test-assembler-arm64.cc b/test/cctest/test-assembler-arm64.cc index 1f6b732808..66a989fcff 100644 --- a/test/cctest/test-assembler-arm64.cc +++ b/test/cctest/test-assembler-arm64.cc @@ -234,6 +234,15 @@ static void InitializeVM() { #define CHECK_FULL_HEAP_OBJECT_IN_REGISTER(expected, result) \ CHECK(Equal64(expected->ptr(), &core, result)) +#define CHECK_NOT_ZERO_AND_NOT_EQUAL_64(reg0, reg1) \ + { \ + int64_t value0 = core.xreg(reg0.code()); \ + int64_t value1 = core.xreg(reg1.code()); \ + CHECK_NE(0, value0); \ + CHECK_NE(0, value1); \ + CHECK_NE(value0, value1); \ + } + #define CHECK_EQUAL_FP64(expected, result) \ CHECK(EqualFP64(expected, &core, result)) @@ -11478,6 +11487,79 @@ TEST(system_msr) { CHECK_EQUAL_64(0, x10); } +TEST(system_pauth_a) { + SETUP(); + START(); + + // Exclude x16 and x17 from the scratch register list so we can use + // Pac/Autia1716 safely. + UseScratchRegisterScope temps(&masm); + temps.Exclude(x16, x17); + temps.Include(x10, x11); + + // Backup stack pointer. + __ Mov(x20, sp); + + // Modifiers + __ Mov(x16, 0x477d469dec0b8768); + __ Mov(sp, 0x477d469dec0b8760); + + // Generate PACs using the 3 system instructions. + __ Mov(x17, 0x0000000012345678); + __ Pacia1716(); + __ Mov(x0, x17); + + __ Mov(lr, 0x0000000012345678); + __ Paciasp(); + __ Mov(x2, lr); + + // Authenticate the pointers above. + __ Mov(x17, x0); + __ Autia1716(); + __ Mov(x3, x17); + + __ Mov(lr, x2); + __ Autiasp(); + __ Mov(x5, lr); + + // Attempt to authenticate incorrect pointers. + __ Mov(x17, x2); + __ Autia1716(); + __ Mov(x6, x17); + + __ Mov(lr, x0); + __ Autiasp(); + __ Mov(x8, lr); + + // Restore stack pointer. + __ Mov(sp, x20); + + // Mask out just the PAC code bits. + __ And(x0, x0, 0x007f000000000000); + __ And(x2, x2, 0x007f000000000000); + + END(); + +// TODO(all): test on real hardware when available +#ifdef USE_SIMULATOR + RUN(); + + // Check PAC codes have been generated and aren't equal. + // NOTE: with a different ComputePAC implementation, there may be a collision. + CHECK_NE(0, core.xreg(2)); + CHECK_NOT_ZERO_AND_NOT_EQUAL_64(x0, x2); + + // Pointers correctly authenticated. + CHECK_EQUAL_64(0x0000000012345678, x3); + CHECK_EQUAL_64(0x0000000012345678, x5); + + // Pointers corrupted after failing to authenticate. + CHECK_EQUAL_64(0x0020000012345678, x6); + CHECK_EQUAL_64(0x0020000012345678, x8); + +#endif // USE_SIMULATOR +} + TEST(system) { INIT_V8(); SETUP(); @@ -14703,6 +14785,7 @@ TEST(internal_reference_linked) { #undef CHECK_EQUAL_FP32 #undef CHECK_EQUAL_64 #undef CHECK_FULL_HEAP_OBJECT_IN_REGISTER +#undef CHECK_NOT_ZERO_AND_NOT_EQUAL_64 #undef CHECK_EQUAL_FP64 #undef CHECK_EQUAL_128 #undef CHECK_CONSTANT_POOL_SIZE diff --git a/test/cctest/test-disasm-arm64.cc b/test/cctest/test-disasm-arm64.cc index ed4fe6c6e0..ba4d92d3a2 100644 --- a/test/cctest/test-disasm-arm64.cc +++ b/test/cctest/test-disasm-arm64.cc @@ -854,7 +854,7 @@ TEST_(branch) { COMPARE(br(x0), "br x0"); COMPARE(blr(x1), "blr x1"); COMPARE(ret(x2), "ret x2"); - COMPARE(ret(lr), "ret") + COMPARE(ret(lr), "ret"); CLEANUP(); } @@ -1881,6 +1881,14 @@ TEST_(system_nop) { CLEANUP(); } +TEST(system_pauth) { + SET_UP_ASM(); + + COMPARE(pacia1716(), "pacia1716"); + COMPARE(paciasp(), "paciasp"); + COMPARE(autia1716(), "autia1716"); + COMPARE(autiasp(), "autiasp"); +} TEST_(debug) { InitializeVM(); diff --git a/test/cctest/test-pointer-auth-arm64.cc b/test/cctest/test-pointer-auth-arm64.cc new file mode 100644 index 0000000000..11632be808 --- /dev/null +++ b/test/cctest/test-pointer-auth-arm64.cc @@ -0,0 +1,76 @@ +// Copyright 2019 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/codegen/arm64/decoder-arm64-inl.h" +#include "src/execution/arm64/simulator-arm64.h" + +#include "test/cctest/cctest.h" + +namespace v8 { +namespace internal { + +#ifdef USE_SIMULATOR +TEST(compute_pac) { + Decoder* decoder = + new Decoder(); + Simulator simulator(decoder); + + uint64_t data1 = 0xfb623599da6e8127; + uint64_t data2 = 0x27979fadf7d53cb7; + uint64_t context = 0x477d469dec0b8762; + Simulator::PACKey key = {0x84be85ce9804e94b, 0xec2802d4e0a488e9, -1}; + + uint64_t pac1 = simulator.ComputePAC(data1, context, key); + uint64_t pac2 = simulator.ComputePAC(data2, context, key); + + // NOTE: If the PAC implementation is changed, this may fail due to a hash + // collision. + CHECK_NE(pac1, pac2); +} + +TEST(add_and_auth_pac) { + Decoder* decoder = + new Decoder(); + Simulator simulator(decoder); + + uint64_t ptr = 0x0000000012345678; + uint64_t context = 0x477d469dec0b8762; + Simulator::PACKey key_a = {0x84be85ce9804e94b, 0xec2802d4e0a488e9, 0}; + Simulator::PACKey key_b = {0xec1119e288704d13, 0xd7f6b76e1cea585e, 1}; + + uint64_t ptr_a = + simulator.AddPAC(ptr, context, key_a, Simulator::kInstructionPointer); + + // Attempt to authenticate the pointer with PAC using different keys. + uint64_t success = + simulator.AuthPAC(ptr_a, context, key_a, Simulator::kInstructionPointer); + uint64_t fail = + simulator.AuthPAC(ptr_a, context, key_b, Simulator::kInstructionPointer); + + uint64_t pac_mask = + simulator.CalculatePACMask(ptr, Simulator::kInstructionPointer, 0); + + // NOTE: If the PAC implementation is changed, this may fail due to a hash + // collision. + CHECK_NE((ptr_a & pac_mask), 0); + CHECK_EQ(success, ptr); + CHECK_NE(fail, ptr); +} + +TEST(add_and_strip_pac) { + Decoder* decoder = + new Decoder(); + Simulator simulator(decoder); + + uint64_t ptr = 0xff00000012345678; + uint64_t pac_mask = + simulator.CalculatePACMask(ptr, Simulator::kInstructionPointer, 0); + uint64_t ptr_a = ptr | pac_mask; + + CHECK_EQ(simulator.StripPAC(ptr_a, Simulator::kInstructionPointer), ptr); +} +#endif // USE_SIMULATOR + +} // namespace internal +} // namespace v8