This is an archive of the discontinued LLVM Phabricator instance.

[IR] Define "ptrauth" operand bundle.
ClosedPublic

Authored by ab on Nov 11 2021, 9:08 AM.

Details

Summary

Building on D90868, this introduces a new "ptrauth" operand bundle to be used in call/invoke. At the IR level, it's semantically equivalent to an @llvm.ptrauth.auth followed by an indirect call, but it additionally provides hardening guarantees, by preventing the intermediate raw pointer from being exposed.

This mostly adds the IR definition, verifier checks, and support in a couple of general helper functions. clang IRGen and backend support will come separately.

Note that we'll eventually want to support this bundle in indirectbr as well, for similar reasons. indirectbr currently doesn't allow bundles at all, and the IR data structures need some rejiggering.

Diff Detail

Event Timeline

ab created this revision.Nov 11 2021, 9:08 AM
ab requested review of this revision.Nov 11 2021, 9:08 AM
bruno accepted this revision.Nov 18 2021, 6:41 PM

Given context from further patches in the set, I believe ptrauth operand bundle has been a good way to represent this and works well. LGTM

This revision is now accepted and ready to land.Nov 18 2021, 6:41 PM

I just have 2 bike-sheddy comments on the documentation text.
My comments should not let you delay in getting this committed if they do not make sense to you.

llvm/docs/PointerAuth.md
234–235

If you allow me to bikeshed on this sentence: "guaranteeing an intermediate call target is never attackable" seems like a very bold claim to me.
I wonder if it would (a) be better, and (b) be possible to more concretely define exactly what is guaranteed in more detail.
Is what is currently guaranteed "it guarantees that the intermediate call target is kept in a register and never stored to memory, e.g. by being spilled"?
If we want to relate this guarantee to reduced attackability, a sentence could be added saying something like "Not storing and reloading the unauthenticated pointer to/from memory removes an attack surface where an attacker would overwrite the unauthenticated pointer in memory".

I'm assuming that no other guarantees are implemented?
If in the future it becomes clear that more guarantees need to be implemented, the documentation here can be extended to list further guarantees?

259

similarly to my other comment "are never attackable" seems a bit strong. Would "are never stored and reloaded to/from memory" be a more exact description of the added guarantee?

This revision was landed with ongoing or failed builds.Feb 14 2022, 11:27 AM
This revision was automatically updated to reflect the committed changes.
ab marked 2 inline comments as done.Feb 14 2022, 11:37 AM

Thanks!

I committed a modified description, happy to make more changes:

However, that exposes the intermediate, unauthenticated pointer, e.g., if it
gets spilled to the stack. An attacker can then overwrite the pointer in
memory, negating the security benefit provided by pointer authentication.
To prevent that, the `ptrauth` operand bundle may be used: it guarantees that
the intermediate call target is kept in a register and never stored to memory.
This hardening benefit is similar to that provided by
[`llvm.ptrauth.resign`](#llvm-ptrauth-resign)).

llvm/docs/PointerAuth.md
234–235

That's fair, I reworded it based on your comment, to specifically mention the main hardening measure: preventing spills.

But for context: there is indeed more hardening provided here: for all the combined "hardened" ptrauth operations we have, we use only specific registers for "critical" values (e.g., the unauthenticated pointer result of the auth here), and the darwin kernel provides additional runtime guarantees on these registers (so that they aren't corruptible even across context switches, etc.)
Another layer is the trapping behavior on auth failures: for several of these operations (including these calls) we rely on the hardware checks, but when we can't, we have the software check/trap sequence, so that these can't be bruteforced.

Either way, that belongs somewhere else in the document, given that it's a property common to most of these operations.