[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