forked from OSchip/llvm-project
[StopInfoMachException] Summarize arm64e BLRAx/LDRAx auth failures
Upstream lldb support for summarizing BLRAx and LDRAx auth failures. rdar://41615322 Differential Revision: https://reviews.llvm.org/D102428
This commit is contained in:
parent
c4048d8f50
commit
66902a32c8
|
@ -210,6 +210,10 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/// Write a description of this object to a Stream.
|
||||
bool GetDescription(Stream &s, Target &target,
|
||||
lldb::DescriptionLevel level) const;
|
||||
|
||||
/// Dump a description of this object to a Stream.
|
||||
///
|
||||
/// Dump a description of the contents of this object to the supplied stream
|
||||
|
|
|
@ -150,6 +150,10 @@ public:
|
|||
|
||||
virtual bool HasDelaySlot();
|
||||
|
||||
virtual bool IsLoad() = 0;
|
||||
|
||||
virtual bool IsAuthenticated() = 0;
|
||||
|
||||
bool CanSetBreakpoint ();
|
||||
|
||||
virtual size_t Decode(const Disassembler &disassembler,
|
||||
|
@ -336,6 +340,10 @@ public:
|
|||
|
||||
bool HasDelaySlot() override;
|
||||
|
||||
bool IsLoad() override;
|
||||
|
||||
bool IsAuthenticated() override;
|
||||
|
||||
void CalculateMnemonicOperandsAndComment(
|
||||
const ExecutionContext *exe_ctx) override {
|
||||
// TODO: fill this in and put opcode name into Instruction::m_opcode_name,
|
||||
|
|
|
@ -389,6 +389,19 @@ bool Address::SetOpcodeLoadAddress(lldb::addr_t load_addr, Target *target,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Address::GetDescription(Stream &s, Target &target,
|
||||
DescriptionLevel level) const {
|
||||
assert(level == eDescriptionLevelBrief &&
|
||||
"Non-brief descriptions not implemented");
|
||||
LineEntry line_entry;
|
||||
if (CalculateSymbolContextLineEntry(line_entry)) {
|
||||
s.Printf(" (%s:%u:%u)", line_entry.file.GetFilename().GetCString(),
|
||||
line_entry.line, line_entry.column);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style,
|
||||
DumpStyle fallback_style, uint32_t addr_size) const {
|
||||
// If the section was nullptr, only load address is going to work unless we
|
||||
|
|
|
@ -1123,6 +1123,10 @@ bool PseudoInstruction::HasDelaySlot() {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool PseudoInstruction::IsLoad() { return false; }
|
||||
|
||||
bool PseudoInstruction::IsAuthenticated() { return false; }
|
||||
|
||||
size_t PseudoInstruction::Decode(const lldb_private::Disassembler &disassembler,
|
||||
const lldb_private::DataExtractor &data,
|
||||
lldb::offset_t data_offset) {
|
||||
|
|
|
@ -61,6 +61,8 @@ public:
|
|||
bool CanBranch(llvm::MCInst &mc_inst) const;
|
||||
bool HasDelaySlot(llvm::MCInst &mc_inst) const;
|
||||
bool IsCall(llvm::MCInst &mc_inst) const;
|
||||
bool IsLoad(llvm::MCInst &mc_inst) const;
|
||||
bool IsAuthenticated(llvm::MCInst &mc_inst) const;
|
||||
|
||||
private:
|
||||
MCDisasmInstance(std::unique_ptr<llvm::MCInstrInfo> &&instr_info_up,
|
||||
|
@ -102,6 +104,16 @@ public:
|
|||
return m_has_delay_slot;
|
||||
}
|
||||
|
||||
bool IsLoad() override {
|
||||
VisitInstruction();
|
||||
return m_is_load;
|
||||
}
|
||||
|
||||
bool IsAuthenticated() override {
|
||||
VisitInstruction();
|
||||
return m_is_authenticated;
|
||||
}
|
||||
|
||||
DisassemblerLLVMC::MCDisasmInstance *GetDisasmToUse(bool &is_alternate_isa) {
|
||||
DisassemblerScope disasm(*this);
|
||||
return GetDisasmToUse(is_alternate_isa, disasm);
|
||||
|
@ -817,9 +829,13 @@ protected:
|
|||
// - Might branch
|
||||
// - Does not have a delay slot
|
||||
// - Is not a call
|
||||
// - Is not a load
|
||||
// - Is not an authenticated instruction
|
||||
bool m_does_branch = true;
|
||||
bool m_has_delay_slot = false;
|
||||
bool m_is_call = false;
|
||||
bool m_is_load = false;
|
||||
bool m_is_authenticated = false;
|
||||
|
||||
void VisitInstruction() {
|
||||
if (m_has_visited_instruction)
|
||||
|
@ -849,6 +865,8 @@ protected:
|
|||
m_does_branch = mc_disasm_ptr->CanBranch(inst);
|
||||
m_has_delay_slot = mc_disasm_ptr->HasDelaySlot(inst);
|
||||
m_is_call = mc_disasm_ptr->IsCall(inst);
|
||||
m_is_load = mc_disasm_ptr->IsLoad(inst);
|
||||
m_is_authenticated = mc_disasm_ptr->IsAuthenticated(inst);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -1027,6 +1045,27 @@ bool DisassemblerLLVMC::MCDisasmInstance::IsCall(llvm::MCInst &mc_inst) const {
|
|||
return m_instr_info_up->get(mc_inst.getOpcode()).isCall();
|
||||
}
|
||||
|
||||
bool DisassemblerLLVMC::MCDisasmInstance::IsLoad(llvm::MCInst &mc_inst) const {
|
||||
return m_instr_info_up->get(mc_inst.getOpcode()).mayLoad();
|
||||
}
|
||||
|
||||
bool DisassemblerLLVMC::MCDisasmInstance::IsAuthenticated(
|
||||
llvm::MCInst &mc_inst) const {
|
||||
auto InstrDesc = m_instr_info_up->get(mc_inst.getOpcode());
|
||||
|
||||
// Treat software auth traps (brk 0xc470 + aut key, where 0x70 == 'p', 0xc4
|
||||
// == 'a' + 'c') as authenticated instructions for reporting purposes, in
|
||||
// addition to the standard authenticated instructions specified in ARMv8.3.
|
||||
bool IsBrkC47x = false;
|
||||
if (InstrDesc.isTrap() && mc_inst.getNumOperands() == 1) {
|
||||
const llvm::MCOperand &Op0 = mc_inst.getOperand(0);
|
||||
if (Op0.isImm() && Op0.getImm() >= 0xc470 && Op0.getImm() <= 0xc474)
|
||||
IsBrkC47x = true;
|
||||
}
|
||||
|
||||
return InstrDesc.isAuthenticated() || IsBrkC47x;
|
||||
}
|
||||
|
||||
DisassemblerLLVMC::DisassemblerLLVMC(const ArchSpec &arch,
|
||||
const char *flavor_string)
|
||||
: Disassembler(arch, flavor_string), m_exe_ctx(nullptr), m_inst(nullptr),
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "lldb/Breakpoint/Watchpoint.h"
|
||||
#include "lldb/Symbol/Symbol.h"
|
||||
#include "lldb/Target/ABI.h"
|
||||
#include "lldb/Target/DynamicLoader.h"
|
||||
#include "lldb/Target/ExecutionContext.h"
|
||||
#include "lldb/Target/Process.h"
|
||||
|
@ -30,6 +31,182 @@
|
|||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
||||
/// Information about a pointer-authentication related instruction.
|
||||
struct PtrauthInstructionInfo {
|
||||
bool IsAuthenticated;
|
||||
bool IsLoad;
|
||||
bool DoesBranch;
|
||||
};
|
||||
|
||||
/// Get any pointer-authentication related information about the instruction
|
||||
/// at address \p at_addr.
|
||||
static llvm::Optional<PtrauthInstructionInfo>
|
||||
GetPtrauthInstructionInfo(Target &target, const ArchSpec &arch,
|
||||
const Address &at_addr) {
|
||||
const char *plugin_name = nullptr;
|
||||
const char *flavor = nullptr;
|
||||
AddressRange range_bounds(at_addr, 4);
|
||||
const bool prefer_file_cache = true;
|
||||
DisassemblerSP disassembler_sp = Disassembler::DisassembleRange(
|
||||
arch, plugin_name, flavor, target, range_bounds, prefer_file_cache);
|
||||
if (!disassembler_sp)
|
||||
return llvm::None;
|
||||
|
||||
InstructionList &insn_list = disassembler_sp->GetInstructionList();
|
||||
InstructionSP insn = insn_list.GetInstructionAtIndex(0);
|
||||
if (!insn)
|
||||
return llvm::None;
|
||||
|
||||
return PtrauthInstructionInfo{insn->IsAuthenticated(), insn->IsLoad(),
|
||||
insn->DoesBranch()};
|
||||
}
|
||||
|
||||
/// Describe the load address of \p addr using the format filename:line:col.
|
||||
static void DescribeAddressBriefly(Stream &strm, const Address &addr,
|
||||
Target &target) {
|
||||
strm.Printf("at address=0x%" PRIx64, addr.GetLoadAddress(&target));
|
||||
StreamString s;
|
||||
if (addr.GetDescription(s, target, eDescriptionLevelBrief))
|
||||
strm.Printf(" %s", s.GetString().data());
|
||||
strm.Printf(".\n");
|
||||
}
|
||||
|
||||
bool StopInfoMachException::DeterminePtrauthFailure(ExecutionContext &exe_ctx) {
|
||||
bool IsBreakpoint = m_value == 6; // EXC_BREAKPOINT
|
||||
bool IsBadAccess = m_value == 1; // EXC_BAD_ACCESS
|
||||
if (!IsBreakpoint && !IsBadAccess)
|
||||
return false;
|
||||
|
||||
// Check that we have a live process.
|
||||
if (!exe_ctx.HasProcessScope() || !exe_ctx.HasThreadScope() ||
|
||||
!exe_ctx.HasTargetScope())
|
||||
return false;
|
||||
|
||||
Thread &thread = *exe_ctx.GetThreadPtr();
|
||||
StackFrameSP current_frame = thread.GetStackFrameAtIndex(0);
|
||||
if (!current_frame)
|
||||
return false;
|
||||
|
||||
Target &target = *exe_ctx.GetTargetPtr();
|
||||
Process &process = *exe_ctx.GetProcessPtr();
|
||||
ABISP abi_sp = process.GetABI();
|
||||
const ArchSpec &arch = target.GetArchitecture();
|
||||
assert(abi_sp && "Missing ABI info");
|
||||
|
||||
// Check for a ptrauth-enabled target.
|
||||
const bool ptrauth_enabled_target =
|
||||
arch.GetCore() == ArchSpec::eCore_arm_arm64e;
|
||||
if (!ptrauth_enabled_target)
|
||||
return false;
|
||||
|
||||
// Set up a stream we can write a diagnostic into.
|
||||
StreamString strm;
|
||||
auto emit_ptrauth_prologue = [&](uint64_t at_address) {
|
||||
strm.Printf("EXC_BAD_ACCESS (code=%" PRIu64 ", address=0x%" PRIx64 ")\n",
|
||||
m_exc_code, at_address);
|
||||
strm.Printf("Note: Possible pointer authentication failure detected.\n");
|
||||
};
|
||||
|
||||
// Check if we have a "brk 0xc47x" trap, where the value that failed to
|
||||
// authenticate is in x16.
|
||||
Address current_address = current_frame->GetFrameCodeAddress();
|
||||
if (IsBreakpoint) {
|
||||
RegisterContext *reg_ctx = exe_ctx.GetRegisterContext();
|
||||
if (!reg_ctx)
|
||||
return false;
|
||||
|
||||
const RegisterInfo *X16Info = reg_ctx->GetRegisterInfoByName("x16");
|
||||
RegisterValue X16Val;
|
||||
if (!reg_ctx->ReadRegister(X16Info, X16Val))
|
||||
return false;
|
||||
uint64_t bad_address = X16Val.GetAsUInt64();
|
||||
|
||||
uint64_t fixed_bad_address = abi_sp->FixCodeAddress(bad_address);
|
||||
Address brk_address;
|
||||
if (!target.ResolveLoadAddress(fixed_bad_address, brk_address))
|
||||
return false;
|
||||
|
||||
auto brk_ptrauth_info =
|
||||
GetPtrauthInstructionInfo(target, arch, current_address);
|
||||
if (brk_ptrauth_info && brk_ptrauth_info->IsAuthenticated) {
|
||||
emit_ptrauth_prologue(bad_address);
|
||||
strm.Printf("Found value that failed to authenticate ");
|
||||
DescribeAddressBriefly(strm, brk_address, target);
|
||||
m_description = std::string(strm.GetString());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(IsBadAccess && "Handle EXC_BAD_ACCESS only after this point");
|
||||
|
||||
// Check that we have the "bad address" from an EXC_BAD_ACCESS.
|
||||
if (m_exc_data_count < 2)
|
||||
return false;
|
||||
|
||||
// Ok, we know the Target is valid and that it describes a ptrauth-enabled
|
||||
// device. Now, we need to determine whether this exception was caused by a
|
||||
// ptrauth failure.
|
||||
|
||||
uint64_t bad_address = m_exc_subcode;
|
||||
uint64_t fixed_bad_address = abi_sp->FixCodeAddress(bad_address);
|
||||
uint64_t current_pc = current_address.GetLoadAddress(&target);
|
||||
|
||||
// Detect: LDRAA, LDRAB (Load Register, with pointer authentication).
|
||||
//
|
||||
// If an authenticated load results in an exception, the instruction at the
|
||||
// current PC should be one of LDRAx.
|
||||
if (bad_address != current_pc && fixed_bad_address != current_pc) {
|
||||
auto ptrauth_info =
|
||||
GetPtrauthInstructionInfo(target, arch, current_address);
|
||||
if (ptrauth_info && ptrauth_info->IsAuthenticated && ptrauth_info->IsLoad) {
|
||||
emit_ptrauth_prologue(bad_address);
|
||||
strm.Printf("Found authenticated load instruction ");
|
||||
DescribeAddressBriefly(strm, current_address, target);
|
||||
m_description = std::string(strm.GetString());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Detect: BLRAA, BLRAAZ, BLRAB, BLRABZ (Branch with Link to Register, with
|
||||
// pointer authentication).
|
||||
//
|
||||
// TODO: Detect: BRAA, BRAAZ, BRAB, BRABZ (Branch to Register, with pointer
|
||||
// authentication). At a minimum, this requires call site info support for
|
||||
// indirect calls.
|
||||
//
|
||||
// If an authenticated call or tail call results in an exception, stripping
|
||||
// the bad address should give the current PC, which points to the address
|
||||
// we tried to branch to.
|
||||
if (bad_address != current_pc && fixed_bad_address == current_pc) {
|
||||
if (StackFrameSP parent_frame = thread.GetStackFrameAtIndex(1)) {
|
||||
addr_t return_pc =
|
||||
parent_frame->GetFrameCodeAddress().GetLoadAddress(&target);
|
||||
Address blr_address;
|
||||
if (!target.ResolveLoadAddress(return_pc - 4, blr_address))
|
||||
return false;
|
||||
|
||||
auto blr_ptrauth_info =
|
||||
GetPtrauthInstructionInfo(target, arch, blr_address);
|
||||
if (blr_ptrauth_info && blr_ptrauth_info->IsAuthenticated &&
|
||||
blr_ptrauth_info->DoesBranch) {
|
||||
emit_ptrauth_prologue(bad_address);
|
||||
strm.Printf("Found authenticated indirect branch ");
|
||||
DescribeAddressBriefly(strm, blr_address, target);
|
||||
m_description = std::string(strm.GetString());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Detect: RETAA, RETAB (Return from subroutine, with pointer
|
||||
// authentication).
|
||||
//
|
||||
// Is there a motivating, non-malicious code snippet that corrupts LR?
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *StopInfoMachException::GetDescription() {
|
||||
if (!m_description.empty())
|
||||
return m_description.c_str();
|
||||
|
@ -79,6 +256,11 @@ const char *StopInfoMachException::GetDescription() {
|
|||
}
|
||||
break;
|
||||
|
||||
case llvm::Triple::aarch64:
|
||||
if (DeterminePtrauthFailure(exe_ctx))
|
||||
return m_description.c_str();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -190,6 +372,11 @@ const char *StopInfoMachException::GetDescription() {
|
|||
}
|
||||
break;
|
||||
|
||||
case llvm::Triple::aarch64:
|
||||
if (DeterminePtrauthFailure(exe_ctx))
|
||||
return m_description.c_str();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
namespace lldb_private {
|
||||
|
||||
class StopInfoMachException : public StopInfo {
|
||||
/// Determine the pointer-authentication related failure that caused this
|
||||
/// exception. Returns true and fills out the failure description if there
|
||||
/// is auth-related failure, and returns false otherwise.
|
||||
bool DeterminePtrauthFailure(ExecutionContext &exe_ctx);
|
||||
|
||||
public:
|
||||
// Constructors and Destructors
|
||||
StopInfoMachException(Thread &thread, uint32_t exc_type,
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
C_SOURCES := blraa.c
|
||||
include Makefile.rules
|
|
@ -0,0 +1,5 @@
|
|||
from lldbsuite.test import lldbinline
|
||||
from lldbsuite.test import decorators
|
||||
|
||||
lldbinline.MakeInlineTest(__file__, globals(),
|
||||
[decorators.skipIf(archs=decorators.no_match(['arm64e']))])
|
|
@ -0,0 +1,28 @@
|
|||
void foo() {}
|
||||
|
||||
int main() {
|
||||
//% self.filecheck("c", "blraa.c")
|
||||
// CHECK: stop reason = EXC_BAD_ACCESS
|
||||
// CHECK-NEXT: Note: Possible pointer authentication failure detected.
|
||||
// CHECK-NEXT: Found authenticated indirect branch at address=0x{{.*}} (blraa.c:[[@LINE+1]]:3).
|
||||
asm volatile (
|
||||
"mov x9, #0xbad \n"
|
||||
"blraa %[target], x9 \n"
|
||||
/* Outputs */ :
|
||||
/* Inputs */ : [target] "r"(&foo)
|
||||
/* Clobbers */ : "x9"
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Expected codegen and exception message without ptrauth diagnostics:
|
||||
// * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x2000000100007f9c)
|
||||
// frame #0: 0x0000000100007f9c blraa2`foo
|
||||
// blraa2`foo:
|
||||
// 0x100007f9c <+0>: ret
|
||||
//
|
||||
// blraa2`main:
|
||||
// 0x100007fa0 <+0>: nop
|
||||
// 0x100007fa4 <+4>: ldr x8, #0x5c
|
||||
// 0x100007fa8 <+8>: mov x9, #0xbad
|
|
@ -0,0 +1,2 @@
|
|||
C_SOURCES := braa.c
|
||||
include Makefile.rules
|
|
@ -0,0 +1,5 @@
|
|||
from lldbsuite.test import lldbinline
|
||||
from lldbsuite.test import decorators
|
||||
|
||||
lldbinline.MakeInlineTest(__file__, globals(),
|
||||
[decorators.skipIf(archs=decorators.no_match(['arm64e']))])
|
|
@ -0,0 +1,29 @@
|
|||
void foo() {}
|
||||
|
||||
int main() {
|
||||
//% self.filecheck("c", "braa.c")
|
||||
// CHECK: stop reason = EXC_BAD_ACCESS
|
||||
//
|
||||
// TODO: We need call site info support for indirect calls to make this work.
|
||||
// CHECK-NOT: pointer authentication failure
|
||||
asm volatile (
|
||||
"mov x9, #0xbad \n"
|
||||
"braa %[target], x9 \n"
|
||||
/* Outputs */ :
|
||||
/* Inputs */ : [target] "r"(&foo)
|
||||
/* Clobbers */ : "x9"
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Expected codegen and exception message without ptrauth diagnostics:
|
||||
// * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x2000000100007f9c)
|
||||
// frame #0: 0x0000000100007f9c braa`foo
|
||||
// braa`foo:
|
||||
// 0x100007f9c <+0>: ret
|
||||
//
|
||||
// braa`main:
|
||||
// 0x100007fa0 <+0>: nop
|
||||
// 0x100007fa4 <+4>: ldr x8, #0x5c
|
||||
// 0x100007fa8 <+8>: mov x9, #0xbad
|
|
@ -0,0 +1,2 @@
|
|||
C_SOURCES := ldraa.c
|
||||
include Makefile.rules
|
|
@ -0,0 +1,5 @@
|
|||
from lldbsuite.test import lldbinline
|
||||
from lldbsuite.test import decorators
|
||||
|
||||
lldbinline.MakeInlineTest(__file__, globals(),
|
||||
[decorators.skipIf(archs=decorators.no_match(['arm64e']))])
|
|
@ -0,0 +1,31 @@
|
|||
int main() {
|
||||
//% self.filecheck("c", "ldraa.c")
|
||||
// CHECK: EXC_BAD_ACCESS
|
||||
// CHECK-NEXT: Note: Possible pointer authentication failure detected.
|
||||
// CHECK-NEXT: Found authenticated load instruction at address=0x{{.*}} (ldraa.c:[[@LINE+3]]:3).
|
||||
long long foo = 0;
|
||||
|
||||
asm volatile (
|
||||
"ldraa x9, [%[target]] \n"
|
||||
/* Outputs */ :
|
||||
/* Inputs */ : [target] "r"(&foo)
|
||||
/* Clobbers */ :
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Expected codegen, register state, and exception message without ptrauth diagnostics:
|
||||
// * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x2000016fdffc38)
|
||||
// frame #0: 0x0000000100007fa8 ldraa`main + 12
|
||||
// ldraa`main:
|
||||
// -> 0x100007fa8 <+12>: ldraa x9, [x8]
|
||||
// 0x100007fac <+16>: orr w0, wzr, #0x1
|
||||
// 0x100007fb0 <+20>: add sp, sp, #0x10 ; =0x10
|
||||
// 0x100007fb4 <+24>: ret
|
||||
// Target 0: (ldraa) stopped.
|
||||
// (lldb) p/x $x8
|
||||
// (unsigned long) $0 = 0x000000016fdffc38
|
||||
// (lldb) x/8 $x8
|
||||
// 0x16fdffc38: 0x00000000 0x00000000 0x80254f30 0x00000001
|
||||
// 0x16fdffc48: 0x00000000 0x00000000 0x00000000 0x00000000
|
|
@ -0,0 +1,2 @@
|
|||
C_SOURCES := brkC47x.c
|
||||
include Makefile.rules
|
|
@ -0,0 +1,5 @@
|
|||
from lldbsuite.test import lldbinline
|
||||
from lldbsuite.test import decorators
|
||||
|
||||
lldbinline.MakeInlineTest(__file__, globals(),
|
||||
[decorators.skipIf(archs=decorators.no_match(['arm64e']))])
|
|
@ -0,0 +1,17 @@
|
|||
void foo() {}
|
||||
|
||||
int main() {
|
||||
//% self.filecheck("c", "brkC47x.c")
|
||||
// CHECK: stop reason = EXC_BAD_ACCESS
|
||||
// CHECK-NEXT: Note: Possible pointer authentication failure detected.
|
||||
// CHECK-NEXT: Found value that failed to authenticate at address=0x{{.*}} (brkC47x.c:1:13).
|
||||
asm volatile (
|
||||
"mov x16, %[target] \n"
|
||||
"brk 0xc470 \n"
|
||||
/* Outputs */ :
|
||||
/* Inputs */ : [target] "r"(&foo)
|
||||
/* Clobbers */ : "x16"
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
C_SOURCES := brkC47x.c
|
||||
include Makefile.rules
|
|
@ -0,0 +1,5 @@
|
|||
from lldbsuite.test import lldbinline
|
||||
from lldbsuite.test import decorators
|
||||
|
||||
lldbinline.MakeInlineTest(__file__, globals(),
|
||||
[decorators.skipIf(archs=decorators.no_match(['arm64e']))])
|
|
@ -0,0 +1,14 @@
|
|||
int main() {
|
||||
//% self.filecheck("c", "brkC47x.c")
|
||||
// CHECK: stop reason = EXC_BAD_ACCESS
|
||||
// CHECK-NOT: Note: Possible pointer authentication failure detected.
|
||||
asm volatile (
|
||||
"mov x16, #0xbad \n"
|
||||
"brk 0xc470 \n"
|
||||
/* Outputs */ :
|
||||
/* Inputs */ :
|
||||
/* Clobbers */ : "x16"
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
Loading…
Reference in New Issue