[llvm-branch-commits] Add pointer field protection feature. (PR #133538)
Oliver Hunt via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed May 14 17:27:27 PDT 2025
https://github.com/ojhunt requested changes to this pull request.
Thoughts:
This should be opt-in on a field or struct granularity, not just a global behavior.
In the RFC I think you mentioned not applying PFP to C types, but I'm unsure how you're deciding what is a C type?
There are a lot of special cases being added in places that should not need to be aware of PFP - the core type queries should be returning correct values for types containing PFP fields.
A lot of the "this read/write/copy/init special work" exactly aligns with the constraints of pointer auth, and I think the overall implementation could be made better by introducing the concept of a `HardenedPointerQualifier` or similar, that could be either PointerAuthQualifier or PFPQualifier. In principle this could even just be treated as a different PointerAuth key set.
That approach would also help with some of the problems you have with offsetof and pointers to PFP fields - the thing that causes the problems with the pointer to a PFP field is that the PFP schema for a given field is in reality part of the type, so given
```cpp
struct A {
void *field1;
void *field2;
};
```
It looks you are trying to maintain the idea that `A::field1` and `A::field2` have the same type, which makes this code valid according to the type system:
```cpp
void f(void** obj);
void g(A *a) {
f(&a->field1);
f(&a->field2);
}
```
but the real types are not the same. The semantic equivalent under pointer auth is something akin to
```cpp
struct A {
void * __ptrauth(1, 1, hash("A::field1")) field1;
void * __ptrauth(1, 1, hash("A::field2")) field2;
};
```
Which makes it very clear that the types are different, and I think trying to pretend they are not is part of what is causing problems.
The more I think about it, the more I feel that this could be implemented almost entirely on top of the existing pointer auth work.
Currently for pointer auth there's a set of ARM keys specified, and I think you just need to create a different set, PFP_Keys or similar, and set up the appropriate schemas (basically null schemas for all the existing cases), and add a new schema: DefaultFieldSchema or similar, and apply that schema to every pointer field in an object unless there's an explicit qualifier applied already.
Then it would in principle just be a matter of making adding the appropriate logic to the ptrauth intrinsics in llvm.
Now there are some moderately large caveats to that idea
* the existing ptrauth semantics simply say that any struct containing address discriminated values is no-longer a pod type and so gets copied through the stack, which is something you're trying to avoid, so you'd still need to keep that logic (though as I said, that should be done by generating and then calling the functions to do rather than regenerating the entire copy repeatedly).
* the way that pointer auth is currently implemented assumes that there is only a single pointer auth abi active so you wouldn't be able to trivially have both pointer auth and PFP live at the same time. That's what makes me think having a SecuredPointer abstraction might be a batter approach, but it might also not be too difficult to update the PointerAuthQualifier to include the ABI being used -- I'm really not sure.
https://github.com/llvm/llvm-project/pull/133538
More information about the llvm-branch-commits
mailing list