diff --git a/lldb/include/lldb/Core/Address.h b/lldb/include/lldb/Core/Address.h --- a/lldb/include/lldb/Core/Address.h +++ b/lldb/include/lldb/Core/Address.h @@ -210,6 +210,10 @@ } }; + /// 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 diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -150,6 +150,10 @@ virtual bool HasDelaySlot(); + virtual bool IsLoad() = 0; + + virtual bool IsAuthenticated() = 0; + bool CanSetBreakpoint (); virtual size_t Decode(const Disassembler &disassembler, @@ -336,6 +340,10 @@ 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, diff --git a/lldb/source/Core/Address.cpp b/lldb/source/Core/Address.cpp --- a/lldb/source/Core/Address.cpp +++ b/lldb/source/Core/Address.cpp @@ -389,6 +389,19 @@ 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 diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -1123,6 +1123,10 @@ 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) { diff --git a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp --- a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp +++ b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp @@ -61,6 +61,8 @@ 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 &&instr_info_up, @@ -102,6 +104,16 @@ 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 @@ // - 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 @@ 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 @@ 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), diff --git a/lldb/source/Plugins/Process/Utility/StopInfoMachException.h b/lldb/source/Plugins/Process/Utility/StopInfoMachException.h --- a/lldb/source/Plugins/Process/Utility/StopInfoMachException.h +++ b/lldb/source/Plugins/Process/Utility/StopInfoMachException.h @@ -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, diff --git a/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp b/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp --- a/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp +++ b/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp @@ -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 +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 @@ } break; + case llvm::Triple::aarch64: + if (DeterminePtrauthFailure(exe_ctx)) + return m_description.c_str(); + break; + default: break; } @@ -190,6 +372,11 @@ } break; + case llvm::Triple::aarch64: + if (DeterminePtrauthFailure(exe_ctx)) + return m_description.c_str(); + break; + default: break; } diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/BLRAA_error/Makefile b/lldb/test/API/functionalities/ptrauth_diagnostics/BLRAA_error/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/BLRAA_error/Makefile @@ -0,0 +1,2 @@ +C_SOURCES := blraa.c +include Makefile.rules diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/BLRAA_error/TestPtrauthBLRAADiagnostic.py b/lldb/test/API/functionalities/ptrauth_diagnostics/BLRAA_error/TestPtrauthBLRAADiagnostic.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/BLRAA_error/TestPtrauthBLRAADiagnostic.py @@ -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']))]) diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/BLRAA_error/blraa.c b/lldb/test/API/functionalities/ptrauth_diagnostics/BLRAA_error/blraa.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/BLRAA_error/blraa.c @@ -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 diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/BRAA_error/Makefile b/lldb/test/API/functionalities/ptrauth_diagnostics/BRAA_error/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/BRAA_error/Makefile @@ -0,0 +1,2 @@ +C_SOURCES := braa.c +include Makefile.rules diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/BRAA_error/TestPtrauthBRAADiagnostic.py b/lldb/test/API/functionalities/ptrauth_diagnostics/BRAA_error/TestPtrauthBRAADiagnostic.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/BRAA_error/TestPtrauthBRAADiagnostic.py @@ -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']))]) diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/BRAA_error/braa.c b/lldb/test/API/functionalities/ptrauth_diagnostics/BRAA_error/braa.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/BRAA_error/braa.c @@ -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 diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/LDRAA_error/Makefile b/lldb/test/API/functionalities/ptrauth_diagnostics/LDRAA_error/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/LDRAA_error/Makefile @@ -0,0 +1,2 @@ +C_SOURCES := ldraa.c +include Makefile.rules diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/LDRAA_error/TestPtrauthLDRAADiagnostic.py b/lldb/test/API/functionalities/ptrauth_diagnostics/LDRAA_error/TestPtrauthLDRAADiagnostic.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/LDRAA_error/TestPtrauthLDRAADiagnostic.py @@ -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']))]) diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/LDRAA_error/ldraa.c b/lldb/test/API/functionalities/ptrauth_diagnostics/LDRAA_error/ldraa.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/LDRAA_error/ldraa.c @@ -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 diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_code/Makefile b/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_code/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_code/Makefile @@ -0,0 +1,2 @@ +C_SOURCES := brkC47x.c +include Makefile.rules diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_code/TestPtrauthBRKc47xDiagnostic.py b/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_code/TestPtrauthBRKc47xDiagnostic.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_code/TestPtrauthBRKc47xDiagnostic.py @@ -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']))]) diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_code/brkC47x.c b/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_code/brkC47x.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_code/brkC47x.c @@ -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; +} diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_x16_invalid/Makefile b/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_x16_invalid/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_x16_invalid/Makefile @@ -0,0 +1,2 @@ +C_SOURCES := brkC47x.c +include Makefile.rules diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_x16_invalid/TestPtrauthBRKc47xX16Invalid.py b/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_x16_invalid/TestPtrauthBRKc47xX16Invalid.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_x16_invalid/TestPtrauthBRKc47xX16Invalid.py @@ -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']))]) diff --git a/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_x16_invalid/brkC47x.c b/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_x16_invalid/brkC47x.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/ptrauth_diagnostics/brkC47x_x16_invalid/brkC47x.c @@ -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; +}