Skip to content

Commit

Permalink
[PAC] Define __builtin_ptrauth_type_discriminator (llvm#100204)
Browse files Browse the repository at this point in the history
The builtin computes the discriminator for a type, which can be used to
sign/authenticate function pointers and member function pointers.

If the type passed to the builtin is a C++ member function pointer type,
the result is the discriminator used to signed member function pointers
of that type. If the type is a function, function pointer, or function
reference type, the result is the discriminator used to sign functions
of that type. It is ill-formed to use this builtin with any other type.

A call to this function is an integer constant expression.

Co-Authored-By: John McCall rjmccall@apple.com
  • Loading branch information
ahatanak authored Jul 24, 2024
1 parent eff6250 commit 666e332
Show file tree
Hide file tree
Showing 14 changed files with 183 additions and 3 deletions.
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,9 @@ def warn_ptrauth_auth_null_pointer :
InGroup<PtrAuthNullPointers>;
def err_ptrauth_string_not_literal : Error<
"argument must be a string literal%select{| of char type}0">;
def err_ptrauth_type_disc_undiscriminated : Error<
"cannot pass undiscriminated type %0 to "
"'__builtin_ptrauth_type_discriminator'">;

def note_ptrauth_virtual_function_pointer_incomplete_arg_ret :
Note<"cannot take an address of a virtual member function if its return or "
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,8 @@ ALIAS("__is_same_as", __is_same, KEYCXX)
KEYWORD(__private_extern__ , KEYALL)
KEYWORD(__module_private__ , KEYALL)

UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_type_discriminator, PtrAuthTypeDiscriminator, KEYALL)

// Extension that will be enabled for Microsoft, Borland and PS4, but can be
// disabled via '-fno-declspec'.
KEYWORD(__declspec , 0)
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -3890,6 +3890,8 @@ class Parser : public CodeCompletionHandler {
ExprResult ParseArrayTypeTrait();
ExprResult ParseExpressionTrait();

ExprResult ParseBuiltinPtrauthTypeDiscriminator();

//===--------------------------------------------------------------------===//
// Preprocessor code-completion pass-through
void CodeCompleteDirective(bool InConditional) override;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -3456,6 +3456,8 @@ class Sema final : public SemaBase {
TemplateIdAnnotation *TemplateId,
bool IsMemberSpecialization);

bool checkPointerAuthEnabled(SourceLocation Loc, SourceRange Range);

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

/// Diagnose function specifiers on a declaration of an identifier that
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14058,6 +14058,12 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
E);
}

case UETT_PtrAuthTypeDiscriminator: {
if (E->getArgumentType()->isDependentType())
return false;
return Success(
Info.Ctx.getPointerAuthTypeDiscriminator(E->getArgumentType()), E);
}
case UETT_VecStep: {
QualType Ty = E->getTypeOfArgument();

Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5179,6 +5179,14 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
Diags.Report(DiagID);
return;
}
case UETT_PtrAuthTypeDiscriminator: {
DiagnosticsEngine &Diags = Context.getDiags();
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"cannot yet mangle __builtin_ptrauth_type_discriminator expression");
Diags.Report(E->getExprLoc(), DiagID);
return;
}
case UETT_VecStep: {
DiagnosticsEngine &Diags = Context.getDiags();
unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/Headers/ptrauth.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,23 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
#define ptrauth_string_discriminator(__string) \
__builtin_ptrauth_string_discriminator(__string)

/* Compute a constant discriminator from the given type.
The result can be used as the second argument to
ptrauth_blend_discriminator or the third argument to the
__ptrauth qualifier. It has type size_t.
If the type is a C++ member function pointer type, the result is
the discriminator used to signed member function pointers of that
type. If the type is a function, function pointer, or function
reference type, the result is the discriminator used to sign
functions of that type. It is ill-formed to use this macro with any
other type.
A call to this function is an integer constant expression. */
#define ptrauth_type_discriminator(__type) \
__builtin_ptrauth_type_discriminator(__type)

/* Compute a signature for the given pair of pointer-sized values.
The order of the arguments is significant.
Expand Down Expand Up @@ -289,6 +306,8 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
((ptrauth_extra_data_t)0); \
})

#define ptrauth_type_discriminator(__type) ((ptrauth_extra_data_t)0)

#define ptrauth_sign_generic_data(__value, __data) \
({ \
(void)__value; \
Expand Down
23 changes: 23 additions & 0 deletions clang/lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,26 @@ bool Parser::isRevertibleTypeTrait(const IdentifierInfo *II,
return false;
}

ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() {
SourceLocation Loc = ConsumeToken();

BalancedDelimiterTracker T(*this, tok::l_paren);
if (T.expectAndConsume())
return ExprError();

TypeResult Ty = ParseTypeName();
if (Ty.isInvalid()) {
SkipUntil(tok::r_paren, StopAtSemi);
return ExprError();
}

SourceLocation EndLoc = Tok.getLocation();
T.consumeClose();
return Actions.ActOnUnaryExprOrTypeTraitExpr(
Loc, UETT_PtrAuthTypeDiscriminator,
/*isType=*/true, Ty.get().getAsOpaquePtr(), SourceRange(Loc, EndLoc));
}

/// Parse a cast-expression, or, if \pisUnaryExpression is true, parse
/// a unary-expression.
///
Expand Down Expand Up @@ -1806,6 +1826,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
Res = ParseArrayTypeTrait();
break;

case tok::kw___builtin_ptrauth_type_discriminator:
return ParseBuiltinPtrauthTypeDiscriminator();

case tok::kw___is_lvalue_expr:
case tok::kw___is_rvalue_expr:
if (NotPrimaryExpression)
Expand Down
10 changes: 7 additions & 3 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1489,14 +1489,18 @@ enum PointerAuthOpKind {
};
}

static bool checkPointerAuthEnabled(Sema &S, Expr *E) {
if (S.getLangOpts().PointerAuthIntrinsics)
bool Sema::checkPointerAuthEnabled(SourceLocation Loc, SourceRange Range) {
if (getLangOpts().PointerAuthIntrinsics)
return false;

S.Diag(E->getExprLoc(), diag::err_ptrauth_disabled) << E->getSourceRange();
Diag(Loc, diag::err_ptrauth_disabled) << Range;
return true;
}

static bool checkPointerAuthEnabled(Sema &S, Expr *E) {
return S.checkPointerAuthEnabled(E->getExprLoc(), E->getSourceRange());
}

static bool checkPointerAuthKey(Sema &S, Expr *&Arg) {
// Convert it to type 'int'.
if (convertArgumentToType(S, Arg, S.Context.IntTy))
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4117,6 +4117,21 @@ static bool CheckVectorElementsTraitOperandType(Sema &S, QualType T,
return false;
}

static bool checkPtrAuthTypeDiscriminatorOperandType(Sema &S, QualType T,
SourceLocation Loc,
SourceRange ArgRange) {
if (S.checkPointerAuthEnabled(Loc, ArgRange))
return true;

if (!T->isFunctionType() && !T->isFunctionPointerType() &&
!T->isFunctionReferenceType() && !T->isMemberFunctionPointerType()) {
S.Diag(Loc, diag::err_ptrauth_type_disc_undiscriminated) << T << ArgRange;
return true;
}

return false;
}

static bool CheckExtensionTraitOperandType(Sema &S, QualType T,
SourceLocation Loc,
SourceRange ArgRange,
Expand Down Expand Up @@ -4511,6 +4526,10 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
return CheckVectorElementsTraitOperandType(*this, ExprType, OpLoc,
ExprRange);

if (ExprKind == UETT_PtrAuthTypeDiscriminator)
return checkPtrAuthTypeDiscriminatorOperandType(*this, ExprType, OpLoc,
ExprRange);

// Explicitly list some types as extensions.
if (!CheckExtensionTraitOperandType(*this, ExprType, OpLoc, ExprRange,
ExprKind))
Expand Down
5 changes: 5 additions & 0 deletions clang/test/AST/ast-dump-ptrauth-json.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -std=c++11 -ast-dump=json %s | FileCheck %s

// CHECK: "name": "__builtin_ptrauth_type_discriminator",

int d = __builtin_ptrauth_type_discriminator(int());
14 changes: 14 additions & 0 deletions clang/test/CodeGenCXX/mangle-fail.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// RUN: %clang_cc1 -emit-llvm-only -x c++ -std=c++11 -triple %itanium_abi_triple -verify %s -DN=1
// RUN: %clang_cc1 -emit-llvm-only -x c++ -std=c++11 -triple %itanium_abi_triple -verify %s -DN=2
// RUN: %clang_cc1 -emit-llvm-only -x c++ -std=c++11 -triple aarch64-linux-gnu -fptrauth-intrinsics -verify %s -DN=3

struct A { int a; };

Expand All @@ -13,6 +14,19 @@ template void test<int>(int (&)[sizeof(int)]);
template<class T> void test(int (&)[sizeof((A){}, T())]) {} // expected-error {{cannot yet mangle}}
template void test<int>(int (&)[sizeof(A)]);

#elif N == 3
// __builtin_ptrauth_type_discriminator
template <class T, unsigned disc>
struct S1 {};

template<class T>
void func(S1<T, __builtin_ptrauth_type_discriminator(T)> s1) { // expected-error {{cannot yet mangle __builtin_ptrauth_type_discriminator expression}}
}

void testfunc1() {
func(S1<int(), __builtin_ptrauth_type_discriminator(int())>());
}

// FIXME: There are several more cases we can't yet mangle.

#else
Expand Down
5 changes: 5 additions & 0 deletions clang/test/Sema/ptrauth-intrinsics-macro.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ void test_string_discriminator(int *dp) {
(void)t0;
}

void test_type_discriminator(int *dp) {
ptrauth_extra_data_t t0 = ptrauth_type_discriminator(int (*)(int));
(void)t0;
}

void test_sign_constant(int *dp) {
dp = ptrauth_sign_constant(&dv, VALID_DATA_KEY, 0);
}
68 changes: 68 additions & 0 deletions clang/test/SemaCXX/ptrauth-type-discriminator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++17 -Wno-vla -fsyntax-only -verify -fptrauth-intrinsics %s
// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++17 -Wno-vla -fsyntax-only -verify -fptrauth-intrinsics %s

// RUN: not %clang_cc1 -triple arm64-apple-ios -std=c++17 -Wno-vla -fsyntax-only %s 2>&1 | FileCheck %s
// CHECK: this target does not support pointer authentication

struct S {
virtual int foo();
};

template <class T>
constexpr unsigned dependentOperandDisc() {
return __builtin_ptrauth_type_discriminator(T);
}

void test_builtin_ptrauth_type_discriminator(unsigned s) {
typedef int (S::*MemFnTy)();
MemFnTy memFnPtr;
int (S::*memFnPtr2)();
constexpr unsigned d0 = __builtin_ptrauth_type_discriminator(MemFnTy);
static_assert(d0 == __builtin_ptrauth_string_discriminator("_ZTSM1SFivE"));
static_assert(d0 == 60844);
static_assert(__builtin_ptrauth_type_discriminator(int (S::*)()) == d0);
static_assert(__builtin_ptrauth_type_discriminator(decltype(memFnPtr)) == d0);
static_assert(__builtin_ptrauth_type_discriminator(decltype(memFnPtr2)) == d0);
static_assert(__builtin_ptrauth_type_discriminator(decltype(&S::foo)) == d0);
static_assert(dependentOperandDisc<decltype(&S::foo)>() == d0);

constexpr unsigned d1 = __builtin_ptrauth_type_discriminator(void (S::*)(int));
static_assert(__builtin_ptrauth_string_discriminator("_ZTSM1SFviE") == d1);
static_assert(d1 == 39121);

constexpr unsigned d2 = __builtin_ptrauth_type_discriminator(void (S::*)(float));
static_assert(__builtin_ptrauth_string_discriminator("_ZTSM1SFvfE") == d2);
static_assert(d2 == 52453);

constexpr unsigned d3 = __builtin_ptrauth_type_discriminator(int (*())[s]);
static_assert(__builtin_ptrauth_string_discriminator("FPE") == d3);
static_assert(d3 == 34128);

int f4(float);
constexpr unsigned d4 = __builtin_ptrauth_type_discriminator(decltype(f4));
static_assert(__builtin_ptrauth_type_discriminator(int (*)(float)) == d4);
static_assert(__builtin_ptrauth_string_discriminator("FifE") == d4);
static_assert(d4 == 48468);

int f5(int);
constexpr unsigned d5 = __builtin_ptrauth_type_discriminator(decltype(f5));
static_assert(__builtin_ptrauth_type_discriminator(int (*)(int)) == d5);
static_assert(__builtin_ptrauth_type_discriminator(short (*)(short)) == d5);
static_assert(__builtin_ptrauth_type_discriminator(char (*)(char)) == d5);
static_assert(__builtin_ptrauth_type_discriminator(long (*)(long)) == d5);
static_assert(__builtin_ptrauth_type_discriminator(unsigned int (*)(unsigned int)) == d5);
static_assert(__builtin_ptrauth_type_discriminator(int (&)(int)) == d5);
static_assert(__builtin_ptrauth_string_discriminator("FiiE") == d5);
static_assert(d5 == 2981);

int t;
int vmarray[s];
(void)__builtin_ptrauth_type_discriminator(t); // expected-error {{unknown type name 't'}}
(void)__builtin_ptrauth_type_discriminator(&t); // expected-error {{expected a type}}
(void)__builtin_ptrauth_type_discriminator(decltype(vmarray)); // expected-error {{cannot pass undiscriminated type 'decltype(vmarray)' (aka 'int[s]')}}
(void)__builtin_ptrauth_type_discriminator(int *); // expected-error {{cannot pass undiscriminated type 'int *' to '__builtin_ptrauth_type_discriminator'}}
(void)__builtin_ptrauth_type_discriminator(); // expected-error {{expected a type}}
(void)__builtin_ptrauth_type_discriminator(int (*)(int), int (*)(int));
// expected-error@-1 {{expected ')'}}
// expected-note@-2 {{to match this '('}}
}

0 comments on commit 666e332

Please sign in to comment.