aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAhmed Bougacha <ahmed@bougacha.org>2024-06-20 12:09:54 -0700
committerGitHub <noreply@github.com>2024-06-20 12:09:54 -0700
commit7c814c13d0df6dbd0ef6a8b2be214d3f6edbb566 (patch)
tree1b86178fc5466607e05b338924be525d1d01b0d5
parent50b919378186ebb59e2dd402720f6ea8576d2477 (diff)
downloadllvm-7c814c13d0df6dbd0ef6a8b2be214d3f6edbb566.zip
llvm-7c814c13d0df6dbd0ef6a8b2be214d3f6edbb566.tar.gz
llvm-7c814c13d0df6dbd0ef6a8b2be214d3f6edbb566.tar.bz2
[clang] Define ptrauth_sign_constant builtin. (#93904)
This is a constant-expression equivalent to ptrauth_sign_unauthenticated. Its constant nature lets us guarantee a non-attackable sequence is generated, unlike ptrauth_sign_unauthenticated which we generally discourage using. It being a constant also allows its usage in global initializers, though requiring constant pointers and discriminators. The value must be a constant expression of pointer type which evaluates to a non-null pointer. The key must be a constant expression of type ptrauth_key. The extra data must be a constant expression of pointer or integer type; if an integer, it will be coerced to ptrauth_extra_data_t. The result will have the same type as the original value. This can be used in constant expressions. Co-authored-by: John McCall <rjmccall@apple.com>
-rw-r--r--clang/docs/PointerAuthentication.rst19
-rw-r--r--clang/include/clang/Basic/Builtins.td6
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td7
-rw-r--r--clang/lib/AST/ExprConstant.cpp1
-rw-r--r--clang/lib/CodeGen/CGBuiltin.cpp3
-rw-r--r--clang/lib/CodeGen/CGExprConstant.cpp59
-rw-r--r--clang/lib/CodeGen/CGPointerAuth.cpp43
-rw-r--r--clang/lib/CodeGen/CMakeLists.txt1
-rw-r--r--clang/lib/CodeGen/CodeGenModule.h5
-rw-r--r--clang/lib/Headers/ptrauth.h25
-rw-r--r--clang/lib/Sema/SemaChecking.cpp123
-rw-r--r--clang/test/CodeGen/ptrauth-intrinsic-sign-constant.c33
-rw-r--r--clang/test/Sema/ptrauth-intrinsics-macro.c4
-rw-r--r--clang/test/Sema/ptrauth.c81
14 files changed, 395 insertions, 15 deletions
diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst
index 130e657..68674f3 100644
--- a/clang/docs/PointerAuthentication.rst
+++ b/clang/docs/PointerAuthentication.rst
@@ -356,6 +356,25 @@ Given that ``signedPointer`` matches the layout for signed pointers signed with
the given key, extract the raw pointer from it. This operation does not trap
and cannot fail, even if the pointer is not validly signed.
+``ptrauth_sign_constant``
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: c
+
+ ptrauth_sign_constant(pointer, key, discriminator)
+
+Return a signed pointer for a constant address in a manner which guarantees
+a non-attackable sequence.
+
+``pointer`` must be a constant expression of pointer type which evaluates to
+a non-null pointer.
+``key`` must be a constant expression of type ``ptrauth_key``.
+``discriminator`` must be a constant expression of pointer or integer type;
+if an integer, it will be coerced to ``ptrauth_extra_data_t``.
+The result will have the same type as ``pointer``.
+
+This can be used in constant expressions.
+
``ptrauth_sign_unauthenticated``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index e07ddf3..9342b6b 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4393,6 +4393,12 @@ def PtrauthSignUnauthenticated : Builtin {
let Prototype = "void*(void*,int,void*)";
}
+def PtrauthSignConstant : Builtin {
+ let Spellings = ["__builtin_ptrauth_sign_constant"];
+ let Attributes = [CustomTypeChecking, NoThrow, Const, Constexpr];
+ let Prototype = "void*(void*,int,void*)";
+}
+
def PtrauthSignGenericData : Builtin {
let Spellings = ["__builtin_ptrauth_sign_generic_data"];
let Attributes = [CustomTypeChecking, NoThrow, Const];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 043d73d..9bf55ac 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -924,6 +924,13 @@ def err_ptrauth_value_bad_type :
Error<"%select{signed value|extra discriminator|blended pointer|blended "
"integer}0 must have %select{pointer|integer|pointer or integer}1 "
"type; type here is %2">;
+def err_ptrauth_bad_constant_pointer :
+ Error<"argument to ptrauth_sign_constant must refer to a global variable "
+ "or function">;
+def err_ptrauth_bad_constant_discriminator :
+ Error<"discriminator argument to ptrauth_sign_constant must be a constant "
+ "integer, the address of the global variable where the result "
+ "will be stored, or a blend of the two">;
def warn_ptrauth_sign_null_pointer :
Warning<"signing a null pointer will yield a non-null pointer">,
InGroup<PtrAuthNullPointers>;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 0576516..fe4b9a5 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2042,6 +2042,7 @@ static bool IsNoOpCall(const CallExpr *E) {
unsigned Builtin = E->getBuiltinCallee();
return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString ||
Builtin == Builtin::BI__builtin___NSStringMakeConstantString ||
+ Builtin == Builtin::BI__builtin_ptrauth_sign_constant ||
Builtin == Builtin::BI__builtin_function_start);
}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 931726a..2516ed4 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -5293,6 +5293,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
case Builtin::BI__iso_volatile_store64:
return RValue::get(EmitISOVolatileStore(*this, E));
+ case Builtin::BI__builtin_ptrauth_sign_constant:
+ return RValue::get(ConstantEmitter(*this).emitAbstract(E, E->getType()));
+
case Builtin::BI__builtin_ptrauth_auth:
case Builtin::BI__builtin_ptrauth_auth_and_resign:
case Builtin::BI__builtin_ptrauth_blend_discriminator:
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 0fd3792..bc5f42d 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1924,6 +1924,12 @@ private:
ConstantLValue VisitMaterializeTemporaryExpr(
const MaterializeTemporaryExpr *E);
+ ConstantLValue emitPointerAuthSignConstant(const CallExpr *E);
+ llvm::Constant *emitPointerAuthPointer(const Expr *E);
+ unsigned emitPointerAuthKey(const Expr *E);
+ std::pair<llvm::Constant *, llvm::ConstantInt *>
+ emitPointerAuthDiscriminator(const Expr *E);
+
bool hasNonZeroOffset() const {
return !Value.getLValueOffset().isZero();
}
@@ -2116,6 +2122,10 @@ ConstantLValueEmitter::VisitCallExpr(const CallExpr *E) {
if (builtin == Builtin::BI__builtin_function_start)
return CGM.GetFunctionStart(
E->getArg(0)->getAsBuiltinConstantDeclRef(CGM.getContext()));
+
+ if (builtin == Builtin::BI__builtin_ptrauth_sign_constant)
+ return emitPointerAuthSignConstant(E);
+
if (builtin != Builtin::BI__builtin___CFStringMakeConstantString &&
builtin != Builtin::BI__builtin___NSStringMakeConstantString)
return nullptr;
@@ -2130,6 +2140,55 @@ ConstantLValueEmitter::VisitCallExpr(const CallExpr *E) {
}
ConstantLValue
+ConstantLValueEmitter::emitPointerAuthSignConstant(const CallExpr *E) {
+ llvm::Constant *UnsignedPointer = emitPointerAuthPointer(E->getArg(0));
+ unsigned Key = emitPointerAuthKey(E->getArg(1));
+ auto [StorageAddress, OtherDiscriminator] =
+ emitPointerAuthDiscriminator(E->getArg(2));
+
+ llvm::Constant *SignedPointer = CGM.getConstantSignedPointer(
+ UnsignedPointer, Key, StorageAddress, OtherDiscriminator);
+ return SignedPointer;
+}
+
+llvm::Constant *ConstantLValueEmitter::emitPointerAuthPointer(const Expr *E) {
+ Expr::EvalResult Result;
+ bool Succeeded = E->EvaluateAsRValue(Result, CGM.getContext());
+ assert(Succeeded);
+ (void)Succeeded;
+
+ // The assertions here are all checked by Sema.
+ assert(Result.Val.isLValue());
+ return ConstantEmitter(CGM, Emitter.CGF)
+ .emitAbstract(E->getExprLoc(), Result.Val, E->getType());
+}
+
+unsigned ConstantLValueEmitter::emitPointerAuthKey(const Expr *E) {
+ return E->EvaluateKnownConstInt(CGM.getContext()).getZExtValue();
+}
+
+std::pair<llvm::Constant *, llvm::ConstantInt *>
+ConstantLValueEmitter::emitPointerAuthDiscriminator(const Expr *E) {
+ E = E->IgnoreParens();
+
+ if (const auto *Call = dyn_cast<CallExpr>(E)) {
+ if (Call->getBuiltinCallee() ==
+ Builtin::BI__builtin_ptrauth_blend_discriminator) {
+ llvm::Constant *Pointer = ConstantEmitter(CGM).emitAbstract(
+ Call->getArg(0), Call->getArg(0)->getType());
+ auto *Extra = cast<llvm::ConstantInt>(ConstantEmitter(CGM).emitAbstract(
+ Call->getArg(1), Call->getArg(1)->getType()));
+ return {Pointer, Extra};
+ }
+ }
+
+ llvm::Constant *Result = ConstantEmitter(CGM).emitAbstract(E, E->getType());
+ if (Result->getType()->isPointerTy())
+ return {Result, nullptr};
+ return {nullptr, cast<llvm::ConstantInt>(Result)};
+}
+
+ConstantLValue
ConstantLValueEmitter::VisitBlockExpr(const BlockExpr *E) {
StringRef functionName;
if (auto CGF = Emitter.CGF)
diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp
new file mode 100644
index 0000000..e1fb0bd
--- /dev/null
+++ b/clang/lib/CodeGen/CGPointerAuth.cpp
@@ -0,0 +1,43 @@
+//===--- CGPointerAuth.cpp - IR generation for pointer authentication -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains common routines relating to the emission of
+// pointer authentication operations.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CodeGenModule.h"
+#include "clang/CodeGen/CodeGenABITypes.h"
+
+using namespace clang;
+using namespace CodeGen;
+
+llvm::Constant *
+CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
+ llvm::Constant *StorageAddress,
+ llvm::ConstantInt *OtherDiscriminator) {
+ llvm::Constant *AddressDiscriminator;
+ if (StorageAddress) {
+ assert(StorageAddress->getType() == UnqualPtrTy);
+ AddressDiscriminator = StorageAddress;
+ } else {
+ AddressDiscriminator = llvm::Constant::getNullValue(UnqualPtrTy);
+ }
+
+ llvm::ConstantInt *IntegerDiscriminator;
+ if (OtherDiscriminator) {
+ assert(OtherDiscriminator->getType() == Int64Ty);
+ IntegerDiscriminator = OtherDiscriminator;
+ } else {
+ IntegerDiscriminator = llvm::ConstantInt::get(Int64Ty, 0);
+ }
+
+ return llvm::ConstantPtrAuth::get(Pointer,
+ llvm::ConstantInt::get(Int32Ty, Key),
+ IntegerDiscriminator, AddressDiscriminator);
+}
diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt
index 7a933d0..8dd9d8b 100644
--- a/clang/lib/CodeGen/CMakeLists.txt
+++ b/clang/lib/CodeGen/CMakeLists.txt
@@ -89,6 +89,7 @@ add_clang_library(clangCodeGen
CGOpenCLRuntime.cpp
CGOpenMPRuntime.cpp
CGOpenMPRuntimeGPU.cpp
+ CGPointerAuth.cpp
CGRecordLayoutBuilder.cpp
CGStmt.cpp
CGStmtOpenMP.cpp
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 9b63f47..04e1a39 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -937,6 +937,11 @@ public:
// Return the function body address of the given function.
llvm::Constant *GetFunctionStart(const ValueDecl *Decl);
+ llvm::Constant *
+ getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
+ llvm::Constant *StorageAddress,
+ llvm::ConstantInt *OtherDiscriminator);
+
// Return whether RTTI information should be emitted for this target.
bool shouldEmitRTTI(bool ForEH = false) {
return (ForEH || getLangOpts().RTTI) && !getLangOpts().CUDAIsDevice &&
diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h
index fd9df16..6aad9ef 100644
--- a/clang/lib/Headers/ptrauth.h
+++ b/clang/lib/Headers/ptrauth.h
@@ -68,12 +68,30 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
On arm64e, the integer must fall within the range of a uint16_t;
other bits may be ignored.
+ For the purposes of ptrauth_sign_constant, the result of calling
+ this function is considered a constant expression if the arguments
+ are constant. Some restrictions may be imposed on the pointer.
+
The first argument must be an expression of pointer type.
The second argument must be an expression of integer type.
The result will have type uintptr_t. */
#define ptrauth_blend_discriminator(__pointer, __integer) \
__builtin_ptrauth_blend_discriminator(__pointer, __integer)
+/* Return a signed pointer for a constant address in a manner which guarantees
+ a non-attackable sequence.
+
+ The value must be a constant expression of pointer type which evaluates to
+ a non-null pointer.
+ The key must be a constant expression of type ptrauth_key.
+ The extra data must be a constant expression of pointer or integer type;
+ if an integer, it will be coerced to ptrauth_extra_data_t.
+ The result will have the same type as the original value.
+
+ This can be used in constant expressions. */
+#define ptrauth_sign_constant(__value, __key, __data) \
+ __builtin_ptrauth_sign_constant(__value, __key, __data)
+
/* Add a signature to the given pointer value using a specific key,
using the given extra data as a salt to the signing process.
@@ -175,6 +193,13 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t;
((ptrauth_extra_data_t)0); \
})
+#define ptrauth_sign_constant(__value, __key, __data) \
+ ({ \
+ (void)__key; \
+ (void)__data; \
+ __value; \
+ })
+
#define ptrauth_sign_unauthenticated(__value, __key, __data) \
({ \
(void)__key; \
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 60a7a38..7a2076d 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2040,8 +2040,23 @@ bool Sema::checkConstantPointerAuthKey(Expr *Arg, unsigned &Result) {
return false;
}
-static bool checkPointerAuthValue(Sema &S, Expr *&Arg,
- PointerAuthOpKind OpKind) {
+static std::pair<const ValueDecl *, CharUnits>
+findConstantBaseAndOffset(Sema &S, Expr *E) {
+ // Must evaluate as a pointer.
+ Expr::EvalResult Result;
+ if (!E->EvaluateAsRValue(Result, S.Context) || !Result.Val.isLValue())
+ return {nullptr, CharUnits()};
+
+ const auto *BaseDecl =
+ Result.Val.getLValueBase().dyn_cast<const ValueDecl *>();
+ if (!BaseDecl)
+ return {nullptr, CharUnits()};
+
+ return {BaseDecl, Result.Val.getLValueOffset()};
+}
+
+static bool checkPointerAuthValue(Sema &S, Expr *&Arg, PointerAuthOpKind OpKind,
+ bool RequireConstant = false) {
if (Arg->hasPlaceholderType()) {
ExprResult R = S.CheckPlaceholderExpr(Arg);
if (R.isInvalid())
@@ -2084,16 +2099,87 @@ static bool checkPointerAuthValue(Sema &S, Expr *&Arg,
if (convertArgumentToType(S, Arg, ExpectedTy))
return true;
- // Warn about null pointers for non-generic sign and auth operations.
- if ((OpKind == PAO_Sign || OpKind == PAO_Auth) &&
- Arg->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNull)) {
- S.Diag(Arg->getExprLoc(), OpKind == PAO_Sign
- ? diag::warn_ptrauth_sign_null_pointer
- : diag::warn_ptrauth_auth_null_pointer)
- << Arg->getSourceRange();
+ if (!RequireConstant) {
+ // Warn about null pointers for non-generic sign and auth operations.
+ if ((OpKind == PAO_Sign || OpKind == PAO_Auth) &&
+ Arg->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNull)) {
+ S.Diag(Arg->getExprLoc(), OpKind == PAO_Sign
+ ? diag::warn_ptrauth_sign_null_pointer
+ : diag::warn_ptrauth_auth_null_pointer)
+ << Arg->getSourceRange();
+ }
+
+ return false;
}
- return false;
+ // Perform special checking on the arguments to ptrauth_sign_constant.
+
+ // The main argument.
+ if (OpKind == PAO_Sign) {
+ // Require the value we're signing to have a special form.
+ auto [BaseDecl, Offset] = findConstantBaseAndOffset(S, Arg);
+ bool Invalid;
+
+ // Must be rooted in a declaration reference.
+ if (!BaseDecl)
+ Invalid = true;
+
+ // If it's a function declaration, we can't have an offset.
+ else if (isa<FunctionDecl>(BaseDecl))
+ Invalid = !Offset.isZero();
+
+ // Otherwise we're fine.
+ else
+ Invalid = false;
+
+ if (Invalid)
+ S.Diag(Arg->getExprLoc(), diag::err_ptrauth_bad_constant_pointer);
+ return Invalid;
+ }
+
+ // The discriminator argument.
+ assert(OpKind == PAO_Discriminator);
+
+ // Must be a pointer or integer or blend thereof.
+ Expr *Pointer = nullptr;
+ Expr *Integer = nullptr;
+ if (auto *Call = dyn_cast<CallExpr>(Arg->IgnoreParens())) {
+ if (Call->getBuiltinCallee() ==
+ Builtin::BI__builtin_ptrauth_blend_discriminator) {
+ Pointer = Call->getArg(0);
+ Integer = Call->getArg(1);
+ }
+ }
+ if (!Pointer && !Integer) {
+ if (Arg->getType()->isPointerType())
+ Pointer = Arg;
+ else
+ Integer = Arg;
+ }
+
+ // Check the pointer.
+ bool Invalid = false;
+ if (Pointer) {
+ assert(Pointer->getType()->isPointerType());
+
+ // TODO: if we're initializing a global, check that the address is
+ // somehow related to what we're initializing. This probably will
+ // never really be feasible and we'll have to catch it at link-time.
+ auto [BaseDecl, Offset] = findConstantBaseAndOffset(S, Pointer);
+ if (!BaseDecl || !isa<VarDecl>(BaseDecl))
+ Invalid = true;
+ }
+
+ // Check the integer.
+ if (Integer) {
+ assert(Integer->getType()->isIntegerType());
+ if (!Integer->isEvaluatable(S.Context))
+ Invalid = true;
+ }
+
+ if (Invalid)
+ S.Diag(Arg->getExprLoc(), diag::err_ptrauth_bad_constant_discriminator);
+ return Invalid;
}
static ExprResult PointerAuthStrip(Sema &S, CallExpr *Call) {
@@ -2136,14 +2222,16 @@ static ExprResult PointerAuthSignGenericData(Sema &S, CallExpr *Call) {
}
static ExprResult PointerAuthSignOrAuth(Sema &S, CallExpr *Call,
- PointerAuthOpKind OpKind) {
+ PointerAuthOpKind OpKind,
+ bool RequireConstant) {
if (S.checkArgCount(Call, 3))
return ExprError();
if (checkPointerAuthEnabled(S, Call))
return ExprError();
- if (checkPointerAuthValue(S, Call->getArgs()[0], OpKind) ||
+ if (checkPointerAuthValue(S, Call->getArgs()[0], OpKind, RequireConstant) ||
checkPointerAuthKey(S, Call->getArgs()[1]) ||
- checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator))
+ checkPointerAuthValue(S, Call->getArgs()[2], PAO_Discriminator,
+ RequireConstant))
return ExprError();
Call->setType(Call->getArgs()[0]->getType());
@@ -2943,10 +3031,15 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
return PointerAuthStrip(*this, TheCall);
case Builtin::BI__builtin_ptrauth_blend_discriminator:
return PointerAuthBlendDiscriminator(*this, TheCall);
+ case Builtin::BI__builtin_ptrauth_sign_constant:
+ return PointerAuthSignOrAuth(*this, TheCall, PAO_Sign,
+ /*RequireConstant=*/true);
case Builtin::BI__builtin_ptrauth_sign_unauthenticated:
- return PointerAuthSignOrAuth(*this, TheCall, PAO_Sign);
+ return PointerAuthSignOrAuth(*this, TheCall, PAO_Sign,
+ /*RequireConstant=*/false);
case Builtin::BI__builtin_ptrauth_auth:
- return PointerAuthSignOrAuth(*this, TheCall, PAO_Auth);
+ return PointerAuthSignOrAuth(*this, TheCall, PAO_Auth,
+ /*RequireConstant=*/false);
case Builtin::BI__builtin_ptrauth_sign_generic_data:
return PointerAuthSignGenericData(*this, TheCall);
case Builtin::BI__builtin_ptrauth_auth_and_resign:
diff --git a/clang/test/CodeGen/ptrauth-intrinsic-sign-constant.c b/clang/test/CodeGen/ptrauth-intrinsic-sign-constant.c
new file mode 100644
index 0000000..bab3989
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-intrinsic-sign-constant.c
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-elf -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s
+
+extern int external;
+
+// CHECK: @ptr1 = global ptr ptrauth (ptr @external, i32 0)
+void *ptr1 = __builtin_ptrauth_sign_constant(&external, 0, 0);
+
+// CHECK: @ptr2 = global ptr ptrauth (ptr @external, i32 0, i64 1234)
+void *ptr2 = __builtin_ptrauth_sign_constant(&external, 0, 1234);
+
+// CHECK: @ptr3 = global ptr ptrauth (ptr @external, i32 2, i64 0, ptr @ptr3)
+void *ptr3 = __builtin_ptrauth_sign_constant(&external, 2, &ptr3);
+
+// CHECK: @ptr4 = global ptr ptrauth (ptr @external, i32 2, i64 26, ptr @ptr4)
+void *ptr4 = __builtin_ptrauth_sign_constant(&external, 2, __builtin_ptrauth_blend_discriminator(&ptr4, 26));
+
+// CHECK: @ptr5 = global ptr null
+void *ptr5;
+
+void test_sign_constant_code() {
+// CHECK-LABEL: define {{.*}}void @test_sign_constant_code()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: store ptr ptrauth (ptr @external, i32 0), ptr @ptr1, align 8
+// CHECK-NEXT: store ptr ptrauth (ptr @external, i32 2, i64 1234), ptr @ptr2, align 8
+// CHECK-NEXT: store ptr ptrauth (ptr @external, i32 2, i64 0, ptr @ptr3), ptr @ptr3, align 8
+// CHECK-NEXT: store ptr ptrauth (ptr @external, i32 2, i64 1234, ptr @ptr4), ptr @ptr4, align 8
+// CHECK-NEXT: ret void
+ ptr1 = __builtin_ptrauth_sign_constant(&external, 0, 0);
+ ptr2 = __builtin_ptrauth_sign_constant(&external, 2, 1234);
+ ptr3 = __builtin_ptrauth_sign_constant(&external, 2, &ptr3);
+ ptr4 = __builtin_ptrauth_sign_constant(&external, 2, __builtin_ptrauth_blend_discriminator(&ptr4, 1234));
+}
diff --git a/clang/test/Sema/ptrauth-intrinsics-macro.c b/clang/test/Sema/ptrauth-intrinsics-macro.c
index 540f284..f76f677 100644
--- a/clang/test/Sema/ptrauth-intrinsics-macro.c
+++ b/clang/test/Sema/ptrauth-intrinsics-macro.c
@@ -37,3 +37,7 @@ void test_string_discriminator(int *dp) {
ptrauth_extra_data_t t0 = ptrauth_string_discriminator("string");
(void)t0;
}
+
+void test_sign_constant(int *dp) {
+ dp = ptrauth_sign_constant(&dv, VALID_DATA_KEY, 0);
+}
diff --git a/clang/test/Sema/ptrauth.c b/clang/test/Sema/ptrauth.c
index 2078609..cd06988 100644
--- a/clang/test/Sema/ptrauth.c
+++ b/clang/test/Sema/ptrauth.c
@@ -136,3 +136,84 @@ void test_sign_generic_data(int *dp) {
int *mismatch = __builtin_ptrauth_sign_generic_data(dp, 0); // expected-error {{incompatible integer to pointer conversion initializing 'int *' with an expression of type}}
}
+
+
+typedef int (*fp_t)(int);
+
+static int dv_weakref __attribute__((weakref("dv")));
+extern int dv_weak __attribute__((weak));
+
+int *t_cst_sig1 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY); // expected-error {{too few arguments}}
+int *t_cst_sig2 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, &dv, &dv); // expected-error {{too many arguments}}
+
+int *t_cst_sig3 = __builtin_ptrauth_sign_constant(mismatched_type, VALID_DATA_KEY, 0); // expected-error {{signed value must have pointer type; type here is 'struct A'}}
+int *t_cst_sig4 = __builtin_ptrauth_sign_constant(&dv, mismatched_type, 0); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}}
+int *t_cst_sig5 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}}
+
+float *t_cst_result = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, 0); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}}
+
+int *t_cst_valid1 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, 0);
+int *t_cst_valid2 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, __builtin_ptrauth_blend_discriminator(&dv, 0));
+int *t_cst_valid3 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, __builtin_ptrauth_blend_discriminator(&dv + 8, 0));
+int *t_cst_valid4 = __builtin_ptrauth_sign_constant(&dv_weak, VALID_DATA_KEY, 0);
+int *t_cst_valid5 = __builtin_ptrauth_sign_constant(&dv_weakref, VALID_DATA_KEY, 0);
+
+int *t_cst_ptr = __builtin_ptrauth_sign_constant(NULL, VALID_DATA_KEY, &dv); // expected-error {{argument to ptrauth_sign_constant must refer to a global variable or function}}
+int *t_cst_key = __builtin_ptrauth_sign_constant(&dv, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+int *t_cst_disc1 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, &fv); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+int *t_cst_disc2 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, __builtin_ptrauth_blend_discriminator(&fv, 0)); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+
+fp_t t_cst_f_valid1 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, 0);
+fp_t t_cst_f_valid2 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, __builtin_ptrauth_blend_discriminator(&dv, 0));
+fp_t t_cst_f_valid3 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, __builtin_ptrauth_blend_discriminator(&dv + 8, 0));
+
+fp_t t_cst_f_key = __builtin_ptrauth_sign_constant(&fv, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+fp_t t_cst_f_disc1 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, &fv); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+fp_t t_cst_f_disc2 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, __builtin_ptrauth_blend_discriminator(&fv, 0)); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+
+int *t_cst_offset = __builtin_ptrauth_sign_constant((int *)((char*)&dv + 16), VALID_DATA_KEY, 0);
+fp_t t_cst_f_offset = __builtin_ptrauth_sign_constant((int (*)(int))((char*)&fv + 16), VALID_CODE_KEY, 0); // expected-error {{argument to ptrauth_sign_constant must refer to a global variable or function}}
+
+void test_sign_constant(int *dp, fp_t fp) {
+ int *sig1 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY); // expected-error {{too few arguments}}
+ int *sig2 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, &dv, &dv); // expected-error {{too many arguments}}
+
+ int *sig3 = __builtin_ptrauth_sign_constant(mismatched_type, VALID_DATA_KEY, 0); // expected-error {{signed value must have pointer type; type here is 'struct A'}}
+ int *sig4 = __builtin_ptrauth_sign_constant(&dv, mismatched_type, 0); // expected-error {{passing 'struct A' to parameter of incompatible type 'int'}}
+ int *sig5 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, mismatched_type); // expected-error {{extra discriminator must have pointer or integer type; type here is 'struct A'}}
+
+ float *result = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, 0); // expected-warning {{incompatible pointer types initializing 'float *' with an expression of type 'int *'}}
+
+ int *valid1 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, 0);
+ int *valid2 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, __builtin_ptrauth_blend_discriminator(&dv, 0));
+ int *valid3 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, __builtin_ptrauth_blend_discriminator(&dv + 8, 0));
+ int *valid4 = __builtin_ptrauth_sign_constant(&dv_weak, VALID_DATA_KEY, 0);
+ int *valid5 = __builtin_ptrauth_sign_constant(&dv_weakref, VALID_DATA_KEY, 0);
+
+ int *ptr = __builtin_ptrauth_sign_constant(NULL, VALID_DATA_KEY, &dv); // expected-error {{argument to ptrauth_sign_constant must refer to a global variable or function}}
+ int *key = __builtin_ptrauth_sign_constant(&dv, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+ int *disc1 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, &fv); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+ int *disc2 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, __builtin_ptrauth_blend_discriminator(&fv, 0)); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+
+ int *ptr2 = __builtin_ptrauth_sign_constant(dp, VALID_DATA_KEY, 0); // expected-error {{argument to ptrauth_sign_constant must refer to a global variable or function}}
+ int *disc3 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, dp); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+ int *disc4 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, __builtin_ptrauth_blend_discriminator(dp, 0)); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+ int *disc5 = __builtin_ptrauth_sign_constant(&dv, VALID_DATA_KEY, __builtin_ptrauth_blend_discriminator(&dv, *dp)); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+
+ fp_t f_valid1 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, 0);
+ fp_t f_valid2 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, __builtin_ptrauth_blend_discriminator(&dv, 0));
+ fp_t f_valid3 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, __builtin_ptrauth_blend_discriminator(&dv + 8, 0));
+
+ fp_t f_key = __builtin_ptrauth_sign_constant(&fv, INVALID_KEY, 0); // expected-error {{does not identify a valid pointer authentication key for the current target}}
+ fp_t f_disc1 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, &fv); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+ fp_t f_disc2 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, __builtin_ptrauth_blend_discriminator(&fv, 0)); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+
+ fp_t f_ptr = __builtin_ptrauth_sign_constant(fp, VALID_CODE_KEY, 0); // expected-error {{argument to ptrauth_sign_constant must refer to a global variable or function}}
+ fp_t f_disc3 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, dp); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+
+ fp_t f_disc4 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, __builtin_ptrauth_blend_discriminator(dp, 0)); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+ fp_t f_disc5 = __builtin_ptrauth_sign_constant(&fv, VALID_CODE_KEY, __builtin_ptrauth_blend_discriminator(&dv, *dp)); // expected-error {{discriminator argument to ptrauth_sign_constant must be a constant integer, the address of the global variable where the result will be stored, or a blend of the two}}
+
+ int *offset = __builtin_ptrauth_sign_constant((int *)((char*)&dv + 16), VALID_DATA_KEY, 0);
+ fp_t f_offset = __builtin_ptrauth_sign_constant((fp_t)((char*)&fv + 16), VALID_CODE_KEY, 0); // expected-error {{argument to ptrauth_sign_constant must refer to a global variable or function}}
+}