[llvm] [BOLT] Gadget scanner: account for BRK when searching for auth oracles (PR #137975)
Anatoly Trosinenko via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 31 09:48:27 PDT 2025
================
@@ -1751,6 +1750,25 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
Inst.addOperand(MCOperand::createImm(0));
}
+ bool isTrap(const MCInst &Inst) const override {
+ if (Inst.getOpcode() != AArch64::BRK)
+ return false;
+ // Only match the immediate values that are likely to indicate this BRK
+ // instruction is emitted to terminate the program immediately and not to
+ // be handled by a SIGTRAP handler, for example.
+ switch (Inst.getOperand(0).getImm()) {
+ case 0xc470:
+ case 0xc471:
+ case 0xc472:
+ case 0xc473:
+ // Explicit Pointer Authentication check failed, see
+ // AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue().
----------------
atrosinenko wrote:
In the original patch, I tried to make `isTrap` function reasonably usable by non-PAuth analysis by describing it as suitable for terminating the program immediately on a security violation (compared to a generic `MCPlusBuilder::isBreakpoint` which does not explicitly mention any security-related properties). My original reasoning for only matching well-known constants was that somebody may use `brk` instruction for "recoverable" break-points by installing a SIGTRAP handler, for example.
As mentioned in 5b3ed529abd6f6025c9012e5930375c5b577e555, there was `brk 0x3e8` instruction spotted in-the-wild - turned out, it is the instruction emitted by GCC for `__builtin_trap()`. By the way, Clang uses `brk 0x1` instead. Furthermore, as [discussed](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99299) in GCC bug tracker, there is `__builtin_debugtrap()` in Clang (but not in GCC) which is **not** no-return (and there is even [`std::breakpoint`](https://en.cppreference.com/w/cpp/utility/breakpoint.html) proposed for C++).
Frankly speaking, I did not succeed in observing any difference between executing `__builtin_trap()` and `__builtin_debugtrap()` on AArch64 Linux - both result in SIGTRAP. But on x86_64, these are SIGILL vs. SIGTRAP and the codegen differs on both targets: for this source code
```cpp
int test_trap(void) {
__builtin_trap();
return 42;
}
int test_debugtrap(void) {
__builtin_debugtrap();
return 42;
}
```
Clang generates AArch64 assembly along the lines
```
test_trap:
brk #0x1
test_debugtrap:
brk #0xf000
mov w0, #42
ret
```
In abe2b92bd35408800033d67dc53cf47bea9029a6, I added a dedicated test file on detecting various instructions as immediately terminating the program vs. recoverable break-points. Turned out, any BRK instruction is treated as noreturn by BOLT, thus `__builtin_debugtrap()` is probably understood differenlty by BOLT disassembler and by LLVM backend on AArch64 - I added a FIXME on this.
https://github.com/llvm/llvm-project/pull/137975
More information about the llvm-commits
mailing list