[arm64] Add support for pointer authentication instructions
Change-Id: I29c88d9e5de34e9a940b76ab76a40376d251c25f Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1373781 Commit-Queue: Georgia Kouveli <georgia.kouveli@arm.com> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Cr-Commit-Position: refs/heads/master@{#62901}
This commit is contained in:
parent
95c7148d82
commit
7cb9984ef9
1
BUILD.gn
1
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",
|
||||
|
@ -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));
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
|
269
src/execution/arm64/pointer-auth-arm64.cc
Normal file
269
src/execution/arm64/pointer-auth-arm64.cc
Normal file
@ -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<uint64_t>((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<uint64_t>(t3) << (4 * (i + 0));
|
||||
s_output |= static_cast<uint64_t>(t2) << (4 * (i + 4));
|
||||
s_output |= static_cast<uint64_t>(t1) << (4 * (i + 8));
|
||||
s_output |= static_cast<uint64_t>(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<uint64_t>(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<uint64_t>(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<uint64_t>(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
|
@ -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()) {
|
||||
|
@ -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) {
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
76
test/cctest/test-pointer-auth-arm64.cc
Normal file
76
test/cctest/test-pointer-auth-arm64.cc
Normal file
@ -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<DispatchingDecoderVisitor>* decoder =
|
||||
new Decoder<DispatchingDecoderVisitor>();
|
||||
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<DispatchingDecoderVisitor>* decoder =
|
||||
new Decoder<DispatchingDecoderVisitor>();
|
||||
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<DispatchingDecoderVisitor>* decoder =
|
||||
new Decoder<DispatchingDecoderVisitor>();
|
||||
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
|
Loading…
Reference in New Issue
Block a user