mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-28 02:36:05 +00:00

This adds an initial pac-ret gadget scanner to the llvm-bolt-binary-analysis-tool. The scanner is taken from the prototype that was published last year at https://github.com/llvm/llvm-project/compare/main...kbeyls:llvm-project:bolt-gadget-scanner-prototype, and has been discussed in RFC https://discourse.llvm.org/t/rfc-bolt-based-binary-analysis-tool-to-verify-correctness-of-security-hardening/78148 and in the EuroLLVM 2024 keynote "Does LLVM implement security hardenings correctly? A BOLT-based static analyzer to the rescue?" [Video](https://youtu.be/Sn_Fxa0tdpY) [Slides](https://llvm.org/devmtg/2024-04/slides/Keynote/Beyls_EuroLLVM2024_security_hardening_keynote.pdf) In the spirit of incremental development, this PR aims to add a minimal implementation that is "fully working" on its own, but has major limitations, as described in the bolt/docs/BinaryAnalysis.md documentation in this proposed commit. These and other limitations will be fixed in follow-on PRs, mostly based on code already existing in the prototype branch. I hope incrementally upstreaming will make it easier to review the code. Note that I believe that this could also form the basis of a scanner to analyze correct implementation of PAuthABI.
194 lines
8.3 KiB
Markdown
194 lines
8.3 KiB
Markdown
# BOLT-based binary analysis
|
|
|
|
As part of post-link-time optimizing, BOLT needs to perform a range of analyses
|
|
on binaries such as recontructing control flow graphs, and more.
|
|
|
|
The `llvm-bolt-binary-analysis` tool enables running requested binary 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?
|
|
|
|
* [Security scanners](#security-scanners)
|
|
* [pac-ret analysis](#pac-ret-analysis)
|
|
|
|
### 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),
|
|
[Chromium](https://www.chromium.org/Home/chromium-security/memory-safety/) and
|
|
[Android](https://security.googleblog.com/2021/01/data-driven-security-hardening-in.html).
|
|
|
|
It is not surprising therefore, that a large number of mitigations have been
|
|
added to instruction sets and toolchains to make it harder to build an exploit
|
|
using a memory vulnerability. Examples are: stack canaries, stack clash,
|
|
pac-ret, shadow stacks, arm64e, and many more.
|
|
|
|
These mitigations guarantee a so-called "security property" on the binaries they
|
|
produce. For example, for stack canaries, the security property is roughly that
|
|
a canary is located on the stack between the set of saved registers and the set
|
|
of local variables. For pac-ret, it is roughly that either the return address is
|
|
never stored/retrieved to/from memory; or, there are no writes to the register
|
|
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
|
|
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
|
|
protected as well as expected given the requested mitigations. Attackers could
|
|
make use of those places (sometimes called gadgets) to circumvent the protection
|
|
that the mitigation should give.
|
|
|
|
One of the reasons that such gadgets, or holes in the mitigation implementation,
|
|
exist is that typically the amount of testing and verification for these
|
|
security properties is limited to checking results on specific examples.
|
|
|
|
In comparison, for testing functional correctness, or for testing performance,
|
|
toolchain and software in general typically get tested with large test suites
|
|
and benchmarks. In contrast, this typically does not get done for testing the
|
|
security properties of binary code.
|
|
|
|
Unlike functional correctness where compilation errors result in test failures,
|
|
and performance where speed and size differences are measurable, broken security
|
|
properties cannot be easily observed using existing testing and benchmarking
|
|
tools.
|
|
|
|
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.
|
|
|
|
|
|
#### pac-ret analysis
|
|
|
|
`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.
|
|
|
|
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.
|
|
|
|
The hardening scheme relies on compilers producing appropriate code sequences when
|
|
processing return addresses, especially when these are stored to and retrieved
|
|
from memory.
|
|
|
|
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:
|
|
|
|
> 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`.
|
|
|
|
##### Example 1
|
|
|
|
For example, a typical non-pac-ret-protected function looks as follows:
|
|
|
|
```
|
|
stp x29, x30, [sp, #-0x10]!
|
|
mov x29, sp
|
|
bl g@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:
|
|
|
|
```
|
|
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@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> ]>
|
|
```
|
|
|
|
The exact format of how `llvm-bolt-binary-analysis` reports this is expected to
|
|
evolve over time.
|
|
|
|
##### Example 2: multiple "last-overwriting" instructions
|
|
|
|
A simple example that shows how there can be a set of "last overwriting"
|
|
instructions of a register follows:
|
|
|
|
```
|
|
paciasp
|
|
stp x29, x30, [sp, #-0x10]!
|
|
ldp x29, x30, [sp], #0x10
|
|
cbnz x0, 1f
|
|
autiasp
|
|
1:
|
|
ret
|
|
```
|
|
|
|
This will produce the following diagnostic:
|
|
|
|
```
|
|
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
|
|
```
|
|
|
|
(Yes, this diagnostic could be improved because the second "overwriting"
|
|
instruction, `autiasp`, is an authenticating instruction...)
|
|
|
|
##### Known false positives or negatives
|
|
|
|
The following are current known cases of false positives:
|
|
|
|
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
|
|
```
|
|
|
|
The following are current known cases of false negatives:
|
|
|
|
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).
|
|
|
|
BOLT cannot currently handle functions with `cfi_negate_ra_state` correctly,
|
|
i.e. any binaries built with `-mbranch-protection=pac-ret`. The scanner is meant
|
|
to be used on specifically such binaries, so this is a major limitation! Work is
|
|
going on in PR [#120064](https://github.com/llvm/llvm-project/pull/120064) to
|
|
fix this.
|
|
|
|
## How to add your own binary analysis
|
|
|
|
_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_
|