Skip to content

Commit 51c21ae

Browse files
committed
[PAC] Add support for __ptrauth type qualifier
The qualifier allows programmer to directly control how pointers are signed when they are stored in a particular variable. The qualifier takes three arguments: the signing key, a flag specifying whether address discrimination should be used, and a non-negative integer that is used for additional discrimination. ``` typedef void (*my_callback)(const void*); my_callback __ptrauth(ptrauth_key_process_dependent_code, 1, 0xe27a) callback; ``` Co-Authored-By: John McCall [email protected]
1 parent 81ce796 commit 51c21ae

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2518
-14
lines changed

clang/include/clang/AST/Type.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,12 @@ class PointerAuthQualifier {
307307
return Result;
308308
}
309309

310+
std::string getAsString() const;
311+
std::string getAsString(const PrintingPolicy &Policy) const;
312+
313+
bool isEmptyWhenPrinted(const PrintingPolicy &Policy) const;
314+
void print(raw_ostream &OS, const PrintingPolicy &Policy) const;
315+
310316
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Data); }
311317
};
312318

@@ -556,7 +562,7 @@ class Qualifiers {
556562

557563
bool hasAddressSpace() const { return Mask & AddressSpaceMask; }
558564
LangAS getAddressSpace() const {
559-
return static_cast<LangAS>(Mask >> AddressSpaceShift);
565+
return static_cast<LangAS>((Mask & AddressSpaceMask) >> AddressSpaceShift);
560566
}
561567
bool hasTargetSpecificAddressSpace() const {
562568
return isTargetAddressSpace(getAddressSpace());
@@ -815,17 +821,18 @@ class Qualifiers {
815821
static_assert(sizeof(PointerAuthQualifier) == sizeof(uint32_t),
816822
"PointerAuthQualifier must be 32 bits");
817823

824+
static constexpr uint64_t PtrAuthShift = 32;
825+
static constexpr uint64_t PtrAuthMask = uint64_t(0xffffffff) << PtrAuthShift;
826+
818827
static constexpr uint64_t UMask = 0x8;
819828
static constexpr uint64_t UShift = 3;
820829
static constexpr uint64_t GCAttrMask = 0x30;
821830
static constexpr uint64_t GCAttrShift = 4;
822831
static constexpr uint64_t LifetimeMask = 0x1C0;
823832
static constexpr uint64_t LifetimeShift = 6;
824833
static constexpr uint64_t AddressSpaceMask =
825-
~(CVRMask | UMask | GCAttrMask | LifetimeMask);
834+
~(CVRMask | UMask | GCAttrMask | LifetimeMask | PtrAuthMask);
826835
static constexpr uint64_t AddressSpaceShift = 9;
827-
static constexpr uint64_t PtrAuthShift = 32;
828-
static constexpr uint64_t PtrAuthMask = uint64_t(0xffffffff) << PtrAuthShift;
829836
};
830837

831838
class QualifiersAndAtomic {
@@ -1460,6 +1467,12 @@ class QualType {
14601467
return getQualifiers().getPointerAuth();
14611468
}
14621469

1470+
bool hasAddressDiscriminatedPointerAuth() const {
1471+
if (auto ptrauth = getPointerAuth())
1472+
return ptrauth.isAddressDiscriminated();
1473+
return false;
1474+
}
1475+
14631476
enum PrimitiveDefaultInitializeKind {
14641477
/// The type does not fall into any of the following categories. Note that
14651478
/// this case is zero-valued so that values of this enum can be used as a

clang/include/clang/Basic/Attr.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3339,6 +3339,14 @@ def ObjCRequiresPropertyDefs : InheritableAttr {
33393339
let SimpleHandler = 1;
33403340
}
33413341

3342+
def PointerAuth : TypeAttr {
3343+
let Spellings = [CustomKeyword<"__ptrauth">];
3344+
let Args = [IntArgument<"Key">,
3345+
BoolArgument<"AddressDiscriminated", 1>,
3346+
IntArgument<"ExtraDiscriminator", 1>];
3347+
let Documentation = [PtrAuthDocs];
3348+
}
3349+
33423350
def Unused : InheritableAttr {
33433351
let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">,
33443352
C23<"", "maybe_unused", 202106>];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,6 +1758,152 @@ Also see the documentation for `@available
17581758
}];
17591759
}
17601760

1761+
def PtrAuthDocs : Documentation {
1762+
let Category = DocCatVariable;
1763+
let Heading = "__ptrauth, __ptrauth_restricted_intptr";
1764+
let Content = [{
1765+
The ``__ptrauth`` qualifier allows the programmer to directly control
1766+
how pointers are signed when they are stored in a particular variable.
1767+
This can be used to strengthen the default protections of pointer
1768+
authentication and make it more difficult for an attacker to escalate
1769+
an ability to alter memory into full control of a process.
1770+
1771+
.. code-block:: c
1772+
1773+
#include <ptrauth.h>
1774+
1775+
typedef void (*my_callback)(const void*);
1776+
my_callback __ptrauth(ptrauth_key_process_dependent_code, 1, 0xe27a) callback;
1777+
1778+
The first argument to ``__ptrauth`` is the name of the signing key.
1779+
Valid key names for the target are defined in ``<ptrauth.h>``.
1780+
1781+
On ARM64, there are four keys:
1782+
1783+
- ``ptrauth_key_process_independent_data``
1784+
- ``ptrauth_key_process_dependent_data``
1785+
- ``ptrauth_key_process_independent_code``
1786+
- ``ptrauth_key_process_dependent_code``
1787+
1788+
In general, prefer using a code key for function pointers and a data key
1789+
for object pointers. The ARM64 architecture allows loads and calls to
1790+
execute more efficiently when the pointer is signed with an appropriate
1791+
key. Using code keys only for function pointers also substantially lessens
1792+
the risk of creating a so-called "signing oracle" for function pointers;
1793+
see the general pointer authentication language documentation.
1794+
1795+
Using a process-dependent key provides stronger protection against
1796+
cross-process attacks. However, it also inhibits certain memory
1797+
optimizations when a shared library is loaded into multiple processes.
1798+
Using a process-independent key also allows signed pointers to be passed
1799+
in shared memory. Note that even the process-independent keys may change
1800+
after a reboot, so signed values should never be serialized.
1801+
1802+
The second argument to ``__ptrauth`` is a flag (0 or 1) specifying whether
1803+
the object should use address discrimination. If only one argument is
1804+
given, the flag defaults to 0. Address discrimination provides strong
1805+
protection against attacks which copy signed pointers around in memory.
1806+
An attacker cannot usefully copy an arbitrary signed pointer over an
1807+
address-discriminated object. Nor can a value taken from an
1808+
address-discriminated object be usefully copied over some other signed
1809+
pointer. However, it is more expensive to copy values from one
1810+
address-discriminated object to another, even if the other arguments to
1811+
``__ptrauth`` are the same, and it is not valid to copy them with
1812+
``memcpy``. It is also not valid to map memory containing an
1813+
address-discriminated object into different places in the address
1814+
space, e.g. with ``mmap``.
1815+
1816+
The third argument to ``__ptrauth`` is a small non-negative integer
1817+
which allows additional discrimination between objects. Using a
1818+
unique extra discriminator provides strong protection against attacks
1819+
which work by substituting one signed value for another. For example,
1820+
an attacker cannot usefully overwrite an object with a pointer from an
1821+
object using a different extra discriminator; this protection is similar
1822+
to the protection offered by address discrimination. A unique extra
1823+
discriminator also protects against "slide" attacks where an attacker
1824+
alters a pointer instead of altering the memory that the pointer points to.
1825+
The extra discriminator must be a constant expression. On ARM64,
1826+
its value must be between 0 and 65535. If the argument is not provided,
1827+
the default value is 0. It is generally preferable not to use the value 0,
1828+
especially with the process-independent keys, as this combination is used
1829+
in various places in the standard language ABI.
1830+
1831+
The type qualified by ``__ptrauth`` must be a pointer type. Currently
1832+
only C pointer types are allowed and not block pointers, Objective-C
1833+
object pointers, or C++ references. ``__ptrauth`` is parsed and interpreted
1834+
using the same language rules as qualifiers like ``const`` and ``volatile``.
1835+
For example:
1836+
1837+
.. code-block:: c
1838+
1839+
__ptrauth(...) int *ex0; /* invalid: qualifies 'int', which is not a pointer type */
1840+
int * __ptrauth(...) ex1; /* valid: ex1 has qualified type */
1841+
int * __ptrauth(...) *ex2; /* valid: ex2 is a pointer to a qualified object */
1842+
1843+
typedef int *intp;
1844+
__ptrauth(...) intp ex3; /* valid: ex3 has qualified type */
1845+
intp __ptrauth(...) ex4; /* valid: means the exact same thing as ex3 */
1846+
1847+
If a ``__ptrauth``-qualified l-value of function pointer type is
1848+
used as the function operand of a call expression, the function pointer
1849+
will be authenticated "atomically" with the call, such that an attacker
1850+
will not be able to corrupt the destination of the call even in the
1851+
presence of undefined behavior. (That is, the compiler must not
1852+
leave an un-signed pointer that it will later unconditionally trust
1853+
in a place where it could be feasibly overwritten by an attacker,
1854+
such as the stack or a callee-save register during an intervening call.
1855+
The compiler is not required to protect against improbable attacks
1856+
such as corruption of the register file, as might occur with a
1857+
corrupted kernel. It also need not guard against jumps to an arbitrary
1858+
place in the instruction stream, since such jumps would require an
1859+
attacker to already fully control the PC.)
1860+
1861+
If the ABI specifies that a pointer is always signed --- that is,
1862+
if the pointer is a function pointer and the target uses ABI function
1863+
pointer authentication --- then signing and authenticating it as part
1864+
of a load/store actually means resigning it to/from the standard ABI
1865+
signature schema. Similarly, if both operands of a simple assignment
1866+
operator are ``__ptrauth``-qualified, the pointer copied by the
1867+
assignment is resigned from the right-hand operand's schema to the
1868+
left-hand operand's schema. These resigning operations are also done
1869+
"atomically" in the same sense as above.
1870+
1871+
As a final guarantee, if the right-hand operand of an assignment or
1872+
the expression used to initialize a ``__ptrauth``-qualified object is
1873+
a direct reference to an object or function (e.g. ``&my_var``), the
1874+
signing of that pointer is atomic with the evaluaton of the reference
1875+
in this same sense.
1876+
1877+
Otherwise, there are no guarantees of atomicity, and it is the
1878+
programmer's responsibility to avoid allowing a store into a
1879+
``__ptrauth``-qualified object to create a potential "signing oracle"
1880+
which an attacker could use to sign an arbitrary pointer of their choice.
1881+
Such oracles are particularly problematic when the signing uses a code
1882+
key because the oracle could potentially be used to allow an attacker
1883+
to construct a validly-signed function pointer, v-table entry, or
1884+
return address that points to an arbitrary instruction, allowing them
1885+
to completely take over the PC. Programmers attempting to use
1886+
``__ptrauth`` to protect a data pointer, or to protect function pointers
1887+
on targets that do not use ABI function pointer authentication, should
1888+
aim to maintain a "chain of authentication" from initialization all
1889+
the way to the point at which the pointer is used. If this is infeasible,
1890+
they should consider using ``ptrauth_sign_generic_data`` instead.
1891+
1892+
Types that are written in r-value positions, such as return types,
1893+
parameter types, and cast types, may not be ``__ptrauth``-qualified
1894+
at the outermost level. This may be supported in the future.
1895+
1896+
In C++, the arguments to ``__ptrauth`` may not be instantiation-dependent.
1897+
This may be supported in the future.
1898+
1899+
This feature may be tested for with ``__has_feature(ptrauth_qualifier)``.
1900+
It is enabled whenever the ``ptrauth`` intrinsics are enabled.
1901+
1902+
``<ptrauth.h>`` provides predefined qualifiers for various language
1903+
features that implicitly use pointer authentication.
1904+
}];
1905+
}
1906+
17611907
def ExternalSourceSymbolDocs : Documentation {
17621908
let Category = DocCatDecl;
17631909
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,25 @@ def err_ptrauth_indirect_goto_addrlabel_arithmetic : Error<
956956
"%select{subtraction|addition}0 of address-of-label expressions is not "
957957
"supported with ptrauth indirect gotos">;
958958

959+
// __ptrauth qualifier
960+
def err_ptrauth_qualifier_invalid : Error<
961+
"%select{return types|parameter types|properties}2 may not be qualified with %select{__ptrauth|__ptrauth_restricted_intptr}1; type is %0">;
962+
def err_ptrauth_qualifier_cast : Error<
963+
"cast types may not be qualified with __ptrauth; type is %0">;
964+
def err_ptrauth_qualifier_nonpointer : Error<
965+
"__ptrauth qualifier may only be applied to pointer types; type here is %0">;
966+
def err_ptrauth_qualifier_redundant : Error<
967+
"type %0 is already %1-qualified">;
968+
def err_ptrauth_qualifier_bad_arg_count : Error<
969+
"%0 qualifier must take between 1 and 3 arguments">;
970+
def err_ptrauth_arg_not_ice : Error<
971+
"argument to __ptrauth must be an integer constant expression">;
972+
def err_ptrauth_address_discrimination_invalid : Error<
973+
"address discrimination flag for __ptrauth must be 0 or 1; value is %0">;
974+
def err_ptrauth_extra_discriminator_invalid : Error<
975+
"extra discriminator for __ptrauth must be between "
976+
"0 and %1; value is %0">;
977+
959978
/// main()
960979
// static main() is not an error in C, just in C++.
961980
def warn_static_main : Warning<"'main' should not be declared static">,
@@ -3870,7 +3889,8 @@ def note_cannot_use_trivial_abi_reason : Note<
38703889
"its copy constructors and move constructors are all deleted|"
38713890
"it is polymorphic|"
38723891
"it has a base of a non-trivial class type|it has a virtual base|"
3873-
"it has a __weak field|it has a field of a non-trivial class type}1">;
3892+
"it has a __weak field|it has a field of a non-trivial class type|"
3893+
"it has an address-discriminated __ptrauth field}1">;
38743894

38753895
// Availability attribute
38763896
def warn_availability_unknown_platform : Warning<
@@ -4927,6 +4947,10 @@ def note_ovl_candidate_bad_ownership : Note<
49274947
"%select{no|__unsafe_unretained|__strong|__weak|__autoreleasing}4 ownership,"
49284948
" but parameter has %select{no|__unsafe_unretained|__strong|__weak|"
49294949
"__autoreleasing}5 ownership">;
4950+
def note_ovl_candidate_bad_ptrauth : Note<
4951+
"candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
4952+
"%ordinal8 argument (%3) has %select{no ptrauth|%5}4 qualifier,"
4953+
" but parameter has %select{no ptrauth|%7}6 qualifier">;
49304954
def note_ovl_candidate_bad_cvr_this : Note<
49314955
"candidate %sub{select_ovl_candidate_kind}0,1,2 not viable: "
49324956
"'this' argument has type %3, but method is not marked "
@@ -6005,7 +6029,7 @@ def note_deleted_special_member_class_subobject : Note<
60056029
"%select{default|corresponding|default|default|default}4 constructor}0|"
60066030
"destructor}5"
60076031
"%select{||s||}4"
6008-
"|is an ObjC pointer}6">;
6032+
"|is an ObjC pointer|has an address-discriminated ptrauth qualifier}6">;
60096033
def note_deleted_default_ctor_uninit_field : Note<
60106034
"%select{default constructor of|constructor inherited by}0 "
60116035
"%1 is implicitly deleted because field %2 of "
@@ -8811,6 +8835,19 @@ def err_typecheck_incompatible_ownership : Error<
88118835
"sending to parameter of different type}0,1"
88128836
"|%diff{casting $ to type $|casting between types}0,1}2"
88138837
" changes retain/release properties of pointer">;
8838+
def err_typecheck_incompatible_ptrauth : Error<
8839+
"%select{%diff{assigning $ to $|assigning to different types}1,0"
8840+
"|%diff{passing $ to parameter of type $|"
8841+
"passing to parameter of different type}0,1"
8842+
"|%diff{returning $ from a function with result type $|"
8843+
"returning from function with different return type}0,1"
8844+
"|%diff{converting $ to type $|converting between types}0,1"
8845+
"|%diff{initializing $ with an expression of type $|"
8846+
"initializing with expression of different type}0,1"
8847+
"|%diff{sending $ to parameter of type $|"
8848+
"sending to parameter of different type}0,1"
8849+
"|%diff{casting $ to type $|casting between types}0,1}2"
8850+
" changes pointer-authentication of pointee type">;
88148851
def err_typecheck_comparison_of_distinct_blocks : Error<
88158852
"comparison of distinct block types%diff{ ($ and $)|}0,1">;
88168853

@@ -8939,6 +8976,9 @@ def err_atomic_op_needs_non_const_atomic : Error<
89398976
def err_atomic_op_needs_non_const_pointer : Error<
89408977
"address argument to atomic operation must be a pointer to non-const "
89418978
"type (%0 invalid)">;
8979+
def err_atomic_op_needs_non_address_discriminated_pointer : Error<
8980+
"address argument to %select{atomic|__sync}0 operation must be a pointer to a non address discriminated "
8981+
"type (%1 invalid)">;
89428982
def err_atomic_op_needs_trivial_copy : Error<
89438983
"address argument to atomic operation must be a pointer to a "
89448984
"trivially-copyable type (%0 invalid)">;
@@ -9205,6 +9245,8 @@ def ext_typecheck_cond_pointer_integer_mismatch : ExtWarn<
92059245
"pointer/integer type mismatch in conditional expression"
92069246
"%diff{ ($ and $)|}0,1">,
92079247
InGroup<DiagGroup<"conditional-type-mismatch">>;
9248+
def err_typecheck_cond_incompatible_ptrauth : Error<
9249+
"__ptrauth qualification mismatch%diff{ ($ and $)|}0,1">;
92089250
def err_typecheck_choose_expr_requires_constant : Error<
92099251
"'__builtin_choose_expr' requires a constant expression">;
92109252
def warn_unused_expr : Warning<"expression result unused">,

clang/include/clang/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread))
104104
FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow))
105105
FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo))
106106
FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics)
107+
FEATURE(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics)
107108
FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls)
108109
FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns)
109110
FEATURE(ptrauth_vtable_pointer_address_discrimination, LangOpts.PointerAuthVTPtrAddressDiscrimination)

clang/include/clang/Basic/TokenKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ KEYWORD(_Thread_local , KEYALL)
346346
KEYWORD(__func__ , KEYALL)
347347
KEYWORD(__objc_yes , KEYALL)
348348
KEYWORD(__objc_no , KEYALL)
349+
KEYWORD(__ptrauth , KEYALL)
349350

350351

351352
// C++ 2.11p1: Keywords.

clang/include/clang/Parse/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3151,6 +3151,8 @@ class Parser : public CodeCompletionHandler {
31513151
SourceLocation *endLoc = nullptr);
31523152
ExprResult ParseExtIntegerArgument();
31533153

3154+
void ParsePtrauthQualifier(ParsedAttributes &Attrs);
3155+
31543156
VirtSpecifiers::Specifier isCXX11VirtSpecifier(const Token &Tok) const;
31553157
VirtSpecifiers::Specifier isCXX11VirtSpecifier() const {
31563158
return isCXX11VirtSpecifier(Tok);

clang/include/clang/Sema/Sema.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3460,6 +3460,17 @@ class Sema final : public SemaBase {
34603460

34613461
bool checkConstantPointerAuthKey(Expr *keyExpr, unsigned &key);
34623462

3463+
enum PointerAuthDiscArgKind {
3464+
// Address discrimination argument of __ptrauth.
3465+
PADAK_AddrDiscPtrAuth,
3466+
3467+
// Extra discriminator argument of __ptrauth.
3468+
PADAK_ExtraDiscPtrAuth,
3469+
};
3470+
3471+
bool checkPointerAuthDiscriminatorArg(Expr *arg, PointerAuthDiscArgKind kind,
3472+
unsigned &intVal);
3473+
34633474
/// Diagnose function specifiers on a declaration of an identifier that
34643475
/// does not identify a function.
34653476
void DiagnoseFunctionSpecifiers(const DeclSpec &DS);

clang/lib/AST/ASTContext.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11046,6 +11046,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer,
1104611046
if (LQuals.getCVRQualifiers() != RQuals.getCVRQualifiers() ||
1104711047
LQuals.getAddressSpace() != RQuals.getAddressSpace() ||
1104811048
LQuals.getObjCLifetime() != RQuals.getObjCLifetime() ||
11049+
!LQuals.getPointerAuth().isEquivalent(RQuals.getPointerAuth()) ||
1104911050
LQuals.hasUnaligned() != RQuals.hasUnaligned())
1105011051
return {};
1105111052

0 commit comments

Comments
 (0)