[llvm-dev] Round table on AArch64 Pauth ABI - minutes
Kristof Beyls via llvm-dev
llvm-dev at lists.llvm.org
Thu Oct 15 05:48:31 PDT 2020
Thank you to everyone who contributed to the round table on AArch64 Pauth ABI at the LLVM developers meeting last week.
Please find the minutes below.
As we discussed at the round table, we’ll have more calls in the future on this topic.
The next one is scheduled on Monday 9th of November; at 17:00 UTC (which should be 9am US pacific time).
Zoom link: https://armltd.zoom.us/j/96797724255?pwd=ZEpuMmJndTBlSVU5OVIxTjN3ZzQ3Zz09
I’ve attached an ical file with invite to that call for convenience.
When: Wed. Oct 7, 2020 8:55 AM - 9:30 AM
The Arm instruction set has introduced a “Pointer Authentication” extension in the Armv8.3-A architecture. All cores implementing the Armv8.3 or later architecture implement this extension.
The extension enables putting a cryptographic hash of the address and other “salt” info into the upper bits of the pointer, effectively “signing” the pointer. Later in the execution, just before the pointer is used, the pointer can be authenticated, i.e. the cryptographic hash stored in the upper bits of the pointer can be verified, checking with high probability that the pointer has not been tampered by a hacker.
Protecting return addresses (i.e. hardening backwards control flow integrity, i.o.w. hardening against ROP attacks) can be done without breaking ABI. This has been implemented in clang/llvm/gcc and is further being implemented in other parts of the system software stack.
Protecting indirect calls/jumps (i.e. hardening forward control flow integrity, i.o.w. hardening against JOP attacks) can NOT be done without breaking ABI.
We are starting to define a new ELF ABI to enable using the Armv8.3 PAuth extension. The topic of this round table is to discuss the design of that ABI and its implementation in LLVM.
This ELF ABI is similar to the Apple arm64e ABI which has similar goals for the Darwin platform (see https://www.youtube.com/watch?v=C1nZvpEBfYA and https://github.com/apple/llvm-project/blob/a63a81bd9911f87a0b5dcd5bdd7ccdda7124af87/clang/docs/PointerAuthentication.rst).
The draft ELF ABI is being developed at https://github.com/ARM-software/abi-aa/blob/master/pauthabielf64/pauthabielf64.rst.
There is a pull-request to take the document to version 0.2 open at the moment. To view 0.2 of the document please use https://github.com/ARM-software/abi-aa/blob/ed1151099f52c8caf5e575e8d8c00450d43dcbc2/pauthabielf64/pauthabielf64.rst
Feel free to comment on the pull-request https://github.com/ARM-software/abi-aa/pull/41
* Breaking the abi, we’ll need multiple iterations to design this new abi. How can we record version information for the different variants of this ABI a binary file assumes?
* current proposal - a note section in the ELF file with a (vendor, version) tuple.
* As a mitigation - it’s needed to record version information. Indeed, ABI is expected to evolve as we understand the tradeoffs (security and others) over time.
* No better solution known than just assuming every different variant is fully incompatible with every other variant.
* Also good to help make sure that all binary objects are actually built with the right abi variant.
* Versioning at the ELF object level may not be needed? as it’s “just” indications/relocations needed how to sign specific pointers? The ELF part stabilises early but the language level mapping to it does not. That implies the compiler should encode the signing scheme it uses in ELF object files?
* Within even a single OS, there probably are going to be different ABI variants - e.g. firmware using a more strict variant (but harder to use/deploy) than user space software.
* Version info probably should contain platform (e.g. “FreeBSD 2.0 signing scheme”)
* Use cases for deeply bare metal or is this mostly an OS-level?
* A bare-metal system may not support RELRO, lazy-binding cannot be RELRO either. What do we do about the GOT? Ideally it is RELRO so does not need to be signed.
* You don’t need to specify how to construct a signed GOT - basically the compiler constructs fragments of a “GOT” without using GOT generating relocations.
* complexity of the draft spec comes mostly from a signed GOT. would be good to get rid of the complexity.
* You should expect a section of the GOT to be signed; another section not signed. All these presumably would need to go on different pages? If you use RELRO, non-lazy binding for all GOTs they can all live on the same set of pages. But maybe on ELF this turns out to not be a big problem? Typically in AArch64 ELF the GOT and PLT GOT are separate sections with the GOT placed in the RELRO segment and a lazy binding enabled PLT GOT in RW segment.
* Lazy binding needs the PLT GOT to be writable, which may be a security issue if it is not signed as an attacker may modify the pointers in there to redirect function calls.
* A signed PLT GOT is a contract between static linker and dynamic linker, we already have a dynamic tag and a signing schema defined for this. LLD and ld.bfd implement support but there is as yet no support in dynamic linkers.
* dlopen/dlsym - what to do about it?
* arm64e found that from process launch either the process is “signed” or “unsigned”. so, a signed application loading a shared library needs that library to be signed. Loading a signed library from a non-signed application works though.
* Could we use fat binaries in ELF for packing a signed and non-signed version in a single library?
* dlsym: whether you get a signed pointer or not depends on whether you are in a signed segment in arm64e?
* Peter Collingbourne has implemented a prototype where an extra side table is present to have the extra per-symbol signing information.
* Another approach is to have the compiler re-sign pointers when casting from void* to a function type.
* Tools to measure “how much security did you get” by using a particular abi version? e.g. post-processing tools.
* front-end diagnostics (some of them have been implemented)?
* in the backend there are cases where a compiler bug might cause an issue. Optimization remarks in the backend very late could help catch these.
* There’s a whole community of security folks that are interested that have their own tools (non-LLVM) like IDA, hydra etc.
* The binary analysis part (last part) is actually really important. patterns are easy to recognize if there is something problematic. (e.g. catching corner cases in the compiler; or bad assembly code).
* the compiler can emit standard code patterns to make spotting these easier.
* upstreaming for arm64e is starting.
* debug these things: major issue (Ahmed): downstream lldb arm64e has enough support to debug this.
* Could introduce new DWARF extensions to make the debugging experience better.
* A spec for this? Would be a separate document for ELF AArch64 at least.
* Could the debugger just strip the PAC codes? It gets complicated(similar to TBI) because the size of the PAC may be different based on whether it is a data or code pointer, and the debugger doesn’t always know if something is a code or data pointer?
* LLDB in UI tries to print both signed and pointer without PAC bits.
* Since debugger runs as different process, it cannot do anything with PAC bits (different keys)
* LLDB uses cross-process JIT to evaluate expressions. Different processes have different crypto keys. How to make that work?
* Kristof will look into setting up a monthly zoom call to discuss this topic further in the future.
-------------- next part --------------
An HTML attachment was scrubbed...
-------------- next part --------------
A non-text attachment was scrubbed...
Name: LLVM Pointer Authentication ABI.ics
Size: 2996 bytes
Desc: LLVM Pointer Authentication ABI.ics
More information about the llvm-dev