[llvm] [BOLT] Rework user-facing documentation of BOLT gadget scanner (PR #176446)

Anatoly Trosinenko via llvm-commits llvm-commits at lists.llvm.org
Mon May 4 09:51:34 PDT 2026


https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/176446

>From 667a8291e103982ef6c0d78a6636c297b7fdc359 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Tue, 13 Jan 2026 22:47:08 +0300
Subject: [PATCH 01/15] [BOLT] Rework user-facing documentation of BOLT gadget
 scanner

Update the documentation to reflect the changes made to the original
prototype.
---
 bolt/docs/BinaryAnalysis.md | 546 +++++++++++++++++++++++++++++-------
 1 file changed, 449 insertions(+), 97 deletions(-)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index 07f096eef784e..59ab4791f8ff9 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -1,23 +1,77 @@
 # BOLT-based binary analysis
 
-As part of post-link-time optimizing, BOLT needs to perform a range of analyses
+As part of post-link-time optimization, BOLT needs to perform a range of analyses
 on binaries such as reconstructing control flow graphs, and more.
 
-The `llvm-bolt-binary-analysis` tool enables running requested binary analyses
+The `llvm-bolt-binary-analysis` tool enables running requested analyses
 on binaries, and generating reports. It does this by building on top of the
 analyses implemented in the BOLT libraries.
 
-## Which binary analyses are implemented?
+## Usage
 
-* [Security scanners](#security-scanners)
-  * [pac-ret analysis](#pac-ret-analysis)
+```
+llvm-bolt-binary-analysis --scanners=<list> [options] <binary>
+```
+
+The `--scanners=` option accepts a comma-separated list of analyses to run on
+the provided binary. The binary to be analyzed can be either ELF executable or
+shared object. Similar to other BOLT tools, `llvm-bolt-binary-analysis` expects
+`binary` to be unstripped and preferably linked with `--emit-relocs` linker option.
+
+In addition to options printed by `llvm-bolt-binary-analysis --help-hidden`,
+other relevant BOLT options can generally be passed, see `llvm-bolt --help-hidden`.
+
+The only analysis which is currently implemented is validation of Pointer
+Authentication hardening applied to the binary.
+The specific set of gadget kinds which are searched for depends on command line
+options. Each gadget found by PtrAuth gadget scanner results in a plain text
+report printed at the end of the analysis.
+Furthermore, an attempt is made to provide an extra information on the
+instructions that made the register unsafe.
+Please note that this extra information is provided on a best-effort basis and
+is not expected to be as accurate as the reports themselves.
+
+Here is an example of the report:
+
+```
+GS-PAUTH: signing oracle found in function function_name, basic block .LBB08, at address 102b8
+  The instruction is     000102b8:      pacda   x0, x1
+  The 1 instructions that write to the affected registers after any authentication are:
+  1.     000102b4:      ldr     x0, [x1]
+  This happens in the following basic block:
+    000102b4:   ldr     x0, [x1]
+    000102b8:   pacda   x0, x1
+    000102bc:   ret
+```
+
+A similar report without the associated extra information looks like this:
+
+```
+GS-PAUTH: signing oracle found in function function_name, basic block .LBB016, at address 10384
+  The instruction is     00010384:      pacda   x0, x1
+  The 0 instructions that write to the affected registers after any authentication are:
+```
+
+Furthermore, a ", basic block `<name>`" part is omitted in a report, if BOLT was
+unable to reconstruct control-flow graph for the particular function:
+
+```
+GS-PAUTH: signing oracle found in function function_name_nocfg, at address 10510
+  The instruction is     00010510:      pacda   x0, x1
+  The 0 instructions that write to the affected registers after any authentication are:
+```
+
+The analysis is likely to be less precise when CFG information is absent or
+incomplete.
+
+## Background and motivation
 
 ### Security scanners
 
 For the past 25 years, a large numbers of exploits have been built and used in
 the wild to undermine computer security. The majority of these exploits abuse
 memory vulnerabilities in programs, see evidence from
-[Microsoft](https://youtu.be/PjbGojjnBZQ?si=oCHCa0SHgaSNr6Gr&t=836),
+[Microsoft](https://youtu.be/PjbGojjnBZQ?t=836),
 [Chromium](https://www.chromium.org/Home/chromium-security/memory-safety/) and
 [Android](https://security.googleblog.com/2021/01/data-driven-security-hardening-in.html).
 
@@ -35,7 +89,7 @@ containing the return address between an instruction authenticating it and a
 return instruction using it.
 
 From time to time, however, a bug gets found in the implementation of such
-mitigations in toolchains. Also, code that is written in assembler by hand
+mitigations in toolchains. Also, code that is manually written in assembler
 requires the developer to ensure these security properties by hand.
 
 In short, it is sometimes found that a few places in the binary code are not
@@ -61,124 +115,422 @@ The security scanners implemented in `llvm-bolt-binary-analysis` aim to enable
 the testing of security hardening in arbitrary programs and not just specific
 examples.
 
+### Pointer Authentication
+
+[Pointer Authentication](https://clang.llvm.org/docs/PointerAuthentication.html)
+is intended to make it harder for an attacker to replace pointers at run time.
+This is achieved by making it possible for the compiler or the programmer to
+produce a *signed* pointer from a raw one, and then to probabilistically
+*authenticate the signature* at another site in the program.
+On AArch64 this is achieved by injecting a cryptographic hash, called a
+["Pointer Authentication Code" (PAC)](https://llsoftsec.github.io/llsoftsecbook/#sec:pointer-authentication),
+to the upper bits of the pointer.
+While this approach can be applied to any pointers in the program, the most
+frequent use case, at least in C and C++, is protecting the code pointers.
+The language rules for such pointers are more restrictive, thus allowing the
+compiler to implement various hardenings transparently to the programmer.
+
+Probably the most simple variant of hardening based on Pointer Authentication is
+[`pac-ret`](https://llsoftsec.github.io/llsoftsecbook/#sec:pac-ret), a security
+hardening scheme implemented in compilers such as GCC and Clang, using the
+command line option `-mbranch-protection=pac-ret`. This option is enabled by
+default on most widely used Linux distributions. The hardening scheme mitigates
+[Return-Oriented Programming (ROP)](https://llsoftsec.github.io/llsoftsecbook/#return-oriented-programming)
+attacks by making sure that return addresses are only ever stored to memory
+in a signed form. This makes it substantially harder for attackers to divert
+control flow by overwriting a return address with a different value.
+
+## Pointer Authentication validator
+
+Pointer Authentication analysis is able to search for a number of gadget kinds,
+with the specific set depending on command line options:
+* [`ptrauth-pac-ret`](#return-address-protection-ptrauth-pac-ret) -
+  non-protected return instruction
+* [`ptrauth-tail-calls`](#return-address-protection-before-tail-call-ptrauth-tail-calls) -
+  performing a tail call with an untrusted value in the link register
+* [`ptrauth-forward-cf`](#indirect-branch-call-target-protection-ptrauth-forward-cf) -
+  non-protected destination of branch or call instruction
+* [`ptrauth-sign-oracles`](#signing-oracles-ptrauth-sign-oracles) -
+  signing of untrusted value (signing oracle)
+* [`ptrauth-auth-oracles`](#authentication-oracles-ptrauth-auth-oracles) -
+  revealing the result of authentication without crashing the program on failure
+  (authentication oracle)
+
+Validation is performed by `llvm-bolt-binary-analysis` on a per-function basis.
+First, the register properties are computed by analyzing the function as a whole.
+Then, the instructions are considered in isolation. For each kind of gadget,
+the set of susceptible instructions is computed. The properties of input or
+output registers of each such instruction are analyzed and reports are produced
+for unsafe instruction usage.
+
+Each gadget kind that is searched for can be characterized by the combination of
+* the set of instructions to analyze
+* the properties of input or output operands to check
+
+Currently, three properties can be computed for each register at any given
+program point:
+* **"trusted"** - the register is known not to be attacker-controlled, either because
+  it successfully passed authentication or because its value was materialized
+  using an instruction sequence that an attacker cannot tamper with
+  * **"safe-to-dereference"** (sometimes referred to as "s-t-d" below) -
+    a weaker property that the register can be controlled by an attacker to some
+    extent, but any memory access using a value crafted by an attacker is known
+    to result in an access to an unmapped memory ("segmentation fault").
+    This makes it possible for authentication instructions to return an invalid
+    address on failure as long as it is known to crash the program on accessing
+    memory, but may requires extra care to be taken when implementing operations
+    like re-signing a pointer with a different signing schema without accessing
+    that address in-between. If any failed authentication instruction is
+    guaranteed to terminate the program abnormally, then "safe-to-dereference"
+    and "trusted" properties are equivalent.
+* **"cannot escape unchecked"** - at every possible execution path after this point,
+  it is known to be impossible for an attacker to determine that the value is
+  a result of a failed authentication operation (for example, the register is
+  zeroed, or its value is checked to be valid, so that failure results in
+  immediate abnormal program termination).
+
+The below sub-sections describe the particular detectors. Please note that while
+the descriptions refer to AArch64 for simplicity, the implementation of gadget
+detectors in `llvm-bolt-binary-analysis` attempts to be target-neutral by
+isolating AArch64 specifics in target-dependent hooks.
+
+### Return address protection (`ptrauth-pac-ret`)
+
+**Instructions:** Return instructions without built-in authentication:
+either `ret` (implicit `x30` register) or `ret <reg>`, but not `retaa` and
+similar instructions.
+
+**Property:** The register holding the return address must be safe-to-dereference.
+
+**Notes:** Cross-exception-level return instructions (`eret`) are not analyzed yet.
+
+A report is generated for a return instruction whose destination is possibly
+attacker-controlled.
+
+**Examples:**
+```asm
+authenticated_return:
+  pacibsp
+  ; ...
+  ; ... some code here ...
+  ; ...
+  retab ; Built-in authentication, thus out of scope.
+
+good_leaf_function:
+  ; x30 is implicitly safe-to-dereference (s-t-d) and trusted at function entry.
+  mov     x0, #42
+  ; x30 was not written to by this function, thus remains s-t-d.
+  ret
+
+good_non_leaf_function:
+  pacibsp
+
+  ; Spilling signed return address.
+  stp     x29, x30, [sp, #-16]!
+  mov     x29, sp
+
+  bl      callee
+
+  ; Re-loading signed return address.
+  ; LDP writes to x30 and thus resets it to neither s-t-d nor trusted state.
+  ldp     x29, x30, [sp], #16
+
+  ; Checking that signature is valid.
+  ; AUTIBSP sets "s-t-d" property of x30, but not "trusted" (unless FEAT_FPAC
+  ; is known to be implemented).
+  autibsp
+
+  ; x30 is s-t-d at this point.
+  ret
+
+bad_spill:
+  ; x30 is implicitly s-t-d at function entry.
+  stp     x29, x30, [sp, #-16]!
+  mov     x29, sp
+
+  bl      callee ; Spilled x30 may have been overwritten on stack.
+
+  ; Writing to x30 resets its s-t-d property.
+  ldp     x29, x30, [sp], #16
+  ; x30 is unsafe by the time it is used by ret, thus generating a report.
+  ret
+
+bad_clobber:
+  pacibsp
+  ; ...
+  ; ... some code here ...
+  ; ...
+  autibsp
+  mov     x30, x1
+  ; The value in LR is unsafe, even though there was autibsp above.
+  ret
+```
 
-#### pac-ret analysis
+### Return address protection before tail call (`ptrauth-tail-calls`)
 
-`pac-ret` protection is a security hardening scheme implemented in compilers
-such as GCC and Clang, using the command line option
-`-mbranch-protection=pac-ret`. This option is enabled by default on most widely
-used Linux distributions.
+**Instructions:** Branch instructions (both direct and indirect, regular or
+with built-in authentication), classified as tail calls either by BOLT or by
+PtrAuth gadget scanner's heuristic.
 
-The hardening scheme mitigates
-[Return-Oriented Programming (ROP)](https://llsoftsec.github.io/llsoftsecbook/#return-oriented-programming)
-attacks by making sure that return addresses are only ever stored to memory with
-a cryptographic hash, called a
-["Pointer Authentication Code" (PAC)](https://llsoftsec.github.io/llsoftsecbook/#pointer-authentication),
-in the upper bits of the pointer. This makes it substantially harder for
-attackers to divert control flow by overwriting a return address with a
-different value.
+**Property:** `x30` must be trusted.
 
-The hardening scheme relies on compilers producing appropriate code sequences when
-processing return addresses, especially when these are stored to and retrieved
-from memory.
+**Notes:** Heuristics are involved to classify instructions either as a tail
+call or as another kind of branch (such as jump table or computed goto).
 
-The `pac-ret` binary analysis can be invoked using the command line option
-`--scanners=pac-ret`. It makes `llvm-bolt-binary-analysis` scan through the
-provided binary, checking each function for the following security property:
+A report is generated if tail call is performed with untrusted link register.
+This basically means that the tail-callee function would have link register
+untrusted on its entry.
 
-> For each procedure and exception return instruction, the destination register
-> must have one of the following properties:
->
-> 1. be immutable within the function, or
-> 2. the last write to the register must be by an authenticating instruction. This
->    includes combined authentication and return instructions such as `RETAA`.
+```asm
+untrusted_tail_call:
+  stp     x29, x30, [sp, #-16]!
+  mov     x29, sp
+  bl      callee
+  ldp     x29, x30, [sp], #16
+  ; x30 is neither trusted nor safe-to-dereference at this point.
+  b       tail_callee
 
-##### Example 1
+tail_callee:
+  pacibsp
+  ; ...
+```
 
-For example, a typical non-pac-ret-protected function looks as follows:
+Even though `x30` is likely to be safe-to-dereference before exit from a function
+(whether via return or tail call) in a consistently pac-ret-protected program,
+with respect to this gadget kind it further must be fully "trusted".
+With `x30` being safe-to-dereference, but not fully trusted at the entry to the
+tail callee, the subsequent `pacibsp` instruction may act as a [signing oracle](#signing-oracles).
+
+**FIXME:** Is it actually possible when none of `FEAT_FPAC`, `FEAT_EPAC`, or `FEAT_PAuth2` are implemented?
+
+Properly mitigating this issue would usually require inserting an explicit
+check after a regular authentication instruction, which may be either too
+expensive (if a fully-generic XPAC-based sequence is being used) on one hand,
+or not required at all (if `FEAT_FPAC` is known to be implemented) on the other hand.
+
+### Indirect branch / call target protection (`ptrauth-forward-cf`)
+
+**Instructions:** Indirect call and branch instructions without built-in
+authentication: either `blr <reg>` or `br <reg>`, but not `blraa`, `braa`
+and similar instructions.
+
+**Property:** Call or branch target register must be safe-to-dereference.
+
+Report is generated for an indirect branch or call instruction whose destination
+is possibly attacker-controlled.
+
+**Examples:**
+
+```asm
+direct_call:
+  ; ...
+  bl     callee ; Direct call, thus out of scope.
+  ; ...
+
+authenticated_call:
+  ; ...
+  ldr     x2, [x1]
+  blraa   x2, x1   ; Built-in authentication, thus out of scope.
+  ; ...
+
+good_call:
+  ; ...
+  ldr     x2, [x1]
+  autia   x2, x1
+  blr     x2
+  ; ...
+
+bad_call:
+  ; ...
+  ldr     x2, [x1]
+  autia   x2, x1
+  ; Store unprotected address.
+  stp     x2, [x3]
+  ; ...
+  ; The callee address may have been overwritten in memory.
+  ldr     x2, [x3]
+  blr     x2
+  ; ...
+
+good_call_dataflow:
+  cbz     x0, .L1
+  ldr     x2, [x1]
+  autia   x2, x1
+  b       .L2
+.L1:
+  adrp    x2, callee
+  add     x2, x2, :lo12:callee
+.L2:
+  ; Dataflow analysis can deduce that x2 is s-t-d on any possible execution
+  ; path leading to the below "br x2" instruction.
+  br      x2
+
+bad_call_dataflow:
+  cbz     x0, .L3
+  adrp    x2, callee
+  add     x2, x2, :lo12:callee
+.L3:
+  ; x2 is untrusted is x0 is 0.
+  br      x2
 
-```
-        stp     x29, x30, [sp, #-0x10]!
-        mov     x29, sp
-        bl      g at PLT
-        add     x0, x0, #0x3
-        ldp     x29, x30, [sp], #0x10
-        ret
 ```
 
-The return instruction `ret` implicitly uses register `x30` as the address to
-return to. Register `x30` was last written by instruction `ldp`, which is not an
-authenticating instruction. `llvm-bolt-binary-analysis --scanners=pac-ret` will
-report this as follows:
+### Signing oracles (`ptrauth-sign-oracles`)
 
-```
-GS-PACRET: non-protected ret found in function f1, basic block .LBB00, at address 10310
-  The return instruction is     00010310:       ret # pacret-gadget: pac-ret-gadget<Ret:MCInstBBRef<BB:.LBB00:6>, Overwriting:[MCInstBBRef<BB:.LBB00:5> ]>
-  The 1 instructions that write to the return register after any authentication are:
-  1.     0001030c:      ldp     x29, x30, [sp], #0x10
-  This happens in the following basic block:
-    000102fc:   stp     x29, x30, [sp, #-0x10]!
-    00010300:   mov     x29, sp
-    00010304:   bl      g at PLT
-    00010308:   add     x0, x0, #0x3
-    0001030c:   ldp     x29, x30, [sp], #0x10
-    00010310:   ret # pacret-gadget: pac-ret-gadget<Ret:MCInstBBRef<BB:.LBB00:6>, Overwriting:[MCInstBBRef<BB:.LBB00:5> ]>
-```
+**Instructions:** Address-signing instructions.
+
+**Property:** The address being signed must be trusted.
+
+Reports signing of untrusted values, as this could make arbitrary and possibly
+attacker-controlled values indistinguishable from perfectly trusted and protected ones.
+
+**FIXME:** Is `aut** + pac**` sequence actually exploitable when none of
+`FEAT_FPAC`, `FEAT_EPAC`, or `FEAT_PAuth2` are implemented?
 
-The exact format of how `llvm-bolt-binary-analysis` reports this is expected to
-evolve over time.
+**Examples:**
 
-##### Example 2: multiple "last-overwriting" instructions
+```asm
+good_sign_constant:
+  ; ...
+  adrp    x0, sym
+  add     x0, x0, :lo12:sym
+  pacda   x0, x1
+  ; ...
 
-A simple example that shows how there can be a set of "last overwriting"
-instructions of a register follows:
+good_resign:
+  ; ...
+  autda   x0, x1
+  ; x0 is s-t-d here.
+  ldr     x2, [x0]
+  ; If we got here without crashing on the above LDR, x0 is fully trusted.
+  pacdb   x0, x1
+  ; ...
 
+bad_resign_if_not_fpac:
+  ; ...
+  autda   x0, x1
+  ; x0 is only s-t-d, but not trusted here, unless autda raises an error on failure.
+  pacdb   x0, x1
+  ; ...
+
+very_bad_function:
+  pacda   x0, x1
+  ret
 ```
-        paciasp
-        stp     x29, x30, [sp, #-0x10]!
-        ldp     x29, x30, [sp], #0x10
-        cbnz    x0, 1f
-        autiasp
-1:
-        ret
+
+### Authentication oracles (`ptrauth-auth-oracles`)
+
+**Instructions:** Standalone authentication instructions: `autda`, `autdb`, etc.
+(i.e. not those built into corresponding memory-accessing instructions, such as
+`ldraa` or `blraa`).
+
+**Property:** The **result** of authentication must be written to a register
+that cannot escape unchecked.
+
+Reports authentication instructions, whose result (success or failure) can be
+observed by the attacker and used to guess the correct PAC field by trial-and-error.
+This check is turned off if `--auth-traps-on-failure` command line option is
+passed to indicate that the target CPU is known to implement `FEAT_FPAC`.
+
+**Examples:**
+
+```asm
+good_auth_call:
+  paciasp
+  stp     x29, x30, [sp, #-16]!
+  mov     x29, sp
+
+  ; The result of authentication is inevitably dereferenced by blr,
+  ; crashing the program in case autia returned non-canonical address due to
+  ; authentication failure.
+  cbz     x2, .L1
+  autia   x0, x1
+  blr     x0
+
+.L1:
+  ldp     x29, x30, [sp], #16
+  autiasp
+  ret
+
+bad_auth_call:
+  paciasp
+  stp     x29, x30, [sp, #-16]!
+  mov     x29, sp
+
+  ; x0 may be observed by the caller of bad_call if x2 is 0.
+  autia   x0, x1
+  cbz     x2, .L2
+  blr     x0
+
+.L2:
+  ldp     x29, x30, [sp], #16
+  autiasp
+  ret
+
+bad_leaks_to_callee:
+  paciasp
+  stp     x29, x30, [sp, #-16]!
+  mov     x29, sp
+
+  ldr     x20, [x0]
+  autda   x20, x0
+  ; The result of authentication is leaked to the called function.
+  bl      callee
+  ; The below ldr instruction would properly check x20 is placed above the call.
+  ldr     x0, [x20]
+
+  ldp     x29, x30, [sp], #16
+  autiasp
+  ret
 ```
 
-This will produce the following diagnostic:
+## Known false positives and negatives
+
+### Control-flow graph availability
+
+The analysis quality is degraded if BOLT is unable to reconstruct control-flow
+graph of the function correctly.
+
+When reconstructing CFG for a particular function, it is possible that BOLT
+finds a code pattern that it is unable to handle. In that case PtrAuth validator
+processes a function containing a flat list of instructions (as opposed to
+interlinked basic blocks) and the reports (if any) are printed without the
+`, basic block <name>` part in the first line.
+
+At the time of writing this, BOLT may fail to properly recognize jump table
+implementation in code but still construct *some* control-flow graph for a
+function. For that reason, `llvm-bolt-binary-analysis` generates a report if
+any basic block in a function is unreachable, according to the reconstructed CFG.
+While this is not inherently impossible, it is unlikely to occur in an optimized
+code and is thus likely to indicate an error in CFG reconstruction.
 
 ```
-GS-PACRET: non-protected ret found in function f_crossbb1, basic block .Ltmp0, at address 102dc
-  The return instruction is     000102dc:       ret # pacret-gadget: pac-ret-gadget<Ret:MCInstBBRef<BB:.Ltmp0:0>, Overwriting:[MCInstBBRef<BB:.LFT0:0> MCInstBBRef<BB:.LBB00:2> ]>
-  The 2 instructions that write to the return register after any authentication are:
-  1.     000102d0:      ldp     x29, x30, [sp], #0x10
-  2.     000102d8:      autiasp
+GS-PAUTH: untrusted link register found before tail call in function luaV_execute/1(*2), basic block .Ltmp2198, at address 338a0
+  The instruction is     000338a0:      br      x16 # UNKNOWN CONTROL FLOW
 ```
 
-(Yes, this diagnostic could be improved because the second "overwriting"
-instruction, `autiasp`, is an authenticating instruction...)
+### Last writing instructions
+
+Some of the reports contain extra information like this
 
-##### Known false positives or negatives
+```
+  The 1 instructions that write to the affected registers after any authentication are:
+  1.     000102b4:      ldr     x0, [x1]
+  This happens in the following basic block:
+    000102b4:   ldr     x0, [x1]
+    000102b8:   pacda   x0, x1
+    000102bc:   ret
+```
 
-The following are current known cases of false positives:
+This information is provided on a best-effort basis and is less reliable than
+gadget reports themselves.
 
-1. Not handling "no-return" functions. See issue
-   [#115154](https://github.com/llvm/llvm-project/issues/115154) for details and
-   pointers to open PRs to fix this.
-2. Not recognizing that a move of a properly authenticated value between registers,
-   results in the destination register having a properly authenticated value.
-   For example, the scanner currently produces a false negative for the following
-   code sequence:
-   ```
-        autiasp
-        mov     x16, x30
-        ret     x16
-   ```
+### Other known issues
 
-The following are current known cases of false negatives:
+* Not handling "no-return" functions. See issue
+  [#115154](https://github.com/llvm/llvm-project/issues/115154) for details and
+  pointers to open PRs to fix this.
 
-1. Not handling functions for which the CFG cannot be reconstructed by BOLT. The
-   plan is to implement support for this, picking up the implementation from the
-   [prototype branch](
-   https://github.com/llvm/llvm-project/compare/main...kbeyls:llvm-project:bolt-gadget-scanner-prototype).
 
 ## How to add your own binary analysis
 

>From 516bc122e68fdeecf1dbf9b4206a1dbc22d91acf Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Tue, 20 Jan 2026 22:05:13 +0300
Subject: [PATCH 02/15] Document more known issues

---
 bolt/docs/BinaryAnalysis.md | 103 +++++++++++++++++++++++++++++++++++-
 1 file changed, 101 insertions(+), 2 deletions(-)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index 59ab4791f8ff9..01f88b2a9b009 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -484,7 +484,7 @@ bad_leaks_to_callee:
   ret
 ```
 
-## Known false positives and negatives
+## Known issues and missing features
 
 ### Control-flow graph availability
 
@@ -525,12 +525,111 @@ Some of the reports contain extra information like this
 This information is provided on a best-effort basis and is less reliable than
 gadget reports themselves.
 
+### Feature: scan for unsafe computation of discriminator value
+
+There is a common pattern on AArch64 to compute the discriminator as a blend
+of an address and an integer modifier by inserting a compile-time constant
+value into the top 16 bits of the storage address. It is not necessarily
+possible to prevent an attacker from modifying the storage address, but the
+insertion of 16-bit constant modifier can always be performed immediately before
+the discriminator value is used by signing or authentication instruction.
+
+While not as bad as signing an arbitrary value or spilling an already authenticated
+value to memory, spilling a "ready-to-use" discriminator value instead of computing
+it right before usage is something we would rather avoid. On the other hand,
+using an arbitrary value as the discriminator should probably be allowed, making
+it hard to distinguish the below patterns:
+
+```asm
+; Valid and not reported.
+good_store_with_address_and_constant_diversity:
+  mov     x16, x1
+  movk    x16, #1234, lsl #48
+  pacda   x0, x16
+  str     x0, [x1]
+  ret
+
+; Valid and not reported.
+good_store_with_address_diversity:
+  pacda   x0, x1
+  str     x0, [x1]
+  ret
+
+; Spilled discriminator. Not critically wrong, but could rather be avoided.
+; Not reported, but probably should (false negative).
+bad_spilling:
+  mov     x16, x1
+  movk    x16, #1234, lsl #48
+
+  ; Spilling the discriminator to memory.
+  str     x16, [x2]
+  ; Reloaded value could have been modified by an attacker.
+  ldr     x16, [x2]
+
+  pacda   x0, x16
+  str     x0, [x1]
+  ret
+
+; Not reported (and should probably not).
+better_spilling:
+  mov     x16, x1
+  str     x16, [x2]
+  ; Reloaded value could have been modified by an attacker.
+  ldr     x16, [x2]
+
+  movk    x16, #1234, lsl #48
+  pacda   x0, x16
+  str     x0, [x1]
+  ret
+```
+
+### Handling of constants
+
+While (PC-relative) address constants are tracked as "trusted" register state
+by `SrcSafetyAnalysis`, constant values are not generally accounted for.
+
+This results in false-positives like reporting
+
+```asm
+  ; Let assume FEAT_FPAC is implemented.
+  autda   x16, x22
+  mov     x17, #0x128
+  add     x16, x16, x17
+  pacda   x16, x22
+```
+
+as a signing oracle, even though
+
+```asm
+  ; Let assume FEAT_FPAC is implemented.
+  autda   x16, x22
+  add     x16, x16, #8
+  pacda   x16, x22
+```
+
+is not reported, because `add Xdst, Xsrc, #imm` is recognized as a safe address
+computation.
+
+As an example of false-negative, it is possible that an instruction like
+`add x0, x0, #1` could be called in a loop with an attacker-controlled number
+of iterations, making it technically possible for an attacker to replace a
+valid pointer with a pointer to an arbitrary *higher* address.
+
 ### Other known issues
 
 * Not handling "no-return" functions. See issue
   [#115154](https://github.com/llvm/llvm-project/issues/115154) for details and
   pointers to open PRs to fix this.
-
+* Scanning of binaries compiled by Clang at `-Oz` optimization level produces a
+  lot of reports due to outlining. Many such reports could probably be considered
+  false negatives as long as an attacker is unable to call `OUTLINED_FUNCTION`s
+  as ROP gadgets in the first place.
+* False positives are possible due to multi-instruction pointer-checking sequences
+  not being detected without CFG.
+* While obviously "checking" the result of pointer authentication, store
+  instructions do not transition their address operand register from safe-to-
+  dereference to trusted state yet. This does not affect scanning regular code
+  hardened neither by `pac-ret`, nor by `arm64e` or `pauthtest`.
 
 ## How to add your own binary analysis
 

>From c07787e4e208efdb75998efcd5e1b49278fc292c Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Tue, 20 Jan 2026 22:05:35 +0300
Subject: [PATCH 03/15] Describe extending PtrAuth gadget scanner

---
 bolt/docs/BinaryAnalysis.md | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index 01f88b2a9b009..f0c3d9cb147af 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -633,6 +633,28 @@ valid pointer with a pointer to an arbitrary *higher* address.
 
 ## How to add your own binary analysis
 
+### Pointer Authentication validator
+
+To implement the detection of a new gadget kind, add new
+`shouldReport*Gadget` function to `bolt/lib/Passes/PAuthGadgetScanner.cpp` and
+call it either from `FunctionAnalysisContext::findUnsafeUses` or
+`FunctionAnalysisContext::findUnsafeDefs`.
+
+To improve overall analysis quality by better computing register properties,
+either modify one of `*SafetyAnalysis` classes in `PAuthGadgetScanner.cpp`
+(if the improvement is target-neutral), or one of target-specific hooks in
+the subclass of `MCPlusBuilder` corresponding to your target (if an analysis of
+target-specific instruction patterns is to be improved).
+
+To add support for a new target, if one eventually implements similar pointer
+protection technique, implement PtrAuth-related hooks in the subclass of
+`MCPlusBuilder` corresponding to your target. Ideally, no changes should be
+needed in `PAuthGadgetScanner.cpp`, as it is intended to be reasonably target-
+independent, though it is possible that some amount of further generalization
+may be required.
+
+### New types of analyses
+
 _TODO: this section needs to be written. Ideally, we should have a simple
 "example" or "template" analysis that can be the starting point for implementing
 custom analyses_

>From adf60550d1798f476e15df63142abcb23376dd44 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Thu, 22 Jan 2026 22:03:58 +0300
Subject: [PATCH 04/15] Link: incomplete help message is a known issue

---
 bolt/docs/BinaryAnalysis.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index f0c3d9cb147af..350ad79eaca3f 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -20,6 +20,8 @@ shared object. Similar to other BOLT tools, `llvm-bolt-binary-analysis` expects
 
 In addition to options printed by `llvm-bolt-binary-analysis --help-hidden`,
 other relevant BOLT options can generally be passed, see `llvm-bolt --help-hidden`.
+Incomplete help message is a known issue and is tracked in
+[#176969](https://github.com/llvm/llvm-project/issues/176969).
 
 The only analysis which is currently implemented is validation of Pointer
 Authentication hardening applied to the binary.

>From 008af4fd77bd51c7c935bc4ab1b6496fbd750dda Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Thu, 22 Jan 2026 22:27:28 +0300
Subject: [PATCH 05/15] Move explanation of no-cfg fallback to the 'detailed'
 section

---
 bolt/docs/BinaryAnalysis.md | 19 +++++++------------
 1 file changed, 7 insertions(+), 12 deletions(-)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index 350ad79eaca3f..83a29d7671955 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -54,18 +54,6 @@ GS-PAUTH: signing oracle found in function function_name, basic block .LBB016, a
   The 0 instructions that write to the affected registers after any authentication are:
 ```
 
-Furthermore, a ", basic block `<name>`" part is omitted in a report, if BOLT was
-unable to reconstruct control-flow graph for the particular function:
-
-```
-GS-PAUTH: signing oracle found in function function_name_nocfg, at address 10510
-  The instruction is     00010510:      pacda   x0, x1
-  The 0 instructions that write to the affected registers after any authentication are:
-```
-
-The analysis is likely to be less precise when CFG information is absent or
-incomplete.
-
 ## Background and motivation
 
 ### Security scanners
@@ -191,6 +179,13 @@ program point:
   zeroed, or its value is checked to be valid, so that failure results in
   immediate abnormal program termination).
 
+Generally, BOLT strives to reconstruct the control-flow graph of each function,
+which is important for dataflow analysis. However, when BOLT fails to recognize
+some control flow in the particular function, that function ends up being
+represented as a flat list of instructions - in such cases
+`llvm-bolt-binary-analysis` computes register properties using a fallback
+analysis implementation, which is less precise.
+
 The below sub-sections describe the particular detectors. Please note that while
 the descriptions refer to AArch64 for simplicity, the implementation of gadget
 detectors in `llvm-bolt-binary-analysis` attempts to be target-neutral by

>From cc86a1193189c5636ffb1f4e63a15cff71d1d280 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Thu, 22 Jan 2026 22:37:08 +0300
Subject: [PATCH 06/15] Move 'Usage' section, add brief algorithm description
 to the above section

---
 bolt/docs/BinaryAnalysis.md | 103 ++++++++++++++++++++----------------
 1 file changed, 56 insertions(+), 47 deletions(-)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index 83a29d7671955..ef4e8d4629e36 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -7,53 +7,6 @@ The `llvm-bolt-binary-analysis` tool enables running requested analyses
 on binaries, and generating reports. It does this by building on top of the
 analyses implemented in the BOLT libraries.
 
-## Usage
-
-```
-llvm-bolt-binary-analysis --scanners=<list> [options] <binary>
-```
-
-The `--scanners=` option accepts a comma-separated list of analyses to run on
-the provided binary. The binary to be analyzed can be either ELF executable or
-shared object. Similar to other BOLT tools, `llvm-bolt-binary-analysis` expects
-`binary` to be unstripped and preferably linked with `--emit-relocs` linker option.
-
-In addition to options printed by `llvm-bolt-binary-analysis --help-hidden`,
-other relevant BOLT options can generally be passed, see `llvm-bolt --help-hidden`.
-Incomplete help message is a known issue and is tracked in
-[#176969](https://github.com/llvm/llvm-project/issues/176969).
-
-The only analysis which is currently implemented is validation of Pointer
-Authentication hardening applied to the binary.
-The specific set of gadget kinds which are searched for depends on command line
-options. Each gadget found by PtrAuth gadget scanner results in a plain text
-report printed at the end of the analysis.
-Furthermore, an attempt is made to provide an extra information on the
-instructions that made the register unsafe.
-Please note that this extra information is provided on a best-effort basis and
-is not expected to be as accurate as the reports themselves.
-
-Here is an example of the report:
-
-```
-GS-PAUTH: signing oracle found in function function_name, basic block .LBB08, at address 102b8
-  The instruction is     000102b8:      pacda   x0, x1
-  The 1 instructions that write to the affected registers after any authentication are:
-  1.     000102b4:      ldr     x0, [x1]
-  This happens in the following basic block:
-    000102b4:   ldr     x0, [x1]
-    000102b8:   pacda   x0, x1
-    000102bc:   ret
-```
-
-A similar report without the associated extra information looks like this:
-
-```
-GS-PAUTH: signing oracle found in function function_name, basic block .LBB016, at address 10384
-  The instruction is     00010384:      pacda   x0, x1
-  The 0 instructions that write to the affected registers after any authentication are:
-```
-
 ## Background and motivation
 
 ### Security scanners
@@ -130,6 +83,62 @@ attacks by making sure that return addresses are only ever stored to memory
 in a signed form. This makes it substantially harder for attackers to divert
 control flow by overwriting a return address with a different value.
 
+The approach to validation of Pointer Authentication hardening implemented in
+`llvm-bolt-binary-analysis` is tracking register safety using dataflow analysis.
+At each program point it is computed whether the particular register can be
+controlled and whether it can be inspected by an attacker under
+[Pointer Authentication threat model](https://clang.llvm.org/docs/PointerAuthentication.html#theory-of-operation).
+Then, for a number of sensitive instruction kinds (such as function calls and
+pointer signing instructions), the properties of input or output operands are
+inspected to check if the particular instruction is emitted in a safe manner.
+
+## Usage
+
+```
+llvm-bolt-binary-analysis --scanners=<list> [options] <binary>
+```
+
+The `--scanners=` option accepts a comma-separated list of analyses to run on
+the provided binary. The binary to be analyzed can be either ELF executable or
+shared object. Similar to other BOLT tools, `llvm-bolt-binary-analysis` expects
+`binary` to be unstripped and preferably linked with `--emit-relocs` linker option.
+
+In addition to options printed by `llvm-bolt-binary-analysis --help-hidden`,
+other relevant BOLT options can generally be passed, see `llvm-bolt --help-hidden`.
+Incomplete help message is a known issue and is tracked in
+[#176969](https://github.com/llvm/llvm-project/issues/176969).
+
+The only analysis which is currently implemented is validation of Pointer
+Authentication hardening applied to the binary.
+The specific set of gadget kinds which are searched for depends on command line
+options. Each gadget found by PtrAuth gadget scanner results in a plain text
+report printed at the end of the analysis.
+Furthermore, an attempt is made to provide an extra information on the
+instructions that made the register unsafe.
+Please note that this extra information is provided on a best-effort basis and
+is not expected to be as accurate as the reports themselves.
+
+Here is an example of the report:
+
+```
+GS-PAUTH: signing oracle found in function function_name, basic block .LBB08, at address 102b8
+  The instruction is     000102b8:      pacda   x0, x1
+  The 1 instructions that write to the affected registers after any authentication are:
+  1.     000102b4:      ldr     x0, [x1]
+  This happens in the following basic block:
+    000102b4:   ldr     x0, [x1]
+    000102b8:   pacda   x0, x1
+    000102bc:   ret
+```
+
+A similar report without the associated extra information looks like this:
+
+```
+GS-PAUTH: signing oracle found in function function_name, basic block .LBB016, at address 10384
+  The instruction is     00010384:      pacda   x0, x1
+  The 0 instructions that write to the affected registers after any authentication are:
+```
+
 ## Pointer Authentication validator
 
 Pointer Authentication analysis is able to search for a number of gadget kinds,

>From c217e27141b997a038906b3a1e64472bc546ad11 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Thu, 22 Jan 2026 22:37:56 +0300
Subject: [PATCH 07/15] Add table of contents

---
 bolt/docs/BinaryAnalysis.md | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index ef4e8d4629e36..1bc039cd6e012 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -7,6 +7,12 @@ The `llvm-bolt-binary-analysis` tool enables running requested analyses
 on binaries, and generating reports. It does this by building on top of the
 analyses implemented in the BOLT libraries.
 
+Contents
+1. [Background and motivation](#background-and-motivation)
+2. [Usage](#usage)
+3. [Pointer Authentication validator](#pointer-authentication-validator)
+4. [How to add your own analysis](#how-to-add-your-own-analysis)
+
 ## Background and motivation
 
 ### Security scanners
@@ -490,9 +496,9 @@ bad_leaks_to_callee:
   ret
 ```
 
-## Known issues and missing features
+### Known issues and missing features
 
-### Control-flow graph availability
+#### Control-flow graph availability
 
 The analysis quality is degraded if BOLT is unable to reconstruct control-flow
 graph of the function correctly.
@@ -515,7 +521,7 @@ GS-PAUTH: untrusted link register found before tail call in function luaV_execut
   The instruction is     000338a0:      br      x16 # UNKNOWN CONTROL FLOW
 ```
 
-### Last writing instructions
+#### Last writing instructions
 
 Some of the reports contain extra information like this
 
@@ -531,7 +537,7 @@ Some of the reports contain extra information like this
 This information is provided on a best-effort basis and is less reliable than
 gadget reports themselves.
 
-### Feature: scan for unsafe computation of discriminator value
+#### Feature: scan for unsafe computation of discriminator value
 
 There is a common pattern on AArch64 to compute the discriminator as a blend
 of an address and an integer modifier by inserting a compile-time constant
@@ -589,7 +595,7 @@ better_spilling:
   ret
 ```
 
-### Handling of constants
+#### Handling of constants
 
 While (PC-relative) address constants are tracked as "trusted" register state
 by `SrcSafetyAnalysis`, constant values are not generally accounted for.
@@ -621,7 +627,7 @@ As an example of false-negative, it is possible that an instruction like
 of iterations, making it technically possible for an attacker to replace a
 valid pointer with a pointer to an arbitrary *higher* address.
 
-### Other known issues
+#### Other known issues
 
 * Not handling "no-return" functions. See issue
   [#115154](https://github.com/llvm/llvm-project/issues/115154) for details and
@@ -637,7 +643,7 @@ valid pointer with a pointer to an arbitrary *higher* address.
   dereference to trusted state yet. This does not affect scanning regular code
   hardened neither by `pac-ret`, nor by `arm64e` or `pauthtest`.
 
-## How to add your own binary analysis
+## How to add your own analysis
 
 ### Pointer Authentication validator
 

>From 3a8c34cfedc3eae540040bd4da2e8fedb5153678 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Thu, 22 Jan 2026 23:41:40 +0300
Subject: [PATCH 08/15] Fix wording in the description of auth oracles

---
 bolt/docs/BinaryAnalysis.md | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index 1bc039cd6e012..2669598f4af28 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -441,20 +441,27 @@ that cannot escape unchecked.
 
 Reports authentication instructions, whose result (success or failure) can be
 observed by the attacker and used to guess the correct PAC field by trial-and-error.
-This check is turned off if `--auth-traps-on-failure` command line option is
-passed to indicate that the target CPU is known to implement `FEAT_FPAC`.
+
+The authentication oracles searched for by this detector are impossible if all
+authentication instructions are known to generate an irrecoverable error on
+failure. On AArch64 this is the case if CPU is known to implement `FEAT_FPAC`
+(though recoverability depends on the OS: for example, on Linux such errors
+result in signals that can be handled if configured accordingly).
+This check is disabled by `--auth-traps-on-failure` command line option.
 
 **Examples:**
 
 ```asm
+; The descriptions assume FEAT_FPAC is not implemented.
+
 good_auth_call:
   paciasp
   stp     x29, x30, [sp, #-16]!
   mov     x29, sp
 
-  ; The result of authentication is inevitably dereferenced by blr,
-  ; crashing the program in case autia returned non-canonical address due to
-  ; authentication failure.
+  ; The result of authentication is inevitably dereferenced by blr.
+  ; If autia returns a non-canonical address due to incorrect signature,
+  ; the program crashes when the resulting address is jumped-to by blr.
   cbz     x2, .L1
   autia   x0, x1
   blr     x0

>From 6a2a6cd03a5e1d995ba92099e0f9a01e97e14327 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Tue, 27 Jan 2026 18:49:23 +0300
Subject: [PATCH 09/15] Mention that ptrauth-tail-calls uses heuristics

---
 bolt/docs/BinaryAnalysis.md | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index 2669598f4af28..b6ee9ab3f8d67 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -634,6 +634,22 @@ As an example of false-negative, it is possible that an instruction like
 of iterations, making it technically possible for an attacker to replace a
 valid pointer with a pointer to an arbitrary *higher* address.
 
+#### Tail call detection
+
+`ptrauth-tail-calls` detector uses a heuristic to classify each branch
+instruction either as a tail call or as an unrelated branch instruction.
+
+Most other parts of BOLT should not break code when rewriting. Unlike them,
+this analyzer tries to keep reasonable balance between false positive reports
+and missed issues. For this reason, it inspects some other branch instructions
+in addition to those BOLT is sure about.
+
+While it should provide reasonable balance between false positives and false
+negatives on general code, this may not be the case for some specific patterns.
+An example of a generally uncommon code pattern that is likely to yield false
+positives is [labels-as-values GCC extension](https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html)
+which is also supported by Clang.
+
 #### Other known issues
 
 * Not handling "no-return" functions. See issue

>From e06d1776b9c7341f06408f526c3bc085d86fa170 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Thu, 29 Jan 2026 02:25:43 +0300
Subject: [PATCH 10/15] Misc cleanups

---
 bolt/docs/BinaryAnalysis.md | 50 ++++++++++++++++++++++---------------
 1 file changed, 30 insertions(+), 20 deletions(-)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index b6ee9ab3f8d67..b8d13ef48e969 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -284,17 +284,18 @@ bad_clobber:
 with built-in authentication), classified as tail calls either by BOLT or by
 PtrAuth gadget scanner's heuristic.
 
-**Property:** `x30` must be trusted.
+**Property:** link register (`x30` on AArch64) must be trusted.
 
 **Notes:** Heuristics are involved to classify instructions either as a tail
 call or as another kind of branch (such as jump table or computed goto).
 
 A report is generated if tail call is performed with untrusted link register.
-This basically means that the tail-callee function would have link register
-untrusted on its entry.
+This basically means that the tail-called function would have link register
+untrusted on its entry (unlike inherently correct address placed to link
+register by one of `bl*` instructions when non-tail call is performed).
 
 ```asm
-untrusted_tail_call:
+non_protected_tail_call:
   stp     x29, x30, [sp, #-16]!
   mov     x29, sp
   bl      callee
@@ -302,6 +303,16 @@ untrusted_tail_call:
   ; x30 is neither trusted nor safe-to-dereference at this point.
   b       tail_callee
 
+non_checked_tail_call:
+  pacibsp
+  stp     x29, x30, [sp, #-16]!
+  mov     x29, sp
+  bl      callee
+  ldp     x29, x30, [sp], #16
+  autibsp
+  ; x30 is safe-to-dereference, but not fully trusted at this point.
+  b       tail_callee
+
 tail_callee:
   pacibsp
   ; ...
@@ -311,7 +322,7 @@ Even though `x30` is likely to be safe-to-dereference before exit from a functio
 (whether via return or tail call) in a consistently pac-ret-protected program,
 with respect to this gadget kind it further must be fully "trusted".
 With `x30` being safe-to-dereference, but not fully trusted at the entry to the
-tail callee, the subsequent `pacibsp` instruction may act as a [signing oracle](#signing-oracles).
+tail callee, the subsequent `pacibsp` instruction may act as a [signing oracle](#signing-oracles-ptrauth-sign-oracles).
 
 **FIXME:** Is it actually possible when none of `FEAT_FPAC`, `FEAT_EPAC`, or `FEAT_PAuth2` are implemented?
 
@@ -414,7 +425,7 @@ good_resign:
   autda   x0, x1
   ; x0 is s-t-d here.
   ldr     x2, [x0]
-  ; If we got here without crashing on the above LDR, x0 is fully trusted.
+  ; If we got here without crashing on the above ldr, x0 is fully trusted.
   pacdb   x0, x1
   ; ...
 
@@ -476,7 +487,7 @@ bad_auth_call:
   stp     x29, x30, [sp, #-16]!
   mov     x29, sp
 
-  ; x0 may be observed by the caller of bad_call if x2 is 0.
+  ; x0 may be observed by the caller of bad_auth_call if x2 is 0.
   autia   x0, x1
   cbz     x2, .L2
   blr     x0
@@ -495,7 +506,7 @@ bad_leaks_to_callee:
   autda   x20, x0
   ; The result of authentication is leaked to the called function.
   bl      callee
-  ; The below ldr instruction would properly check x20 is placed above the call.
+  ; The below ldr instruction would properly check x20 if placed above the call.
   ldr     x0, [x20]
 
   ldp     x29, x30, [sp], #16
@@ -516,21 +527,20 @@ processes a function containing a flat list of instructions (as opposed to
 interlinked basic blocks) and the reports (if any) are printed without the
 `, basic block <name>` part in the first line.
 
-At the time of writing this, BOLT may fail to properly recognize jump table
-implementation in code but still construct *some* control-flow graph for a
-function. For that reason, `llvm-bolt-binary-analysis` generates a report if
-any basic block in a function is unreachable, according to the reconstructed CFG.
-While this is not inherently impossible, it is unlikely to occur in an optimized
-code and is thus likely to indicate an error in CFG reconstruction.
-
-```
-GS-PAUTH: untrusted link register found before tail call in function luaV_execute/1(*2), basic block .Ltmp2198, at address 338a0
-  The instruction is     000338a0:      br      x16 # UNKNOWN CONTROL FLOW
-```
+Furthermore, it is possible that CFG information is returned by BOLT for a
+function, even though the graph is imprecise (known issues are tracked in
+[#177761](https://github.com/llvm/llvm-project/issues/177761),
+[#178058](https://github.com/llvm/llvm-project/issues/178058),
+[#178232](https://github.com/llvm/llvm-project/issues/178232)).
+Inaccurate CFG information may result in false positives and false negatives,
+thus PtrAuth gadget scanner produces warning messages (at most once per
+function) for non-entry basic blocks without any predecessors in CFG: while
+unreachable basic blocks are technically correct, truly unreachable blocks are
+unlikely to exist in optimized code.
 
 #### Last writing instructions
 
-Some of the reports contain extra information like this
+Some of the reports contain extra information along these lines
 
 ```
   The 1 instructions that write to the affected registers after any authentication are:

>From 2742ebcafb790795d228429107e37ae5fec8eab3 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Fri, 27 Feb 2026 15:56:31 +0300
Subject: [PATCH 11/15] Apply suggestions from code review

Co-authored-by: Kristof Beyls <kristof.beyls at arm.com>
---
 bolt/docs/BinaryAnalysis.md | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index b8d13ef48e969..14952a53d171f 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -289,10 +289,10 @@ PtrAuth gadget scanner's heuristic.
 **Notes:** Heuristics are involved to classify instructions either as a tail
 call or as another kind of branch (such as jump table or computed goto).
 
-A report is generated if tail call is performed with untrusted link register.
-This basically means that the tail-called function would have link register
-untrusted on its entry (unlike inherently correct address placed to link
-register by one of `bl*` instructions when non-tail call is performed).
+A report is generated if a tail call is performed with an untrusted link register.
+This basically means that the tail-called function would have the link register
+untrusted on its entry (unlike the inherently correct address placed in the link
+register by one of `bl*` instructions when a non-tail call is performed).
 
 ```asm
 non_protected_tail_call:
@@ -337,9 +337,9 @@ or not required at all (if `FEAT_FPAC` is known to be implemented) on the other
 authentication: either `blr <reg>` or `br <reg>`, but not `blraa`, `braa`
 and similar instructions.
 
-**Property:** Call or branch target register must be safe-to-dereference.
+**Property:** The call or branch target register must be safe-to-dereference.
 
-Report is generated for an indirect branch or call instruction whose destination
+A report is generated for an indirect branch or call instruction whose destination
 is possibly attacker-controlled.
 
 **Examples:**
@@ -368,7 +368,7 @@ bad_call:
   ldr     x2, [x1]
   autia   x2, x1
   ; Store unprotected address.
-  stp     x2, [x3]
+  str     x2, [x3]
   ; ...
   ; The callee address may have been overwritten in memory.
   ldr     x2, [x3]
@@ -393,7 +393,7 @@ bad_call_dataflow:
   adrp    x2, callee
   add     x2, x2, :lo12:callee
 .L3:
-  ; x2 is untrusted is x0 is 0.
+  ; x2 is untrusted if x0 is 0.
   br      x2
 
 ```

>From e849f5dd079102674f4d0226c643490ea80ccc65 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Fri, 27 Feb 2026 16:03:49 +0300
Subject: [PATCH 12/15] Apply more suggestions

---
 bolt/docs/BinaryAnalysis.md | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index 14952a53d171f..119758fdb52fc 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -92,7 +92,7 @@ control flow by overwriting a return address with a different value.
 The approach to validation of Pointer Authentication hardening implemented in
 `llvm-bolt-binary-analysis` is tracking register safety using dataflow analysis.
 At each program point it is computed whether the particular register can be
-controlled and whether it can be inspected by an attacker under
+controlled and whether it can be inspected by an attacker under the
 [Pointer Authentication threat model](https://clang.llvm.org/docs/PointerAuthentication.html#theory-of-operation).
 Then, for a number of sensitive instruction kinds (such as function calls and
 pointer signing instructions), the properties of input or output operands are
@@ -105,8 +105,8 @@ llvm-bolt-binary-analysis --scanners=<list> [options] <binary>
 ```
 
 The `--scanners=` option accepts a comma-separated list of analyses to run on
-the provided binary. The binary to be analyzed can be either ELF executable or
-shared object. Similar to other BOLT tools, `llvm-bolt-binary-analysis` expects
+the provided binary. The binary to be analyzed can be either an ELF executable or
+a shared object. Similar to other BOLT tools, `llvm-bolt-binary-analysis` expects
 `binary` to be unstripped and preferably linked with `--emit-relocs` linker option.
 
 In addition to options printed by `llvm-bolt-binary-analysis --help-hidden`,
@@ -119,7 +119,7 @@ Authentication hardening applied to the binary.
 The specific set of gadget kinds which are searched for depends on command line
 options. Each gadget found by PtrAuth gadget scanner results in a plain text
 report printed at the end of the analysis.
-Furthermore, an attempt is made to provide an extra information on the
+Furthermore, an attempt is made to provide additional information on the
 instructions that made the register unsafe.
 Please note that this extra information is provided on a best-effort basis and
 is not expected to be as accurate as the reports themselves.
@@ -183,7 +183,7 @@ program point:
     to result in an access to an unmapped memory ("segmentation fault").
     This makes it possible for authentication instructions to return an invalid
     address on failure as long as it is known to crash the program on accessing
-    memory, but may requires extra care to be taken when implementing operations
+    memory, but may require extra care to be taken when implementing operations
     like re-signing a pointer with a different signing schema without accessing
     that address in-between. If any failed authentication instruction is
     guaranteed to terminate the program abnormally, then "safe-to-dereference"

>From 4096ce43ad54660092aae9d3e524a105c242d7b4 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Sat, 28 Feb 2026 12:53:21 +0300
Subject: [PATCH 13/15] Address other comments

---
 bolt/docs/BinaryAnalysis.md | 43 +++++++++++++++++++++++++++++++++----
 1 file changed, 39 insertions(+), 4 deletions(-)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index 119758fdb52fc..5496b024d1005 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -89,14 +89,49 @@ attacks by making sure that return addresses are only ever stored to memory
 in a signed form. This makes it substantially harder for attackers to divert
 control flow by overwriting a return address with a different value.
 
+It is possible to link object files with different pac-ret hardening modes together,
+as each particular function is responsible for signing the LR value in the prologue
+and authenticating it in the epilogue. Other hardening variants exist that break
+ABI compatibility, see the description of
+[PAuth ABI Extensions](https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst)
+for details. While pac-ret hardens (signs and authenticates) return addresses,
+PAuth ABI may harden most other code pointers as well, such as function pointers
+and labels used as the destination of computed goto.
+
 The approach to validation of Pointer Authentication hardening implemented in
-`llvm-bolt-binary-analysis` is tracking register safety using dataflow analysis.
+`llvm-bolt-binary-analysis` is tracking register properties using dataflow analysis.
 At each program point it is computed whether the particular register can be
 controlled and whether it can be inspected by an attacker under the
 [Pointer Authentication threat model](https://clang.llvm.org/docs/PointerAuthentication.html#theory-of-operation).
 Then, for a number of sensitive instruction kinds (such as function calls and
 pointer signing instructions), the properties of input or output operands are
 inspected to check if the particular instruction is emitted in a safe manner.
+For example, for a return instruction this usually means that either the link
+register (x30) was never clobbered in the function or that it was authenticated
+at some point and never written to since then:
+
+```
+foo:
+    ; x30 is assumed to be trusted on function entry
+  cbnz    x0, .L1
+    ; For every possible execution path leading to this point, x30 was never
+    ; clobbered and is thus safe to be used by `ret`.
+  ret
+.L1:
+  paciasp
+  stp     x29, x30, [sp, #-16]!
+  mov     x29, sp
+
+  bl      bar
+
+  ldp     x29, x30, [sp], #16
+  autiasp
+    ; At this point, x30 was never written to since `autiasp`.
+  ret
+```
+
+The real rules being used by `llvm-bolt-binary-analysis` are somewhat more
+complex, see the below sections for the detailed explanation.
 
 ## Usage
 
@@ -114,8 +149,8 @@ other relevant BOLT options can generally be passed, see `llvm-bolt --help-hidde
 Incomplete help message is a known issue and is tracked in
 [#176969](https://github.com/llvm/llvm-project/issues/176969).
 
-The only analysis which is currently implemented is validation of Pointer
-Authentication hardening applied to the binary.
+The only analysis which is currently implemented is validation of a number of
+Pointer Authentication-based hardening schemes such as pac-ret and PAuth ABI.
 The specific set of gadget kinds which are searched for depends on command line
 options. Each gadget found by PtrAuth gadget scanner results in a plain text
 report printed at the end of the analysis.
@@ -166,7 +201,7 @@ First, the register properties are computed by analyzing the function as a whole
 Then, the instructions are considered in isolation. For each kind of gadget,
 the set of susceptible instructions is computed. The properties of input or
 output registers of each such instruction are analyzed and reports are produced
-for unsafe instruction usage.
+for each detected gadget.
 
 Each gadget kind that is searched for can be characterized by the combination of
 * the set of instructions to analyze

>From 1aebbb732c315534a74a5fea6025069cdfdbc5a3 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Thu, 12 Mar 2026 21:54:37 +0300
Subject: [PATCH 14/15] Drop FIXMEs, open separate issues instead

---
 bolt/docs/BinaryAnalysis.md | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index 5496b024d1005..c4be115ef8f64 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -327,7 +327,7 @@ call or as another kind of branch (such as jump table or computed goto).
 A report is generated if a tail call is performed with an untrusted link register.
 This basically means that the tail-called function would have the link register
 untrusted on its entry (unlike the inherently correct address placed in the link
-register by one of `bl*` instructions when a non-tail call is performed).
+register by one of the `bl*` instructions when a non-tail call is performed).
 
 ```asm
 non_protected_tail_call:
@@ -356,10 +356,9 @@ tail_callee:
 Even though `x30` is likely to be safe-to-dereference before exit from a function
 (whether via return or tail call) in a consistently pac-ret-protected program,
 with respect to this gadget kind it further must be fully "trusted".
-With `x30` being safe-to-dereference, but not fully trusted at the entry to the
-tail callee, the subsequent `pacibsp` instruction may act as a [signing oracle](#signing-oracles-ptrauth-sign-oracles).
-
-**FIXME:** Is it actually possible when none of `FEAT_FPAC`, `FEAT_EPAC`, or `FEAT_PAuth2` are implemented?
+This corresponds to the way tail calls are emitted when `--aarch64-authenticated-lr-check-method=`
+option is specified with an argument other than `none`, or when such checks are
+requested for the particular subtarget by default.
 
 Properly mitigating this issue would usually require inserting an explicit
 check after a regular authentication instruction, which may be either too
@@ -442,8 +441,10 @@ bad_call_dataflow:
 Reports signing of untrusted values, as this could make arbitrary and possibly
 attacker-controlled values indistinguishable from perfectly trusted and protected ones.
 
-**FIXME:** Is `aut** + pac**` sequence actually exploitable when none of
-`FEAT_FPAC`, `FEAT_EPAC`, or `FEAT_PAuth2` are implemented?
+In absence of `--auth-traps-on-failure` command line option, this detector
+reports auth+sign pairs unless an explicit check is emitted to make sure the
+authentication operation succeeded. This corresponds to the way LLVM emits
+instructions on AArch64.
 
 **Examples:**
 
@@ -697,6 +698,9 @@ which is also supported by Clang.
 
 #### Other known issues
 
+* No lightweight variant of [`ptrauth-tail-calls`](#return-address-protection-before-tail-call-ptrauth-tail-calls),
+  see issue [#186204](https://github.com/llvm/llvm-project/issues/186204)
+  for the details.
 * Not handling "no-return" functions. See issue
   [#115154](https://github.com/llvm/llvm-project/issues/115154) for details and
   pointers to open PRs to fix this.

>From 4b19ac10cb5b96324ea32eb211806ecee95c982e Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Mon, 4 May 2026 17:25:38 +0300
Subject: [PATCH 15/15] Address the review comments. Misc cleanups

---
 bolt/docs/BinaryAnalysis.md | 71 ++++++++++++++++++++++---------------
 1 file changed, 43 insertions(+), 28 deletions(-)

diff --git a/bolt/docs/BinaryAnalysis.md b/bolt/docs/BinaryAnalysis.md
index c4be115ef8f64..30be23fbf8b57 100644
--- a/bolt/docs/BinaryAnalysis.md
+++ b/bolt/docs/BinaryAnalysis.md
@@ -7,7 +7,11 @@ The `llvm-bolt-binary-analysis` tool enables running requested analyses
 on binaries, and generating reports. It does this by building on top of the
 analyses implemented in the BOLT libraries.
 
-Contents
+For now, the only analysis implemented is validation of Pointer Authentication
+hardening on AArch64, more types of analyses can be added later.
+
+## Contents
+
 1. [Background and motivation](#background-and-motivation)
 2. [Usage](#usage)
 3. [Pointer Authentication validator](#pointer-authentication-validator)
@@ -38,7 +42,7 @@ containing the return address between an instruction authenticating it and a
 return instruction using it.
 
 From time to time, however, a bug gets found in the implementation of such
-mitigations in toolchains. Also, code that is manually written in assembler
+mitigations in toolchains. Also, code that is manually written in assembly
 requires the developer to ensure these security properties by hand.
 
 In short, it is sometimes found that a few places in the binary code are not
@@ -81,9 +85,10 @@ compiler to implement various hardenings transparently to the programmer.
 
 Probably the most simple variant of hardening based on Pointer Authentication is
 [`pac-ret`](https://llsoftsec.github.io/llsoftsecbook/#sec:pac-ret), a security
-hardening scheme implemented in compilers such as GCC and Clang, using the
-command line option `-mbranch-protection=pac-ret`. This option is enabled by
-default on most widely used Linux distributions. The hardening scheme mitigates
+hardening scheme implemented in compilers such as GCC and Clang, which can be
+enabled with a command line option like `-mbranch-protection=pac-ret`.
+On AArch64, `pac-ret` hardening is enabled by default on most widely used Linux
+distributions. The hardening scheme mitigates
 [Return-Oriented Programming (ROP)](https://llsoftsec.github.io/llsoftsecbook/#return-oriented-programming)
 attacks by making sure that return addresses are only ever stored to memory
 in a signed form. This makes it substantially harder for attackers to divert
@@ -106,9 +111,10 @@ controlled and whether it can be inspected by an attacker under the
 Then, for a number of sensitive instruction kinds (such as function calls and
 pointer signing instructions), the properties of input or output operands are
 inspected to check if the particular instruction is emitted in a safe manner.
-For example, for a return instruction this usually means that either the link
-register (x30) was never clobbered in the function or that it was authenticated
-at some point and never written to since then:
+As an example, for a return instruction this usually means that either the link
+register (`x30`, which is also referred to as LR on AArch64) was never clobbered
+in the function, or that it was authenticated at some point and never written to
+since then:
 
 ```
 foo:
@@ -153,9 +159,8 @@ The only analysis which is currently implemented is validation of a number of
 Pointer Authentication-based hardening schemes such as pac-ret and PAuth ABI.
 The specific set of gadget kinds which are searched for depends on command line
 options. Each gadget found by PtrAuth gadget scanner results in a plain text
-report printed at the end of the analysis.
-Furthermore, an attempt is made to provide additional information on the
-instructions that made the register unsafe.
+report printed at the end of the analysis. Furthermore, an attempt is made to
+provide additional information on the instructions that made the register unsafe.
 Please note that this extra information is provided on a best-effort basis and
 is not expected to be as accurate as the reports themselves.
 
@@ -441,7 +446,7 @@ bad_call_dataflow:
 Reports signing of untrusted values, as this could make arbitrary and possibly
 attacker-controlled values indistinguishable from perfectly trusted and protected ones.
 
-In absence of `--auth-traps-on-failure` command line option, this detector
+Note that in absence of `--auth-traps-on-failure` command line option, this detector
 reports auth+sign pairs unless an explicit check is emitted to make sure the
 authentication operation succeeded. This corresponds to the way LLVM emits
 instructions on AArch64.
@@ -564,15 +569,16 @@ interlinked basic blocks) and the reports (if any) are printed without the
 `, basic block <name>` part in the first line.
 
 Furthermore, it is possible that CFG information is returned by BOLT for a
-function, even though the graph is imprecise (known issues are tracked in
+function, even though the graph is imprecise. Inaccurate CFG information may
+result in false positives and false negatives, thus PtrAuth gadget scanner
+produces warning messages (at most once per function) for non-entry basic blocks
+without any predecessors in CFG: while unreachable basic blocks are technically
+correct, truly unreachable blocks are unlikely to exist in an optimized code.
+
+Known gadget scanner issues related to CFG reconstruction are tracked in
 [#177761](https://github.com/llvm/llvm-project/issues/177761),
 [#178058](https://github.com/llvm/llvm-project/issues/178058),
-[#178232](https://github.com/llvm/llvm-project/issues/178232)).
-Inaccurate CFG information may result in false positives and false negatives,
-thus PtrAuth gadget scanner produces warning messages (at most once per
-function) for non-entry basic blocks without any predecessors in CFG: while
-unreachable basic blocks are technically correct, truly unreachable blocks are
-unlikely to exist in optimized code.
+[#178232](https://github.com/llvm/llvm-project/issues/178232).
 
 #### Last writing instructions
 
@@ -590,7 +596,15 @@ Some of the reports contain extra information along these lines
 This information is provided on a best-effort basis and is less reliable than
 gadget reports themselves.
 
-#### Feature: scan for unsafe computation of discriminator value
+#### Feature: scan for unsafe computation of discriminators
+
+On AArch64, signing and authentication operations are parameterized by the pair
+of the key identifier and a 64-bit *discriminator* value. The key is one of
+`IA`, `IB`, `DA`, or `DB`, and its identifier is directly encoded into the
+instruction (the `GA` key differs significantly and is thus out of scope here).
+The discriminator, on the other hand, is computed at run-time and is intended
+to be [derived independently](https://clang.llvm.org/docs/PointerAuthentication.html#discriminators)
+at the signing and authentication sites.
 
 There is a common pattern on AArch64 to compute the discriminator as a blend
 of an address and an integer modifier by inserting a compile-time constant
@@ -599,11 +613,11 @@ possible to prevent an attacker from modifying the storage address, but the
 insertion of 16-bit constant modifier can always be performed immediately before
 the discriminator value is used by signing or authentication instruction.
 
-While not as bad as signing an arbitrary value or spilling an already authenticated
-value to memory, spilling a "ready-to-use" discriminator value instead of computing
-it right before usage is something we would rather avoid. On the other hand,
-using an arbitrary value as the discriminator should probably be allowed, making
-it hard to distinguish the below patterns:
+While not as bad as signing an arbitrary untrusted pointer, spilling a
+"ready-to-use" discriminator and then reloading a potentially modified value
+later is something we would rather avoid. On the other hand, using an arbitrary
+value as the discriminator should probably be allowed, making it hard to
+distinguish the below patterns:
 
 ```asm
 ; Valid and not reported.
@@ -678,12 +692,13 @@ computation.
 As an example of false-negative, it is possible that an instruction like
 `add x0, x0, #1` could be called in a loop with an attacker-controlled number
 of iterations, making it technically possible for an attacker to replace a
-valid pointer with a pointer to an arbitrary *higher* address.
+valid pointer with a pointer to an arbitrary address.
 
 #### Tail call detection
 
-`ptrauth-tail-calls` detector uses a heuristic to classify each branch
-instruction either as a tail call or as an unrelated branch instruction.
+`ptrauth-tail-calls` detector uses a heuristic to guess which branch instructions
+(both direct and indirect) correspond to performing tail calls, as opposed to
+other kinds of control flow.
 
 Most other parts of BOLT should not break code when rewriting. Unlike them,
 this analyzer tries to keep reasonable balance between false positive reports



More information about the llvm-commits mailing list