From a42c9e5a90140793946f38a1b38242c236995180 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 1 Jul 2024 15:26:25 -0700 Subject: [PATCH 01/58] [InstCombine] Combine ptrauth intrin. callee into ptrauth bundle. Try to optimize a call to the result of a ptrauth intrinsic, potentially into the ptrauth call bundle: - call(ptrauth.resign(p)), ["ptrauth"()] -> call p, ["ptrauth"()] - call(ptrauth.sign(p)), ["ptrauth"()] -> call p - call(ptrauth.auth(p)) -> call p, ["ptrauth"()] as long as the key/discriminator are the same in sign and auth-bundle. This relaxes the existing combine in one major way: it can generate calls with ptrauth bundles with a different key from the existing call. Without knowledge of the target keys, this may be unsound. Generating a plain call to a raw unauthenticated pointer is generally undesirable, but if we ended up seeing a naked ptrauth.sign in the first place, we already have suspicious code. Unauthenticated calls are also easier to spot than naked signs, so let the indirect call shine. --- .../InstCombine/InstCombineCalls.cpp | 43 ++++----- .../InstCombine/InstCombineInternal.h | 9 +- .../Transforms/InstCombine/ptrauth-call.ll | 77 ---------------- .../InstCombine/ptrauth-intrinsics-call.ll | 88 +++++++++++++++++++ 4 files changed, 119 insertions(+), 98 deletions(-) delete mode 100644 llvm/test/Transforms/InstCombine/ptrauth-call.ll create mode 100644 llvm/test/Transforms/InstCombine/ptrauth-intrinsics-call.ll diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index 2b97302e8990f..b4a34a60e60dc 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3691,7 +3691,7 @@ static IntrinsicInst *findInitTrampoline(Value *Callee) { return nullptr; } -Instruction *InstCombinerImpl::tryCombinePtrAuthCall(CallBase &Call) { +Instruction *InstCombinerImpl::foldPtrAuthIntrinsicCallee(CallBase &Call) { Value *Callee = Call.getCalledOperand(); auto *IPC = dyn_cast(Callee); if (!IPC || !IPC->isNoopCast(DL)) @@ -3701,24 +3701,28 @@ Instruction *InstCombinerImpl::tryCombinePtrAuthCall(CallBase &Call) { if (!II) return nullptr; - auto PtrAuthBundleOrNone = Call.getOperandBundle(LLVMContext::OB_ptrauth); - assert(Call.getNumOperandBundles() <= 1 && - "unimplemented support for ptrauth and other bundle"); + // Isolate the ptrauth bundle from the others. + std::optional PtrAuthBundleOrNone; + SmallVector NewBundles; + for (unsigned BI = 0, BE = Call.getNumOperandBundles(); BI != BE; ++BI) { + OperandBundleUse Bundle = Call.getOperandBundleAt(BI); + if (Bundle.getTagID() == LLVMContext::OB_ptrauth) + PtrAuthBundleOrNone = Bundle; + else + NewBundles.emplace_back(Bundle); + } Value *NewCallee = nullptr; - SmallVector NewBundles; switch (II->getIntrinsicID()) { default: return nullptr; - // call(ptrauth_resign(p)), ["ptrauth"()] -> call p, ["ptrauth"()] + // call(ptrauth.resign(p)), ["ptrauth"()] -> call p, ["ptrauth"()] // assuming the call bundle and the sign operands match. case Intrinsic::ptrauth_resign: { - if (!PtrAuthBundleOrNone) - return nullptr; - auto PtrAuthBundle = *PtrAuthBundleOrNone; - if (II->getOperand(3) != PtrAuthBundle.Inputs[0] || - II->getOperand(4) != PtrAuthBundle.Inputs[1]) + if (!PtrAuthBundleOrNone || + II->getOperand(3) != PtrAuthBundleOrNone->Inputs[0] || + II->getOperand(4) != PtrAuthBundleOrNone->Inputs[1]) return nullptr; Value *NewBundleOps[] = {II->getOperand(1), II->getOperand(2)}; @@ -3727,20 +3731,19 @@ Instruction *InstCombinerImpl::tryCombinePtrAuthCall(CallBase &Call) { break; } - // call(ptrauth_sign(p)), ["ptrauth"()] -> call p + // call(ptrauth.sign(p)), ["ptrauth"()] -> call p // assuming the call bundle and the sign operands match. + // Non-ptrauth indirect calls are undesirable, but so is ptrauth.sign. case Intrinsic::ptrauth_sign: { - if (!PtrAuthBundleOrNone) - return nullptr; - auto PtrAuthBundle = *PtrAuthBundleOrNone; - if (II->getOperand(1) != PtrAuthBundle.Inputs[0] || - II->getOperand(2) != PtrAuthBundle.Inputs[1]) + if (!PtrAuthBundleOrNone || + II->getOperand(1) != PtrAuthBundleOrNone->Inputs[0] || + II->getOperand(2) != PtrAuthBundleOrNone->Inputs[1]) return nullptr; NewCallee = II->getOperand(0); break; } - // call(ptrauth_auth(p)) -> call p, ["ptrauth"()] + // call(ptrauth.auth(p)) -> call p, ["ptrauth"()] case Intrinsic::ptrauth_auth: { if (PtrAuthBundleOrNone) return nullptr; @@ -3913,8 +3916,8 @@ Instruction *InstCombinerImpl::visitCallBase(CallBase &Call) { if (IntrinsicInst *II = findInitTrampoline(Callee)) return transformCallThroughTrampoline(Call, *II); - // Combine calls involving pointer authentication - if (Instruction *NewCall = tryCombinePtrAuthCall(Call)) + // Combine calls involving pointer authentication intrinsics. + if (Instruction *NewCall = foldPtrAuthIntrinsicCallee(Call)) return NewCall; if (isa(Callee) && !Call.doesNotThrow()) { diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index 0969b6ed9d3f6..b1c62b93f46bd 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -281,7 +281,14 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final bool transformConstExprCastCall(CallBase &Call); Instruction *transformCallThroughTrampoline(CallBase &Call, IntrinsicInst &Tramp); - Instruction *tryCombinePtrAuthCall(CallBase &Call); + + /// Try to optimize a call to the result of a ptrauth intrinsic, potentially + /// into the ptrauth call bundle: + /// - call(ptrauth.resign(p)), ["ptrauth"()] -> call p, ["ptrauth"()] + /// - call(ptrauth.sign(p)), ["ptrauth"()] -> call p + /// - call(ptrauth.auth(p)) -> call p, ["ptrauth"()] + /// as long as the key/discriminator are the same in sign and auth-bundle. + Instruction *foldPtrAuthIntrinsicCallee(CallBase &Call); // Return (a, b) if (LHS, RHS) is known to be (a, b) or (b, a). // Otherwise, return std::nullopt diff --git a/llvm/test/Transforms/InstCombine/ptrauth-call.ll b/llvm/test/Transforms/InstCombine/ptrauth-call.ll deleted file mode 100644 index 9684efdcec490..0000000000000 --- a/llvm/test/Transforms/InstCombine/ptrauth-call.ll +++ /dev/null @@ -1,77 +0,0 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt < %s -passes=instcombine -S | FileCheck %s - -define i32 @test_ptrauth_call_resign(ptr %p) { -; CHECK-LABEL: @test_ptrauth_call_resign( -; CHECK-NEXT: [[TMP3:%.*]] = call i32 [[P:%.*]]() [ "ptrauth"(i32 0, i64 1234) ] -; CHECK-NEXT: ret i32 [[TMP3]] -; - %tmp0 = ptrtoint ptr %p to i64 - %tmp1 = call i64 @llvm.ptrauth.resign(i64 %tmp0, i32 0, i64 1234, i32 2, i64 5678) - %tmp2 = inttoptr i64 %tmp1 to ptr - %tmp3 = call i32 %tmp2() [ "ptrauth"(i32 2, i64 5678) ] - ret i32 %tmp3 -} - -define i32 @test_ptrauth_call_resign_blend(ptr %pp) { -; CHECK-LABEL: @test_ptrauth_call_resign_blend( -; CHECK-NEXT: [[TMP01:%.*]] = load ptr, ptr [[PP:%.*]], align 8 -; CHECK-NEXT: [[TMP6:%.*]] = call i32 [[TMP01]]() [ "ptrauth"(i32 0, i64 1234) ] -; CHECK-NEXT: ret i32 [[TMP6]] -; - %tmp0 = load ptr, ptr %pp, align 8 - %tmp1 = ptrtoint ptr %pp to i64 - %tmp2 = ptrtoint ptr %tmp0 to i64 - %tmp3 = call i64 @llvm.ptrauth.blend(i64 %tmp1, i64 5678) - %tmp4 = call i64 @llvm.ptrauth.resign(i64 %tmp2, i32 0, i64 1234, i32 1, i64 %tmp3) - %tmp5 = inttoptr i64 %tmp4 to ptr - %tmp6 = call i32 %tmp5() [ "ptrauth"(i32 1, i64 %tmp3) ] - ret i32 %tmp6 -} - -define i32 @test_ptrauth_call_resign_blend_2(ptr %pp) { -; CHECK-LABEL: @test_ptrauth_call_resign_blend_2( -; CHECK-NEXT: [[TMP01:%.*]] = load ptr, ptr [[PP:%.*]], align 8 -; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[PP]] to i64 -; CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[TMP1]], i64 5678) -; CHECK-NEXT: [[TMP6:%.*]] = call i32 [[TMP01]]() [ "ptrauth"(i32 1, i64 [[TMP3]]) ] -; CHECK-NEXT: ret i32 [[TMP6]] -; - %tmp0 = load ptr, ptr %pp, align 8 - %tmp1 = ptrtoint ptr %pp to i64 - %tmp2 = ptrtoint ptr %tmp0 to i64 - %tmp3 = call i64 @llvm.ptrauth.blend(i64 %tmp1, i64 5678) - %tmp4 = call i64 @llvm.ptrauth.resign(i64 %tmp2, i32 1, i64 %tmp3, i32 0, i64 1234) - %tmp5 = inttoptr i64 %tmp4 to ptr - %tmp6 = call i32 %tmp5() [ "ptrauth"(i32 0, i64 1234) ] - ret i32 %tmp6 -} - -define i32 @test_ptrauth_call_auth(ptr %p) { -; CHECK-LABEL: @test_ptrauth_call_auth( -; CHECK-NEXT: [[TMP3:%.*]] = call i32 [[P:%.*]]() [ "ptrauth"(i32 2, i64 5678) ] -; CHECK-NEXT: ret i32 [[TMP3]] -; - %tmp0 = ptrtoint ptr %p to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 5678) - %tmp2 = inttoptr i64 %tmp1 to ptr - %tmp3 = call i32 %tmp2() - ret i32 %tmp3 -} - -define i32 @test_ptrauth_call_sign(ptr %p) { -; CHECK-LABEL: @test_ptrauth_call_sign( -; CHECK-NEXT: [[TMP3:%.*]] = call i32 [[P:%.*]]() -; CHECK-NEXT: ret i32 [[TMP3]] -; - %tmp0 = ptrtoint ptr %p to i64 - %tmp1 = call i64 @llvm.ptrauth.sign(i64 %tmp0, i32 2, i64 5678) - %tmp2 = inttoptr i64 %tmp1 to ptr - %tmp3 = call i32 %tmp2() [ "ptrauth"(i32 2, i64 5678) ] - ret i32 %tmp3 -} - -declare i64 @llvm.ptrauth.auth(i64, i32, i64) -declare i64 @llvm.ptrauth.sign(i64, i32, i64) -declare i64 @llvm.ptrauth.resign(i64, i32, i64, i32, i64) -declare i64 @llvm.ptrauth.blend(i64, i64) diff --git a/llvm/test/Transforms/InstCombine/ptrauth-intrinsics-call.ll b/llvm/test/Transforms/InstCombine/ptrauth-intrinsics-call.ll new file mode 100644 index 0000000000000..d0298432dd470 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/ptrauth-intrinsics-call.ll @@ -0,0 +1,88 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +define i32 @test_ptrauth_call_resign(ptr %p) { +; CHECK-LABEL: @test_ptrauth_call_resign( +; CHECK-NEXT: [[V3:%.*]] = call i32 [[P:%.*]]() [ "ptrauth"(i32 0, i64 1234) ] +; CHECK-NEXT: ret i32 [[V3]] +; + %v0 = ptrtoint ptr %p to i64 + %v1 = call i64 @llvm.ptrauth.resign(i64 %v0, i32 0, i64 1234, i32 2, i64 5678) + %v2 = inttoptr i64 %v1 to ptr + %v3 = call i32 %v2() [ "ptrauth"(i32 2, i64 5678) ] + ret i32 %v3 +} + +define i32 @test_ptrauth_call_resign_blend(ptr %pp) { +; CHECK-LABEL: @test_ptrauth_call_resign_blend( +; CHECK-NEXT: [[V01:%.*]] = load ptr, ptr [[PP:%.*]], align 8 +; CHECK-NEXT: [[V6:%.*]] = call i32 [[V01]]() [ "ptrauth"(i32 0, i64 1234) ] +; CHECK-NEXT: ret i32 [[V6]] +; + %v0 = load ptr, ptr %pp, align 8 + %v1 = ptrtoint ptr %pp to i64 + %v2 = ptrtoint ptr %v0 to i64 + %v3 = call i64 @llvm.ptrauth.blend(i64 %v1, i64 5678) + %v4 = call i64 @llvm.ptrauth.resign(i64 %v2, i32 0, i64 1234, i32 1, i64 %v3) + %v5 = inttoptr i64 %v4 to ptr + %v6 = call i32 %v5() [ "ptrauth"(i32 1, i64 %v3) ] + ret i32 %v6 +} + +define i32 @test_ptrauth_call_resign_blend_2(ptr %pp) { +; CHECK-LABEL: @test_ptrauth_call_resign_blend_2( +; CHECK-NEXT: [[V01:%.*]] = load ptr, ptr [[PP:%.*]], align 8 +; CHECK-NEXT: [[V1:%.*]] = ptrtoint ptr [[PP]] to i64 +; CHECK-NEXT: [[V3:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[V1]], i64 5678) +; CHECK-NEXT: [[V6:%.*]] = call i32 [[V01]]() [ "ptrauth"(i32 1, i64 [[V3]]) ] +; CHECK-NEXT: ret i32 [[V6]] +; + %v0 = load ptr, ptr %pp, align 8 + %v1 = ptrtoint ptr %pp to i64 + %v2 = ptrtoint ptr %v0 to i64 + %v3 = call i64 @llvm.ptrauth.blend(i64 %v1, i64 5678) + %v4 = call i64 @llvm.ptrauth.resign(i64 %v2, i32 1, i64 %v3, i32 0, i64 1234) + %v5 = inttoptr i64 %v4 to ptr + %v6 = call i32 %v5() [ "ptrauth"(i32 0, i64 1234) ] + ret i32 %v6 +} + +define i32 @test_ptrauth_call_auth(ptr %p) { +; CHECK-LABEL: @test_ptrauth_call_auth( +; CHECK-NEXT: [[V3:%.*]] = call i32 [[P:%.*]]() [ "ptrauth"(i32 2, i64 5678) ] +; CHECK-NEXT: ret i32 [[V3]] +; + %v0 = ptrtoint ptr %p to i64 + %v1 = call i64 @llvm.ptrauth.auth(i64 %v0, i32 2, i64 5678) + %v2 = inttoptr i64 %v1 to i32()* + %v3 = call i32 %v2() + ret i32 %v3 +} + +define i32 @test_ptrauth_call_sign(ptr %p) { +; CHECK-LABEL: @test_ptrauth_call_sign( +; CHECK-NEXT: [[V3:%.*]] = call i32 [[P:%.*]]() +; CHECK-NEXT: ret i32 [[V3]] +; + %v0 = ptrtoint ptr %p to i64 + %v1 = call i64 @llvm.ptrauth.sign(i64 %v0, i32 2, i64 5678) + %v2 = inttoptr i64 %v1 to ptr + %v3 = call i32 %v2() [ "ptrauth"(i32 2, i64 5678) ] + ret i32 %v3 +} + +define i32 @test_ptrauth_call_sign_otherbundle(ptr %p) { +; CHECK-LABEL: @test_ptrauth_call_sign_otherbundle( +; CHECK-NEXT: [[V3:%.*]] = call i32 [[P:%.*]]() [ "somebundle"(ptr null), "otherbundle"(i64 0) ] +; CHECK-NEXT: ret i32 [[V3]] +; + %v0 = ptrtoint ptr %p to i64 + %v1 = call i64 @llvm.ptrauth.sign(i64 %v0, i32 2, i64 5678) + %v2 = inttoptr i64 %v1 to ptr + %v3 = call i32 %v2() [ "somebundle"(ptr null), "ptrauth"(i32 2, i64 5678), "otherbundle"(i64 0) ] + ret i32 %v3 +} + +declare i64 @llvm.ptrauth.sign(i64, i32, i64) +declare i64 @llvm.ptrauth.resign(i64, i32, i64, i32, i64) +declare i64 @llvm.ptrauth.blend(i64, i64) From 2222706fcce1585f49765fefb2a040ba7540e048 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 27 Sep 2021 08:00:00 -0700 Subject: [PATCH 02/58] [InstCombine] Combine ptrauth constant callee into bundle. Try to optimize a call to a ptrauth constant, into its ptrauth bundle: call(ptrauth(f)), ["ptrauth"()] -> call f as long as the key/discriminator are the same in constant and bundle. --- .../InstCombine/InstCombineCalls.cpp | 32 +++++++ .../InstCombine/InstCombineInternal.h | 5 ++ .../Transforms/InstCombine/ptrauth-call.ll | 89 +++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 llvm/test/Transforms/InstCombine/ptrauth-call.ll diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index b4a34a60e60dc..fc50e73f29157 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3769,6 +3769,34 @@ Instruction *InstCombinerImpl::foldPtrAuthIntrinsicCallee(CallBase &Call) { return NewCall; } +Instruction *InstCombinerImpl::foldPtrAuthConstantCallee(CallBase &Call) { + auto *CPA = dyn_cast(Call.getCalledOperand()); + if (!CPA) + return nullptr; + + auto *CalleeF = dyn_cast(CPA->getPointer()); + // If the ptrauth constant isn't based on a function pointer, bail out. + if (!CalleeF) + return nullptr; + + // Inspect the call ptrauth bundle to check it matches the ptrauth constant. + auto PAB = Call.getOperandBundle(LLVMContext::OB_ptrauth); + if (!PAB) + return nullptr; + + auto *Key = cast(PAB->Inputs[0]); + Value *Discriminator = PAB->Inputs[1]; + + // If the bundle doesn't match, this is probably going to fail to auth. + if (!CPA->isKnownCompatibleWith(Key, Discriminator, DL)) + return nullptr; + + // If the bundle matches the constant, proceed in making this a direct call. + auto *NewCall = CallBase::removeOperandBundle(&Call, LLVMContext::OB_ptrauth); + NewCall->setCalledOperand(CalleeF); + return NewCall; +} + bool InstCombinerImpl::annotateAnyAllocSite(CallBase &Call, const TargetLibraryInfo *TLI) { // Note: We only handle cases which can't be driven from generic attributes @@ -3920,6 +3948,10 @@ Instruction *InstCombinerImpl::visitCallBase(CallBase &Call) { if (Instruction *NewCall = foldPtrAuthIntrinsicCallee(Call)) return NewCall; + // Combine calls to ptrauth constants. + if (Instruction *NewCall = foldPtrAuthConstantCallee(Call)) + return NewCall; + if (isa(Callee) && !Call.doesNotThrow()) { InlineAsm *IA = cast(Callee); if (!IA->canThrow()) { diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index b1c62b93f46bd..ec5fda60fb4b5 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -290,6 +290,11 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final /// as long as the key/discriminator are the same in sign and auth-bundle. Instruction *foldPtrAuthIntrinsicCallee(CallBase &Call); + /// Try to optimize a call to a ptrauth constant, into its ptrauth bundle: + /// call(ptrauth(f)), ["ptrauth"()] -> call f + /// as long as the key/discriminator are the same in constant and bundle. + Instruction *foldPtrAuthConstantCallee(CallBase &Call); + // Return (a, b) if (LHS, RHS) is known to be (a, b) or (b, a). // Otherwise, return std::nullopt // Currently it matches: diff --git a/llvm/test/Transforms/InstCombine/ptrauth-call.ll b/llvm/test/Transforms/InstCombine/ptrauth-call.ll new file mode 100644 index 0000000000000..b4363b528d4e2 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/ptrauth-call.ll @@ -0,0 +1,89 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + +declare i64 @f(i32) +declare ptr @f2(i32) + +define i32 @test_ptrauth_call(i32 %a0) { +; CHECK-LABEL: @test_ptrauth_call( +; CHECK-NEXT: [[V0:%.*]] = call i32 @f(i32 [[A0:%.*]]) +; CHECK-NEXT: ret i32 [[V0]] +; + %v0 = call i32 ptrauth(ptr @f, i32 0)(i32 %a0) [ "ptrauth"(i32 0, i64 0) ] + ret i32 %v0 +} + +define i32 @test_ptrauth_call_disc(i32 %a0) { +; CHECK-LABEL: @test_ptrauth_call_disc( +; CHECK-NEXT: [[V0:%.*]] = call i32 @f(i32 [[A0:%.*]]) +; CHECK-NEXT: ret i32 [[V0]] +; + %v0 = call i32 ptrauth(ptr @f, i32 1, i64 5678)(i32 %a0) [ "ptrauth"(i32 1, i64 5678) ] + ret i32 %v0 +} + +@f_addr_disc.ref = constant ptr ptrauth(ptr @f, i32 1, i64 0, ptr @f_addr_disc.ref) + +define i32 @test_ptrauth_call_addr_disc(i32 %a0) { +; CHECK-LABEL: @test_ptrauth_call_addr_disc( +; CHECK-NEXT: [[V0:%.*]] = call i32 @f(i32 [[A0:%.*]]) +; CHECK-NEXT: ret i32 [[V0]] +; + %v0 = call i32 ptrauth(ptr @f, i32 1, i64 0, ptr @f_addr_disc.ref)(i32 %a0) [ "ptrauth"(i32 1, i64 ptrtoint (ptr @f_addr_disc.ref to i64)) ] + ret i32 %v0 +} + +@f_both_disc.ref = constant ptr ptrauth(ptr @f, i32 1, i64 1234, ptr @f_both_disc.ref) + +define i32 @test_ptrauth_call_blend(i32 %a0) { +; CHECK-LABEL: @test_ptrauth_call_blend( +; CHECK-NEXT: [[V0:%.*]] = call i32 @f(i32 [[A0:%.*]]) +; CHECK-NEXT: ret i32 [[V0]] +; + %v = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @f_both_disc.ref to i64), i64 1234) + %v0 = call i32 ptrauth(ptr @f, i32 1, i64 1234, ptr @f_both_disc.ref)(i32 %a0) [ "ptrauth"(i32 1, i64 %v) ] + ret i32 %v0 +} + +define i64 @test_ptrauth_call_cast(i32 %a0) { +; CHECK-LABEL: @test_ptrauth_call_cast( +; CHECK-NEXT: [[V0:%.*]] = call ptr @f2(i32 [[A0:%.*]]) +; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[V0]] to i64 +; CHECK-NEXT: ret i64 [[TMP1]] +; + %v0 = call i64 ptrauth(ptr @f2, i32 0)(i32 %a0) [ "ptrauth"(i32 0, i64 0) ] + ret i64 %v0 +} + +define i32 @test_ptrauth_call_mismatch_key(i32 %a0) { +; CHECK-LABEL: @test_ptrauth_call_mismatch_key( +; CHECK-NEXT: [[V0:%.*]] = call i32 ptrauth (ptr @f, i32 1, i64 5678)(i32 [[A0:%.*]]) [ "ptrauth"(i32 0, i64 5678) ] +; CHECK-NEXT: ret i32 [[V0]] +; + %v0 = call i32 ptrauth(ptr @f, i32 1, i64 5678)(i32 %a0) [ "ptrauth"(i32 0, i64 5678) ] + ret i32 %v0 +} + +define i32 @test_ptrauth_call_mismatch_disc(i32 %a0) { +; CHECK-LABEL: @test_ptrauth_call_mismatch_disc( +; CHECK-NEXT: [[V0:%.*]] = call i32 ptrauth (ptr @f, i32 1, i64 5678)(i32 [[A0:%.*]]) [ "ptrauth"(i32 1, i64 0) ] +; CHECK-NEXT: ret i32 [[V0]] +; + %v0 = call i32 ptrauth(ptr @f, i32 1, i64 5678)(i32 %a0) [ "ptrauth"(i32 1, i64 0) ] + ret i32 %v0 +} + +define i32 @test_ptrauth_call_mismatch_blend(i32 %a0) { +; CHECK-LABEL: @test_ptrauth_call_mismatch_blend( +; CHECK-NEXT: [[V:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @f_both_disc.ref to i64), i64 0) +; CHECK-NEXT: [[V0:%.*]] = call i32 ptrauth (ptr @f, i32 1, i64 1234, ptr @f_both_disc.ref)(i32 [[A0:%.*]]) [ "ptrauth"(i32 1, i64 [[V]]) ] +; CHECK-NEXT: ret i32 [[V0]] +; + %v = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @f_both_disc.ref to i64), i64 0) + %v0 = call i32 ptrauth(ptr @f, i32 1, i64 1234, ptr @f_both_disc.ref)(i32 %a0) [ "ptrauth"(i32 1, i64 %v) ] + ret i32 %v0 +} + +declare i64 @llvm.ptrauth.blend(i64, i64) From 295a7554530830f750962d9d993a7e6fc9529313 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 1 Jul 2024 15:32:37 -0700 Subject: [PATCH 03/58] [AArch64][PAC] Lower auth/resign into checked sequence. This introduces 3 hardening modes in the authentication step of auth/resign lowering: - unchecked, which uses the AUT instructions as-is - poison, which detects authentication failure (using an XPAC+CMP sequence), explicitly yielding the XPAC result rather than the AUT result, to avoid leaking - trap, which additionally traps on authentication failure, using BRK #0xC470 + key (IA C470, IB C471, DA C472, DB C473.) Not all modes are necessarily useful in all contexts, and there are more performant alternative lowerings in specific contexts (e.g., when I/D TBI enablement is a target ABI guarantee.) This is controlled by the `ptrauth-auth-traps` function attributes, and can be overridden using `-aarch64-ptrauth-auth-checks=`. --- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 396 ++++++++++-------- .../Target/AArch64/AArch64ISelDAGToDAG.cpp | 162 ++++--- llvm/lib/Target/AArch64/AArch64InstrInfo.td | 22 +- .../GISel/AArch64InstructionSelector.cpp | 58 +++ ...trauth-intrinsic-auth-resign-with-blend.ll | 261 ++++++++++++ ...cs.ll => ptrauth-intrinsic-auth-resign.ll} | 315 ++++++++------ 6 files changed, 853 insertions(+), 361 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-with-blend.ll rename llvm/test/CodeGen/AArch64/{arm64e-ptrauth-intrinsics.ll => ptrauth-intrinsic-auth-resign.ll} (73%) diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 78d13e0141a50..21efd38342640 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -73,14 +73,13 @@ using namespace llvm; enum PtrauthCheckMode { Default, Unchecked, Poison, Trap }; -static cl::opt -PtrauthAuthChecks("aarch64-ptrauth-auth-checks", cl::Hidden, - cl::values( - clEnumValN(Unchecked, "none", "don't test for failure"), - clEnumValN(Poison, "poison", "poison on failure"), - clEnumValN(Trap, "trap", "trap on failure")), - cl::desc("Check pointer authentication auth/resign failures"), - cl::init(Default)); +static cl::opt PtrauthAuthChecks( + "aarch64-ptrauth-auth-checks", cl::Hidden, + cl::values(clEnumValN(Unchecked, "none", "don't test for failure"), + clEnumValN(Poison, "poison", "poison on failure"), + clEnumValN(Trap, "trap", "trap on failure")), + cl::desc("Check pointer authentication auth/resign failures"), + cl::init(Default)); #define DEBUG_TYPE "asm-printer" @@ -148,6 +147,10 @@ class AArch64AsmPrinter : public AsmPrinter { // Emit the sequence for BLRA (authenticate + branch). void emitPtrauthBranch(const MachineInstr *MI); + + // Emit the sequence for AUT or AUTPAC. + void emitPtrauthAuthResign(const MachineInstr *MI); + // Emit the sequence to compute a discriminator into x17, or reuse AddrDisc. unsigned emitPtrauthDiscriminator(uint16_t Disc, unsigned AddrDisc, unsigned &InstsEmitted); @@ -1712,6 +1715,217 @@ void AArch64AsmPrinter::emitPtrauthBranch(const MachineInstr *MI) { assert(STI->getInstrInfo()->getInstSizeInBytes(*MI) >= InstsEmitted * 4); } +void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) { + unsigned InstsEmitted = 0; + const bool IsAUTPAC = MI->getOpcode() == AArch64::AUTPAC; + + // We can expand AUT/AUTPAC into 3 possible sequences: + // - unchecked: + // autia x16, x0 + // pacib x16, x1 ; if AUTPAC + // + // - checked and clearing: + // mov x17, x0 + // movk x17, #disc, lsl #48 + // autia x16, x17 + // mov x17, x16 + // xpaci x17 + // cmp x16, x17 + // b.eq Lsuccess + // mov x16, x17 + // b Lend + // Lsuccess: + // mov x17, x1 + // movk x17, #disc, lsl #48 + // pacib x16, x17 + // Lend: + // Where we only emit the AUT if we started with an AUT. + // + // - checked and trapping: + // mov x17, x0 + // movk x17, #disc, lsl #48 + // autia x16, x0 + // mov x17, x16 + // xpaci x17 + // cmp x16, x17 + // b.eq Lsuccess + // brk #<0xc470 + aut key> + // Lsuccess: + // mov x17, x1 + // movk x17, #disc, lsl #48 + // pacib x16, x17 ; if AUTPAC + // Where the b.eq skips over the trap if the PAC is valid. + // + // This sequence is expensive, but we need more information to be able to + // do better. + // + // We can't TBZ the poison bit because EnhancedPAC2 XORs the PAC bits + // on failure. + // We can't TST the PAC bits because we don't always know how the address + // space is setup for the target environment (and the bottom PAC bit is + // based on that). + // Either way, we also don't always know whether TBI is enabled or not for + // the specific target environment. + + // By default, auth/resign sequences check for auth failures. + bool ShouldCheck = true; + // In the checked sequence, we only trap if explicitly requested. + bool ShouldTrap = MF->getFunction().hasFnAttribute("ptrauth-auth-traps"); + + // However, command-line flags can override this, for experimentation. + switch (PtrauthAuthChecks) { + case PtrauthCheckMode::Default: + break; + case PtrauthCheckMode::Unchecked: + ShouldCheck = ShouldTrap = false; + break; + case PtrauthCheckMode::Poison: + ShouldCheck = true; + ShouldTrap = false; + break; + case PtrauthCheckMode::Trap: + ShouldCheck = ShouldTrap = true; + break; + } + + auto AUTKey = (AArch64PACKey::ID)MI->getOperand(0).getImm(); + uint64_t AUTDisc = MI->getOperand(1).getImm(); + unsigned AUTAddrDisc = MI->getOperand(2).getReg(); + + unsigned XPACOpc = getXPACOpcodeForKey(AUTKey); + + // Compute aut discriminator into x17 + assert(isUInt<16>(AUTDisc)); + unsigned AUTDiscReg = + emitPtrauthDiscriminator(AUTDisc, AUTAddrDisc, InstsEmitted); + bool AUTZero = AUTDiscReg == AArch64::XZR; + unsigned AUTOpc = getAUTOpcodeForKey(AUTKey, AUTZero); + + // autiza x16 ; if AUTZero + // autia x16, x17 ; if !AUTZero + MCInst AUTInst; + AUTInst.setOpcode(AUTOpc); + AUTInst.addOperand(MCOperand::createReg(AArch64::X16)); + AUTInst.addOperand(MCOperand::createReg(AArch64::X16)); + if (!AUTZero) + AUTInst.addOperand(MCOperand::createReg(AUTDiscReg)); + EmitToStreamer(*OutStreamer, AUTInst); + ++InstsEmitted; + + // Unchecked or checked-but-non-trapping AUT is just an "AUT": we're done. + if (!IsAUTPAC && (!ShouldCheck || !ShouldTrap)) { + assert(STI->getInstrInfo()->getInstSizeInBytes(*MI) >= InstsEmitted * 4); + return; + } + + MCSymbol *EndSym = nullptr; + + // Checked sequences do an additional strip-and-compare. + if (ShouldCheck) { + MCSymbol *SuccessSym = createTempSymbol("auth_success_"); + + // XPAC has tied src/dst: use x17 as a temporary copy. + // mov x17, x16 + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ORRXrs) + .addReg(AArch64::X17) + .addReg(AArch64::XZR) + .addReg(AArch64::X16) + .addImm(0)); + ++InstsEmitted; + + // xpaci x17 + EmitToStreamer( + *OutStreamer, + MCInstBuilder(XPACOpc).addReg(AArch64::X17).addReg(AArch64::X17)); + ++InstsEmitted; + + // cmp x16, x17 + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::SUBSXrs) + .addReg(AArch64::XZR) + .addReg(AArch64::X16) + .addReg(AArch64::X17) + .addImm(0)); + ++InstsEmitted; + + // b.eq Lsuccess + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::Bcc) + .addImm(AArch64CC::EQ) + .addExpr(MCSymbolRefExpr::create( + SuccessSym, OutContext))); + ++InstsEmitted; + + if (ShouldTrap) { + // Trapping sequences do a 'brk'. + // brk #<0xc470 + aut key> + EmitToStreamer(*OutStreamer, + MCInstBuilder(AArch64::BRK).addImm(0xc470 | AUTKey)); + ++InstsEmitted; + } else { + // Non-trapping checked sequences return the stripped result in x16, + // skipping over the PAC if there is one. + + // FIXME: can we simply return the AUT result, already in x16? without.. + // ..traps this is usable as an oracle anyway, based on high bits + // mov x17, x16 + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ORRXrs) + .addReg(AArch64::X16) + .addReg(AArch64::XZR) + .addReg(AArch64::X17) + .addImm(0)); + ++InstsEmitted; + + if (IsAUTPAC) { + EndSym = createTempSymbol("resign_end_"); + + // b Lend + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::B) + .addExpr(MCSymbolRefExpr::create( + EndSym, OutContext))); + ++InstsEmitted; + } + } + + // If the auth check succeeds, we can continue. + // Lsuccess: + OutStreamer->emitLabel(SuccessSym); + } + + // We already emitted unchecked and checked-but-non-trapping AUTs. + // That left us with trapping AUTs, and AUTPACs. + // Trapping AUTs don't need PAC: we're done. + if (!IsAUTPAC) { + assert(STI->getInstrInfo()->getInstSizeInBytes(*MI) >= InstsEmitted * 4); + return; + } + + auto PACKey = (AArch64PACKey::ID)MI->getOperand(3).getImm(); + uint64_t PACDisc = MI->getOperand(4).getImm(); + unsigned PACAddrDisc = MI->getOperand(5).getReg(); + + // Compute pac discriminator into x17 + assert(isUInt<16>(PACDisc)); + unsigned PACDiscReg = + emitPtrauthDiscriminator(PACDisc, PACAddrDisc, InstsEmitted); + bool PACZero = PACDiscReg == AArch64::XZR; + unsigned PACOpc = getPACOpcodeForKey(PACKey, PACZero); + + // pacizb x16 ; if PACZero + // pacib x16, x17 ; if !PACZero + MCInst PACInst; + PACInst.setOpcode(PACOpc); + PACInst.addOperand(MCOperand::createReg(AArch64::X16)); + PACInst.addOperand(MCOperand::createReg(AArch64::X16)); + if (!PACZero) + PACInst.addOperand(MCOperand::createReg(PACDiscReg)); + EmitToStreamer(*OutStreamer, PACInst); + ++InstsEmitted; + + assert(STI->getInstrInfo()->getInstSizeInBytes(*MI) >= InstsEmitted * 4); + // Lend: + if (EndSym) + OutStreamer->emitLabel(EndSym); +} + const MCExpr * AArch64AsmPrinter::lowerPtrAuthGlobalConstant(const GlobalPtrAuthInfo &PAI) { MCContext &Ctx = OutContext; @@ -2149,167 +2363,6 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { return; } - case AArch64::AUT: - case AArch64::AUTPAC: { - const bool IsAUTPAC = MI->getOpcode() == AArch64::AUTPAC; - - // We can expand AUT/AUTPAC into 3 possible sequences: - // - unchecked: - // autia x16, x0 - // pacib x16, x1 ; if AUTPAC - // - // - checked and clearing: - // mov x17, x16 - // autia x16, x0 - // xpaci x17 - // cmp x16, x17 - // pacib x16, x1 - // csel x16, x16, x17, eq - // Where we only emit the AUT if we started with an AUT. - // - // - checked and trapping: - // mov x17, x16 - // autia x16, x0 - // xpaci x17 - // cmp x16, x17 - // b.eq Lsuccess - // brk #<0xc470 + aut key> - // Lsuccess: - // pacib x16, x1 ; if AUTPAC - // Where the b.eq skips over the trap if the PAC is valid. - // - // This sequence is expensive, but we need more information to be able to - // do better. - // - // We can't TBZ the poison bit because EnhancedPAC2 XORs the PAC bits - // on failure. - // We can't TST the PAC bits because we don't always know how the address - // space is setup for the target environment (and the bottom PAC bit is - // based on that). - // Either way, we also don't always know whether TBI is enabled or not for - // the specific target environment. - // - // FIXME: we could re-use AUTReg as a temporary register, but that would - // require splitting the XZR cases into separate opcodes. - - // By default, auth/resign sequences check for auth failures. - bool ShouldCheck = true; - // In the checked sequence, we only trap if explicitly requested. - bool ShouldTrap = MF->getFunction().hasFnAttribute("ptrauth-auth-traps"); - - // However, command-line flags can override this, for experimentation. - switch (PtrauthAuthChecks) { - case PtrauthCheckMode::Default: break; - case PtrauthCheckMode::Unchecked: - ShouldCheck = ShouldTrap = false; - break; - case PtrauthCheckMode::Poison: - ShouldCheck = true; - ShouldTrap = false; - break; - case PtrauthCheckMode::Trap: - ShouldCheck = ShouldTrap = true; - break; - } - - const auto AUTKey = (AArch64PACKey::ID)MI->getOperand(0).getImm(); - const unsigned AUTReg = MI->getOperand(1).getReg(); - - const unsigned XPACOpc = getXPACOpcodeForKey(AUTKey); - const bool AUTZero = AUTReg == AArch64::XZR; - const unsigned AUTOpc = getAUTOpcodeForKey(AUTKey, AUTZero); - - // Checked AUTPACs and trapping AUTs need a temporary copy of the input: x17 - if ((IsAUTPAC && ShouldCheck) || ShouldTrap) { - // mov x17, x16 - EmitToStreamer(*OutStreamer, - MCInstBuilder(AArch64::ORRXrs) - .addReg(AArch64::X17) - .addReg(AArch64::XZR) - .addReg(AArch64::X16) - .addImm(0)); - } - - // autia x16, x0 - MCInst AUTInst; - AUTInst.setOpcode(AUTOpc); - AUTInst.addOperand(MCOperand::createReg(AArch64::X16)); - AUTInst.addOperand(MCOperand::createReg(AArch64::X16)); - if (!AUTZero) - AUTInst.addOperand(MCOperand::createReg(AUTReg)); - EmitToStreamer(*OutStreamer, AUTInst); - - // Unchecked or checked-but-non-trapping AUT is just an "AUT": we're done. - if (!IsAUTPAC && (!ShouldCheck || !ShouldTrap)) - return; - - // Checked sequences do an additional strip-and-compare. - if (ShouldCheck) { - // xpaci x17 - EmitToStreamer(*OutStreamer, - MCInstBuilder(XPACOpc) - .addReg(AArch64::X17) - .addReg(AArch64::X17)); - - // cmp x16, x17 - EmitToStreamer(*OutStreamer, - MCInstBuilder(AArch64::SUBSXrs) - .addReg(AArch64::XZR) - .addReg(AArch64::X16) - .addReg(AArch64::X17) - .addImm(0)); - - // Trapping sequences do a 'brk'. - if (ShouldTrap) { - // b.eq Lsuccess - // where Lsuccess is encoded as 2 (the offset from this instruction to - // what's after the brk, divided by 4) - EmitToStreamer(*OutStreamer, - MCInstBuilder(AArch64::Bcc) - .addImm(AArch64CC::EQ) - .addImm(2)); - - // brk #<0xc470 + aut key> - EmitToStreamer(*OutStreamer, - MCInstBuilder(AArch64::BRK) - .addImm(0xc470 | AUTKey)); - } - } - - // We already emitted unchecked and checked-but-non-trapping AUTs. - // That left us with trapping AUTs, and AUTPACs. - // Trapping AUTs don't need PAC: we're done. - if (!IsAUTPAC) - return; - - const auto PACKey = (AArch64PACKey::ID)MI->getOperand(2).getImm(); - const unsigned PACReg = MI->getOperand(3).getReg(); - const bool PACZero = PACReg == AArch64::XZR; - const unsigned PACOpc = getPACOpcodeForKey(PACKey, PACZero); - - // pacib x16, x9 - MCInst PACInst; - PACInst.setOpcode(PACOpc); - PACInst.addOperand(MCOperand::createReg(AArch64::X16)); - PACInst.addOperand(MCOperand::createReg(AArch64::X16)); - if (!PACZero) - PACInst.addOperand(MCOperand::createReg(PACReg)); - EmitToStreamer(*OutStreamer, PACInst); - - // Non-trapping AUTPAC selects the result based on the xpac check. - // Trapping AUTPAC already trapped; unchecked AUTPAC didn't even check. - if (ShouldTrap || !ShouldCheck) - return; - - // csel x16, x16, x17, eq - EmitToStreamer(*OutStreamer, - MCInstBuilder(AArch64::CSELXr) - .addReg(AArch64::X16) - .addReg(AArch64::X16) - .addReg(AArch64::X17) - .addImm(0)); - return; - } case AArch64::EMITMTETAGGED: { ExceptionHandling ExceptionHandlingType = MAI->getExceptionHandlingType(); if (ExceptionHandlingType != ExceptionHandling::DwarfCFI && @@ -2330,6 +2383,11 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { LowerMOVaddrPAC(*MI); return; + case AArch64::AUT: + case AArch64::AUTPAC: + emitPtrauthAuthResign(MI); + return; + case AArch64::BLRA: emitPtrauthBranch(MI); return; diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp index af07b7e82243b..d6e7278fa4cc7 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp @@ -368,6 +368,9 @@ class AArch64DAGToDAGISel : public SelectionDAGISel { bool tryAuthLoad(SDNode *N); + void SelectPtrauthAuth(SDNode *N); + void SelectPtrauthResign(SDNode *N); + bool trySelectStackSlotTagP(SDNode *N); void SelectTagP(SDNode *N); @@ -1483,6 +1486,96 @@ void AArch64DAGToDAGISel::SelectTable(SDNode *N, unsigned NumVecs, unsigned Opc, ReplaceNode(N, CurDAG->getMachineNode(Opc, dl, VT, Ops)); } +static std::tuple +extractPtrauthBlendDiscriminators(SDValue Disc, SelectionDAG *DAG) { + SDLoc DL(Disc); + SDValue AddrDisc; + SDValue ConstDisc; + + // If this is a blend, remember the constant and address discriminators. + // Otherwise, it's either a constant discriminator, or a non-blended + // address discriminator. + if (Disc->getOpcode() == ISD::INTRINSIC_WO_CHAIN && + Disc->getConstantOperandVal(0) == Intrinsic::ptrauth_blend) { + AddrDisc = Disc->getOperand(1); + ConstDisc = Disc->getOperand(2); + } else { + ConstDisc = Disc; + } + + // If the constant discriminator (either the blend RHS, or the entire + // discriminator value) isn't a 16-bit constant, bail out, and let the + // discriminator be computed separately. + auto *ConstDiscN = dyn_cast(ConstDisc); + if (!ConstDiscN || !isUInt<16>(ConstDiscN->getZExtValue())) + return std::make_tuple(DAG->getTargetConstant(0, DL, MVT::i64), Disc); + + // If there's no address discriminator, use XZR directly. + if (!AddrDisc) + AddrDisc = DAG->getRegister(AArch64::XZR, MVT::i64); + + return std::make_tuple( + DAG->getTargetConstant(ConstDiscN->getZExtValue(), DL, MVT::i64), + AddrDisc); +} + +void AArch64DAGToDAGISel::SelectPtrauthAuth(SDNode *N) { + SDLoc DL(N); + // IntrinsicID is operand #0 + SDValue Val = N->getOperand(1); + SDValue AUTKey = N->getOperand(2); + SDValue AUTDisc = N->getOperand(3); + + unsigned AUTKeyC = cast(AUTKey)->getZExtValue(); + AUTKey = CurDAG->getTargetConstant(AUTKeyC, DL, MVT::i64); + + SDValue AUTAddrDisc, AUTConstDisc; + std::tie(AUTConstDisc, AUTAddrDisc) = + extractPtrauthBlendDiscriminators(AUTDisc, CurDAG); + + SDValue X16Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, + AArch64::X16, Val, SDValue()); + SDValue Ops[] = {AUTKey, AUTConstDisc, AUTAddrDisc, X16Copy.getValue(1)}; + + SDNode *AUT = CurDAG->getMachineNode(AArch64::AUT, DL, MVT::i64, Ops); + ReplaceNode(N, AUT); + return; +} + +void AArch64DAGToDAGISel::SelectPtrauthResign(SDNode *N) { + SDLoc DL(N); + // IntrinsicID is operand #0 + SDValue Val = N->getOperand(1); + SDValue AUTKey = N->getOperand(2); + SDValue AUTDisc = N->getOperand(3); + SDValue PACKey = N->getOperand(4); + SDValue PACDisc = N->getOperand(5); + + unsigned AUTKeyC = cast(AUTKey)->getZExtValue(); + unsigned PACKeyC = cast(PACKey)->getZExtValue(); + + AUTKey = CurDAG->getTargetConstant(AUTKeyC, DL, MVT::i64); + PACKey = CurDAG->getTargetConstant(PACKeyC, DL, MVT::i64); + + SDValue AUTAddrDisc, AUTConstDisc; + std::tie(AUTConstDisc, AUTAddrDisc) = + extractPtrauthBlendDiscriminators(AUTDisc, CurDAG); + + SDValue PACAddrDisc, PACConstDisc; + std::tie(PACConstDisc, PACAddrDisc) = + extractPtrauthBlendDiscriminators(PACDisc, CurDAG); + + SDValue X16Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, + AArch64::X16, Val, SDValue()); + + SDValue Ops[] = {AUTKey, AUTConstDisc, AUTAddrDisc, PACKey, + PACConstDisc, PACAddrDisc, X16Copy.getValue(1)}; + + SDNode *AUTPAC = CurDAG->getMachineNode(AArch64::AUTPAC, DL, MVT::i64, Ops); + ReplaceNode(N, AUTPAC); + return; +} + bool AArch64DAGToDAGISel::tryIndexedLoad(SDNode *N) { LoadSDNode *LD = cast(N); if (LD->isUnindexed()) @@ -5554,6 +5647,15 @@ void AArch64DAGToDAGISel::Select(SDNode *Node) { case Intrinsic::aarch64_tagp: SelectTagP(Node); return; + + case Intrinsic::ptrauth_auth: + SelectPtrauthAuth(Node); + return; + + case Intrinsic::ptrauth_resign: + SelectPtrauthResign(Node); + return; + case Intrinsic::aarch64_neon_tbl2: SelectTable(Node, 2, VT == MVT::v8i8 ? AArch64::TBLv8i8Two : AArch64::TBLv16i8Two, @@ -5584,66 +5686,6 @@ void AArch64DAGToDAGISel::Select(SDNode *Node) { : AArch64::TBXv16i8Four, true); return; - case Intrinsic::ptrauth_resign: { - SDLoc DL(Node); - // IntrinsicID is operand #0 - SDValue Val = Node->getOperand(1); - SDValue AUTKey = Node->getOperand(2); - SDValue AUTDisc = Node->getOperand(3); - SDValue PACKey = Node->getOperand(4); - SDValue PACDisc = Node->getOperand(5); - - unsigned AUTKeyC = cast(AUTKey)->getZExtValue(); - unsigned PACKeyC = cast(PACKey)->getZExtValue(); - - AUTKey = CurDAG->getTargetConstant(AUTKeyC, DL, MVT::i64); - PACKey = CurDAG->getTargetConstant(PACKeyC, DL, MVT::i64); - - SDValue ImpDef = SDValue( - CurDAG->getMachineNode(TargetOpcode::IMPLICIT_DEF, DL, MVT::i64), 0); - SDValue X16Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, - AArch64::X16, Val, SDValue()); - SDValue X17Copy = - CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, AArch64::X17, - ImpDef, X16Copy.getValue(1)); - - SDValue Ops[] = {AUTKey, AUTDisc, PACKey, PACDisc, X17Copy.getValue(1)}; - SDVTList VTs = CurDAG->getVTList(MVT::Other, MVT::Glue); - SDNode *N = CurDAG->getMachineNode(AArch64::AUTPAC, DL, VTs, Ops); - N = CurDAG->getCopyFromReg(SDValue(N, 0), DL, AArch64::X16, MVT::i64, - SDValue(N, 1)).getNode(); - ReplaceNode(Node, N); - return; - } - - - case Intrinsic::ptrauth_auth: { - SDLoc DL(Node); - // IntrinsicID is operand #0 - SDValue Val = Node->getOperand(1); - SDValue AUTKey = Node->getOperand(2); - SDValue AUTDisc = Node->getOperand(3); - - unsigned AUTKeyC = cast(AUTKey)->getZExtValue(); - AUTKey = CurDAG->getTargetConstant(AUTKeyC, DL, MVT::i64); - - SDValue ImpDef = SDValue( - CurDAG->getMachineNode(TargetOpcode::IMPLICIT_DEF, DL, MVT::i64), 0); - SDValue X16Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, - AArch64::X16, Val, SDValue()); - SDValue X17Copy = - CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, AArch64::X17, - ImpDef, X16Copy.getValue(1)); - - SDValue Ops[] = {AUTKey, AUTDisc, X17Copy.getValue(1)}; - - SDVTList VTs = CurDAG->getVTList(MVT::Other, MVT::Glue); - SDNode *N = CurDAG->getMachineNode(AArch64::AUT, DL, VTs, Ops); - N = CurDAG->getCopyFromReg(SDValue(N, 0), DL, AArch64::X16, MVT::i64, - SDValue(N, 1)).getNode(); - ReplaceNode(Node, N); - return; - } case Intrinsic::aarch64_sve_srshl_single_x2: if (auto Op = SelectOpcodeFromVT( diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index bc79e97419997..476539fda5cdf 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -1787,32 +1787,35 @@ let Predicates = [HasPAuth] in { // AUT pseudo. // This directly manipulates x16/x17, which are the only registers the OS // guarantees are safe to use for sensitive operations. - def AUT : Pseudo<(outs), (ins i32imm:$Key, GPR64all:$Rn), []>, - Sched<[WriteI, ReadI]> { + def AUT : Pseudo<(outs), (ins i32imm:$Key, i64imm:$Disc, GPR64noip:$AddrDisc), + []>, Sched<[WriteI, ReadI]> { let isCodeGenOnly = 1; let hasSideEffects = 1; let mayStore = 0; let mayLoad = 0; + let Size = 32; let Defs = [X16,X17,NZCV]; - let Uses = [X16,X17]; - let Size = 24; + let Uses = [X16]; } // AUT and re-PAC a value, using different keys/data. // This directly manipulates x16/x17, which are the only registers the OS // guarantees are safe to use for sensitive operations. - def AUTPAC : Pseudo<(outs), (ins i32imm:$AUTKey, GPR64:$AUTRn, - i32imm:$PACKey, GPR64:$PACRn), []>, - Sched<[WriteI, ReadI]> { + def AUTPAC + : Pseudo<(outs), + (ins i32imm:$AUTKey, i64imm:$AUTDisc, GPR64noip:$AUTAddrDisc, + i32imm:$PACKey, i64imm:$PACDisc, GPR64noip:$PACAddrDisc), + []>, Sched<[WriteI, ReadI]> { let isCodeGenOnly = 1; let hasSideEffects = 1; let mayStore = 0; let mayLoad = 0; + let Size = 48; let Defs = [X16,X17,NZCV]; - let Uses = [X16,X17]; - let Size = 28; + let Uses = [X16]; } + // Materialize a signed global address, with adrp+add and PAC. def MOVaddrPAC : Pseudo<(outs), (ins i64imm:$Addr, i32imm:$Key, @@ -1870,7 +1873,6 @@ let Predicates = [HasPAuth] in { tcGPR64:$AddrDisc), (AUTH_TCRETURN_BTI tcGPRx16x17:$dst, imm:$FPDiff, imm:$Key, imm:$Disc, tcGPR64:$AddrDisc)>; - } // v9.5-A pointer authentication extensions diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp index 9e0860934f777..f4e798287d50c 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp @@ -6494,6 +6494,64 @@ bool AArch64InstructionSelector::selectIntrinsic(MachineInstr &I, I.eraseFromParent(); return true; } + case Intrinsic::ptrauth_resign: { + Register DstReg = I.getOperand(0).getReg(); + Register ValReg = I.getOperand(2).getReg(); + uint64_t AUTKey = I.getOperand(3).getImm(); + Register AUTDisc = I.getOperand(4).getReg(); + uint64_t PACKey = I.getOperand(5).getImm(); + Register PACDisc = I.getOperand(6).getReg(); + + Register AUTAddrDisc = AUTDisc; + uint16_t AUTConstDiscC = 0; + std::tie(AUTConstDiscC, AUTAddrDisc) = + extractPtrauthBlendDiscriminators(AUTDisc, MRI); + + Register PACAddrDisc = PACDisc; + uint16_t PACConstDiscC = 0; + std::tie(PACConstDiscC, PACAddrDisc) = + extractPtrauthBlendDiscriminators(PACDisc, MRI); + + MIB.buildCopy({AArch64::X16}, {ValReg}); + MIB.buildInstr(TargetOpcode::IMPLICIT_DEF, {AArch64::X17}, {}); + MIB.buildInstr(AArch64::AUTPAC) + .addImm(AUTKey) + .addImm(AUTConstDiscC) + .addUse(AUTAddrDisc) + .addImm(PACKey) + .addImm(PACConstDiscC) + .addUse(PACAddrDisc) + .constrainAllUses(TII, TRI, RBI); + MIB.buildCopy({DstReg}, Register(AArch64::X16)); + + RBI.constrainGenericRegister(DstReg, AArch64::GPR64RegClass, MRI); + I.eraseFromParent(); + return true; + } + case Intrinsic::ptrauth_auth: { + Register DstReg = I.getOperand(0).getReg(); + Register ValReg = I.getOperand(2).getReg(); + uint64_t AUTKey = I.getOperand(3).getImm(); + Register AUTDisc = I.getOperand(4).getReg(); + + Register AUTAddrDisc = AUTDisc; + uint16_t AUTConstDiscC = 0; + std::tie(AUTConstDiscC, AUTAddrDisc) = + extractPtrauthBlendDiscriminators(AUTDisc, MRI); + + MIB.buildCopy({AArch64::X16}, {ValReg}); + MIB.buildInstr(TargetOpcode::IMPLICIT_DEF, {AArch64::X17}, {}); + MIB.buildInstr(AArch64::AUT) + .addImm(AUTKey) + .addImm(AUTConstDiscC) + .addUse(AUTAddrDisc) + .constrainAllUses(TII, TRI, RBI); + MIB.buildCopy({DstReg}, Register(AArch64::X16)); + + RBI.constrainGenericRegister(DstReg, AArch64::GPR64RegClass, MRI); + I.eraseFromParent(); + return true; + } case Intrinsic::frameaddress: case Intrinsic::returnaddress: { MachineFunction &MF = *I.getParent()->getParent(); diff --git a/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-with-blend.ll b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-with-blend.ll new file mode 100644 index 0000000000000..5de08f587477f --- /dev/null +++ b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-with-blend.ll @@ -0,0 +1,261 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0 -verify-machineinstrs \ +; RUN: -aarch64-ptrauth-auth-checks=none | FileCheck %s --check-prefixes=UNCHECKED,SDAG-UNCHECKED +; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \ +; RUN: -aarch64-ptrauth-auth-checks=none | FileCheck %s --check-prefixes=UNCHECKED,GISEL-UNCHECKED + +; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0 -verify-machineinstrs \ +; RUN: | FileCheck %s --check-prefixes=CHECKED,SDAG-CHECKED +; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \ +; RUN: | FileCheck %s --check-prefixes=CHECKED,GISEL-CHECKED + +; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0 -verify-machineinstrs \ +; RUN: -aarch64-ptrauth-auth-checks=trap | FileCheck %s --check-prefixes=TRAP,SDAG-TRAP +; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \ +; RUN: -aarch64-ptrauth-auth-checks=trap | FileCheck %s --check-prefixes=TRAP,GISEL-TRAP + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + +define i64 @test_auth_blend(i64 %arg, i64 %arg1) { +; UNCHECKED-LABEL: test_auth_blend: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: mov x17, x1 +; UNCHECKED-NEXT: movk x17, #65535, lsl #48 +; UNCHECKED-NEXT: autda x16, x17 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_blend: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x1 +; CHECKED-NEXT: movk x17, #65535, lsl #48 +; CHECKED-NEXT: autda x16, x17 +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_blend: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x1 +; TRAP-NEXT: movk x17, #65535, lsl #48 +; TRAP-NEXT: autda x16, x17 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq Lauth_success_0 +; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: Lauth_success_0: +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %arg1, i64 65535) + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %arg, i32 2, i64 %tmp0) + ret i64 %tmp1 +} + +define i64 @test_resign_blend(i64 %arg, i64 %arg1, i64 %arg2) { +; UNCHECKED-LABEL: test_resign_blend: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: mov x17, x1 +; UNCHECKED-NEXT: movk x17, #12345, lsl #48 +; UNCHECKED-NEXT: autda x16, x17 +; UNCHECKED-NEXT: mov x17, x2 +; UNCHECKED-NEXT: movk x17, #56789, lsl #48 +; UNCHECKED-NEXT: pacdb x16, x17 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_resign_blend: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x1 +; CHECKED-NEXT: movk x17, #12345, lsl #48 +; CHECKED-NEXT: autda x16, x17 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: xpacd x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_0 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_0 +; CHECKED-NEXT: Lauth_success_0: +; CHECKED-NEXT: mov x17, x2 +; CHECKED-NEXT: movk x17, #56789, lsl #48 +; CHECKED-NEXT: pacdb x16, x17 +; CHECKED-NEXT: Lresign_end_0: +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_resign_blend: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x1 +; TRAP-NEXT: movk x17, #12345, lsl #48 +; TRAP-NEXT: autda x16, x17 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq Lauth_success_1 +; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: Lauth_success_1: +; TRAP-NEXT: mov x17, x2 +; TRAP-NEXT: movk x17, #56789, lsl #48 +; TRAP-NEXT: pacdb x16, x17 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %arg1, i64 12345) + %tmp1 = call i64 @llvm.ptrauth.blend(i64 %arg2, i64 56789) + %tmp2 = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %tmp0, i32 3, i64 %tmp1) + ret i64 %tmp2 +} + +define i64 @test_resign_blend_and_const(i64 %arg, i64 %arg1) { +; UNCHECKED-LABEL: test_resign_blend_and_const: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: mov x17, x1 +; UNCHECKED-NEXT: movk x17, #12345, lsl #48 +; UNCHECKED-NEXT: autda x16, x17 +; UNCHECKED-NEXT: mov x17, #56789 ; =0xddd5 +; UNCHECKED-NEXT: pacdb x16, x17 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_resign_blend_and_const: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x1 +; CHECKED-NEXT: movk x17, #12345, lsl #48 +; CHECKED-NEXT: autda x16, x17 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: xpacd x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_1 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_1 +; CHECKED-NEXT: Lauth_success_1: +; CHECKED-NEXT: mov x17, #56789 ; =0xddd5 +; CHECKED-NEXT: pacdb x16, x17 +; CHECKED-NEXT: Lresign_end_1: +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_resign_blend_and_const: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x1 +; TRAP-NEXT: movk x17, #12345, lsl #48 +; TRAP-NEXT: autda x16, x17 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq Lauth_success_2 +; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: Lauth_success_2: +; TRAP-NEXT: mov x17, #56789 ; =0xddd5 +; TRAP-NEXT: pacdb x16, x17 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %arg1, i64 12345) + %tmp1 = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %tmp0, i32 3, i64 56789) + ret i64 %tmp1 +} + +define i64 @test_resign_blend_and_addr(i64 %arg, i64 %arg1, i64 %arg2) { +; UNCHECKED-LABEL: test_resign_blend_and_addr: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: mov x17, x1 +; UNCHECKED-NEXT: movk x17, #12345, lsl #48 +; UNCHECKED-NEXT: autda x16, x17 +; UNCHECKED-NEXT: pacdb x16, x2 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_resign_blend_and_addr: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, x1 +; CHECKED-NEXT: movk x17, #12345, lsl #48 +; CHECKED-NEXT: autda x16, x17 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: xpacd x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_2 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_2 +; CHECKED-NEXT: Lauth_success_2: +; CHECKED-NEXT: pacdb x16, x2 +; CHECKED-NEXT: Lresign_end_2: +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_resign_blend_and_addr: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, x1 +; TRAP-NEXT: movk x17, #12345, lsl #48 +; TRAP-NEXT: autda x16, x17 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq Lauth_success_3 +; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: Lauth_success_3: +; TRAP-NEXT: pacdb x16, x2 +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %arg1, i64 12345) + %tmp1 = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %tmp0, i32 3, i64 %arg2) + ret i64 %tmp1 +} + +define i64 @test_auth_too_large_discriminator(i64 %arg, i64 %arg1) { +; UNCHECKED-LABEL: test_auth_too_large_discriminator: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov w8, #65536 ; =0x10000 +; UNCHECKED-NEXT: bfi x1, x8, #48, #16 +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: autda x16, x1 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_too_large_discriminator: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov w8, #65536 ; =0x10000 +; CHECKED-NEXT: bfi x1, x8, #48, #16 +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: autda x16, x1 +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_too_large_discriminator: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov w8, #65536 ; =0x10000 +; TRAP-NEXT: bfi x1, x8, #48, #16 +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: autda x16, x1 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: xpacd x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq Lauth_success_4 +; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: Lauth_success_4: +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %arg1, i64 65536) + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %arg, i32 2, i64 %tmp0) + ret i64 %tmp1 +} + +declare i64 @llvm.ptrauth.auth(i64, i32, i64) +declare i64 @llvm.ptrauth.resign(i64, i32, i64, i32, i64) +declare i64 @llvm.ptrauth.blend(i64, i64) +;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +; GISEL-CHECKED: {{.*}} +; GISEL-TRAP: {{.*}} +; GISEL-UNCHECKED: {{.*}} +; SDAG-CHECKED: {{.*}} +; SDAG-TRAP: {{.*}} +; SDAG-UNCHECKED: {{.*}} diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign.ll similarity index 73% rename from llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll rename to llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign.ll index c65275720b8b1..e818d2aa982a8 100644 --- a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-intrinsics.ll +++ b/llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign.ll @@ -1,7 +1,18 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py -; RUN: llc < %s -mtriple arm64e-apple-darwin -aarch64-ptrauth-auth-checks=none -verify-machineinstrs | FileCheck %s --check-prefixes=ALL,UNCHECKED -; RUN: llc < %s -mtriple arm64e-apple-darwin -verify-machineinstrs | FileCheck %s --check-prefixes=ALL,CHECKED -; RUN: llc < %s -mtriple arm64e-apple-darwin -aarch64-ptrauth-auth-checks=trap -verify-machineinstrs | FileCheck %s --check-prefixes=ALL,TRAP +; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0 -verify-machineinstrs \ +; RUN: -aarch64-ptrauth-auth-checks=none | FileCheck %s --check-prefixes=UNCHECKED +; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \ +; RUN: -aarch64-ptrauth-auth-checks=none | FileCheck %s --check-prefixes=UNCHECKED + +; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0 -verify-machineinstrs \ +; RUN: | FileCheck %s --check-prefixes=CHECKED +; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \ +; RUN: | FileCheck %s --check-prefixes=CHECKED + +; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel=0 -verify-machineinstrs \ +; RUN: -aarch64-ptrauth-auth-checks=trap | FileCheck %s --check-prefixes=TRAP +; RUN: llc < %s -mtriple arm64e-apple-darwin -global-isel -global-isel-abort=1 -verify-machineinstrs \ +; RUN: -aarch64-ptrauth-auth-checks=trap | FileCheck %s --check-prefixes=TRAP target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" @@ -23,12 +34,13 @@ define i64 @test_auth_ia(i64 %arg, i64 %arg1) { ; TRAP-LABEL: test_auth_ia: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autia x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpaci x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_0 ; TRAP-NEXT: brk #0xc470 +; TRAP-NEXT: Lauth_success_0: ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 %arg1) @@ -53,12 +65,13 @@ define i64 @test_auth_ia_zero(i64 %arg) { ; TRAP-LABEL: test_auth_ia_zero: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autiza x16 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpaci x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_1 ; TRAP-NEXT: brk #0xc470 +; TRAP-NEXT: Lauth_success_1: ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 0) @@ -83,12 +96,13 @@ define i64 @test_auth_ib(i64 %arg, i64 %arg1) { ; TRAP-LABEL: test_auth_ib: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autib x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpaci x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_2 ; TRAP-NEXT: brk #0xc471 +; TRAP-NEXT: Lauth_success_2: ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 1, i64 %arg1) @@ -113,12 +127,13 @@ define i64 @test_auth_ib_zero(i64 %arg) { ; TRAP-LABEL: test_auth_ib_zero: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autizb x16 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpaci x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_3 ; TRAP-NEXT: brk #0xc471 +; TRAP-NEXT: Lauth_success_3: ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 1, i64 0) @@ -143,12 +158,13 @@ define i64 @test_auth_da(i64 %arg, i64 %arg1) { ; TRAP-LABEL: test_auth_da: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autda x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpacd x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_4 ; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: Lauth_success_4: ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 2, i64 %arg1) @@ -173,12 +189,13 @@ define i64 @test_auth_da_zero(i64 %arg) { ; TRAP-LABEL: test_auth_da_zero: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autdza x16 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpacd x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_5 ; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: Lauth_success_5: ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 2, i64 0) @@ -203,12 +220,13 @@ define i64 @test_auth_db(i64 %arg, i64 %arg1) { ; TRAP-LABEL: test_auth_db: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autdb x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpacd x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_6 ; TRAP-NEXT: brk #0xc473 +; TRAP-NEXT: Lauth_success_6: ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 3, i64 %arg1) @@ -233,12 +251,13 @@ define i64 @test_auth_db_zero(i64 %arg) { ; TRAP-LABEL: test_auth_db_zero: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autdzb x16 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpacd x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_7 ; TRAP-NEXT: brk #0xc473 +; TRAP-NEXT: Lauth_success_7: ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 3, i64 0) @@ -259,24 +278,29 @@ define i64 @test_resign_ia_ia(i64 %arg, i64 %arg1, i64 %arg2) { ; CHECKED-LABEL: test_resign_ia_ia: ; CHECKED: ; %bb.0: ; CHECKED-NEXT: mov x16, x0 -; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: autia x16, x1 +; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: xpaci x17 ; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_0 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_0 +; CHECKED-NEXT: Lauth_success_0: ; CHECKED-NEXT: pacia x16, x2 -; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: Lresign_end_0: ; CHECKED-NEXT: mov x0, x16 ; CHECKED-NEXT: ret ; ; TRAP-LABEL: test_resign_ia_ia: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autia x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpaci x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_8 ; TRAP-NEXT: brk #0xc470 +; TRAP-NEXT: Lauth_success_8: ; TRAP-NEXT: pacia x16, x2 ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret @@ -296,24 +320,29 @@ define i64 @test_resign_ib_ia(i64 %arg, i64 %arg1, i64 %arg2) { ; CHECKED-LABEL: test_resign_ib_ia: ; CHECKED: ; %bb.0: ; CHECKED-NEXT: mov x16, x0 -; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: autib x16, x1 +; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: xpaci x17 ; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_1 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_1 +; CHECKED-NEXT: Lauth_success_1: ; CHECKED-NEXT: pacia x16, x2 -; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: Lresign_end_1: ; CHECKED-NEXT: mov x0, x16 ; CHECKED-NEXT: ret ; ; TRAP-LABEL: test_resign_ib_ia: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autib x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpaci x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_9 ; TRAP-NEXT: brk #0xc471 +; TRAP-NEXT: Lauth_success_9: ; TRAP-NEXT: pacia x16, x2 ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret @@ -333,24 +362,29 @@ define i64 @test_resign_da_ia(i64 %arg, i64 %arg1, i64 %arg2) { ; CHECKED-LABEL: test_resign_da_ia: ; CHECKED: ; %bb.0: ; CHECKED-NEXT: mov x16, x0 -; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: autda x16, x1 +; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: xpacd x17 ; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_2 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_2 +; CHECKED-NEXT: Lauth_success_2: ; CHECKED-NEXT: pacia x16, x2 -; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: Lresign_end_2: ; CHECKED-NEXT: mov x0, x16 ; CHECKED-NEXT: ret ; ; TRAP-LABEL: test_resign_da_ia: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autda x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpacd x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_10 ; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: Lauth_success_10: ; TRAP-NEXT: pacia x16, x2 ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret @@ -370,24 +404,29 @@ define i64 @test_resign_db_ia(i64 %arg, i64 %arg1, i64 %arg2) { ; CHECKED-LABEL: test_resign_db_ia: ; CHECKED: ; %bb.0: ; CHECKED-NEXT: mov x16, x0 -; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: autdb x16, x1 +; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: xpacd x17 ; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_3 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_3 +; CHECKED-NEXT: Lauth_success_3: ; CHECKED-NEXT: pacia x16, x2 -; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: Lresign_end_3: ; CHECKED-NEXT: mov x0, x16 ; CHECKED-NEXT: ret ; ; TRAP-LABEL: test_resign_db_ia: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autdb x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpacd x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_11 ; TRAP-NEXT: brk #0xc473 +; TRAP-NEXT: Lauth_success_11: ; TRAP-NEXT: pacia x16, x2 ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret @@ -407,24 +446,29 @@ define i64 @test_resign_db_ib(i64 %arg, i64 %arg1, i64 %arg2) { ; CHECKED-LABEL: test_resign_db_ib: ; CHECKED: ; %bb.0: ; CHECKED-NEXT: mov x16, x0 -; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: autdb x16, x1 +; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: xpacd x17 ; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_4 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_4 +; CHECKED-NEXT: Lauth_success_4: ; CHECKED-NEXT: pacib x16, x2 -; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: Lresign_end_4: ; CHECKED-NEXT: mov x0, x16 ; CHECKED-NEXT: ret ; ; TRAP-LABEL: test_resign_db_ib: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autdb x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpacd x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_12 ; TRAP-NEXT: brk #0xc473 +; TRAP-NEXT: Lauth_success_12: ; TRAP-NEXT: pacib x16, x2 ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret @@ -444,24 +488,29 @@ define i64 @test_resign_db_da(i64 %arg, i64 %arg1, i64 %arg2) { ; CHECKED-LABEL: test_resign_db_da: ; CHECKED: ; %bb.0: ; CHECKED-NEXT: mov x16, x0 -; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: autdb x16, x1 +; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: xpacd x17 ; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_5 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_5 +; CHECKED-NEXT: Lauth_success_5: ; CHECKED-NEXT: pacda x16, x2 -; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: Lresign_end_5: ; CHECKED-NEXT: mov x0, x16 ; CHECKED-NEXT: ret ; ; TRAP-LABEL: test_resign_db_da: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autdb x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpacd x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_13 ; TRAP-NEXT: brk #0xc473 +; TRAP-NEXT: Lauth_success_13: ; TRAP-NEXT: pacda x16, x2 ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret @@ -481,24 +530,29 @@ define i64 @test_resign_db_db(i64 %arg, i64 %arg1, i64 %arg2) { ; CHECKED-LABEL: test_resign_db_db: ; CHECKED: ; %bb.0: ; CHECKED-NEXT: mov x16, x0 -; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: autdb x16, x1 +; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: xpacd x17 ; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_6 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_6 +; CHECKED-NEXT: Lauth_success_6: ; CHECKED-NEXT: pacdb x16, x2 -; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: Lresign_end_6: ; CHECKED-NEXT: mov x0, x16 ; CHECKED-NEXT: ret ; ; TRAP-LABEL: test_resign_db_db: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autdb x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpacd x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_14 ; TRAP-NEXT: brk #0xc473 +; TRAP-NEXT: Lauth_success_14: ; TRAP-NEXT: pacdb x16, x2 ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret @@ -518,24 +572,29 @@ define i64 @test_resign_iza_db(i64 %arg, i64 %arg1, i64 %arg2) { ; CHECKED-LABEL: test_resign_iza_db: ; CHECKED: ; %bb.0: ; CHECKED-NEXT: mov x16, x0 -; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: autiza x16 +; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: xpaci x17 ; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_7 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_7 +; CHECKED-NEXT: Lauth_success_7: ; CHECKED-NEXT: pacdb x16, x2 -; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: Lresign_end_7: ; CHECKED-NEXT: mov x0, x16 ; CHECKED-NEXT: ret ; ; TRAP-LABEL: test_resign_iza_db: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autiza x16 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpaci x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_15 ; TRAP-NEXT: brk #0xc470 +; TRAP-NEXT: Lauth_success_15: ; TRAP-NEXT: pacdb x16, x2 ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret @@ -555,24 +614,29 @@ define i64 @test_resign_da_dzb(i64 %arg, i64 %arg1, i64 %arg2) { ; CHECKED-LABEL: test_resign_da_dzb: ; CHECKED: ; %bb.0: ; CHECKED-NEXT: mov x16, x0 -; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: autda x16, x1 +; CHECKED-NEXT: mov x17, x16 ; CHECKED-NEXT: xpacd x17 ; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_8 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_8 +; CHECKED-NEXT: Lauth_success_8: ; CHECKED-NEXT: pacdzb x16 -; CHECKED-NEXT: csel x16, x16, x17, eq +; CHECKED-NEXT: Lresign_end_8: ; CHECKED-NEXT: mov x0, x16 ; CHECKED-NEXT: ret ; ; TRAP-LABEL: test_resign_da_dzb: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autda x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpacd x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_16 ; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: Lauth_success_16: ; TRAP-NEXT: pacdzb x16 ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret @@ -580,114 +644,121 @@ define i64 @test_resign_da_dzb(i64 %arg, i64 %arg1, i64 %arg2) { ret i64 %tmp } -define i64 @test_strip(i64 %arg) { -; ALL-LABEL: test_strip: -; ALL: ; %bb.0: -; ALL-NEXT: xpaci x0 -; ALL-NEXT: ret - %tmp = call i64 @llvm.ptrauth.strip(i64 %arg, i32 0) - ret i64 %tmp -} - -define i64 @test_blend(i64 %arg, i64 %arg1) { -; ALL-LABEL: test_blend: -; ALL: ; %bb.0: -; ALL-NEXT: bfi x0, x1, #48, #16 -; ALL-NEXT: ret - %tmp = call i64 @llvm.ptrauth.blend(i64 %arg, i64 %arg1) - ret i64 %tmp -} - -define i64 @test_blend_constant(i64 %arg) { -; ALL-LABEL: test_blend_constant: -; ALL: ; %bb.0: -; ALL-NEXT: movk x0, #12345, lsl #48 -; ALL-NEXT: ret - %tmp = call i64 @llvm.ptrauth.blend(i64 %arg, i64 12345) - ret i64 %tmp -} - - -define i64 @test_auth_cse(i64 %arg, i64 %arg1) { -; UNCHECKED-LABEL: test_auth_cse: +define i64 @test_auth_trap_attribute(i64 %arg, i64 %arg1) "ptrauth-auth-traps" { +; UNCHECKED-LABEL: test_auth_trap_attribute: ; UNCHECKED: ; %bb.0: ; UNCHECKED-NEXT: mov x16, x0 ; UNCHECKED-NEXT: autia x16, x1 -; UNCHECKED-NEXT: add x0, x16, x16 +; UNCHECKED-NEXT: mov x0, x16 ; UNCHECKED-NEXT: ret ; -; CHECKED-LABEL: test_auth_cse: +; CHECKED-LABEL: test_auth_trap_attribute: ; CHECKED: ; %bb.0: ; CHECKED-NEXT: mov x16, x0 ; CHECKED-NEXT: autia x16, x1 -; CHECKED-NEXT: add x0, x16, x16 +; CHECKED-NEXT: mov x17, x16 +; CHECKED-NEXT: xpaci x17 +; CHECKED-NEXT: cmp x16, x17 +; CHECKED-NEXT: b.eq Lauth_success_9 +; CHECKED-NEXT: brk #0xc470 +; CHECKED-NEXT: Lauth_success_9: +; CHECKED-NEXT: mov x0, x16 ; CHECKED-NEXT: ret ; -; TRAP-LABEL: test_auth_cse: +; TRAP-LABEL: test_auth_trap_attribute: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 -; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: autia x16, x1 +; TRAP-NEXT: mov x17, x16 ; TRAP-NEXT: xpaci x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 +; TRAP-NEXT: b.eq Lauth_success_17 ; TRAP-NEXT: brk #0xc470 -; TRAP-NEXT: add x0, x16, x16 +; TRAP-NEXT: Lauth_success_17: +; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret - %tmp0 = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 %arg1) - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 %arg1) - %tmp2 = add i64 %tmp0, %tmp1 - ret i64 %tmp2 + %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 %arg1) + ret i64 %tmp } -define i64 @test_blend_cse(i64 %arg, i64 %arg1) { -; ALL-LABEL: test_blend_cse: -; ALL: ; %bb.0: -; ALL-NEXT: bfi x0, x1, #48, #16 -; ALL-NEXT: add x0, x0, x0 -; ALL-NEXT: ret - %tmp0 = call i64 @llvm.ptrauth.blend(i64 %arg, i64 %arg1) - %tmp1 = call i64 @llvm.ptrauth.blend(i64 %arg, i64 %arg1) - %tmp2 = add i64 %tmp0, %tmp1 - ret i64 %tmp2 +define i64 @test_auth_ia_constdisc(i64 %arg) { +; UNCHECKED-LABEL: test_auth_ia_constdisc: +; UNCHECKED: ; %bb.0: +; UNCHECKED-NEXT: mov x16, x0 +; UNCHECKED-NEXT: mov x17, #256 +; UNCHECKED-NEXT: autia x16, x17 +; UNCHECKED-NEXT: mov x0, x16 +; UNCHECKED-NEXT: ret +; +; CHECKED-LABEL: test_auth_ia_constdisc: +; CHECKED: ; %bb.0: +; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: mov x17, #256 +; CHECKED-NEXT: autia x16, x17 +; CHECKED-NEXT: mov x0, x16 +; CHECKED-NEXT: ret +; +; TRAP-LABEL: test_auth_ia_constdisc: +; TRAP: ; %bb.0: +; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: mov x17, #256 +; TRAP-NEXT: autia x16, x17 +; TRAP-NEXT: mov x17, x16 +; TRAP-NEXT: xpaci x17 +; TRAP-NEXT: cmp x16, x17 +; TRAP-NEXT: b.eq Lauth_success_18 +; TRAP-NEXT: brk #0xc470 +; TRAP-NEXT: Lauth_success_18: +; TRAP-NEXT: mov x0, x16 +; TRAP-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 256) + ret i64 %tmp } -define i64 @test_auth_trap_attribute(i64 %arg, i64 %arg1) "ptrauth-auth-traps" { -; UNCHECKED-LABEL: test_auth_trap_attribute: +define i64 @test_resign_da_constdisc(i64 %arg, i64 %arg1) { +; UNCHECKED-LABEL: test_resign_da_constdisc: ; UNCHECKED: ; %bb.0: ; UNCHECKED-NEXT: mov x16, x0 -; UNCHECKED-NEXT: autia x16, x1 +; UNCHECKED-NEXT: autda x16, x1 +; UNCHECKED-NEXT: mov x17, #256 +; UNCHECKED-NEXT: pacda x16, x17 ; UNCHECKED-NEXT: mov x0, x16 ; UNCHECKED-NEXT: ret ; -; CHECKED-LABEL: test_auth_trap_attribute: +; CHECKED-LABEL: test_resign_da_constdisc: ; CHECKED: ; %bb.0: ; CHECKED-NEXT: mov x16, x0 +; CHECKED-NEXT: autda x16, x1 ; CHECKED-NEXT: mov x17, x16 -; CHECKED-NEXT: autia x16, x1 -; CHECKED-NEXT: xpaci x17 +; CHECKED-NEXT: xpacd x17 ; CHECKED-NEXT: cmp x16, x17 -; CHECKED-NEXT: b.eq #8 -; CHECKED-NEXT: brk #0xc470 +; CHECKED-NEXT: b.eq Lauth_success_10 +; CHECKED-NEXT: mov x16, x17 +; CHECKED-NEXT: b Lresign_end_9 +; CHECKED-NEXT: Lauth_success_10: +; CHECKED-NEXT: mov x17, #256 +; CHECKED-NEXT: pacda x16, x17 +; CHECKED-NEXT: Lresign_end_9: ; CHECKED-NEXT: mov x0, x16 ; CHECKED-NEXT: ret ; -; TRAP-LABEL: test_auth_trap_attribute: +; TRAP-LABEL: test_resign_da_constdisc: ; TRAP: ; %bb.0: ; TRAP-NEXT: mov x16, x0 +; TRAP-NEXT: autda x16, x1 ; TRAP-NEXT: mov x17, x16 -; TRAP-NEXT: autia x16, x1 -; TRAP-NEXT: xpaci x17 +; TRAP-NEXT: xpacd x17 ; TRAP-NEXT: cmp x16, x17 -; TRAP-NEXT: b.eq #8 -; TRAP-NEXT: brk #0xc470 +; TRAP-NEXT: b.eq Lauth_success_19 +; TRAP-NEXT: brk #0xc472 +; TRAP-NEXT: Lauth_success_19: +; TRAP-NEXT: mov x17, #256 +; TRAP-NEXT: pacda x16, x17 ; TRAP-NEXT: mov x0, x16 ; TRAP-NEXT: ret - %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 %arg1) + %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %arg1, i32 2, i64 256) ret i64 %tmp } declare i64 @llvm.ptrauth.auth(i64, i32, i64) declare i64 @llvm.ptrauth.resign(i64, i32, i64, i32, i64) -declare i64 @llvm.ptrauth.strip(i64, i32) -declare i64 @llvm.ptrauth.blend(i64, i64) From 68c6fd9d63dbe29f7de960b111c9748f9275fa8c Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 24 Oct 2022 08:39:10 -0700 Subject: [PATCH 04/58] [AArch64] Add FPAC feature. --- llvm/lib/Target/AArch64/AArch64Features.td | 3 +++ llvm/lib/Target/AArch64/AArch64InstrInfo.td | 2 ++ 2 files changed, 5 insertions(+) diff --git a/llvm/lib/Target/AArch64/AArch64Features.td b/llvm/lib/Target/AArch64/AArch64Features.td index 8754ea4974999..40b15723e1a4d 100644 --- a/llvm/lib/Target/AArch64/AArch64Features.td +++ b/llvm/lib/Target/AArch64/AArch64Features.td @@ -188,6 +188,9 @@ def FeatureJS : ExtensionWithMArch<"jsconv", "JS", "FEAT_JSCVT", def FeatureCCIDX : Extension<"ccidx", "CCIDX", "FEAT_CCIDX", "Enable v8.3-A Extend of the CCSIDR number of sets">; +def FeatureFPAC : Extension<"fpac", "FPAC", "FEAT_FPAC", + "Enable v8.3-A Pointer Authentication Faulting enhancement">; + let ArchExtKindSpelling = "AEK_FCMA", UserVisibleName = "fcma" in def FeatureComplxNum : ExtensionWithMArch<"complxnum", "ComplxNum", "FEAT_FCMA", "Enable v8.3-A Floating-point complex number support", diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 476539fda5cdf..d7d7f3cedd752 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -276,6 +276,8 @@ def HasMatMulFP32 : Predicate<"Subtarget->hasMatMulFP32()">, AssemblerPredicateWithAll<(all_of FeatureMatMulFP32), "f32mm">; def HasMatMulFP64 : Predicate<"Subtarget->hasMatMulFP64()">, AssemblerPredicateWithAll<(all_of FeatureMatMulFP64), "f64mm">; +def HasFPAC : Predicate<"Subtarget->hasFPAC())">, + AssemblerPredicateWithAll<(all_of FeatureFPAC), "fpac">; def HasXS : Predicate<"Subtarget->hasXS()">, AssemblerPredicateWithAll<(all_of FeatureXS), "xs">; def HasWFxT : Predicate<"Subtarget->hasWFxT()">, From 5241d61f5e30f16110d27bf1e2f15b26b5b4e9f7 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 24 Oct 2022 08:36:13 -0700 Subject: [PATCH 05/58] [AArch64][PAC] Don't emit auth/resign checks when targeting FPAC. When the FPAC feature is present, we can rely on its faulting behavior to avoid emitting the expensive authentication failure check sequence ourvelves. In which case we emit the same sequence as a plain unchecked auth/resign. --- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 5 + llvm/test/CodeGen/AArch64/ptrauth-fpac.ll | 374 ++++++++++++++++++ 2 files changed, 379 insertions(+) create mode 100644 llvm/test/CodeGen/AArch64/ptrauth-fpac.ll diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 21efd38342640..6821c30272741 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -1772,6 +1772,11 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) { // In the checked sequence, we only trap if explicitly requested. bool ShouldTrap = MF->getFunction().hasFnAttribute("ptrauth-auth-traps"); + // On an FPAC CPU, you get traps whether you want them or not: there's + // no point in emitting checks or traps. + if (STI->hasFPAC()) + ShouldCheck = ShouldTrap = false; + // However, command-line flags can override this, for experimentation. switch (PtrauthAuthChecks) { case PtrauthCheckMode::Default: diff --git a/llvm/test/CodeGen/AArch64/ptrauth-fpac.ll b/llvm/test/CodeGen/AArch64/ptrauth-fpac.ll new file mode 100644 index 0000000000000..6afe1a93d986e --- /dev/null +++ b/llvm/test/CodeGen/AArch64/ptrauth-fpac.ll @@ -0,0 +1,374 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple arm64e-apple-darwin -verify-machineinstrs | FileCheck %s --check-prefixes=ALL,NOFPAC +; RUN: llc < %s -mtriple arm64e-apple-darwin -mattr=+fpac -verify-machineinstrs | FileCheck %s --check-prefixes=ALL,FPAC + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + +define i64 @test_auth_ia(i64 %arg, i64 %arg1) { +; ALL-LABEL: test_auth_ia: +; ALL: ; %bb.0: +; ALL-NEXT: mov x16, x0 +; ALL-NEXT: autia x16, x1 +; ALL-NEXT: mov x0, x16 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_auth_ia_zero(i64 %arg) { +; ALL-LABEL: test_auth_ia_zero: +; ALL: ; %bb.0: +; ALL-NEXT: mov x16, x0 +; ALL-NEXT: autiza x16 +; ALL-NEXT: mov x0, x16 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 0) + ret i64 %tmp +} + +define i64 @test_auth_ib(i64 %arg, i64 %arg1) { +; ALL-LABEL: test_auth_ib: +; ALL: ; %bb.0: +; ALL-NEXT: mov x16, x0 +; ALL-NEXT: autib x16, x1 +; ALL-NEXT: mov x0, x16 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 1, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_auth_ib_zero(i64 %arg) { +; ALL-LABEL: test_auth_ib_zero: +; ALL: ; %bb.0: +; ALL-NEXT: mov x16, x0 +; ALL-NEXT: autizb x16 +; ALL-NEXT: mov x0, x16 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 1, i64 0) + ret i64 %tmp +} + +define i64 @test_auth_da(i64 %arg, i64 %arg1) { +; ALL-LABEL: test_auth_da: +; ALL: ; %bb.0: +; ALL-NEXT: mov x16, x0 +; ALL-NEXT: autda x16, x1 +; ALL-NEXT: mov x0, x16 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 2, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_auth_da_zero(i64 %arg) { +; ALL-LABEL: test_auth_da_zero: +; ALL: ; %bb.0: +; ALL-NEXT: mov x16, x0 +; ALL-NEXT: autdza x16 +; ALL-NEXT: mov x0, x16 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 2, i64 0) + ret i64 %tmp +} + +define i64 @test_auth_db(i64 %arg, i64 %arg1) { +; ALL-LABEL: test_auth_db: +; ALL: ; %bb.0: +; ALL-NEXT: mov x16, x0 +; ALL-NEXT: autdb x16, x1 +; ALL-NEXT: mov x0, x16 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 3, i64 %arg1) + ret i64 %tmp +} + +define i64 @test_auth_db_zero(i64 %arg) { +; ALL-LABEL: test_auth_db_zero: +; ALL: ; %bb.0: +; ALL-NEXT: mov x16, x0 +; ALL-NEXT: autdzb x16 +; ALL-NEXT: mov x0, x16 +; ALL-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 3, i64 0) + ret i64 %tmp +} + +; Note that this might seem like a no-op but is actually a valid way to enforce +; the validity of a signature. +define i64 @test_resign_ia_ia(i64 %arg, i64 %arg1, i64 %arg2) { +; NOFPAC-LABEL: test_resign_ia_ia: +; NOFPAC: ; %bb.0: +; NOFPAC-NEXT: mov x16, x0 +; NOFPAC-NEXT: autia x16, x1 +; NOFPAC-NEXT: mov x17, x16 +; NOFPAC-NEXT: xpaci x17 +; NOFPAC-NEXT: cmp x16, x17 +; NOFPAC-NEXT: b.eq Lauth_success_0 +; NOFPAC-NEXT: mov x16, x17 +; NOFPAC-NEXT: b Lresign_end_0 +; NOFPAC-NEXT: Lauth_success_0: +; NOFPAC-NEXT: pacia x16, x2 +; NOFPAC-NEXT: Lresign_end_0: +; NOFPAC-NEXT: mov x0, x16 +; NOFPAC-NEXT: ret +; +; FPAC-LABEL: test_resign_ia_ia: +; FPAC: ; %bb.0: +; FPAC-NEXT: mov x16, x0 +; FPAC-NEXT: autia x16, x1 +; FPAC-NEXT: pacia x16, x2 +; FPAC-NEXT: mov x0, x16 +; FPAC-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 0, i64 %arg1, i32 0, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_ib_ia(i64 %arg, i64 %arg1, i64 %arg2) { +; NOFPAC-LABEL: test_resign_ib_ia: +; NOFPAC: ; %bb.0: +; NOFPAC-NEXT: mov x16, x0 +; NOFPAC-NEXT: autib x16, x1 +; NOFPAC-NEXT: mov x17, x16 +; NOFPAC-NEXT: xpaci x17 +; NOFPAC-NEXT: cmp x16, x17 +; NOFPAC-NEXT: b.eq Lauth_success_1 +; NOFPAC-NEXT: mov x16, x17 +; NOFPAC-NEXT: b Lresign_end_1 +; NOFPAC-NEXT: Lauth_success_1: +; NOFPAC-NEXT: pacia x16, x2 +; NOFPAC-NEXT: Lresign_end_1: +; NOFPAC-NEXT: mov x0, x16 +; NOFPAC-NEXT: ret +; +; FPAC-LABEL: test_resign_ib_ia: +; FPAC: ; %bb.0: +; FPAC-NEXT: mov x16, x0 +; FPAC-NEXT: autib x16, x1 +; FPAC-NEXT: pacia x16, x2 +; FPAC-NEXT: mov x0, x16 +; FPAC-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 1, i64 %arg1, i32 0, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_da_ia(i64 %arg, i64 %arg1, i64 %arg2) { +; NOFPAC-LABEL: test_resign_da_ia: +; NOFPAC: ; %bb.0: +; NOFPAC-NEXT: mov x16, x0 +; NOFPAC-NEXT: autda x16, x1 +; NOFPAC-NEXT: mov x17, x16 +; NOFPAC-NEXT: xpacd x17 +; NOFPAC-NEXT: cmp x16, x17 +; NOFPAC-NEXT: b.eq Lauth_success_2 +; NOFPAC-NEXT: mov x16, x17 +; NOFPAC-NEXT: b Lresign_end_2 +; NOFPAC-NEXT: Lauth_success_2: +; NOFPAC-NEXT: pacia x16, x2 +; NOFPAC-NEXT: Lresign_end_2: +; NOFPAC-NEXT: mov x0, x16 +; NOFPAC-NEXT: ret +; +; FPAC-LABEL: test_resign_da_ia: +; FPAC: ; %bb.0: +; FPAC-NEXT: mov x16, x0 +; FPAC-NEXT: autda x16, x1 +; FPAC-NEXT: pacia x16, x2 +; FPAC-NEXT: mov x0, x16 +; FPAC-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %arg1, i32 0, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_db_ia(i64 %arg, i64 %arg1, i64 %arg2) { +; NOFPAC-LABEL: test_resign_db_ia: +; NOFPAC: ; %bb.0: +; NOFPAC-NEXT: mov x16, x0 +; NOFPAC-NEXT: autdb x16, x1 +; NOFPAC-NEXT: mov x17, x16 +; NOFPAC-NEXT: xpacd x17 +; NOFPAC-NEXT: cmp x16, x17 +; NOFPAC-NEXT: b.eq Lauth_success_3 +; NOFPAC-NEXT: mov x16, x17 +; NOFPAC-NEXT: b Lresign_end_3 +; NOFPAC-NEXT: Lauth_success_3: +; NOFPAC-NEXT: pacia x16, x2 +; NOFPAC-NEXT: Lresign_end_3: +; NOFPAC-NEXT: mov x0, x16 +; NOFPAC-NEXT: ret +; +; FPAC-LABEL: test_resign_db_ia: +; FPAC: ; %bb.0: +; FPAC-NEXT: mov x16, x0 +; FPAC-NEXT: autdb x16, x1 +; FPAC-NEXT: pacia x16, x2 +; FPAC-NEXT: mov x0, x16 +; FPAC-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 3, i64 %arg1, i32 0, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_db_ib(i64 %arg, i64 %arg1, i64 %arg2) { +; NOFPAC-LABEL: test_resign_db_ib: +; NOFPAC: ; %bb.0: +; NOFPAC-NEXT: mov x16, x0 +; NOFPAC-NEXT: autdb x16, x1 +; NOFPAC-NEXT: mov x17, x16 +; NOFPAC-NEXT: xpacd x17 +; NOFPAC-NEXT: cmp x16, x17 +; NOFPAC-NEXT: b.eq Lauth_success_4 +; NOFPAC-NEXT: mov x16, x17 +; NOFPAC-NEXT: b Lresign_end_4 +; NOFPAC-NEXT: Lauth_success_4: +; NOFPAC-NEXT: pacib x16, x2 +; NOFPAC-NEXT: Lresign_end_4: +; NOFPAC-NEXT: mov x0, x16 +; NOFPAC-NEXT: ret +; +; FPAC-LABEL: test_resign_db_ib: +; FPAC: ; %bb.0: +; FPAC-NEXT: mov x16, x0 +; FPAC-NEXT: autdb x16, x1 +; FPAC-NEXT: pacib x16, x2 +; FPAC-NEXT: mov x0, x16 +; FPAC-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 3, i64 %arg1, i32 1, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_db_da(i64 %arg, i64 %arg1, i64 %arg2) { +; NOFPAC-LABEL: test_resign_db_da: +; NOFPAC: ; %bb.0: +; NOFPAC-NEXT: mov x16, x0 +; NOFPAC-NEXT: autdb x16, x1 +; NOFPAC-NEXT: mov x17, x16 +; NOFPAC-NEXT: xpacd x17 +; NOFPAC-NEXT: cmp x16, x17 +; NOFPAC-NEXT: b.eq Lauth_success_5 +; NOFPAC-NEXT: mov x16, x17 +; NOFPAC-NEXT: b Lresign_end_5 +; NOFPAC-NEXT: Lauth_success_5: +; NOFPAC-NEXT: pacda x16, x2 +; NOFPAC-NEXT: Lresign_end_5: +; NOFPAC-NEXT: mov x0, x16 +; NOFPAC-NEXT: ret +; +; FPAC-LABEL: test_resign_db_da: +; FPAC: ; %bb.0: +; FPAC-NEXT: mov x16, x0 +; FPAC-NEXT: autdb x16, x1 +; FPAC-NEXT: pacda x16, x2 +; FPAC-NEXT: mov x0, x16 +; FPAC-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 3, i64 %arg1, i32 2, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_db_db(i64 %arg, i64 %arg1, i64 %arg2) { +; NOFPAC-LABEL: test_resign_db_db: +; NOFPAC: ; %bb.0: +; NOFPAC-NEXT: mov x16, x0 +; NOFPAC-NEXT: autdb x16, x1 +; NOFPAC-NEXT: mov x17, x16 +; NOFPAC-NEXT: xpacd x17 +; NOFPAC-NEXT: cmp x16, x17 +; NOFPAC-NEXT: b.eq Lauth_success_6 +; NOFPAC-NEXT: mov x16, x17 +; NOFPAC-NEXT: b Lresign_end_6 +; NOFPAC-NEXT: Lauth_success_6: +; NOFPAC-NEXT: pacdb x16, x2 +; NOFPAC-NEXT: Lresign_end_6: +; NOFPAC-NEXT: mov x0, x16 +; NOFPAC-NEXT: ret +; +; FPAC-LABEL: test_resign_db_db: +; FPAC: ; %bb.0: +; FPAC-NEXT: mov x16, x0 +; FPAC-NEXT: autdb x16, x1 +; FPAC-NEXT: pacdb x16, x2 +; FPAC-NEXT: mov x0, x16 +; FPAC-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 3, i64 %arg1, i32 3, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_iza_db(i64 %arg, i64 %arg1, i64 %arg2) { +; NOFPAC-LABEL: test_resign_iza_db: +; NOFPAC: ; %bb.0: +; NOFPAC-NEXT: mov x16, x0 +; NOFPAC-NEXT: autiza x16 +; NOFPAC-NEXT: mov x17, x16 +; NOFPAC-NEXT: xpaci x17 +; NOFPAC-NEXT: cmp x16, x17 +; NOFPAC-NEXT: b.eq Lauth_success_7 +; NOFPAC-NEXT: mov x16, x17 +; NOFPAC-NEXT: b Lresign_end_7 +; NOFPAC-NEXT: Lauth_success_7: +; NOFPAC-NEXT: pacdb x16, x2 +; NOFPAC-NEXT: Lresign_end_7: +; NOFPAC-NEXT: mov x0, x16 +; NOFPAC-NEXT: ret +; +; FPAC-LABEL: test_resign_iza_db: +; FPAC: ; %bb.0: +; FPAC-NEXT: mov x16, x0 +; FPAC-NEXT: autiza x16 +; FPAC-NEXT: pacdb x16, x2 +; FPAC-NEXT: mov x0, x16 +; FPAC-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 0, i64 0, i32 3, i64 %arg2) + ret i64 %tmp +} + +define i64 @test_resign_da_dzb(i64 %arg, i64 %arg1, i64 %arg2) { +; NOFPAC-LABEL: test_resign_da_dzb: +; NOFPAC: ; %bb.0: +; NOFPAC-NEXT: mov x16, x0 +; NOFPAC-NEXT: autda x16, x1 +; NOFPAC-NEXT: mov x17, x16 +; NOFPAC-NEXT: xpacd x17 +; NOFPAC-NEXT: cmp x16, x17 +; NOFPAC-NEXT: b.eq Lauth_success_8 +; NOFPAC-NEXT: mov x16, x17 +; NOFPAC-NEXT: b Lresign_end_8 +; NOFPAC-NEXT: Lauth_success_8: +; NOFPAC-NEXT: pacdzb x16 +; NOFPAC-NEXT: Lresign_end_8: +; NOFPAC-NEXT: mov x0, x16 +; NOFPAC-NEXT: ret +; +; FPAC-LABEL: test_resign_da_dzb: +; FPAC: ; %bb.0: +; FPAC-NEXT: mov x16, x0 +; FPAC-NEXT: autda x16, x1 +; FPAC-NEXT: pacdzb x16 +; FPAC-NEXT: mov x0, x16 +; FPAC-NEXT: ret + %tmp = call i64 @llvm.ptrauth.resign(i64 %arg, i32 2, i64 %arg1, i32 3, i64 0) + ret i64 %tmp +} + +define i64 @test_auth_trap_attribute(i64 %arg, i64 %arg1) "ptrauth-auth-traps" { +; NOFPAC-LABEL: test_auth_trap_attribute: +; NOFPAC: ; %bb.0: +; NOFPAC-NEXT: mov x16, x0 +; NOFPAC-NEXT: autia x16, x1 +; NOFPAC-NEXT: mov x17, x16 +; NOFPAC-NEXT: xpaci x17 +; NOFPAC-NEXT: cmp x16, x17 +; NOFPAC-NEXT: b.eq Lauth_success_9 +; NOFPAC-NEXT: brk #0xc470 +; NOFPAC-NEXT: Lauth_success_9: +; NOFPAC-NEXT: mov x0, x16 +; NOFPAC-NEXT: ret +; +; FPAC-LABEL: test_auth_trap_attribute: +; FPAC: ; %bb.0: +; FPAC-NEXT: mov x16, x0 +; FPAC-NEXT: autia x16, x1 +; FPAC-NEXT: mov x0, x16 +; FPAC-NEXT: ret + %tmp = call i64 @llvm.ptrauth.auth(i64 %arg, i32 0, i64 %arg1) + ret i64 %tmp +} + +declare i64 @llvm.ptrauth.auth(i64, i32, i64) +declare i64 @llvm.ptrauth.resign(i64, i32, i64, i32, i64) From 07eccf79b05d709b424b5539d742a60712a41089 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Fri, 31 May 2024 16:28:17 -0700 Subject: [PATCH 06/58] [Docs] Describe function attributes. --- llvm/docs/PointerAuth.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/llvm/docs/PointerAuth.md b/llvm/docs/PointerAuth.md index 12b220b81a65c..5cfa0a572b49e 100644 --- a/llvm/docs/PointerAuth.md +++ b/llvm/docs/PointerAuth.md @@ -20,6 +20,9 @@ At the IR level, it is represented using: (to sign globals) * a [signed pointer constant](#constant) (to sign globals) * a [call operand bundle](#operand-bundle) (to authenticate called pointers) +* a [set of function attributes](#function-attributes) (to describe what + pointers are signed and how, to control implicit codegen in the backend, as + well as preserve invariants in the mid-level optimizer) The current implementation leverages the [Armv8.3-A PAuth/Pointer Authentication Code](#armv8-3-a-pauth-pointer-authentication-code) From c7a93185eea69a787db2341c15639df0b2235149 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 27 Sep 2021 08:00:00 -0700 Subject: [PATCH 07/58] [Docs] Update ARMv8.3 -> Armv8.3-A in arm64e arch subtype doc. --- llvm/docs/PointerAuth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/docs/PointerAuth.md b/llvm/docs/PointerAuth.md index 5cfa0a572b49e..fcf21ec179450 100644 --- a/llvm/docs/PointerAuth.md +++ b/llvm/docs/PointerAuth.md @@ -453,7 +453,7 @@ as follows: ### arm64e -Darwin supports ARMv8.3 Pointer Authentication Codes via the arm64e MachO +Darwin supports Armv8.3-A Pointer Authentication Codes via the arm64e MachO architecture slice. #### CPU Subtype From d53af5723550ad79c243d1e10a7767343ca37a76 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 1 Jul 2024 15:51:18 -0700 Subject: [PATCH 08/58] [AArch64][PAC] Emit LR auth-failure checks on tail-calls. This removes x16/x17 from tcGPR64, to use them for the check sequence. That further triggers a change in synthesized regclasses, and both that and tcGPR64 in turn cause various regalloc changes. --- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 62 ++++++++++++ llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 18 ++++ llvm/lib/Target/AArch64/AArch64InstrInfo.td | 6 +- .../lib/Target/AArch64/AArch64RegisterInfo.td | 5 +- .../AArch64/GISel/AArch64RegisterBankInfo.cpp | 1 - .../call-translator-variadic-musttail.ll | 16 +-- ...-homogeneous-prolog-epilog-bad-outline.mir | 2 +- ...ranch-target-enforcement-indirect-calls.ll | 2 +- llvm/test/CodeGen/AArch64/preserve_nonecc.ll | 18 ++-- .../CodeGen/AArch64/preserve_nonecc_call.ll | 43 ++++---- llvm/test/CodeGen/AArch64/ptrauth-ret-trap.ll | 98 +++++++++++++++++++ .../CodeGen/AArch64/speculation-hardening.mir | 6 +- ...re-swift-async-context-clobber-live-reg.ll | 8 +- 13 files changed, 236 insertions(+), 49 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/ptrauth-ret-trap.ll diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 6821c30272741..3200b5b67f60d 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -26,6 +26,7 @@ #include "Utils/AArch64BaseInfo.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/MachO.h" @@ -110,6 +111,8 @@ class AArch64AsmPrinter : public AsmPrinter { const MCExpr *lowerConstantPtrAuth(const ConstantPtrAuth &CPA) override; + void authLRBeforeTailCall(const MachineFunction &MF, unsigned ScratchReg); + void emitStartOfAsmFile(Module &M) override; void emitJumpTableInfo() override; std::tuple()->hasStackFrame()) + return; + + // We know TBI is disabled for instruction keys on Darwin, so bits 62 and 61 + // or LR will be different if and only if authentication failed. + if (!STI->isTargetMachO() || STI->hasFPAC()) + return; + + // We want: + // eor x9, lr, lr, lsl #1 + // tbz x9, #62, Lgoodsig + // brk #0xc471 + // Lgoodsig: + // + MCInst EOR; + EOR.setOpcode(AArch64::EORXrs); + EOR.addOperand(MCOperand::createReg(ScratchReg)); + EOR.addOperand(MCOperand::createReg(AArch64::LR)); + EOR.addOperand(MCOperand::createReg(AArch64::LR)); + EOR.addOperand(MCOperand::createImm(1)); + EmitToStreamer(*OutStreamer, EOR); + + MCContext &Ctx = OutStreamer->getContext(); + MCSymbol *GoodSigSym = Ctx.createTempSymbol(); + const MCExpr *GoodSig = MCSymbolRefExpr::create(GoodSigSym, Ctx); + MCInst TBZ; + TBZ.setOpcode(AArch64::TBZX); + TBZ.addOperand(MCOperand::createReg(ScratchReg)); + TBZ.addOperand(MCOperand::createImm(62)); + TBZ.addOperand(MCOperand::createExpr(GoodSig)); + EmitToStreamer(*OutStreamer, TBZ); + + MCInst BRK; + BRK.setOpcode(AArch64::BRK); + BRK.addOperand(MCOperand::createImm(0xc471)); + EmitToStreamer(*OutStreamer, BRK); + + OutStreamer->emitLabel(GoodSigSym); +} unsigned AArch64AsmPrinter::emitPtrauthDiscriminator(uint16_t Disc, unsigned AddrDisc, @@ -2415,6 +2468,8 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { ? AArch64::X17 : AArch64::X16; + authLRBeforeTailCall(*MI->getParent()->getParent(), ScratchReg); + unsigned DiscReg = AddrDisc; if (Disc) { if (AddrDisc != AArch64::NoRegister) { @@ -2455,6 +2510,11 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { case AArch64::TCRETURNrix17: case AArch64::TCRETURNrinotx16: case AArch64::TCRETURNriALL: { + authLRBeforeTailCall(*MI->getParent()->getParent(), + MI->getOperand(0).getReg() == AArch64::X16 + ? AArch64::X17 + : AArch64::X16); + MCInst TmpInst; TmpInst.setOpcode(AArch64::BR); TmpInst.addOperand(MCOperand::createReg(MI->getOperand(0).getReg())); @@ -2462,6 +2522,8 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { return; } case AArch64::TCRETURNdi: { + authLRBeforeTailCall(*MI->getParent()->getParent(), AArch64::X16); + MCOperand Dest; MCInstLowering.lowerOperand(MI->getOperand(0), Dest); MCInst TmpInst; diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp index 93278a2ba0d09..f2f7a9c376499 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -120,6 +120,24 @@ unsigned AArch64InstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { // 4-byte insn. NumBytes = 4; break; + case AArch64::TCRETURNdi: + case AArch64::TCRETURNri: + case AArch64::TCRETURNriALL: + case AArch64::TCRETURNrix16x17: + case AArch64::TCRETURNrix17: + case AArch64::TCRETURNrinotx16: + case AArch64::AUTH_TCRETURN: + case AArch64::AUTH_TCRETURN_BTI: { + NumBytes = Desc.getSize() ? Desc.getSize() : 4; + // In some cases we prepend these with an LR auth-failure check, to + // emulate FPAC. This adds size overhead of 2 insts. + const AArch64Subtarget &STI = MF->getSubtarget(); + if ((F.hasFnAttribute("ptrauth-auth-traps") || + F.hasFnAttribute("ptrauth-returns")) && + !STI.hasFPAC()) + NumBytes += 8; + break; + } case TargetOpcode::STACKMAP: // The upper bound for a stackmap intrinsic is the full length of its shadow NumBytes = StackMapOpers(&MI).getNumPatchBytes(); diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index d7d7f3cedd752..087000b5cff46 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -1850,8 +1850,9 @@ let Predicates = [HasPAuth] in { let Size = 8; } - // Size 16: 4 fixed + 8 variable, to compute discriminator. - let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Size = 16, + // Size 12: 4 fixed + 8 variable to compute discriminator. + // potentially + 8 to check LR. + let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Size = 12, Uses = [SP] in { def AUTH_TCRETURN : Pseudo<(outs), (ins tcGPR64:$dst, i32imm:$FPDiff, i32imm:$Key, @@ -9568,6 +9569,7 @@ def : Pat<(nontemporalstore GPR64:$Rt, // Tail call return handling. These are all compiler pseudo-instructions, // so no encoding information or anything like that. +// Size potentially 12 (4 + 8) to check LR. let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [SP] in { def TCRETURNdi : Pseudo<(outs), (ins i64imm:$dst, i32imm:$FPDiff), []>, Sched<[WriteBrReg]>; diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.td b/llvm/lib/Target/AArch64/AArch64RegisterInfo.td index dfaa67dd1959d..4e42a2b15891f 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.td +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.td @@ -216,7 +216,10 @@ def GPR64all : RegisterClass<"AArch64", [i64], 64, (add GPR64common, XZR, SP)>; // For tail calls, we can't use callee-saved registers, as they are restored // to the saved value before the tail call, which would clobber a call address. // This is for indirect tail calls to store the address of the destination. -def tcGPR64 : RegisterClass<"AArch64", [i64], 64, (sub GPR64common, X19, X20, X21, +// Also, avoid X16 and X17 so that they will be available to check LR was +// authenticated correctly. +def tcGPR64 : RegisterClass<"AArch64", [i64], 64, (sub GPR64common, X16, X17, + X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, FP, LR)>; diff --git a/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp index 5616d063f70bc..e713bb5e09130 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64RegisterBankInfo.cpp @@ -275,7 +275,6 @@ AArch64RegisterBankInfo::getRegBankFromRegClass(const TargetRegisterClass &RC, case AArch64::GPR64allRegClassID: case AArch64::GPR64noipRegClassID: case AArch64::GPR64common_and_GPR64noipRegClassID: - case AArch64::GPR64noip_and_tcGPR64RegClassID: case AArch64::tcGPR64RegClassID: case AArch64::tcGPRx16x17RegClassID: case AArch64::tcGPRx17RegClassID: diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/call-translator-variadic-musttail.ll b/llvm/test/CodeGen/AArch64/GlobalISel/call-translator-variadic-musttail.ll index d2e94b29fe0e3..7d4dd3988fcf3 100644 --- a/llvm/test/CodeGen/AArch64/GlobalISel/call-translator-variadic-musttail.ll +++ b/llvm/test/CodeGen/AArch64/GlobalISel/call-translator-variadic-musttail.ll @@ -71,16 +71,16 @@ define i32 @test_musttail_variadic_spill(i32 %arg0, ...) { ; CHECK-NEXT: mov w0, w19 ; CHECK-NEXT: mov x1, x20 ; CHECK-NEXT: mov x2, x21 +; CHECK-NEXT: ldp q1, q0, [sp, #96] ; 32-byte Folded Reload ; CHECK-NEXT: mov x3, x22 +; CHECK-NEXT: ldp q3, q2, [sp, #64] ; 32-byte Folded Reload ; CHECK-NEXT: mov x4, x23 +; CHECK-NEXT: ldp q5, q4, [sp, #32] ; 32-byte Folded Reload ; CHECK-NEXT: mov x5, x24 +; CHECK-NEXT: ldp q7, q6, [sp] ; 32-byte Folded Reload ; CHECK-NEXT: mov x6, x25 ; CHECK-NEXT: mov x7, x26 ; CHECK-NEXT: mov x8, x27 -; CHECK-NEXT: ldp q1, q0, [sp, #96] ; 32-byte Folded Reload -; CHECK-NEXT: ldp q3, q2, [sp, #64] ; 32-byte Folded Reload -; CHECK-NEXT: ldp q5, q4, [sp, #32] ; 32-byte Folded Reload -; CHECK-NEXT: ldp q7, q6, [sp] ; 32-byte Folded Reload ; CHECK-NEXT: ldp x29, x30, [sp, #208] ; 16-byte Folded Reload ; CHECK-NEXT: ldp x20, x19, [sp, #192] ; 16-byte Folded Reload ; CHECK-NEXT: ldp x22, x21, [sp, #176] ; 16-byte Folded Reload @@ -142,17 +142,17 @@ define void @f_thunk(ptr %this, ...) { ; CHECK-NEXT: mov x9, x0 ; CHECK-NEXT: mov x0, x19 ; CHECK-NEXT: mov x1, x20 +; CHECK-NEXT: ldp q1, q0, [sp, #96] ; 32-byte Folded Reload ; CHECK-NEXT: mov x2, x21 +; CHECK-NEXT: ldp q3, q2, [sp, #64] ; 32-byte Folded Reload ; CHECK-NEXT: mov x3, x22 +; CHECK-NEXT: ldp q5, q4, [sp, #32] ; 32-byte Folded Reload ; CHECK-NEXT: mov x4, x23 +; CHECK-NEXT: ldp q7, q6, [sp] ; 32-byte Folded Reload ; CHECK-NEXT: mov x5, x24 ; CHECK-NEXT: mov x6, x25 ; CHECK-NEXT: mov x7, x26 -; CHECK-NEXT: ldp q1, q0, [sp, #96] ; 32-byte Folded Reload ; CHECK-NEXT: mov x8, x27 -; CHECK-NEXT: ldp q3, q2, [sp, #64] ; 32-byte Folded Reload -; CHECK-NEXT: ldp q5, q4, [sp, #32] ; 32-byte Folded Reload -; CHECK-NEXT: ldp q7, q6, [sp] ; 32-byte Folded Reload ; CHECK-NEXT: ldp x29, x30, [sp, #240] ; 16-byte Folded Reload ; CHECK-NEXT: ldp x20, x19, [sp, #224] ; 16-byte Folded Reload ; CHECK-NEXT: ldp x22, x21, [sp, #208] ; 16-byte Folded Reload diff --git a/llvm/test/CodeGen/AArch64/arm64-homogeneous-prolog-epilog-bad-outline.mir b/llvm/test/CodeGen/AArch64/arm64-homogeneous-prolog-epilog-bad-outline.mir index ab4f0af97e324..b459816320b6c 100644 --- a/llvm/test/CodeGen/AArch64/arm64-homogeneous-prolog-epilog-bad-outline.mir +++ b/llvm/test/CodeGen/AArch64/arm64-homogeneous-prolog-epilog-bad-outline.mir @@ -35,6 +35,6 @@ body: | bb.1: liveins: $w0, $x16 - TCRETURNri killed renamable $x16, 0, csr_aarch64_aapcs, implicit $sp, implicit $w0 + RET_ReallyLR implicit $x16 # CHECK: _OUTLINED_FUNCTION_PROLOG_FRAME16_x30x29x19x20: # CHECK-NOT: _OUTLINED_FUNCTION_EPILOG_x30x29x19x20: diff --git a/llvm/test/CodeGen/AArch64/branch-target-enforcement-indirect-calls.ll b/llvm/test/CodeGen/AArch64/branch-target-enforcement-indirect-calls.ll index 3d8a5976559ba..4d51a779f7629 100644 --- a/llvm/test/CodeGen/AArch64/branch-target-enforcement-indirect-calls.ll +++ b/llvm/test/CodeGen/AArch64/branch-target-enforcement-indirect-calls.ll @@ -86,6 +86,6 @@ define void @pac_pc_enabled_force_x17(ptr %p) "sign-return-address"="all" "branc entry: %p_x17 = tail call ptr asm "", "={x17},{x17},~{lr}"(ptr %p) tail call void %p_x17() -; CHECK: br x17 +; CHECK: br {{(x[0-9]|x1[0-578])$}} ret void } diff --git a/llvm/test/CodeGen/AArch64/preserve_nonecc.ll b/llvm/test/CodeGen/AArch64/preserve_nonecc.ll index 2ef9f650d70fb..ee89540972a75 100644 --- a/llvm/test/CodeGen/AArch64/preserve_nonecc.ll +++ b/llvm/test/CodeGen/AArch64/preserve_nonecc.ll @@ -30,24 +30,26 @@ entry: ;ALL: stp x17, x16 ;ALL: stp x20, x19 ;ALL: stp x22, x21 -;ALL: stp x24, x23 -;ALL: stp x26, x25 -;ALL: stp x28, x27 +;ALL: str x23 +;ALL: stp x25, x24 +;ALL: stp x27, x26 +;ALL: str x28 ;ALL: stp d8, d7 ;ALL: stp d10, d9 ;ALL: stp d12, d11 ;ALL: stp d14, d13 ;ALL: stp d16, d15 -;ALL: ldp x20, x19 -;ALL: ldp x22, x21 -;ALL: ldp x24, x23 -;ALL: ldp x26, x25 -;ALL: ldp x28, x27 +;ALL: ldp x27, x26 +;ALL: ldr x28 ;ALL: ldp d8, d7 +;ALL: ldr x23 ;ALL: ldp d10, d9 ;ALL: ldp d12, d11 ;ALL: ldp d14, d13 ;ALL: ldp d16, d15 +;ALL: ldp x20, x19 +;ALL: ldp x22, x21 +;ALL: ldp x25, x24 ;ALL: ldp x9, x8 ;ALL: ldp x11, x10 ;ALL: ldp x13, x12 diff --git a/llvm/test/CodeGen/AArch64/preserve_nonecc_call.ll b/llvm/test/CodeGen/AArch64/preserve_nonecc_call.ll index 9b9717c19321e..c80daca769faf 100644 --- a/llvm/test/CodeGen/AArch64/preserve_nonecc_call.ll +++ b/llvm/test/CodeGen/AArch64/preserve_nonecc_call.ll @@ -222,17 +222,18 @@ define preserve_nonecc i64 @callee_with_many_param(i64 %a1, i64 %a2, i64 %a3, i6 ; DARWIN-NEXT: .cfi_def_cfa_offset 16 ; DARWIN-NEXT: .cfi_offset w30, -8 ; DARWIN-NEXT: .cfi_offset w29, -16 -; DARWIN-NEXT: mov x8, x15 +; DARWIN-NEXT: mov x16, x15 +; DARWIN-NEXT: mov x8, x25 +; DARWIN-NEXT: mov x25, x26 ; DARWIN-NEXT: mov x15, x20 ; DARWIN-NEXT: mov x20, x21 ; DARWIN-NEXT: mov x21, x22 -; DARWIN-NEXT: mov x22, x23 -; DARWIN-NEXT: mov x23, x24 -; DARWIN-NEXT: mov x24, x25 -; DARWIN-NEXT: mov x25, x26 ; DARWIN-NEXT: mov x26, x27 ; DARWIN-NEXT: mov x27, x28 ; DARWIN-NEXT: mov x28, x0 +; DARWIN-NEXT: mov x22, x23 +; DARWIN-NEXT: mov x23, x24 +; DARWIN-NEXT: mov x24, x8 ; DARWIN-NEXT: mov x0, x1 ; DARWIN-NEXT: mov x1, x2 ; DARWIN-NEXT: mov x2, x3 @@ -246,7 +247,7 @@ define preserve_nonecc i64 @callee_with_many_param(i64 %a1, i64 %a2, i64 %a3, i6 ; DARWIN-NEXT: mov x12, x13 ; DARWIN-NEXT: mov x13, x14 ; DARWIN-NEXT: mov x14, x9 -; DARWIN-NEXT: mov x9, x8 +; DARWIN-NEXT: mov x9, x16 ; DARWIN-NEXT: bl _callee_with_many_param2 ; DARWIN-NEXT: ldp x29, x30, [sp], #16 ; 16-byte Folded Reload ; DARWIN-NEXT: ret @@ -259,17 +260,18 @@ define preserve_nonecc i64 @callee_with_many_param(i64 %a1, i64 %a2, i64 %a3, i6 ; WIN-NEXT: str x30, [sp, #16] // 8-byte Folded Spill ; WIN-NEXT: .seh_save_reg x30, 16 ; WIN-NEXT: .seh_endprologue -; WIN-NEXT: ldr x8, [sp, #32] -; WIN-NEXT: mov x15, x20 +; WIN-NEXT: ldr x15, [sp, #32] +; WIN-NEXT: mov x8, x25 +; WIN-NEXT: mov x25, x26 +; WIN-NEXT: mov x16, x20 ; WIN-NEXT: mov x20, x21 ; WIN-NEXT: mov x21, x22 -; WIN-NEXT: mov x22, x23 -; WIN-NEXT: mov x23, x24 -; WIN-NEXT: mov x24, x25 -; WIN-NEXT: mov x25, x26 ; WIN-NEXT: mov x26, x27 ; WIN-NEXT: mov x27, x28 ; WIN-NEXT: mov x28, x0 +; WIN-NEXT: mov x22, x23 +; WIN-NEXT: mov x23, x24 +; WIN-NEXT: mov x24, x8 ; WIN-NEXT: mov x0, x1 ; WIN-NEXT: mov x1, x2 ; WIN-NEXT: mov x2, x3 @@ -283,8 +285,8 @@ define preserve_nonecc i64 @callee_with_many_param(i64 %a1, i64 %a2, i64 %a3, i6 ; WIN-NEXT: mov x12, x13 ; WIN-NEXT: mov x13, x14 ; WIN-NEXT: mov x14, x9 -; WIN-NEXT: mov x9, x8 -; WIN-NEXT: str x15, [sp] +; WIN-NEXT: mov x9, x15 +; WIN-NEXT: str x16, [sp] ; WIN-NEXT: bl callee_with_many_param2 ; WIN-NEXT: .seh_startepilogue ; WIN-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload @@ -404,13 +406,13 @@ define i64 @caller3() { ; DARWIN-NEXT: .cfi_offset b15, -160 ; DARWIN-NEXT: mov w20, #1 ; =0x1 ; DARWIN-NEXT: mov w21, #2 ; =0x2 +; DARWIN-NEXT: mov w26, #7 ; =0x7 +; DARWIN-NEXT: mov w27, #8 ; =0x8 +; DARWIN-NEXT: mov w28, #9 ; =0x9 ; DARWIN-NEXT: mov w22, #3 ; =0x3 ; DARWIN-NEXT: mov w23, #4 ; =0x4 ; DARWIN-NEXT: mov w24, #5 ; =0x5 ; DARWIN-NEXT: mov w25, #6 ; =0x6 -; DARWIN-NEXT: mov w26, #7 ; =0x7 -; DARWIN-NEXT: mov w27, #8 ; =0x8 -; DARWIN-NEXT: mov w28, #9 ; =0x9 ; DARWIN-NEXT: mov w0, #10 ; =0xa ; DARWIN-NEXT: mov w1, #11 ; =0xb ; DARWIN-NEXT: mov w2, #12 ; =0xc @@ -438,6 +440,7 @@ define i64 @caller3() { ; DARWIN-NEXT: ldp d13, d12, [sp, #16] ; 16-byte Folded Reload ; DARWIN-NEXT: ldp d15, d14, [sp], #160 ; 16-byte Folded Reload ; DARWIN-NEXT: ret + ; ; WIN-LABEL: caller3: ; WIN: .seh_proc caller3 @@ -468,13 +471,13 @@ define i64 @caller3() { ; WIN-NEXT: mov w8, #24 // =0x18 ; WIN-NEXT: mov w20, #1 // =0x1 ; WIN-NEXT: mov w21, #2 // =0x2 +; WIN-NEXT: mov w26, #7 // =0x7 +; WIN-NEXT: mov w27, #8 // =0x8 +; WIN-NEXT: mov w28, #9 // =0x9 ; WIN-NEXT: mov w22, #3 // =0x3 ; WIN-NEXT: mov w23, #4 // =0x4 ; WIN-NEXT: mov w24, #5 // =0x5 ; WIN-NEXT: mov w25, #6 // =0x6 -; WIN-NEXT: mov w26, #7 // =0x7 -; WIN-NEXT: mov w27, #8 // =0x8 -; WIN-NEXT: mov w28, #9 // =0x9 ; WIN-NEXT: mov w0, #10 // =0xa ; WIN-NEXT: mov w1, #11 // =0xb ; WIN-NEXT: mov w2, #12 // =0xc diff --git a/llvm/test/CodeGen/AArch64/ptrauth-ret-trap.ll b/llvm/test/CodeGen/AArch64/ptrauth-ret-trap.ll new file mode 100644 index 0000000000000..8543c44a16d15 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/ptrauth-ret-trap.ll @@ -0,0 +1,98 @@ +; RUN: llc -mtriple arm64e-apple-darwin -asm-verbose=false -disable-post-ra -o - %s | FileCheck %s + +; CHECK-LABEL: _test_tailcall: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! +; CHECK-NEXT: bl _bar +; CHECK-NEXT: ldp x29, x30, [sp], #16 +; CHECK-NEXT: autibsp +; CHECK-NEXT: eor x16, x30, x30, lsl #1 +; CHECK-NEXT: tbz x16, #62, [[GOOD:L.*]] +; CHECK-NEXT: brk #0xc471 +; CHECK-NEXT: [[GOOD]]: +; CHECK-NEXT: b _bar +define i32 @test_tailcall() #0 { + call i32 @bar() + %c = tail call i32 @bar() + ret i32 %c +} + +; CHECK-LABEL: _test_tailcall_noframe: +; CHECK-NEXT: b _bar +define i32 @test_tailcall_noframe() #0 { + %c = tail call i32 @bar() + ret i32 %c +} + +; CHECK-LABEL: _test_tailcall_indirect: +; CHECK: autibsp +; CHECK: eor x16, x30, x30, lsl #1 +; CHECK: tbz x16, #62, [[GOOD:L.*]] +; CHECK: brk #0xc471 +; CHECK: [[GOOD]]: +; CHECK: br x0 +define void @test_tailcall_indirect(void ()* %fptr) #0 { + call i32 @test_tailcall() + tail call void %fptr() + ret void +} + +; CHECK-LABEL: _test_tailcall_indirect_in_x9: +; CHECK: autibsp +; CHECK: eor x16, x30, x30, lsl #1 +; CHECK: tbz x16, #62, [[GOOD:L.*]] +; CHECK: brk #0xc471 +; CHECK: [[GOOD]]: +; CHECK: br x9 +define void @test_tailcall_indirect_in_x9(i64* sret(i64) %ret, [8 x i64] %in, void (i64*, [8 x i64])* %fptr) #0 { + %ptr = alloca i8, i32 16 + call i32 @test_tailcall() + tail call void %fptr(i64* sret(i64) %ret, [8 x i64] %in) + ret void +} + +; CHECK-LABEL: _test_auth_tailcall_indirect: +; CHECK: autibsp +; CHECK: eor x16, x30, x30, lsl #1 +; CHECK: tbz x16, #62, [[GOOD:L.*]] +; CHECK: brk #0xc471 +; CHECK: [[GOOD]]: +; CHECK: mov x16, #42 +; CHECK: braa x0, x16 +define void @test_auth_tailcall_indirect(void ()* %fptr) #0 { + call i32 @test_tailcall() + tail call void %fptr() [ "ptrauth"(i32 0, i64 42) ] + ret void +} + +; CHECK-LABEL: _test_auth_tailcall_indirect_in_x9: +; CHECK: autibsp +; CHECK: eor x16, x30, x30, lsl #1 +; CHECK: tbz x16, #62, [[GOOD:L.*]] +; CHECK: brk #0xc471 +; CHECK: [[GOOD]]: +; CHECK: brabz x9 +define void @test_auth_tailcall_indirect_in_x9(i64* sret(i64) %ret, [8 x i64] %in, void (i64*, [8 x i64])* %fptr) #0 { + %ptr = alloca i8, i32 16 + call i32 @test_tailcall() + tail call void %fptr(i64* sret(i64) %ret, [8 x i64] %in) [ "ptrauth"(i32 1, i64 0) ] + ret void +} + +; CHECK-LABEL: _test_auth_tailcall_indirect_bti: +; CHECK: autibsp +; CHECK: eor x17, x30, x30, lsl #1 +; CHECK: tbz x17, #62, [[GOOD:L.*]] +; CHECK: brk #0xc471 +; CHECK: [[GOOD]]: +; CHECK: brabz x16 +define void @test_auth_tailcall_indirect_bti(i64* sret(i64) %ret, [8 x i64] %in, void (i64*, [8 x i64])* %fptr) #0 "branch-target-enforcement"="true" { + %ptr = alloca i8, i32 16 + call i32 @test_tailcall() + tail call void %fptr(i64* sret(i64) %ret, [8 x i64] %in) [ "ptrauth"(i32 1, i64 0) ] + ret void +} + +declare i32 @bar() + +attributes #0 = { nounwind "ptrauth-returns" "ptrauth-auth-traps" } diff --git a/llvm/test/CodeGen/AArch64/speculation-hardening.mir b/llvm/test/CodeGen/AArch64/speculation-hardening.mir index 1e5fafb7242b8..55c23338b5fe2 100644 --- a/llvm/test/CodeGen/AArch64/speculation-hardening.mir +++ b/llvm/test/CodeGen/AArch64/speculation-hardening.mir @@ -155,10 +155,10 @@ body: | ; CHECK: mov x1, sp ; CHECK: and x1, x1, x16 ; CHECK: mov sp, x1 - ; CHECK: br x17 + ; CHECK: br x18 $x8 = ADRP target-flags(aarch64-page) @g - $x17 = LDRXui killed $x8, target-flags(aarch64-pageoff, aarch64-nc) @g - TCRETURNri killed $x17, 0, implicit $sp, implicit $x0 + $x18 = LDRXui killed $x8, target-flags(aarch64-pageoff, aarch64-nc) @g + TCRETURNri killed $x18, 0, implicit $sp, implicit $x0 ... --- name: indirect_call_lr diff --git a/llvm/test/CodeGen/AArch64/store-swift-async-context-clobber-live-reg.ll b/llvm/test/CodeGen/AArch64/store-swift-async-context-clobber-live-reg.ll index 1f7584b57e4aa..ac83231336d8a 100644 --- a/llvm/test/CodeGen/AArch64/store-swift-async-context-clobber-live-reg.ll +++ b/llvm/test/CodeGen/AArch64/store-swift-async-context-clobber-live-reg.ll @@ -239,6 +239,8 @@ define swifttailcc void @test_async_with_jumptable_2_available_regs_left(ptr %sr ; CHECK-NEXT: ; InlineAsm End ; CHECK-NEXT: ; InlineAsm Start ; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: ldr x10, [x0] +; CHECK-NEXT: mov x0, x20 ; CHECK-NEXT: ; InlineAsm Start ; CHECK-NEXT: ; InlineAsm End ; CHECK-NEXT: ; InlineAsm Start @@ -263,8 +265,6 @@ define swifttailcc void @test_async_with_jumptable_2_available_regs_left(ptr %sr ; CHECK-NEXT: ; InlineAsm End ; CHECK-NEXT: ; InlineAsm Start ; CHECK-NEXT: ; InlineAsm End -; CHECK-NEXT: mov x0, x20 -; CHECK-NEXT: ldr x10, [x22] ; CHECK-NEXT: cbnz x10, LBB4_2 ; CHECK-NEXT: ; %bb.1: ; %then.1 ; CHECK-NEXT: str xzr, [x22] @@ -386,6 +386,8 @@ define swifttailcc void @test_async_with_jumptable_1_available_reg_left(ptr %src ; CHECK-NEXT: ; InlineAsm End ; CHECK-NEXT: ; InlineAsm Start ; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: ldr x10, [x0] +; CHECK-NEXT: mov x0, x20 ; CHECK-NEXT: ; InlineAsm Start ; CHECK-NEXT: ; InlineAsm End ; CHECK-NEXT: ; InlineAsm Start @@ -412,8 +414,6 @@ define swifttailcc void @test_async_with_jumptable_1_available_reg_left(ptr %src ; CHECK-NEXT: ; InlineAsm End ; CHECK-NEXT: ; InlineAsm Start ; CHECK-NEXT: ; InlineAsm End -; CHECK-NEXT: mov x0, x20 -; CHECK-NEXT: ldr x10, [x22] ; CHECK-NEXT: cbnz x10, LBB5_2 ; CHECK-NEXT: ; %bb.1: ; %then.1 ; CHECK-NEXT: str xzr, [x22] From 5fd1d958251672b0e78c967b509c2dd30f57429d Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 27 Sep 2021 08:00:00 -0700 Subject: [PATCH 09/58] [AArch64][PAC] Emit auth call for tlv access thunk function pointer. --- .../Target/AArch64/AArch64ISelLowering.cpp | 3 +-- .../GISel/AArch64InstructionSelector.cpp | 10 ++++++- .../CodeGen/AArch64/arm64e-ptrauth-tls.ll | 17 ------------ .../CodeGen/AArch64/ptrauth-darwin-tls.ll | 27 +++++++++++++++++++ 4 files changed, 37 insertions(+), 20 deletions(-) delete mode 100644 llvm/test/CodeGen/AArch64/arm64e-ptrauth-tls.ll create mode 100644 llvm/test/CodeGen/AArch64/ptrauth-darwin-tls.ll diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index d762cd0d936a1..a98499d1af3d9 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -9254,8 +9254,7 @@ AArch64TargetLowering::LowerDarwinGlobalTLSAddress(SDValue Op, Ops.push_back(DAG.getRegister(AArch64::X0, MVT::i64)); Ops.push_back(DAG.getRegisterMask(Mask)); Ops.push_back(Chain.getValue(1)); - Chain = - DAG.getNode(Opcode, DL, DAG.getVTList(MVT::Other, MVT::Glue), Ops); + Chain = DAG.getNode(Opcode, DL, DAG.getVTList(MVT::Other, MVT::Glue), Ops); return DAG.getCopyFromReg(Chain, DL, AArch64::X0, PtrVT, Chain.getValue(1)); } diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp index f4e798287d50c..715574c989e22 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp @@ -3653,7 +3653,15 @@ bool AArch64InstructionSelector::selectTLSGlobalValue( // TLS calls preserve all registers except those that absolutely must be // trashed: X0 (it takes an argument), LR (it's a call) and NZCV (let's not be // silly). - MIB.buildInstr(getBLRCallOpcode(MF), {}, {Load}) + unsigned Opcode = getBLRCallOpcode(MF); + + // With ptrauth-calls, the tlv access thunk pointer is authenticated (IA, 0). + if (MF.getFunction().hasFnAttribute("ptrauth-calls")) { + assert(Opcode == AArch64::BLR); + Opcode = AArch64::BLRAAZ; + } + + MIB.buildInstr(Opcode, {}, {Load}) .addUse(AArch64::X0, RegState::Implicit) .addDef(AArch64::X0, RegState::Implicit) .addRegMask(TRI.getTLSCallPreservedMask()); diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-tls.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-tls.ll deleted file mode 100644 index 05904005563be..0000000000000 --- a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-tls.ll +++ /dev/null @@ -1,17 +0,0 @@ -; RUN: llc -mtriple=arm64e-apple-ios %s -o - | FileCheck %s - -@var = thread_local global i8 0 - -define i8 @get_var() #0 { -; CHECK-LABEL: get_var: -; CHECK: adrp x[[TLVPDESC_SLOT_HI:[0-9]+]], _var@TLVPPAGE -; CHECK: ldr x0, [x[[TLVPDESC_SLOT_HI]], _var@TLVPPAGEOFF] -; CHECK: ldr [[TLV_GET_ADDR:x[0-9]+]], [x0] -; CHECK: blraaz [[TLV_GET_ADDR]] -; CHECK: ldrb w0, [x0] - - %val = load i8, i8* @var, align 1 - ret i8 %val -} - -attributes #0 = { nounwind "ptrauth-calls" } diff --git a/llvm/test/CodeGen/AArch64/ptrauth-darwin-tls.ll b/llvm/test/CodeGen/AArch64/ptrauth-darwin-tls.ll new file mode 100644 index 0000000000000..e3cb0e818d16d --- /dev/null +++ b/llvm/test/CodeGen/AArch64/ptrauth-darwin-tls.ll @@ -0,0 +1,27 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 +; RUN: llc -mtriple=arm64e-apple-darwin %s -o - \ +; RUN: -aarch64-enable-collect-loh=0 | FileCheck %s + +; RUN: llc -mtriple=arm64e-apple-darwin %s -o - \ +; RUN: -global-isel -global-isel-abort=1 -verify-machineinstrs \ +; RUN: -aarch64-enable-collect-loh=0 | FileCheck %s + +@var = thread_local global i8 0 + +define i8 @get_var() #0 { +; CHECK-LABEL: get_var: +; CHECK: ; %bb.0: +; CHECK-NEXT: stp x29, x30, [sp, #-16]! ; 16-byte Folded Spill +; CHECK-NEXT: adrp x0, _var@TLVPPAGE +; CHECK-NEXT: ldr x0, [x0, _var@TLVPPAGEOFF] +; CHECK-NEXT: ldr x8, [x0] +; CHECK-NEXT: blraaz x8 +; CHECK-NEXT: ldrb w0, [x0] +; CHECK-NEXT: ldp x29, x30, [sp], #16 ; 16-byte Folded Reload +; CHECK-NEXT: ret + + %val = load i8, ptr @var, align 1 + ret i8 %val +} + +attributes #0 = { nounwind "ptrauth-calls" } From 50696ced6439f1eba2e77a5b2e1d236e7889d2c7 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 1 Jul 2024 17:16:34 -0700 Subject: [PATCH 10/58] [AArch64][PAC] Add MachO support for __DATA,__auth_ptr. Used for lowering some code refs to ptrauth constants. --- llvm/include/llvm/CodeGen/MachineModuleInfo.h | 8 +++ .../llvm/CodeGen/MachineModuleInfoImpls.h | 44 ++++----------- llvm/lib/CodeGen/MachineModuleInfoImpls.cpp | 29 +++++----- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 19 +++---- .../lib/Target/AArch64/AArch64MCInstLower.cpp | 7 ++- .../AArch64/AArch64TargetObjectFile.cpp | 53 +++++-------------- .../Target/AArch64/AArch64TargetObjectFile.h | 5 +- 7 files changed, 56 insertions(+), 109 deletions(-) diff --git a/llvm/include/llvm/CodeGen/MachineModuleInfo.h b/llvm/include/llvm/CodeGen/MachineModuleInfo.h index 92ea3c902ce95..97b439c726b0a 100644 --- a/llvm/include/llvm/CodeGen/MachineModuleInfo.h +++ b/llvm/include/llvm/CodeGen/MachineModuleInfo.h @@ -58,12 +58,20 @@ class MachineModuleInfoImpl { using StubValueTy = PointerIntPair; using SymbolListTy = std::vector>; + /// A variant of SymbolListTy where the stub is a generalized MCExpr. + using ExprStubListTy = std::vector>; + virtual ~MachineModuleInfoImpl(); protected: /// Return the entries from a DenseMap in a deterministic sorted orer. /// Clears the map. static SymbolListTy getSortedStubs(DenseMap&); + + /// Return the entries from a DenseMap in a deterministic sorted orer. + /// Clears the map. + static ExprStubListTy + getSortedExprStubs(DenseMap &); }; //===----------------------------------------------------------------------===// diff --git a/llvm/include/llvm/CodeGen/MachineModuleInfoImpls.h b/llvm/include/llvm/CodeGen/MachineModuleInfoImpls.h index 9e1446f5be47e..c19c290281e68 100644 --- a/llvm/include/llvm/CodeGen/MachineModuleInfoImpls.h +++ b/llvm/include/llvm/CodeGen/MachineModuleInfoImpls.h @@ -27,13 +27,6 @@ class MCSymbol; /// MachineModuleInfoMachO - This is a MachineModuleInfoImpl implementation /// for MachO targets. class MachineModuleInfoMachO : public MachineModuleInfoImpl { -public: - /// The information specific to a Darwin '$auth_ptr' stub. - struct AuthStubInfo { - const MCExpr *AuthPtrRef; - }; - -private: /// GVStubs - Darwin '$non_lazy_ptr' stubs. The key is something like /// "Lfoo$non_lazy_ptr", the value is something like "_foo". The extra bit /// is true if this GV is external. @@ -47,7 +40,7 @@ class MachineModuleInfoMachO : public MachineModuleInfoImpl { /// Darwin '$auth_ptr' stubs. The key is the stub symbol, like /// "Lfoo$addend$auth_ptr$ib$12". The value is the MCExpr representing that /// pointer, something like "_foo+addend@AUTH(ib, 12)". - DenseMap AuthPtrStubs; + DenseMap AuthPtrStubs; virtual void anchor(); // Out of line virtual method. @@ -64,7 +57,7 @@ class MachineModuleInfoMachO : public MachineModuleInfoImpl { return ThreadLocalGVStubs[Sym]; } - AuthStubInfo &getAuthPtrStubEntry(MCSymbol *Sym) { + const MCExpr *&getAuthPtrStubEntry(MCSymbol *Sym) { assert(Sym && "Key cannot be null"); return AuthPtrStubs[Sym]; } @@ -75,39 +68,21 @@ class MachineModuleInfoMachO : public MachineModuleInfoImpl { return getSortedStubs(ThreadLocalGVStubs); } - typedef std::pair AuthStubPairTy; - typedef std::vector AuthStubListTy; - - AuthStubListTy getAuthGVStubList() { - AuthStubListTy List(AuthPtrStubs.begin(), AuthPtrStubs.end()); - - if (!List.empty()) - std::sort(List.begin(), List.end(), - [](const AuthStubPairTy &LHS, const AuthStubPairTy &RHS) { - return LHS.first->getName() < RHS.first->getName(); - }); - - AuthPtrStubs.clear(); - return List; + ExprStubListTy getAuthGVStubList() { + return getSortedExprStubs(AuthPtrStubs); } }; /// MachineModuleInfoELF - This is a MachineModuleInfoImpl implementation /// for ELF targets. class MachineModuleInfoELF : public MachineModuleInfoImpl { -public: - struct AuthStubInfo { - const MCExpr *AuthPtrRef; - }; - -private: /// GVStubs - These stubs are used to materialize global addresses in PIC /// mode. DenseMap GVStubs; /// AuthPtrStubs - These stubs are used to materialize signed addresses for /// extern_weak symbols. - DenseMap AuthPtrStubs; + DenseMap AuthPtrStubs; virtual void anchor(); // Out of line virtual method. @@ -119,7 +94,7 @@ class MachineModuleInfoELF : public MachineModuleInfoImpl { return GVStubs[Sym]; } - AuthStubInfo &getAuthPtrStubEntry(MCSymbol *Sym) { + const MCExpr *&getAuthPtrStubEntry(MCSymbol *Sym) { assert(Sym && "Key cannot be null"); return AuthPtrStubs[Sym]; } @@ -128,10 +103,9 @@ class MachineModuleInfoELF : public MachineModuleInfoImpl { SymbolListTy GetGVStubList() { return getSortedStubs(GVStubs); } - using AuthStubPairTy = std::pair; - typedef std::vector AuthStubListTy; - - AuthStubListTy getAuthGVStubList(); + ExprStubListTy getAuthGVStubList() { + return getSortedExprStubs(AuthPtrStubs); + } }; /// MachineModuleInfoCOFF - This is a MachineModuleInfoImpl implementation diff --git a/llvm/lib/CodeGen/MachineModuleInfoImpls.cpp b/llvm/lib/CodeGen/MachineModuleInfoImpls.cpp index f114f1ecc0bae..956317510dc73 100644 --- a/llvm/lib/CodeGen/MachineModuleInfoImpls.cpp +++ b/llvm/lib/CodeGen/MachineModuleInfoImpls.cpp @@ -43,24 +43,19 @@ MachineModuleInfoImpl::SymbolListTy MachineModuleInfoImpl::getSortedStubs( return List; } -template -static typename MachineModuleInfoTarget::AuthStubListTy getAuthGVStubListHelper( - DenseMap - &AuthPtrStubs) { - typename MachineModuleInfoTarget::AuthStubListTy List(AuthPtrStubs.begin(), - AuthPtrStubs.end()); +using ExprStubPairTy = std::pair; +static int SortAuthStubPair(const ExprStubPairTy *LHS, + const ExprStubPairTy *RHS) { + return LHS->first->getName().compare(RHS->first->getName()); +} - if (!List.empty()) - llvm::sort(List.begin(), List.end(), - [](const typename MachineModuleInfoTarget::AuthStubPairTy &LHS, - const typename MachineModuleInfoTarget::AuthStubPairTy &RHS) { - return LHS.first->getName() < RHS.first->getName(); - }); +MachineModuleInfoImpl::ExprStubListTy MachineModuleInfoImpl::getSortedExprStubs( + DenseMap &ExprStubs) { + MachineModuleInfoImpl::ExprStubListTy List(ExprStubs.begin(), + ExprStubs.end()); - AuthPtrStubs.clear(); - return List; -} + array_pod_sort(List.begin(), List.end(), SortAuthStubPair); -MachineModuleInfoELF::AuthStubListTy MachineModuleInfoELF::getAuthGVStubList() { - return getAuthGVStubListHelper(AuthPtrStubs); + ExprStubs.clear(); + return List; } diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 3200b5b67f60d..eefa8977bd267 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -931,13 +931,12 @@ void AArch64AsmPrinter::emitHwasanMemaccessSymbols(Module &M) { } } -template -static void emitAuthenticatedPointer( - MCStreamer &OutStreamer, MCSymbol *StubLabel, - const typename MachineModuleInfoTarget::AuthStubInfo &StubInfo) { +static void emitAuthenticatedPointer(MCStreamer &OutStreamer, + MCSymbol *StubLabel, + const MCExpr *StubAuthPtrRef) { // sym$auth_ptr$key$disc: OutStreamer.emitLabel(StubLabel); - OutStreamer.emitValue(StubInfo.AuthPtrRef, /*size=*/8); + OutStreamer.emitValue(StubAuthPtrRef, /*size=*/8); } void AArch64AsmPrinter::emitEndOfAsmFile(Module &M) { @@ -960,8 +959,7 @@ void AArch64AsmPrinter::emitEndOfAsmFile(Module &M) { emitAlignment(Align(8)); for (auto &Stub : Stubs) - emitAuthenticatedPointer( - *OutStreamer, Stub.first, Stub.second); + emitAuthenticatedPointer(*OutStreamer, Stub.first, Stub.second); OutStreamer->addBlankLine(); } @@ -986,8 +984,7 @@ void AArch64AsmPrinter::emitEndOfAsmFile(Module &M) { emitAlignment(Align(8)); for (const auto &Stub : Stubs) - emitAuthenticatedPointer(*OutStreamer, Stub.first, - Stub.second); + emitAuthenticatedPointer(*OutStreamer, Stub.first, Stub.second); OutStreamer->addBlankLine(); } @@ -2113,8 +2110,8 @@ void AArch64AsmPrinter::LowerLOADauthptrstatic(const MachineInstr &MI) { assert(GAOp.getOffset() == 0 && "non-zero offset for $auth_ptr$ stub slots is not supported"); const MCSymbol *GASym = TM.getSymbol(GAOp.getGlobal()); - AuthPtrStubSym = TLOF.getAuthPtrSlotSymbol(TM, &MF->getMMI(), GASym, - /*RawSymOffset=*/0, Key, Disc); + AuthPtrStubSym = + TLOF.getAuthPtrSlotSymbol(TM, &MF->getMMI(), GASym, Key, Disc); } diff --git a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp index 8c686849ae080..b1af8bd491ae4 100644 --- a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp +++ b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp @@ -90,11 +90,10 @@ static MCSymbol *getAuthGVStub(const GlobalVariable *GVB, AsmPrinter &Printer) { MachineModuleInfoMachO &MMIMachO = Printer.MMI->getObjFileInfo(); - MachineModuleInfoMachO::AuthStubInfo &StubInfo = - MMIMachO.getAuthPtrStubEntry(MCSym); + const MCExpr *&StubAuthPtrRef = MMIMachO.getAuthPtrStubEntry(MCSym); - if (!StubInfo.AuthPtrRef) - StubInfo.AuthPtrRef = Printer.lowerPtrAuthGlobalConstant(PAI); + if (!StubAuthPtrRef) + StubAuthPtrRef = Printer.lowerPtrAuthGlobalConstant(PAI); return MCSym; } diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp index 16635e6c03a15..d916f644de9b5 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.cpp @@ -97,44 +97,24 @@ template static MCSymbol *getAuthPtrSlotSymbolHelper( MCContext &Ctx, const TargetMachine &TM, MachineModuleInfo *MMI, MachineModuleInfoTarget &TargetMMI, const MCSymbol *RawSym, - int64_t RawSymOffset, AArch64PACKey::ID Key, uint16_t Discriminator, - bool HasAddressDiversity) { + AArch64PACKey::ID Key, uint16_t Discriminator) { const DataLayout &DL = MMI->getModule()->getDataLayout(); - // Mangle the offset into the stub name. Avoid '-' in symbols and extra logic - // by using the uint64_t representation for negative numbers. - uint64_t OffsetV = RawSymOffset; - std::string Suffix = "$"; - if (OffsetV) - Suffix += utostr(OffsetV) + "$"; - Suffix += (Twine("auth_ptr$") + AArch64PACKeyIDToString(Key) + "$" + - utostr(Discriminator)) - .str(); + MCSymbol *StubSym = Ctx.getOrCreateSymbol( + DL.getLinkerPrivateGlobalPrefix() + RawSym->getName() + + Twine("$auth_ptr$") + AArch64PACKeyIDToString(Key) + Twine('$') + + Twine(Discriminator)); - if (HasAddressDiversity) - Suffix += "$addr"; + const MCExpr *&StubAuthPtrRef = TargetMMI.getAuthPtrStubEntry(StubSym); - MCSymbol *StubSym = Ctx.getOrCreateSymbol(DL.getLinkerPrivateGlobalPrefix() + - RawSym->getName() + Suffix); - - typename MachineModuleInfoTarget::AuthStubInfo &StubInfo = - TargetMMI.getAuthPtrStubEntry(StubSym); - - if (StubInfo.AuthPtrRef) + if (StubAuthPtrRef) return StubSym; const MCExpr *Sym = MCSymbolRefExpr::create(RawSym, Ctx); - // If there is an addend, turn that into the appropriate MCExpr. - if (RawSymOffset > 0) - Sym = MCBinaryExpr::createAdd( - Sym, MCConstantExpr::create(RawSymOffset, Ctx), Ctx); - else if (RawSymOffset < 0) - Sym = MCBinaryExpr::createSub( - Sym, MCConstantExpr::create(-RawSymOffset, Ctx), Ctx); - - StubInfo.AuthPtrRef = AArch64AuthMCExpr::create(Sym, Discriminator, Key, - HasAddressDiversity, Ctx); + StubAuthPtrRef = + AArch64AuthMCExpr::create(Sym, Discriminator, Key, + /*HasAddressDiversity=*/false, Ctx); return StubSym; } @@ -142,19 +122,14 @@ MCSymbol *AArch64_ELFTargetObjectFile::getAuthPtrSlotSymbol( const TargetMachine &TM, MachineModuleInfo *MMI, const MCSymbol *RawSym, AArch64PACKey::ID Key, uint16_t Discriminator) const { auto &ELFMMI = MMI->getObjFileInfo(); - // ELF only uses the auth_ptr stub for extern_weak, which disallows offsets - // and doesn't need address diversity. - return getAuthPtrSlotSymbolHelper(getContext(), TM, MMI, ELFMMI, RawSym, - /*RawSymOffset=*/0, Key, Discriminator, - /*HasAddressDiversity=*/false); + return getAuthPtrSlotSymbolHelper(getContext(), TM, MMI, ELFMMI, RawSym, Key, + Discriminator); } MCSymbol *AArch64_MachoTargetObjectFile::getAuthPtrSlotSymbol( const TargetMachine &TM, MachineModuleInfo *MMI, const MCSymbol *RawSym, - int64_t RawSymOffset, AArch64PACKey::ID Key, uint16_t Discriminator, - bool HasAddressDiversity) const { + AArch64PACKey::ID Key, uint16_t Discriminator) const { auto &MachOMMI = MMI->getObjFileInfo(); return getAuthPtrSlotSymbolHelper(getContext(), TM, MMI, MachOMMI, RawSym, - RawSymOffset, Key, Discriminator, - HasAddressDiversity); + Key, Discriminator); } diff --git a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h index 2e0ab7a3325e5..2ef8bda2988d4 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h +++ b/llvm/lib/Target/AArch64/AArch64TargetObjectFile.h @@ -63,9 +63,8 @@ class AArch64_MachoTargetObjectFile : public TargetLoweringObjectFileMachO { MCSymbol *getAuthPtrSlotSymbol(const TargetMachine &TM, MachineModuleInfo *MMI, const MCSymbol *RawSym, - int64_t RawSymOffset, AArch64PACKey::ID Key, - uint16_t Discriminator, - bool HasAddressDiversity = false) const; + AArch64PACKey::ID Key, + uint16_t Discriminator) const; }; /// This implementation is used for AArch64 COFF targets. From 97415e5720ac26e9c7f85f677d1559615e2ba617 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 1 Jul 2024 17:18:02 -0700 Subject: [PATCH 11/58] [AArch64][PAC] Lower ptrauth constants in code for MachO. --- .../Target/AArch64/AArch64ISelLowering.cpp | 3 +- .../GISel/AArch64InstructionSelector.cpp | 4 +- .../GlobalISel/ptrauth-constant-in-code.ll | 330 ++++++++++++------ 3 files changed, 234 insertions(+), 103 deletions(-) diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index a98499d1af3d9..8b38a55b942d9 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -9563,8 +9563,7 @@ SDValue AArch64TargetLowering::LowerGlobalTLSAddress(SDValue Op, // Load a signed pointer for symbol 'sym' from a stub slot named // 'sym$auth_ptr$key$disc' filled by dynamic linker during relocation // resolving. This usually lowers to adrp+ldr, but also emits an entry into -// .data with an -// @AUTH relocation. See LowerLOADauthptrstatic. +// .data with an @AUTH relocation. See LowerLOADauthptrstatic. // // All 3 are pseudos that are expand late to longer sequences: this lets us // provide integrity guarantees on the to-be-signed intermediate values. diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp index 715574c989e22..d92a8ee6c306c 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp @@ -6702,8 +6702,8 @@ bool AArch64InstructionSelector::selectPtrAuthGlobalValue( "constant discriminator in ptrauth global out of range [0, 0xffff]"); // Choosing between 3 lowering alternatives is target-specific. - if (!STI.isTargetELF()) - report_fatal_error("ptrauth global lowering is only implemented for ELF"); + if (!STI.isTargetELF() && !STI.isTargetMachO()) + report_fatal_error("ptrauth global lowering only supported on MachO/ELF"); if (!MRI.hasOneDef(Addr)) return false; diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/ptrauth-constant-in-code.ll b/llvm/test/CodeGen/AArch64/GlobalISel/ptrauth-constant-in-code.ll index 7b85b12bb8952..094b85431f404 100644 --- a/llvm/test/CodeGen/AArch64/GlobalISel/ptrauth-constant-in-code.ll +++ b/llvm/test/CodeGen/AArch64/GlobalISel/ptrauth-constant-in-code.ll @@ -5,6 +5,9 @@ ; RUN: not --crash llc < err1.ll -mtriple aarch64-elf -mattr=+pauth \ ; RUN: -global-isel=1 -verify-machineinstrs -global-isel-abort=1 2>&1 | \ ; RUN: FileCheck --check-prefix=ERR1 %s +; RUN: not --crash llc < err1.ll -mtriple arm64-apple-ios -mattr=+pauth \ +; RUN: -global-isel=1 -verify-machineinstrs -global-isel-abort=1 2>&1 | \ +; RUN: FileCheck --check-prefix=ERR1 %s @g = external global i32 @@ -18,6 +21,9 @@ define ptr @foo() { ; RUN: not --crash llc < err2.ll -mtriple aarch64-elf -mattr=+pauth \ ; RUN: -global-isel=1 -verify-machineinstrs -global-isel-abort=1 2>&1 | \ ; RUN: FileCheck --check-prefix=ERR2 %s +; RUN: not --crash llc < err2.ll -mtriple arm64-apple-ios -mattr=+pauth \ +; RUN: -global-isel=1 -verify-machineinstrs -global-isel-abort=1 2>&1 | \ +; RUN: FileCheck --check-prefix=ERR2 %s @g = external global i32 @@ -31,6 +37,9 @@ define ptr @foo() { ; RUN: not --crash llc < err3.ll -mtriple aarch64-elf -mattr=+pauth \ ; RUN: -global-isel=1 -verify-machineinstrs -global-isel-abort=1 2>&1 | \ ; RUN: FileCheck --check-prefix=ERR3 %s +; RUN: not --crash llc < err3.ll -mtriple arm64-apple-ios -mattr=+pauth \ +; RUN: -global-isel=1 -verify-machineinstrs -global-isel-abort=1 2>&1 | \ +; RUN: FileCheck --check-prefix=ERR3 %s @g_weak = extern_weak global i32 @@ -44,6 +53,9 @@ define ptr @foo() { ; RUN: not --crash llc < err4.ll -mtriple aarch64-elf -mattr=+pauth \ ; RUN: -global-isel=1 -verify-machineinstrs -global-isel-abort=1 2>&1 | \ ; RUN: FileCheck --check-prefix=ERR4 %s +; RUN: not --crash llc < err4.ll -mtriple arm64-apple-ios -mattr=+pauth \ +; RUN: -global-isel=1 -verify-machineinstrs -global-isel-abort=1 2>&1 | \ +; RUN: FileCheck --check-prefix=ERR4 %s @g_weak = extern_weak global i32 @g_weak.ref.da.42.addr = dso_local constant ptr ptrauth (ptr @g_weak, i32 2, i64 42, ptr @g_weak.ref.da.42.addr) @@ -55,21 +67,28 @@ define ptr @foo() { ;--- err5.ll -; RUN: not --crash llc < err5.ll -mtriple arm64-apple-darwin -mattr=+pauth \ +; RUN: not --crash llc < err5.ll -mtriple aarch64-windows -mattr=+pauth \ ; RUN: -global-isel=1 -verify-machineinstrs -global-isel-abort=1 2>&1 | \ ; RUN: FileCheck --check-prefix=ERR5 %s @g = external global i32 define ptr @foo() { -; ERR5: LLVM ERROR: ptrauth global lowering is only implemented for ELF +; ERR5: LLVM ERROR: ptrauth global lowering only supported on MachO/ELF ret ptr ptrauth (ptr @g, i32 0) } ;--- ok.ll ; RUN: llc < ok.ll -mtriple aarch64-elf -mattr=+pauth -global-isel=1 \ -; RUN: -verify-machineinstrs -global-isel-abort=1 | FileCheck %s +; RUN: -verify-machineinstrs -global-isel-abort=1 | \ +; RUN: FileCheck %s --check-prefix=ELF +; RUN: llc < ok.ll -mtriple aarch64-elf -mattr=+pauth -global-isel=1 \ +; RUN: -verify-machineinstrs -global-isel-abort=1 -filetype=obj + +; RUN: llc < ok.ll -mtriple arm64-apple-ios -mattr=+pauth -global-isel=1 \ +; RUN: -verify-machineinstrs -global-isel-abort=1 | \ +; RUN: FileCheck %s --check-prefix=MACHO ; RUN: llc < ok.ll -mtriple aarch64-elf -mattr=+pauth -global-isel=1 \ ; RUN: -verify-machineinstrs -global-isel-abort=1 -filetype=obj @@ -78,100 +97,171 @@ define ptr @foo() { @g_strong_def = dso_local constant i32 42 define ptr @test_global_zero_disc() { -; CHECK-LABEL: test_global_zero_disc: -; CHECK: // %bb.0: -; CHECK-NEXT: adrp x16, :got:g -; CHECK-NEXT: ldr x16, [x16, :got_lo12:g] -; CHECK-NEXT: paciza x16 -; CHECK-NEXT: mov x0, x16 -; CHECK-NEXT: ret +; ELF-LABEL: test_global_zero_disc: +; ELF: // %bb.0: +; ELF-NEXT: adrp x16, :got:g +; ELF-NEXT: ldr x16, [x16, :got_lo12:g] +; ELF-NEXT: paciza x16 +; ELF-NEXT: mov x0, x16 +; ELF-NEXT: ret + +; MACHO-LABEL: _test_global_zero_disc: +; MACHO: ; %bb.0: +; MACHO-NEXT: adrp x16, _g@GOTPAGE +; MACHO-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; MACHO-NEXT: paciza x16 +; MACHO-NEXT: mov x0, x16 +; MACHO-NEXT: ret ret ptr ptrauth (ptr @g, i32 0) } define ptr @test_global_offset_zero_disc() { -; CHECK-LABEL: test_global_offset_zero_disc: -; CHECK: // %bb.0: -; CHECK-NEXT: adrp x16, :got:g -; CHECK-NEXT: ldr x16, [x16, :got_lo12:g] -; CHECK-NEXT: add x16, x16, #16 -; CHECK-NEXT: pacdza x16 -; CHECK-NEXT: mov x0, x16 -; CHECK-NEXT: ret +; ELF-LABEL: test_global_offset_zero_disc: +; ELF: // %bb.0: +; ELF-NEXT: adrp x16, :got:g +; ELF-NEXT: ldr x16, [x16, :got_lo12:g] +; ELF-NEXT: add x16, x16, #16 +; ELF-NEXT: pacdza x16 +; ELF-NEXT: mov x0, x16 +; ELF-NEXT: ret + +; MACHO-LABEL: _test_global_offset_zero_disc: +; MACHO: ; %bb.0: +; MACHO-NEXT: adrp x16, _g@GOTPAGE +; MACHO-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; MACHO-NEXT: add x16, x16, #16 +; MACHO-NEXT: pacdza x16 +; MACHO-NEXT: mov x0, x16 +; MACHO-NEXT: ret ret ptr ptrauth (ptr getelementptr (i8, ptr @g, i64 16), i32 2) } define ptr @test_global_neg_offset_zero_disc() { -; CHECK-LABEL: test_global_neg_offset_zero_disc: -; CHECK: // %bb.0: -; CHECK-NEXT: adrp x16, :got:g -; CHECK-NEXT: ldr x16, [x16, :got_lo12:g] -; CHECK-NEXT: sub x16, x16, #576 -; CHECK-NEXT: sub x16, x16, #30, lsl #12 -; CHECK-NEXT: pacdza x16 -; CHECK-NEXT: mov x0, x16 -; CHECK-NEXT: ret +; ELF-LABEL: test_global_neg_offset_zero_disc: +; ELF: // %bb.0: +; ELF-NEXT: adrp x16, :got:g +; ELF-NEXT: ldr x16, [x16, :got_lo12:g] +; ELF-NEXT: sub x16, x16, #576 +; ELF-NEXT: sub x16, x16, #30, lsl #12 +; ELF-NEXT: pacdza x16 +; ELF-NEXT: mov x0, x16 +; ELF-NEXT: ret + +; MACHO-LABEL: _test_global_neg_offset_zero_disc: +; MACHO: ; %bb.0: +; MACHO-NEXT: adrp x16, _g@GOTPAGE +; MACHO-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; MACHO-NEXT: sub x16, x16, #576 +; MACHO-NEXT: sub x16, x16, #30, lsl #12 +; MACHO-NEXT: pacdza x16 +; MACHO-NEXT: mov x0, x16 +; MACHO-NEXT: ret ret ptr ptrauth (ptr getelementptr (i8, ptr @g, i64 -123456), i32 2) } define ptr @test_global_big_offset_zero_disc() { -; CHECK-LABEL: test_global_big_offset_zero_disc: -; CHECK: // %bb.0: -; CHECK-NEXT: adrp x16, :got:g -; CHECK-NEXT: ldr x16, [x16, :got_lo12:g] -; CHECK-NEXT: mov x17, #1 -; CHECK-NEXT: movk x17, #32769, lsl #16 -; CHECK-NEXT: add x16, x16, x17 -; CHECK-NEXT: pacdza x16 -; CHECK-NEXT: mov x0, x16 -; CHECK-NEXT: ret +; ELF-LABEL: test_global_big_offset_zero_disc: +; ELF: // %bb.0: +; ELF-NEXT: adrp x16, :got:g +; ELF-NEXT: ldr x16, [x16, :got_lo12:g] +; ELF-NEXT: mov x17, #1 +; ELF-NEXT: movk x17, #32769, lsl #16 +; ELF-NEXT: add x16, x16, x17 +; ELF-NEXT: pacdza x16 +; ELF-NEXT: mov x0, x16 +; ELF-NEXT: ret + +; MACHO-LABEL: _test_global_big_offset_zero_disc: +; MACHO: ; %bb.0: +; MACHO-NEXT: adrp x16, _g@GOTPAGE +; MACHO-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; MACHO-NEXT: mov x17, #1 +; MACHO-NEXT: movk x17, #32769, lsl #16 +; MACHO-NEXT: add x16, x16, x17 +; MACHO-NEXT: pacdza x16 +; MACHO-NEXT: mov x0, x16 +; MACHO-NEXT: ret ret ptr ptrauth (ptr getelementptr (i8, ptr @g, i64 add (i64 2147483648, i64 65537)), i32 2) } define ptr @test_global_big_neg_offset_zero_disc() { -; CHECK-LABEL: test_global_big_neg_offset_zero_disc: -; CHECK: // %bb.0: -; CHECK-NEXT: adrp x16, :got:g -; CHECK-NEXT: ldr x16, [x16, :got_lo12:g] -; CHECK-NEXT: mov x17, #-52501 -; CHECK-NEXT: movk x17, #63652, lsl #16 -; CHECK-NEXT: add x16, x16, x17 -; CHECK-NEXT: pacdza x16 -; CHECK-NEXT: mov x0, x16 -; CHECK-NEXT: ret +; ELF-LABEL: test_global_big_neg_offset_zero_disc: +; ELF: // %bb.0: +; ELF-NEXT: adrp x16, :got:g +; ELF-NEXT: ldr x16, [x16, :got_lo12:g] +; ELF-NEXT: mov x17, #-52501 +; ELF-NEXT: movk x17, #63652, lsl #16 +; ELF-NEXT: add x16, x16, x17 +; ELF-NEXT: pacdza x16 +; ELF-NEXT: mov x0, x16 +; ELF-NEXT: ret + +; MACHO-LABEL: _test_global_big_neg_offset_zero_disc: +; MACHO: ; %bb.0: +; MACHO-NEXT: adrp x16, _g@GOTPAGE +; MACHO-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; MACHO-NEXT: mov x17, #-52501 +; MACHO-NEXT: movk x17, #63652, lsl #16 +; MACHO-NEXT: add x16, x16, x17 +; MACHO-NEXT: pacdza x16 +; MACHO-NEXT: mov x0, x16 +; MACHO-NEXT: ret ret ptr ptrauth (ptr getelementptr (i8, ptr @g, i64 -123456789), i32 2) } define ptr @test_global_huge_neg_offset_zero_disc() { -; CHECK-LABEL: test_global_huge_neg_offset_zero_disc: -; CHECK: // %bb.0: -; CHECK-NEXT: adrp x16, :got:g -; CHECK-NEXT: ldr x16, [x16, :got_lo12:g] -; CHECK-NEXT: mov x17, #-65536 -; CHECK-NEXT: movk x17, #0, lsl #16 -; CHECK-NEXT: movk x17, #0, lsl #32 -; CHECK-NEXT: movk x17, #32768, lsl #48 -; CHECK-NEXT: add x16, x16, x17 -; CHECK-NEXT: pacdza x16 -; CHECK-NEXT: mov x0, x16 -; CHECK-NEXT: ret +; ELF-LABEL: test_global_huge_neg_offset_zero_disc: +; ELF: // %bb.0: +; ELF-NEXT: adrp x16, :got:g +; ELF-NEXT: ldr x16, [x16, :got_lo12:g] +; ELF-NEXT: mov x17, #-65536 +; ELF-NEXT: movk x17, #0, lsl #16 +; ELF-NEXT: movk x17, #0, lsl #32 +; ELF-NEXT: movk x17, #32768, lsl #48 +; ELF-NEXT: add x16, x16, x17 +; ELF-NEXT: pacdza x16 +; ELF-NEXT: mov x0, x16 +; ELF-NEXT: ret + +; MACHO-LABEL: _test_global_huge_neg_offset_zero_disc: +; MACHO: ; %bb.0: +; MACHO-NEXT: adrp x16, _g@GOTPAGE +; MACHO-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; MACHO-NEXT: mov x17, #-65536 +; MACHO-NEXT: movk x17, #0, lsl #16 +; MACHO-NEXT: movk x17, #0, lsl #32 +; MACHO-NEXT: movk x17, #32768, lsl #48 +; MACHO-NEXT: add x16, x16, x17 +; MACHO-NEXT: pacdza x16 +; MACHO-NEXT: mov x0, x16 +; MACHO-NEXT: ret ret ptr ptrauth (ptr getelementptr (i8, ptr @g, i64 -9223372036854775808), i32 2) } define ptr @test_global_disc() { -; CHECK-LABEL: test_global_disc: -; CHECK: // %bb.0: -; CHECK-NEXT: adrp x16, :got:g -; CHECK-NEXT: ldr x16, [x16, :got_lo12:g] -; CHECK-NEXT: mov x17, #42 // =0x2a -; CHECK-NEXT: pacia x16, x17 -; CHECK-NEXT: mov x0, x16 -; CHECK-NEXT: ret +; ELF-LABEL: test_global_disc: +; ELF: // %bb.0: +; ELF-NEXT: adrp x16, :got:g +; ELF-NEXT: ldr x16, [x16, :got_lo12:g] +; ELF-NEXT: mov x17, #42 // =0x2a +; ELF-NEXT: pacia x16, x17 +; ELF-NEXT: mov x0, x16 +; ELF-NEXT: ret + +; MACHO-LABEL: _test_global_disc: +; MACHO: ; %bb.0: +; MACHO-NEXT: adrp x16, _g@GOTPAGE +; MACHO-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; MACHO-NEXT: mov x17, #42 ; =0x2a +; MACHO-NEXT: pacia x16, x17 +; MACHO-NEXT: mov x0, x16 +; MACHO-NEXT: ret ret ptr ptrauth (ptr @g, i32 0, i64 42) } @@ -179,29 +269,52 @@ define ptr @test_global_disc() { @g.ref.da.42.addr = dso_local constant ptr ptrauth (ptr @g, i32 2, i64 42, ptr @g.ref.da.42.addr) define ptr @test_global_addr_disc() { -; CHECK-LABEL: test_global_addr_disc: -; CHECK: // %bb.0: -; CHECK-NEXT: adrp x8, g.ref.da.42.addr -; CHECK-NEXT: add x8, x8, :lo12:g.ref.da.42.addr -; CHECK-NEXT: adrp x16, :got:g -; CHECK-NEXT: ldr x16, [x16, :got_lo12:g] -; CHECK-NEXT: mov x17, x8 -; CHECK-NEXT: movk x17, #42, lsl #48 -; CHECK-NEXT: pacda x16, x17 -; CHECK-NEXT: mov x0, x16 -; CHECK-NEXT: ret +; ELF-LABEL: test_global_addr_disc: +; ELF: // %bb.0: +; ELF-NEXT: adrp x8, g.ref.da.42.addr +; ELF-NEXT: add x8, x8, :lo12:g.ref.da.42.addr +; ELF-NEXT: adrp x16, :got:g +; ELF-NEXT: ldr x16, [x16, :got_lo12:g] +; ELF-NEXT: mov x17, x8 +; ELF-NEXT: movk x17, #42, lsl #48 +; ELF-NEXT: pacda x16, x17 +; ELF-NEXT: mov x0, x16 +; ELF-NEXT: ret + +; MACHO-LABEL: _test_global_addr_disc: +; MACHO: ; %bb.0: +; MACHO-NEXT: Lloh{{.*}}: +; MACHO-NEXT: adrp x8, _g.ref.da.42.addr@PAGE +; MACHO-NEXT: Lloh{{.*}}: +; MACHO-NEXT: add x8, x8, _g.ref.da.42.addr@PAGEOFF +; MACHO-NEXT: adrp x16, _g@GOTPAGE +; MACHO-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; MACHO-NEXT: mov x17, x8 +; MACHO-NEXT: movk x17, #42, lsl #48 +; MACHO-NEXT: pacda x16, x17 +; MACHO-NEXT: mov x0, x16 +; MACHO-NEXT: ret ret ptr ptrauth (ptr @g, i32 2, i64 42, ptr @g.ref.da.42.addr) } define ptr @test_global_process_specific() { -; CHECK-LABEL: test_global_process_specific: -; CHECK: // %bb.0: -; CHECK-NEXT: adrp x16, :got:g -; CHECK-NEXT: ldr x16, [x16, :got_lo12:g] -; CHECK-NEXT: pacizb x16 -; CHECK-NEXT: mov x0, x16 -; CHECK-NEXT: ret +; ELF-LABEL: test_global_process_specific: +; ELF: // %bb.0: +; ELF-NEXT: adrp x16, :got:g +; ELF-NEXT: ldr x16, [x16, :got_lo12:g] +; ELF-NEXT: pacizb x16 +; ELF-NEXT: mov x0, x16 +; ELF-NEXT: ret + +; MACHO-LABEL: _test_global_process_specific: +; MACHO: ; %bb.0: +; MACHO-NEXT: adrp x16, _g@GOTPAGE +; MACHO-NEXT: ldr x16, [x16, _g@GOTPAGEOFF] +; MACHO-NEXT: pacizb x16 +; MACHO-NEXT: mov x0, x16 +; MACHO-NEXT: ret + ret ptr ptrauth (ptr @g, i32 1) } @@ -210,26 +323,45 @@ define ptr @test_global_process_specific() { ; whatever null-check follows in user code. define ptr @test_global_weak() { -; CHECK-LABEL: test_global_weak: -; CHECK: // %bb.0: -; CHECK-NEXT: adrp x0, g_weak$auth_ptr$ia$42 -; CHECK-NEXT: ldr x0, [x0, :lo12:g_weak$auth_ptr$ia$42] -; CHECK-NEXT: ret +; ELF-LABEL: test_global_weak: +; ELF: // %bb.0: +; ELF-NEXT: adrp x0, g_weak$auth_ptr$ia$42 +; ELF-NEXT: ldr x0, [x0, :lo12:g_weak$auth_ptr$ia$42] +; ELF-NEXT: ret + +; MACHO-LABEL: _test_global_weak: +; MACHO: ; %bb.0: +; MACHO-NEXT: adrp x0, l_g_weak$auth_ptr$ia$42@PAGE +; MACHO-NEXT: ldr x0, [x0, l_g_weak$auth_ptr$ia$42@PAGEOFF] +; MACHO-NEXT: ret + ret ptr ptrauth (ptr @g_weak, i32 0, i64 42) } ; Non-external symbols don't need to be accessed through the GOT. define ptr @test_global_strong_def() { -; CHECK-LABEL: test_global_strong_def: -; CHECK: // %bb.0: -; CHECK-NEXT: adrp x16, g_strong_def -; CHECK-NEXT: add x16, x16, :lo12:g_strong_def -; CHECK-NEXT: pacdza x16 -; CHECK-NEXT: mov x0, x16 -; CHECK-NEXT: ret +; ELF-LABEL: test_global_strong_def: +; ELF: // %bb.0: +; ELF-NEXT: adrp x16, g_strong_def +; ELF-NEXT: add x16, x16, :lo12:g_strong_def +; ELF-NEXT: pacdza x16 +; ELF-NEXT: mov x0, x16 +; ELF-NEXT: ret + +; MACHO-LABEL: _test_global_strong_def: +; MACHO: ; %bb.0: +; MACHO-NEXT: adrp x16, _g_strong_def@PAGE +; MACHO-NEXT: add x16, x16, _g_strong_def@PAGEOFF +; MACHO-NEXT: pacdza x16 +; MACHO-NEXT: mov x0, x16 +; MACHO-NEXT: ret + ret ptr ptrauth (ptr @g_strong_def, i32 2) } -; CHECK-LABEL: g_weak$auth_ptr$ia$42: -; CHECK-NEXT: .xword g_weak@AUTH(ia,42) +; ELF-LABEL: g_weak$auth_ptr$ia$42: +; ELF-NEXT: .xword g_weak@AUTH(ia,42) + +; MACHO-LABEL: l_g_weak$auth_ptr$ia$42: +; MACHO-NEXT: .quad _g_weak@AUTH(ia,42) From 20b797307fa7efc6d0aef28c6b0411bc091ab9bf Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 1 Jul 2024 17:23:06 -0700 Subject: [PATCH 12/58] [AArch64][PAC] Lower direct authenticated calls to ptrauth constants. --- llvm/lib/CodeGen/GlobalISel/CallLowering.cpp | 8 ++ llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 23 +-- .../SelectionDAG/SelectionDAGBuilder.cpp | 10 +- llvm/test/CodeGen/AArch64/ptrauth-call.ll | 134 ++++++++++++++++++ ...=> ptrauth-direct-call-wrapper-globals.ll} | 0 ...e.ll => ptrauth-invoke-wrapper-globals.ll} | 0 llvm/test/CodeGen/AArch64/ptrauth-invoke.ll | 85 +++++++++++ 7 files changed, 251 insertions(+), 9 deletions(-) rename llvm/test/CodeGen/AArch64/{arm64e-ptrauth-direct-call.ll => ptrauth-direct-call-wrapper-globals.ll} (100%) rename llvm/test/CodeGen/AArch64/{arm64e-ptrauth-invoke.ll => ptrauth-invoke-wrapper-globals.ll} (100%) diff --git a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp index ee94c0bfbf9d0..d16585b5650a7 100644 --- a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp @@ -149,6 +149,14 @@ bool CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, const CallBase &CB, // Try looking through a bitcast from one function type to another. // Commonly happens with calls to objc_msgSend(). const Value *CalleeV = CB.getCalledOperand()->stripPointerCasts(); + + // If IRTranslator chose to drop the ptrauth info, we can turn this into + // a direct call. + if (!PAI && CB.countOperandBundlesOfType(LLVMContext::OB_ptrauth)) { + CalleeV = cast(CalleeV)->getPointer(); + assert(isa(CalleeV)); + } + if (const Function *F = dyn_cast(CalleeV)) { if (F->hasFnAttribute(Attribute::NonLazyBind)) { LLT Ty = getLLTForType(*F->getType(), DL); diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp index 14e6b79c583fe..8cfbc11b735f5 100644 --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -2649,17 +2649,24 @@ bool IRTranslator::translateCallBase(const CallBase &CB, } std::optional PAI; - if (CB.countOperandBundlesOfType(LLVMContext::OB_ptrauth)) { + if (auto Bundle = CB.getOperandBundle(LLVMContext::OB_ptrauth)) { // Functions should never be ptrauth-called directly. assert(!CB.getCalledFunction() && "invalid direct ptrauth call"); - auto PAB = CB.getOperandBundle("ptrauth"); - const Value *Key = PAB->Inputs[0]; - const Value *Discriminator = PAB->Inputs[1]; - - Register DiscReg = getOrCreateVReg(*Discriminator); - PAI = CallLowering::PtrAuthInfo{cast(Key)->getZExtValue(), - DiscReg}; + const Value *Key = Bundle->Inputs[0]; + const Value *Discriminator = Bundle->Inputs[1]; + + // Look through ptrauth constants to try to eliminate the matching bundle + // and turn this into a direct call with no ptrauth. + // CallLowering will use the raw pointer if it doesn't find the PAI. + auto *CalleeCPA = dyn_cast(CB.getCalledOperand()); + if (!CalleeCPA || !isa(CalleeCPA->getPointer()) || + !CalleeCPA->isKnownCompatibleWith(Key, Discriminator, *DL)) { + // If we can't make it direct, package the bundle into PAI. + Register DiscReg = getOrCreateVReg(*Discriminator); + PAI = CallLowering::PtrAuthInfo{cast(Key)->getZExtValue(), + DiscReg}; + } } Register ConvergenceCtrlToken = 0; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 703524c0cfc30..6369cddeb3074 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -9385,7 +9385,7 @@ void SelectionDAGBuilder::LowerCallSiteWithPtrAuthBundle( assert(Discriminator->getType()->isIntegerTy(64) && "Invalid ptrauth discriminator"); - // Look through ptrauth globals to find the raw callee. + // Look through ptrauth constants to find the raw callee. // Do a direct unauthenticated call if we found it and everything matches. if (auto CalleePAI = GlobalPtrAuthInfo::analyze(CalleeV)) { // FIXME: bring back a static diagnostic when we can guarantee the mismatch @@ -9396,6 +9396,14 @@ void SelectionDAGBuilder::LowerCallSiteWithPtrAuthBundle( } } + // Look through ptrauth globals to find the raw callee. + // Do a direct unauthenticated call if we found it and everything matches. + if (auto *CalleeCPA = dyn_cast(CalleeV)) + if (CalleeCPA->isKnownCompatibleWith(Key, Discriminator, + DAG.getDataLayout())) + return LowerCallTo(CB, getValue(CalleeCPA->getPointer()), CB.isTailCall(), + CB.isMustTailCall(), EHPadBB); + // Functions should never be ptrauth-called directly. assert(!isa(CalleeV) && "invalid direct ptrauth call"); diff --git a/llvm/test/CodeGen/AArch64/ptrauth-call.ll b/llvm/test/CodeGen/AArch64/ptrauth-call.ll index b1249891b04b4..8c69c40a46726 100644 --- a/llvm/test/CodeGen/AArch64/ptrauth-call.ll +++ b/llvm/test/CodeGen/AArch64/ptrauth-call.ll @@ -269,4 +269,138 @@ define i32 @test_tailcall_ib_arg_ind(i32 ()** %arg0, i64 %arg1) #0 { ret i32 %tmp1 } +; Test direct calls + +define i32 @test_direct_call() #0 { +; DARWIN-LABEL: test_direct_call: +; DARWIN-NEXT: stp x29, x30, [sp, #-16]! +; DARWIN-NEXT: bl _f +; DARWIN-NEXT: ldp x29, x30, [sp], #16 +; DARWIN-NEXT: ret +; +; ELF-LABEL: test_direct_call: +; ELF-NEXT: str x30, [sp, #-16]! +; ELF-NEXT: bl f +; ELF-NEXT: ldr x30, [sp], #16 +; ELF-NEXT: ret + %tmp0 = call i32 ptrauth(ptr @f, i32 0, i64 42)() [ "ptrauth"(i32 0, i64 42) ] + ret i32 %tmp0 +} + +define i32 @test_direct_tailcall(ptr %arg0) #0 { +; DARWIN-LABEL: test_direct_tailcall: +; DARWIN: b _f +; +; ELF-LABEL: test_direct_tailcall: +; ELF-NEXT: b f + %tmp0 = tail call i32 ptrauth(ptr @f, i32 0, i64 42)() [ "ptrauth"(i32 0, i64 42) ] + ret i32 %tmp0 +} + +define i32 @test_direct_call_mismatch() #0 { +; DARWIN-LABEL: test_direct_call_mismatch: +; DARWIN-NEXT: stp x29, x30, [sp, #-16]! +; DARWIN-NEXT: adrp x16, _f@GOTPAGE +; DARWIN-NEXT: ldr x16, [x16, _f@GOTPAGEOFF] +; DARWIN-NEXT: mov x17, #42 +; DARWIN-NEXT: pacia x16, x17 +; DARWIN-NEXT: mov x8, x16 +; DARWIN-NEXT: mov x17, #42 +; DARWIN-NEXT: blrab x8, x17 +; DARWIN-NEXT: ldp x29, x30, [sp], #16 +; DARWIN-NEXT: ret +; +; ELF-LABEL: test_direct_call_mismatch: +; ELF-NEXT: str x30, [sp, #-16]! +; ELF-NEXT: adrp x16, :got:f +; ELF-NEXT: ldr x16, [x16, :got_lo12:f] +; ELF-NEXT: mov x17, #42 +; ELF-NEXT: pacia x16, x17 +; ELF-NEXT: mov x8, x16 +; ELF-NEXT: mov x17, #42 +; ELF-NEXT: blrab x8, x17 +; ELF-NEXT: ldr x30, [sp], #16 +; ELF-NEXT: ret + %tmp0 = call i32 ptrauth(ptr @f, i32 0, i64 42)() [ "ptrauth"(i32 1, i64 42) ] + ret i32 %tmp0 +} + +define i32 @test_direct_call_addr() #0 { +; DARWIN-LABEL: test_direct_call_addr: +; DARWIN-NEXT: stp x29, x30, [sp, #-16]! +; DARWIN-NEXT: bl _f +; DARWIN-NEXT: ldp x29, x30, [sp], #16 +; DARWIN-NEXT: ret +; +; ELF-LABEL: test_direct_call_addr: +; ELF-NEXT: str x30, [sp, #-16]! +; ELF-NEXT: bl f +; ELF-NEXT: ldr x30, [sp], #16 +; ELF-NEXT: ret + %tmp0 = call i32 ptrauth(ptr @f, i32 1, i64 0, ptr @f.ref.ib.0.addr)() [ "ptrauth"(i32 1, i64 ptrtoint (ptr @f.ref.ib.0.addr to i64)) ] + ret i32 %tmp0 +} + +define i32 @test_direct_call_addr_blend() #0 { +; DARWIN-LABEL: test_direct_call_addr_blend: +; DARWIN-NEXT: stp x29, x30, [sp, #-16]! +; DARWIN-NEXT: bl _f +; DARWIN-NEXT: ldp x29, x30, [sp], #16 +; DARWIN-NEXT: ret +; +; ELF-LABEL: test_direct_call_addr_blend: +; ELF-NEXT: str x30, [sp, #-16]! +; ELF-NEXT: bl f +; ELF-NEXT: ldr x30, [sp], #16 +; ELF-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @f.ref.ib.42.addr to i64), i64 42) + %tmp1 = call i32 ptrauth(ptr @f, i32 1, i64 42, ptr @f.ref.ib.42.addr)() [ "ptrauth"(i32 1, i64 %tmp0) ] + ret i32 %tmp1 +} + +define i32 @test_direct_call_addr_gep_different_index_types() #0 { +; DARWIN-LABEL: test_direct_call_addr_gep_different_index_types: +; DARWIN-NEXT: stp x29, x30, [sp, #-16]! +; DARWIN-NEXT: bl _f +; DARWIN-NEXT: ldp x29, x30, [sp], #16 +; DARWIN-NEXT: ret +; +; ELF-LABEL: test_direct_call_addr_gep_different_index_types: +; ELF-NEXT: str x30, [sp, #-16]! +; ELF-NEXT: bl f +; ELF-NEXT: ldr x30, [sp], #16 +; ELF-NEXT: ret + %tmp0 = call i32 ptrauth(ptr @f, i32 1, i64 0, ptr getelementptr ({ ptr }, ptr @f_struct.ref.ib.0.addr, i64 0, i32 0))() [ "ptrauth"(i32 1, i64 ptrtoint (ptr getelementptr ({ ptr }, ptr @f_struct.ref.ib.0.addr, i32 0, i32 0) to i64)) ] + ret i32 %tmp0 +} + +define i32 @test_direct_call_addr_blend_gep_different_index_types() #0 { +; DARWIN-LABEL: test_direct_call_addr_blend_gep_different_index_types: +; DARWIN-NEXT: stp x29, x30, [sp, #-16]! +; DARWIN-NEXT: bl _f +; DARWIN-NEXT: ldp x29, x30, [sp], #16 +; DARWIN-NEXT: ret +; +; ELF-LABEL: test_direct_call_addr_blend_gep_different_index_types: +; ELF-NEXT: str x30, [sp, #-16]! +; ELF-NEXT: bl f +; ELF-NEXT: ldr x30, [sp], #16 +; ELF-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr ({ ptr }, ptr @f_struct.ref.ib.123.addr, i32 0, i32 0) to i64), i64 123) + %tmp1 = call i32 ptrauth(ptr @f, i32 1, i64 123, ptr getelementptr ({ ptr }, ptr @f_struct.ref.ib.123.addr, i64 0, i32 0))() [ "ptrauth"(i32 1, i64 %tmp0) ] + ret i32 %tmp1 +} + +attributes #0 = { nounwind } + +@f.ref.ib.42.addr = external global ptr +@f.ref.ib.0.addr = external global ptr +@f_struct.ref.ib.0.addr = external global ptr +@f_struct.ref.ib.123.addr = external global ptr + +declare void @f() + +declare i64 @llvm.ptrauth.auth(i64, i32, i64) +declare i64 @llvm.ptrauth.blend(i64, i64) + attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-direct-call.ll b/llvm/test/CodeGen/AArch64/ptrauth-direct-call-wrapper-globals.ll similarity index 100% rename from llvm/test/CodeGen/AArch64/arm64e-ptrauth-direct-call.ll rename to llvm/test/CodeGen/AArch64/ptrauth-direct-call-wrapper-globals.ll diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-invoke.ll b/llvm/test/CodeGen/AArch64/ptrauth-invoke-wrapper-globals.ll similarity index 100% rename from llvm/test/CodeGen/AArch64/arm64e-ptrauth-invoke.ll rename to llvm/test/CodeGen/AArch64/ptrauth-invoke-wrapper-globals.ll diff --git a/llvm/test/CodeGen/AArch64/ptrauth-invoke.ll b/llvm/test/CodeGen/AArch64/ptrauth-invoke.ll index fcd0ddb788336..dead82603935c 100644 --- a/llvm/test/CodeGen/AArch64/ptrauth-invoke.ll +++ b/llvm/test/CodeGen/AArch64/ptrauth-invoke.ll @@ -96,6 +96,90 @@ continuebb: ret i32 %tmp0 } +; DARWIN-LABEL: _test_invoke_ia_0_direct: +; DARWIN-NEXT: [[FNBEGIN:L.*]]: +; DARWIN-NEXT: .cfi_startproc +; DARWIN-NEXT: .cfi_personality 155, ___gxx_personality_v0 +; DARWIN-NEXT: .cfi_lsda 16, [[EXCEPT:Lexception[0-9]+]] +; DARWIN-NEXT: ; %bb.0: +; DARWIN-NEXT: stp x20, x19, [sp, #-32]! +; DARWIN-NEXT: stp x29, x30, [sp, #16] +; DARWIN-NEXT: .cfi_def_cfa_offset 32 +; DARWIN-NEXT: .cfi_offset w30, -8 +; DARWIN-NEXT: .cfi_offset w29, -16 +; DARWIN-NEXT: .cfi_offset w19, -24 +; DARWIN-NEXT: .cfi_offset w20, -32 +; DARWIN-NEXT: [[PRECALL:L.*]]: +; DARWIN-NEXT: bl _baz + +; DARWIN-SDAG-NEXT: [[POSTCALL:L.*]]: +; DARWIN-SDAG-NEXT: ; %bb.1: +; DARWIN-SDAG-NEXT: mov x19, x0 + +; DARWIN-GISEL-NEXT: mov x19, x0 +; DARWIN-GISEL-NEXT: [[POSTCALL:L.*]]: + +; DARWIN-NEXT: [[CALLBB:L.*]]: +; DARWIN-NEXT: bl _foo +; DARWIN-NEXT: mov x0, x19 +; DARWIN-NEXT: ldp x29, x30, [sp, #16] +; DARWIN-NEXT: ldp x20, x19, [sp], #32 +; DARWIN-NEXT: ret +; DARWIN-NEXT: [[LPADBB:LBB[0-9_]+]]: +; DARWIN-NEXT: [[LPAD:L.*]]: +; DARWIN-NEXT: mov w19, #-1 +; DARWIN-NEXT: b [[CALLBB]] + +; ELF-LABEL: test_invoke_ia_0_direct: +; ELF-NEXT: [[FNBEGIN:.L.*]]: +; ELF-NEXT: .cfi_startproc +; ELF-NEXT: .cfi_personality 156, DW.ref.__gxx_personality_v0 +; ELF-NEXT: .cfi_lsda 28, [[EXCEPT:.Lexception[0-9]+]] +; ELF-NEXT: // %bb.0: +; ELF-NEXT: stp x30, x19, [sp, #-16]! +; ELF-NEXT: .cfi_def_cfa_offset 16 +; ELF-NEXT: .cfi_offset w19, -8 +; ELF-NEXT: .cfi_offset w30, -16 +; ELF-NEXT: [[PRECALL:.L.*]]: +; ELF-NEXT: bl baz + +; ELF-SDAG-NEXT: [[POSTCALL:.L.*]]: +; ELF-SDAG-NEXT: // %bb.1: +; ELF-SDAG-NEXT: mov w19, w0 + +; ELF-GISEL-NEXT: mov w19, w0 +; ELF-GISEL-NEXT: [[POSTCALL:.L.*]]: + +; ELF-NEXT: [[CALLBB:.L.*]]: +; ELF-NEXT: bl foo +; ELF-NEXT: mov w0, w19 +; ELF-NEXT: ldp x30, x19, [sp], #16 +; ELF-NEXT: ret +; ELF-NEXT: [[LPADBB:.LBB[0-9_]+]]: +; ELF-NEXT: [[LPAD:.L.*]]: +; ELF-NEXT: mov w19, #-1 +; ELF-NEXT: b [[CALLBB]] + +; CHECK-LABEL: GCC_except_table{{.*}}: +; CHECK-NEXT: [[EXCEPT]]: +; CHECK: .uleb128 [[POSTCALL]]-[[PRECALL]] {{.*}} Call between [[PRECALL]] and [[POSTCALL]] +; CHECK-NEXT: .uleb128 [[LPAD]]-[[FNBEGIN]] {{.*}} jumps to [[LPAD]] +; CHECK-NEXT: .byte 0 {{.*}} On action: cleanup + +define i32 @test_invoke_ia_0_direct() #0 personality ptr @__gxx_personality_v0 { + %tmp0 = invoke i32 ptrauth (ptr @baz, i32 0)() [ "ptrauth"(i32 0, i64 0) ] to label %continuebb + unwind label %unwindbb + +unwindbb: + %tmp1 = landingpad { ptr, i32 } cleanup + call void @foo() + ret i32 -1 + +continuebb: + call void @foo() + ret i32 %tmp0 +} + @_ZTIPKc = external constant ptr @hello_str = private unnamed_addr constant [6 x i8] c"hello\00", align 1 @@ -265,6 +349,7 @@ continuebb: declare void @foo() declare void @bar(ptr) +declare i32 @baz() declare i32 @__gxx_personality_v0(...) declare ptr @__cxa_allocate_exception(i64) From dd78f85124141811d6a3fe72548ee5f5c5398d12 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 1 Jul 2024 17:28:31 -0700 Subject: [PATCH 13/58] [AArch64][PAC] Lower jump-tables using hardened pseudo. This was the last one of the ExpandHardenedPseudos, as it's now done in AsmPrinter. The original LOH use-case can now be replaced with late LOH emission from within AsmPrinter itself. --- llvm/lib/Target/AArch64/AArch64.h | 2 - llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 156 +++++++++++++-- .../AArch64/AArch64ExpandHardenedPseudos.cpp | 187 ------------------ .../Target/AArch64/AArch64ISelLowering.cpp | 14 +- llvm/lib/Target/AArch64/AArch64InstrInfo.td | 22 ++- .../Target/AArch64/AArch64TargetMachine.cpp | 5 - llvm/lib/Target/AArch64/CMakeLists.txt | 1 - .../GISel/AArch64InstructionSelector.cpp | 14 +- llvm/test/CodeGen/AArch64/O0-pipeline.ll | 1 - llvm/test/CodeGen/AArch64/O3-pipeline.ll | 1 - .../AArch64/arm64e-jump-table-hardening.ll | 51 ----- .../CodeGen/AArch64/hardened-jump-table-br.ll | 53 +++++ 12 files changed, 230 insertions(+), 277 deletions(-) delete mode 100644 llvm/lib/Target/AArch64/AArch64ExpandHardenedPseudos.cpp delete mode 100644 llvm/test/CodeGen/AArch64/arm64e-jump-table-hardening.ll create mode 100644 llvm/test/CodeGen/AArch64/hardened-jump-table-br.ll diff --git a/llvm/lib/Target/AArch64/AArch64.h b/llvm/lib/Target/AArch64/AArch64.h index d51a1b8cd8501..6f2aeb83a451a 100644 --- a/llvm/lib/Target/AArch64/AArch64.h +++ b/llvm/lib/Target/AArch64/AArch64.h @@ -54,7 +54,6 @@ FunctionPass *createFalkorMarkStridedAccessesPass(); FunctionPass *createAArch64PointerAuthPass(); FunctionPass *createAArch64BranchTargetsPass(); FunctionPass *createAArch64MIPeepholeOptPass(); -FunctionPass *createAArch64ExpandHardenedPseudosPass(); FunctionPass *createAArch64PostCoalescerPass(); FunctionPass *createAArch64CleanupLocalDynamicTLSPass(); @@ -88,7 +87,6 @@ void initializeAArch64ConditionOptimizerPass(PassRegistry&); void initializeAArch64ConditionalComparesPass(PassRegistry &); void initializeAArch64DAGToDAGISelLegacyPass(PassRegistry &); void initializeAArch64DeadRegisterDefinitionsPass(PassRegistry&); -void initializeAArch64ExpandHardenedPseudosPass(PassRegistry&); void initializeAArch64ExpandPseudoPass(PassRegistry &); void initializeAArch64GlobalsTaggingPass(PassRegistry &); void initializeAArch64LoadStoreOptPass(PassRegistry&); diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index eefa8977bd267..2b8f94506cc63 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -124,6 +124,8 @@ class AArch64AsmPrinter : public AsmPrinter { void LowerJumpTableDest(MCStreamer &OutStreamer, const MachineInstr &MI); + void LowerHardenedBRJumpTable(const MachineInstr &MI); + void LowerMOPS(MCStreamer &OutStreamer, const MachineInstr &MI); void LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM, @@ -1411,6 +1413,141 @@ void AArch64AsmPrinter::LowerJumpTableDest(llvm::MCStreamer &OutStreamer, .addImm(Size == 4 ? 0 : 2)); } +void AArch64AsmPrinter::LowerHardenedBRJumpTable(const MachineInstr &MI) { + unsigned InstsEmitted = 0; + + const MachineJumpTableInfo *MJTI = MF->getJumpTableInfo(); + assert(MJTI && "Can't lower jump-table dispatch without JTI"); + + const std::vector &JTs = MJTI->getJumpTables(); + assert(!JTs.empty() && "Invalid JT index for jump-table dispatch"); + + // Emit: + // mov x17, # ; depending on table size, with MOVKs + // cmp x16, x17 ; or #imm if table size fits in 12-bit + // csel x16, x16, xzr, ls ; check for index overflow + // + // adrp x17, Ltable@PAGE ; materialize table address + // add x17, Ltable@PAGEOFF + // ldrsw x16, [x17, x16, lsl #2] ; load table entry + // + // Lanchor: + // adr x17, Lanchor ; compute target address + // add x16, x17, x16 + // br x16 ; branch to target + + MachineOperand JTOp = MI.getOperand(0); + + unsigned JTI = JTOp.getIndex(); + assert(!AArch64FI->getJumpTableEntryPCRelSymbol(JTI) && + "unsupported compressed jump table"); + + const uint64_t NumTableEntries = JTs[JTI].MBBs.size(); + + // cmp only supports a 12-bit immediate. If we need more, materialize the + // immediate, using x17 as a scratch register. + uint64_t MaxTableEntry = NumTableEntries - 1; + if (isUInt<12>(MaxTableEntry)) { + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::SUBSXri) + .addReg(AArch64::XZR) + .addReg(AArch64::X16) + .addImm(MaxTableEntry) + .addImm(0)); + ++InstsEmitted; + } else { + EmitToStreamer(*OutStreamer, + MCInstBuilder(AArch64::MOVZXi) + .addReg(AArch64::X17) + .addImm(static_cast(MaxTableEntry)) + .addImm(0)); + ++InstsEmitted; + // It's sad that we have to manually materialize instructions, but we can't + // trivially reuse the main pseudo expansion logic. + // A MOVK sequence is easy enough to generate and handles the general case. + for (int Offset = 16; Offset < 64; Offset += 16) { + if ((MaxTableEntry >> Offset) == 0) + break; + EmitToStreamer(*OutStreamer, + MCInstBuilder(AArch64::MOVKXi) + .addReg(AArch64::X17) + .addReg(AArch64::X17) + .addImm(static_cast(MaxTableEntry >> Offset)) + .addImm(Offset)); + ++InstsEmitted; + } + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::SUBSXrs) + .addReg(AArch64::XZR) + .addReg(AArch64::X16) + .addReg(AArch64::X17) + .addImm(0)); + ++InstsEmitted; + } + + // This picks entry #0 on failure. + // We might want to trap instead. + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::CSELXr) + .addReg(AArch64::X16) + .addReg(AArch64::X16) + .addReg(AArch64::XZR) + .addImm(AArch64CC::LS)); + ++InstsEmitted; + + // Prepare the @PAGE/@PAGEOFF low/high operands. + MachineOperand JTMOHi(JTOp), JTMOLo(JTOp); + MCOperand JTMCHi, JTMCLo; + + JTMOHi.setTargetFlags(AArch64II::MO_PAGE); + JTMOLo.setTargetFlags(AArch64II::MO_PAGEOFF | AArch64II::MO_NC); + + MCInstLowering.lowerOperand(JTMOHi, JTMCHi); + MCInstLowering.lowerOperand(JTMOLo, JTMCLo); + + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADRP) + .addReg(AArch64::X17) + .addOperand(JTMCHi)); + ++InstsEmitted; + + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXri) + .addReg(AArch64::X17) + .addReg(AArch64::X17) + .addOperand(JTMCLo) + .addImm(0)); + ++InstsEmitted; + + + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRSWroX) + .addReg(AArch64::X16) + .addReg(AArch64::X17) + .addReg(AArch64::X16) + .addImm(0) + .addImm(1)); + ++InstsEmitted; + + MCSymbol *AdrLabel = MF->getContext().createTempSymbol(); + auto *AdrLabelE = MCSymbolRefExpr::create(AdrLabel, MF->getContext()); + AArch64FI->setJumpTableEntryInfo(JTI, 4, AdrLabel); + + OutStreamer->emitLabel(AdrLabel); + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADR) + .addReg(AArch64::X17) + .addExpr(AdrLabelE)); + ++InstsEmitted; + + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXrs) + .addReg(AArch64::X16) + .addReg(AArch64::X17) + .addReg(AArch64::X16) + .addImm(0)); + ++InstsEmitted; + + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::BR) + .addReg(AArch64::X16)); + ++InstsEmitted; + + assert(STI->getInstrInfo()->getInstSizeInBytes(MI) >= InstsEmitted * 4); +} + + void AArch64AsmPrinter::LowerMOPS(llvm::MCStreamer &OutStreamer, const llvm::MachineInstr &MI) { unsigned Opcode = MI.getOpcode(); @@ -2618,22 +2755,11 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { case AArch64::JumpTableDest8: LowerJumpTableDest(*OutStreamer, *MI); return; - case AArch64::JumpTableAnchor: { - int JTI = MI->getOperand(1).getIndex(); - assert(!AArch64FI->getJumpTableEntryPCRelSymbol(JTI) && - "unsupported compressed jump table"); - - auto Label = MF->getContext().createTempSymbol(); - auto LabelE = MCSymbolRefExpr::create(Label, MF->getContext()); - AArch64FI->setJumpTableEntryInfo(JTI, 4, Label); - - OutStreamer->emitLabel(Label); - EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADR) - .addReg(MI->getOperand(0).getReg()) - .addExpr(LabelE)); + + case AArch64::BR_JumpTable: + LowerHardenedBRJumpTable(*MI); return; - } - + case AArch64::FMOVH0: case AArch64::FMOVS0: case AArch64::FMOVD0: diff --git a/llvm/lib/Target/AArch64/AArch64ExpandHardenedPseudos.cpp b/llvm/lib/Target/AArch64/AArch64ExpandHardenedPseudos.cpp deleted file mode 100644 index 06dbedf5e824e..0000000000000 --- a/llvm/lib/Target/AArch64/AArch64ExpandHardenedPseudos.cpp +++ /dev/null @@ -1,187 +0,0 @@ -//===- AArch64ExpandHardenedPseudos.cpp --------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -//===----------------------------------------------------------------------===// - -#include "AArch64InstrInfo.h" -#include "AArch64Subtarget.h" -#include "AArch64MachineFunctionInfo.h" -#include "MCTargetDesc/AArch64AddressingModes.h" -#include "Utils/AArch64BaseInfo.h" -#include "llvm/ADT/BitVector.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/CodeGen/MachineBasicBlock.h" -#include "llvm/CodeGen/MachineFunction.h" -#include "llvm/CodeGen/MachineFunctionPass.h" -#include "llvm/CodeGen/MachineInstr.h" -#include "llvm/CodeGen/MachineInstrBuilder.h" -#include "llvm/CodeGen/MachineJumpTableInfo.h" -#include "llvm/CodeGen/MachineOperand.h" -#include "llvm/CodeGen/MachineRegisterInfo.h" -#include "llvm/IR/DebugLoc.h" -#include "llvm/Pass.h" -#include "llvm/Support/CodeGen.h" -#include "llvm/Support/Debug.h" -#include "llvm/Target/TargetMachine.h" -#include - -using namespace llvm; - -#define DEBUG_TYPE "aarch64-expand-hardened-pseudos" - -#define PASS_NAME "AArch64 Expand Hardened Pseudos" - -namespace { - -class AArch64ExpandHardenedPseudos : public MachineFunctionPass { -public: - static char ID; - - AArch64ExpandHardenedPseudos() : MachineFunctionPass(ID) { - initializeAArch64ExpandHardenedPseudosPass(*PassRegistry::getPassRegistry()); - } - - bool runOnMachineFunction(MachineFunction &Fn) override; - - StringRef getPassName() const override { - return PASS_NAME; - } - -private: - bool expandMI(MachineInstr &MI); -}; - -} // end anonymous namespace - -char AArch64ExpandHardenedPseudos::ID = 0; - -INITIALIZE_PASS(AArch64ExpandHardenedPseudos, DEBUG_TYPE, PASS_NAME, false, false) - -bool AArch64ExpandHardenedPseudos::expandMI(MachineInstr &MI) { - MachineBasicBlock &MBB = *MI.getParent(); - MachineFunction &MF = *MBB.getParent(); - DebugLoc DL = MI.getDebugLoc(); - auto MBBI = MI.getIterator(); - - const AArch64Subtarget &STI = MF.getSubtarget(); - const AArch64InstrInfo *TII = STI.getInstrInfo(); - - if (MI.getOpcode() != AArch64::BR_JumpTable) - return false; - - LLVM_DEBUG(dbgs() << "Expanding: " << MI << "\n"); - const MachineJumpTableInfo *MJTI = MF.getJumpTableInfo(); - assert(MJTI && "Can't lower jump-table dispatch without JTI"); - - const std::vector &JTs = MJTI->getJumpTables(); - assert(!JTs.empty() && "Invalid JT index for jump-table dispatch"); - - // Emit: - // adrp xTable, Ltable@PAGE - // add xTable, Ltable@PAGEOFF - // mov xEntry, # ; depending on table size, with MOVKs - // cmp xEntry, # ; if table size fits in 12-bit immediate - // csel xEntry, xEntry, xzr, ls - // ldrsw xScratch, [xTable, xEntry, lsl #2] ; kill xEntry, xScratch = xEntry - // Ltmp: - // adr xTable, Ltmp - // add xDest, xTable, xScratch ; kill xTable, xDest = xTable - // br xDest - - MachineOperand JTOp = MI.getOperand(0); - - unsigned JTI = JTOp.getIndex(); - const uint64_t NumTableEntries = JTs[JTI].MBBs.size(); - - // cmp only supports a 12-bit immediate. If we need more, materialize the - // immediate, using TableReg as a scratch register. - uint64_t MaxTableEntry = NumTableEntries - 1; - if (isUInt<12>(MaxTableEntry)) { - BuildMI(MBB, MBBI, DL, TII->get(AArch64::SUBSXri), AArch64::XZR) - .addReg(AArch64::X16) - .addImm(MaxTableEntry) - .addImm(0); - } else { - BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVZXi), AArch64::X17) - .addImm(static_cast(MaxTableEntry)) - .addImm(0); - // It's sad that we have to manually materialize instructions, but we can't - // trivially reuse the main pseudo expansion logic. - // A MOVK sequence is easy enough to generate and handles the general case. - for (int Offset = 16; Offset < 64; Offset += 16) { - if ((MaxTableEntry >> Offset) == 0) - break; - BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVKXi), AArch64::X17) - .addReg(AArch64::X17) - .addImm(static_cast(MaxTableEntry >> Offset)) - .addImm(Offset); - } - BuildMI(MBB, MBBI, DL, TII->get(AArch64::SUBSXrs), AArch64::XZR) - .addReg(AArch64::X16) - .addReg(AArch64::X17) - .addImm(0); - } - - // This picks entry #0 on failure. - // We might want to trap instead. - BuildMI(MBB, MBBI, DL, TII->get(AArch64::CSELXr), AArch64::X16) - .addReg(AArch64::X16) - .addReg(AArch64::XZR) - .addImm(AArch64CC::LS); - - MachineOperand JTHiOp(JTOp); - MachineOperand JTLoOp(JTOp); - JTHiOp.setTargetFlags(AArch64II::MO_PAGE); - JTLoOp.setTargetFlags(AArch64II::MO_PAGEOFF); - - BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADRP), AArch64::X17) - .add(JTHiOp); - BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADDXri), AArch64::X17) - .addReg(AArch64::X17) - .add(JTLoOp) - .addImm(0); - - BuildMI(MBB, MBBI, DL, TII->get(AArch64::LDRSWroX), AArch64::X16) - .addReg(AArch64::X17) - .addReg(AArch64::X16) - .addImm(0) - .addImm(1); - - // Really an ADR with a label attached. - BuildMI(MBB, MBBI, DL, TII->get(AArch64::JumpTableAnchor), AArch64::X17) - .addJumpTableIndex(JTI); - - BuildMI(MBB, MBBI, DL, TII->get(AArch64::ADDXrs), AArch64::X16) - .addReg(AArch64::X17) - .addReg(AArch64::X16) - .addImm(0); - - BuildMI(MBB, MBBI, DL, TII->get(AArch64::BR)) - .addReg(AArch64::X16); - - MI.eraseFromParent(); - return true; -} - - -bool AArch64ExpandHardenedPseudos::runOnMachineFunction(MachineFunction &MF) { - LLVM_DEBUG(dbgs() << "***** AArch64ExpandHardenedPseudos *****\n"); - - bool Modified = false; - for (auto &MBB : MF) { - for (auto MBBI = MBB.begin(), MBBE = MBB.end(); MBBI != MBBE; ) { - auto &MI = *MBBI++; - Modified |= expandMI(MI); - } - } - return Modified; -} - -FunctionPass *llvm::createAArch64ExpandHardenedPseudosPass() { - return new AArch64ExpandHardenedPseudos(); -} diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index 8b38a55b942d9..79e85d7a9ba92 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -10675,18 +10675,18 @@ SDValue AArch64TargetLowering::LowerBR_JT(SDValue Op, auto *AFI = DAG.getMachineFunction().getInfo(); AFI->setJumpTableEntryInfo(JTI, 4, nullptr); - // With aarch64-hardened-codegen, we only expand the full jump table dispatch + // With jump-table-hardening, we only expand the full jump table dispatch // sequence later, to guarantee the integrity of the intermediate values. if (DAG.getMachineFunction().getFunction() .hasFnAttribute("jump-table-hardening") || - Subtarget->getTargetTriple().getArchName() == "arm64e") { - if (getTargetMachine().getCodeModel() != CodeModel::Small) - report_fatal_error("Unsupported code-model for hardened jump-table"); - SDValue Chain = DAG.getCopyToReg(DAG.getEntryNode(), DL, AArch64::X16, - Entry, SDValue()); + Subtarget->getTargetTriple().isArm64e()) { + assert(Subtarget->isTargetMachO() && + "hardened jump-table not yet supported on non-macho"); + SDValue X16Copy = DAG.getCopyToReg(DAG.getEntryNode(), DL, AArch64::X16, + Entry, SDValue()); SDNode *B = DAG.getMachineNode(AArch64::BR_JumpTable, DL, MVT::Other, DAG.getTargetJumpTable(JTI, MVT::i32), - Chain.getValue(0), Chain.getValue(1)); + X16Copy.getValue(0), X16Copy.getValue(1)); return SDValue(B, 0); } diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 087000b5cff46..583d32b12a263 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -1145,17 +1145,27 @@ def JumpTableDest8 : Pseudo<(outs GPR64:$dst, GPR64sp:$scratch), Sched<[]>; } +// A hardened but more expensive version of jump-table dispatch. +// This combines the target address computation (otherwise done using the +// JumpTableDest pseudos above) with the branch itself (otherwise done using +// a plain BR) in a single non-attackable sequence. +// +// We take the final entry index as an operand to allow isel freedom. This does +// mean that the index can be attacker-controlled. To address that, we also do +// limited checking of the offset, mainly ensuring it still points within the +// jump-table array. When it doesn't, this branches to the first entry. +// +// This is intended for use in conjunction with ptrauth for other code pointers, +// to avoid signing jump-table entries and turning them into pointers. +// +// Entry index is passed in x16. Clobbers x16/x17/nzcv. let isNotDuplicable = 1 in -def JumpTableAnchor : Pseudo<(outs GPR64:$pc), (ins i32imm:$jti), []>, - Sched<[WriteI]>; - -let isNotDuplicable = 1 in -def BR_JumpTable : Pseudo<(outs), (ins i32imm:$jti), []>, - Sched<[]> { +def BR_JumpTable : Pseudo<(outs), (ins i32imm:$jti), []>, Sched<[]> { let isBranch = 1; let isTerminator = 1; let isIndirectBranch = 1; let isBarrier = 1; + let isNotDuplicable = 1; let Defs = [X16,X17,NZCV]; let Uses = [X16]; let Size = 44; // 28 fixed + 16 variable, for table size materialization diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp index 865b43373f1a8..3209209d51067 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -263,7 +263,6 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAArch64Target() { initializeAArch64StackTaggingPreRAPass(*PR); initializeAArch64LowerHomogeneousPrologEpilogPass(*PR); initializeAArch64DAGToDAGISelLegacyPass(*PR); - initializeAArch64ExpandHardenedPseudosPass(*PR); initializeAArch64GlobalsTaggingPass(*PR); } @@ -857,10 +856,6 @@ void AArch64PassConfig::addPreEmitPass() { addPass(createEHContGuardCatchretPass()); } - // Expand hardened pseudo-instructions. - // Do this now to enable LOH emission. - addPass(createAArch64ExpandHardenedPseudosPass()); - if (TM->getOptLevel() != CodeGenOptLevel::None && EnableCollectLOH && TM->getTargetTriple().isOSBinFormatMachO()) addPass(createAArch64CollectLOHPass()); diff --git a/llvm/lib/Target/AArch64/CMakeLists.txt b/llvm/lib/Target/AArch64/CMakeLists.txt index b6d42d39d383b..639bc0707dff2 100644 --- a/llvm/lib/Target/AArch64/CMakeLists.txt +++ b/llvm/lib/Target/AArch64/CMakeLists.txt @@ -51,7 +51,6 @@ add_llvm_target(AArch64CodeGen AArch64CondBrTuning.cpp AArch64ConditionalCompares.cpp AArch64DeadRegisterDefinitionsPass.cpp - AArch64ExpandHardenedPseudos.cpp AArch64ExpandImm.cpp AArch64ExpandPseudoInsts.cpp AArch64FalkorHWPFFix.cpp diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp index d92a8ee6c306c..894aa0e22bfdd 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp @@ -3597,10 +3597,22 @@ bool AArch64InstructionSelector::selectBrJT(MachineInstr &I, unsigned JTI = I.getOperand(1).getIndex(); Register Index = I.getOperand(2).getReg(); + MF->getInfo()->setJumpTableEntryInfo(JTI, 4, nullptr); + if (MF->getFunction().hasFnAttribute("jump-table-hardening") || + STI.getTargetTriple().isArm64e()) { + if (TM.getCodeModel() != CodeModel::Small) + report_fatal_error("Unsupported code-model for hardened jump-table"); + + MIB.buildCopy({AArch64::X16}, I.getOperand(2).getReg()); + MIB.buildInstr(AArch64::BR_JumpTable) + .addJumpTableIndex(I.getOperand(1).getIndex()); + I.eraseFromParent(); + return true; + } + Register TargetReg = MRI.createVirtualRegister(&AArch64::GPR64RegClass); Register ScratchReg = MRI.createVirtualRegister(&AArch64::GPR64spRegClass); - MF->getInfo()->setJumpTableEntryInfo(JTI, 4, nullptr); auto JumpTableInst = MIB.buildInstr(AArch64::JumpTableDest32, {TargetReg, ScratchReg}, {JTAddr, Index}) .addJumpTableIndex(JTI); diff --git a/llvm/test/CodeGen/AArch64/O0-pipeline.ll b/llvm/test/CodeGen/AArch64/O0-pipeline.ll index b8a9d32938f05..a0306b8e1e924 100644 --- a/llvm/test/CodeGen/AArch64/O0-pipeline.ll +++ b/llvm/test/CodeGen/AArch64/O0-pipeline.ll @@ -70,7 +70,6 @@ ; CHECK-NEXT: Insert XRay ops ; CHECK-NEXT: Implement the 'patchable-function' attribute ; CHECK-NEXT: Workaround A53 erratum 835769 pass -; CHECK-NEXT: AArch64 Expand Hardened Pseudos ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis diff --git a/llvm/test/CodeGen/AArch64/O3-pipeline.ll b/llvm/test/CodeGen/AArch64/O3-pipeline.ll index ec74ea6cac713..84e672d14d99d 100644 --- a/llvm/test/CodeGen/AArch64/O3-pipeline.ll +++ b/llvm/test/CodeGen/AArch64/O3-pipeline.ll @@ -221,7 +221,6 @@ ; CHECK-NEXT: AArch64 load / store optimization pass ; CHECK-NEXT: Machine Copy Propagation Pass ; CHECK-NEXT: Workaround A53 erratum 835769 pass -; CHECK-NEXT: AArch64 Expand Hardened Pseudos ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis diff --git a/llvm/test/CodeGen/AArch64/arm64e-jump-table-hardening.ll b/llvm/test/CodeGen/AArch64/arm64e-jump-table-hardening.ll deleted file mode 100644 index 99dbd1102af8d..0000000000000 --- a/llvm/test/CodeGen/AArch64/arm64e-jump-table-hardening.ll +++ /dev/null @@ -1,51 +0,0 @@ -; RUN: llc -verify-machineinstrs -o - %s -mtriple=arm64-apple-ios -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 | FileCheck %s - -; CHECK-LABEL: test_jumptable: -; CHECK: mov w[[INDEX:[0-9]+]], w0 -; CHECK: cmp x[[INDEX]], #5 -; CHECK: csel [[INDEX2:x[0-9]+]], x[[INDEX]], xzr, ls -; CHECK: adrp [[JTPAGE:x[0-9]+]], LJTI0_0@PAGE -; CHECK: add x[[JT:[0-9]+]], [[JTPAGE]], LJTI0_0@PAGEOFF -; CHECK: ldrsw [[OFFSET:x[0-9]+]], [x[[JT]], [[INDEX2]], lsl #2] -; CHECK: Ltmp0: -; CHECK: adr [[TABLE:x[0-9]+]], Ltmp0 -; CHECK: add [[DEST:x[0-9]+]], [[TABLE]], [[OFFSET]] -; CHECK: br [[DEST]] - -define i32 @test_jumptable(i32 %in) "jump-table-hardening" { - - switch i32 %in, label %def [ - i32 0, label %lbl1 - i32 1, label %lbl2 - i32 2, label %lbl3 - i32 4, label %lbl4 - i32 5, label %lbl5 - ] - -def: - ret i32 0 - -lbl1: - ret i32 1 - -lbl2: - ret i32 2 - -lbl3: - ret i32 4 - -lbl4: - ret i32 8 - -lbl5: - ret i32 10 - -} - -; CHECK: LJTI0_0: -; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 -; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 -; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 -; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 -; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 -; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 diff --git a/llvm/test/CodeGen/AArch64/hardened-jump-table-br.ll b/llvm/test/CodeGen/AArch64/hardened-jump-table-br.ll new file mode 100644 index 0000000000000..fa71b15b285aa --- /dev/null +++ b/llvm/test/CodeGen/AArch64/hardened-jump-table-br.ll @@ -0,0 +1,53 @@ +; RUN: llc -verify-machineinstrs -o - %s -mtriple=arm64-apple-ios -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 | FileCheck %s +; RUN: llc -verify-machineinstrs -o - %s -mtriple=arm64-apple-ios -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 -code-model=large | FileCheck %s +; RUN: llc -verify-machineinstrs -o - %s -mtriple=arm64-apple-ios -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 -global-isel -global-isel-abort=1 | FileCheck %s + +; CHECK-LABEL: test_jumptable: +; CHECK: mov w[[INDEX:[0-9]+]], w0 +; CHECK: cmp x[[INDEX]], #5 +; CHECK: csel [[INDEX2:x[0-9]+]], x[[INDEX]], xzr, ls +; CHECK-NEXT: adrp [[JTPAGE:x[0-9]+]], LJTI0_0@PAGE +; CHECK-NEXT: add x[[JT:[0-9]+]], [[JTPAGE]], LJTI0_0@PAGEOFF +; CHECK-NEXT: ldrsw [[OFFSET:x[0-9]+]], [x[[JT]], [[INDEX2]], lsl #2] +; CHECK-NEXT: Ltmp0: +; CHECK-NEXT: adr [[TABLE:x[0-9]+]], Ltmp0 +; CHECK-NEXT: add [[DEST:x[0-9]+]], [[TABLE]], [[OFFSET]] +; CHECK-NEXT: br [[DEST]] + +define i32 @test_jumptable(i32 %in) "jump-table-hardening" { + + switch i32 %in, label %def [ + i32 0, label %lbl1 + i32 1, label %lbl2 + i32 2, label %lbl3 + i32 4, label %lbl4 + i32 5, label %lbl5 + ] + +def: + ret i32 0 + +lbl1: + ret i32 1 + +lbl2: + ret i32 2 + +lbl3: + ret i32 4 + +lbl4: + ret i32 8 + +lbl5: + ret i32 10 + +} + +; CHECK: LJTI0_0: +; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 +; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 +; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 +; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 +; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 +; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 From 4c35505a080b8317dbb7063ab33e05af52a1d71c Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 27 Mar 2023 07:39:47 -0700 Subject: [PATCH 14/58] [AArch64][PAC] Handle signed LR in outliner. --- llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 50 +++++++++++-- .../arm64e-machine-outliner-no-pacibsp.mir | 72 +++++++++++++++++++ .../arm64e-machine-outliner-regsaves.mir | 52 ++++++++++++++ .../AArch64/arm64e-machine-outliner.mir | 70 ++++++++++++++++++ 4 files changed, 238 insertions(+), 6 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/arm64e-machine-outliner-no-pacibsp.mir create mode 100644 llvm/test/CodeGen/AArch64/arm64e-machine-outliner-regsaves.mir create mode 100644 llvm/test/CodeGen/AArch64/arm64e-machine-outliner.mir diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp index f2f7a9c376499..4827db8f02983 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -8427,6 +8427,11 @@ AArch64InstrInfo::getOutliningCandidateInfo( unsigned FrameID = MachineOutlinerDefault; NumBytesToCreateFrame += 4; + // Check if we are in arm64e; if we are, we might need to sign + auth. + MachineFunction *MF = + RepeatedSequenceLocs[0].front().getParent()->getParent(); + bool HaveARM64e = MF->getSubtarget().getTargetTriple().isArm64e(); + bool HasBTI = any_of(RepeatedSequenceLocs, [](outliner::Candidate &C) { return C.getMF()->getInfo()->branchTargetEnforcement(); }); @@ -8570,7 +8575,7 @@ AArch64InstrInfo::getOutliningCandidateInfo( // Is SP used in the sequence at all? If not, we don't have to modify // the stack, so we are guaranteed to get the same frame. - else if (C.isAvailableInsideSeq(AArch64::SP, TRI)) { + else if (C.isAvailableInsideSeq(AArch64::SP, TRI) && !HaveARM64e) { NumBytesNoStackCalls += 12; C.setCallInfo(MachineOutlinerDefault, 12); CandidatesWithoutStackFixups.push_back(C); @@ -8593,6 +8598,12 @@ AArch64InstrInfo::getOutliningCandidateInfo( if (RepeatedSequenceLocs.size() < 2) return std::nullopt; } else { + if (HaveARM64e) { + // In the case that we have ARM64e, let's play it safe and not ever + // outline these. + RepeatedSequenceLocs.clear(); + return std::nullopt; + } SetCandidateCallInfo(MachineOutlinerDefault, 12); // Bugzilla ID: 46767 @@ -8686,6 +8697,9 @@ AArch64InstrInfo::getOutliningCandidateInfo( // Save + restore LR. NumBytesToCreateFrame += 8; + if (HaveARM64e && FrameID != MachineOutlinerThunk && + FrameID != MachineOutlinerTailCall) + NumBytesToCreateFrame += 4; // PACIBSP } } @@ -8930,6 +8944,10 @@ AArch64InstrInfo::getOutliningTypeImpl(MachineBasicBlock::iterator &MIT, return outliner::InstrType::Illegal; } + // Don't outline pointer authentication instructions. + if (MI.getOpcode() == AArch64::PACIBSP) + return outliner::InstrType::Illegal; + // Special cases for instructions that can always be outlined, but will fail // the later tests. e.g, ADRPs, which are PC-relative use LR, but can always // be outlined because they don't require a *specific* value to be in LR. @@ -9059,6 +9077,13 @@ static void signOutlinedFunction(MachineFunction &MF, MachineBasicBlock &MBB, void AArch64InstrInfo::buildOutlinedFrame( MachineBasicBlock &MBB, MachineFunction &MF, const outliner::OutlinedFunction &OF) const { + // ARM64e could require authentication if we saved LR to the stack. + bool HaveARM64e = MF.getSubtarget().getTargetTriple().isArm64e(); + bool SavedLRToStack = false; + + // ARM64e: If we aren't inserting a return, we don't want to sign. + bool InsertsReturn = OF.FrameConstructionID != MachineOutlinerThunk && + OF.FrameConstructionID != MachineOutlinerTailCall; AArch64FunctionInfo *FI = MF.getInfo(); @@ -9151,6 +9176,9 @@ void AArch64InstrInfo::buildOutlinedFrame( .addReg(AArch64::SP) .addImm(16); Et = MBB.insert(Et, LDRXpost); + + // We need to authenticate LR. + SavedLRToStack = true; } bool ShouldSignReturnAddr = FI->shouldSignReturnAddress(!IsLeafFunction); @@ -9162,15 +9190,25 @@ void AArch64InstrInfo::buildOutlinedFrame( return; } - // It's not a tail call, so we have to insert the return ourselves. - // LR has to be a live in so that we can return to it. if (!MBB.isLiveIn(AArch64::LR)) MBB.addLiveIn(AArch64::LR); - MachineInstr *ret = BuildMI(MF, DebugLoc(), get(AArch64::RET)) - .addReg(AArch64::LR); - MBB.insert(MBB.end(), ret); + // If we saved LR to the stack and are in ARM64e, then we need to insert a + // PACIBSP. + if (SavedLRToStack && HaveARM64e && InsertsReturn) + MBB.insert(MBB.begin(), BuildMI(MF, DebugLoc(), get(AArch64::PACIBSP))); + + // If we're in ARM64e and we signed, insert a RETAB. + if (SavedLRToStack && HaveARM64e) + MBB.insert(MBB.end(), BuildMI(MF, DebugLoc(), get(AArch64::RETAB))); + + // Otherwise, a normal return. + else { + MachineInstr *ret = BuildMI(MF, DebugLoc(), get(AArch64::RET)) + .addReg(AArch64::LR); + MBB.insert(MBB.end(), ret); + } signOutlinedFunction(MF, MBB, this, ShouldSignReturnAddr); diff --git a/llvm/test/CodeGen/AArch64/arm64e-machine-outliner-no-pacibsp.mir b/llvm/test/CodeGen/AArch64/arm64e-machine-outliner-no-pacibsp.mir new file mode 100644 index 0000000000000..33cd103873f12 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-machine-outliner-no-pacibsp.mir @@ -0,0 +1,72 @@ +# RUN: llc -mtriple=aarch64--- -run-pass=prologepilog -run-pass=machine-outliner -verify-machineinstrs %s -o - | FileCheck %s +# Ensure that we don't emit a PACIBSP if our outlined function doesn't end in +# a RETAB. +--- | + define void @baz() #0 { + ret void + } + + define void @bar(i32 %a) #0 { + ret void + } + + attributes #0 = { noredzone } +... +--- + +name: bar +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $lr, $w8 + $sp = frame-setup SUBXri $sp, 32, 0 + $fp = frame-setup ADDXri $sp, 16, 0 + + bb.1: + ; CHECK-LABEL: name: bar + ; CHECK: BL @OUTLINED_FUNCTION + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 1 + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 4 + BL @baz, implicit-def dead $lr, implicit $sp + $w0 = ORRWri $wzr, 4 + + ; CHECK-DAG: BL @OUTLINED_FUNCTION + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 1 + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 4 + BL @baz, implicit-def dead $lr, implicit $sp + $w0 = ORRWri $wzr, 3 + + ; CHECK-DAG: BL @OUTLINED_FUNCTION + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 1 + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 4 + BL @baz, implicit-def dead $lr, implicit $sp + $w0 = ORRWri $wzr, 2 + + ; CHECK-DAG: BL @OUTLINED_FUNCTION + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 1 + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 4 + BL @baz, implicit-def dead $lr, implicit $sp + $w0 = ORRWri $wzr, 1 + bb.2: + $fp, $lr = LDPXi $sp, 2 + RET undef $lr +... +--- +name: baz +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $lr, $w8 + RET undef $lr + +# CHECK-LABEL: name: OUTLINED_FUNCTION +# CHECK-NOT: PACIBSP +# CHECK: TCRETURNdi diff --git a/llvm/test/CodeGen/AArch64/arm64e-machine-outliner-regsaves.mir b/llvm/test/CodeGen/AArch64/arm64e-machine-outliner-regsaves.mir new file mode 100644 index 0000000000000..d0b04800c2fe2 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-machine-outliner-regsaves.mir @@ -0,0 +1,52 @@ +# RUN: llc -mtriple=arm64e-apple-darwin -run-pass=machine-outliner \ +# RUN: -verify-machineinstrs %s -o - | FileCheck %s + +# Ensure that in arm64e, we don't save LR to the stack at an outlined call-site. +# FIXME: It should be possible to do this. However, it seems kind of sketchy, +# and so for the sake of correctness, the behaviour is disabled. + +--- | + define void @foo() #0 { + ret void + } + + attributes #0 = { minsize noinline noredzone "no-frame-pointer-elim"="true" } +... +--- + +# In arm64e, right now, we never outline anything that would require a save +# to the stack at the call-site. +# CHECK-LABEL: name: foo +# CHECK-NOT: BL +# CHECK-NOT: early-clobber $sp, $lr = LDRXpost $sp, 16 +# CHECK-NOT: early-clobber $sp = STRXpre $lr, $sp, -16 +name: foo +tracksRegLiveness: true +body: | + bb.0: + liveins: $lr, $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x19, $x20, $x21, $x22, $x23, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27, $x28 + $w10 = ORRWri $wzr, 1 + $w10 = ORRWri $wzr, 1 + $w10 = ORRWri $wzr, 1 + $w10 = ORRWri $wzr, 1 + $w10 = ORRWri $wzr, 1 + $w12 = ORRWri $wzr, 2 + bb.1: + liveins: $lr, $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x19, $x20, $x21, $x22, $x23, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27, $x28 + $w10 = ORRWri $wzr, 1 + $w10 = ORRWri $wzr, 1 + $w10 = ORRWri $wzr, 1 + $w10 = ORRWri $wzr, 1 + $w10 = ORRWri $wzr, 1 + $w12 = ORRWri $wzr, 2 + bb.2: + liveins: $lr, $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x19, $x20, $x21, $x22, $x23, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27, $x28 + $w10 = ORRWri $wzr, 1 + $w10 = ORRWri $wzr, 1 + $w10 = ORRWri $wzr, 1 + $w10 = ORRWri $wzr, 1 + $w10 = ORRWri $wzr, 1 + $w12 = ORRWri $wzr, 2 + bb.3: + liveins: $lr, $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x19, $x20, $x21, $x22, $x23, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27, $x28 + RET undef $lr diff --git a/llvm/test/CodeGen/AArch64/arm64e-machine-outliner.mir b/llvm/test/CodeGen/AArch64/arm64e-machine-outliner.mir new file mode 100644 index 0000000000000..54f3228163460 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-machine-outliner.mir @@ -0,0 +1,70 @@ +# RUN: llc -mtriple=arm64e-apple-darwin -run-pass=prologepilog -run-pass=machine-outliner -verify-machineinstrs %s -o - | FileCheck %s + +--- | + define void @baz() #0 { + ret void + } + + define void @bar(i32 %a) #0 { + ret void + } + + attributes #0 = { noredzone } +... +--- + +name: bar +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $lr, $w8 + $sp = frame-setup SUBXri $sp, 32, 0 + $fp = frame-setup ADDXri $sp, 16, 0 + + bb.1: + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 1 + $w17 = ORRWri $wzr, 1 + $w0 = ORRWri $wzr, 4 + + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 1 + $w17 = ORRWri $wzr, 1 + $w0 = ORRWri $wzr, 3 + + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 1 + $w17 = ORRWri $wzr, 1 + $w0 = ORRWri $wzr, 2 + + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 1 + $w17 = ORRWri $wzr, 1 + $w0 = ORRWri $wzr, 5 + + BL @baz, implicit-def dead $lr, implicit $sp + $w17 = ORRWri $wzr, 1 + $w17 = ORRWri $wzr, 1 + $w0 = ORRWri $wzr, 1 + + bb.2: + $fp, $lr = LDPXi $sp, 2 + RET undef $lr +... +--- +name: baz +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $lr, $w8 + RET undef $lr + +# CHECK: name: OUTLINED_FUNCTION +# CHECK-DAG: bb.0: +# CHECK-NEXT: liveins: $d15, $x19, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27, $x28, $d8, $d9, $d10, $d11, $d12, $d13, $d14, $lr +# CHECK: PACIBSP +# CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16 +# CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $w30, -16 +# CHECK-NEXT: early-clobber $sp = STRXpre $lr, $sp, -16 +# CHECK-DAG: early-clobber $sp, $lr = LDRXpost $sp, 16 +# CHECK-NEXT: RETAB From 0223233a16c95a722c86fc685b312b9c5f44df7e Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Mon, 27 Sep 2021 08:00:00 -0700 Subject: [PATCH 15/58] [AArch64][PAC] Emit LR auth-failure checks in the outliner. --- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 10 +++-- llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 22 ++++++++- .../AArch64/arm64e-auth-outlined-noframe.ll | 43 ++++++++++++++++++ .../AArch64/arm64e-auth-outlined-tail-call.ll | 45 +++++++++++++++++++ 4 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/arm64e-auth-outlined-noframe.ll create mode 100644 llvm/test/CodeGen/AArch64/arm64e-auth-outlined-tail-call.ll diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 2b8f94506cc63..aaa313ee85cc1 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -1790,7 +1790,8 @@ void AArch64AsmPrinter::authLRBeforeTailCall(const MachineFunction &MF, // If there's no stack frame then there's no AUTIBSP, and so no reason to // check for the particular form of invalid LR that produces. - if (!MF.getInfo()->hasStackFrame()) + if (!MF.getInfo()->hasStackFrame() && + !Fn.hasFnAttribute("outlined-function")) return; // We know TBI is disabled for instruction keys on Darwin, so bits 62 and 61 @@ -2656,10 +2657,13 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { return; } case AArch64::TCRETURNdi: { - authLRBeforeTailCall(*MI->getParent()->getParent(), AArch64::X16); + auto &DestOp = MI->getOperand(0); + if (!DestOp.isGlobal() || + !DestOp.getGlobal()->getName().starts_with("OUTLINED_FUNCTION")) + authLRBeforeTailCall(*MI->getParent()->getParent(), AArch64::X16); MCOperand Dest; - MCInstLowering.lowerOperand(MI->getOperand(0), Dest); + MCInstLowering.lowerOperand(DestOp, Dest); MCInst TmpInst; TmpInst.setOpcode(AArch64::B); TmpInst.addOperand(Dest); diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp index 4827db8f02983..6102695ec8943 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -9087,9 +9087,9 @@ void AArch64InstrInfo::buildOutlinedFrame( AArch64FunctionInfo *FI = MF.getInfo(); - if (OF.FrameConstructionID == MachineOutlinerTailCall) + if (OF.FrameConstructionID == MachineOutlinerTailCall) { FI->setOutliningStyle("Tail Call"); - else if (OF.FrameConstructionID == MachineOutlinerThunk) { + } else if (OF.FrameConstructionID == MachineOutlinerThunk) { // For thunk outlining, rewrite the last instruction from a call to a // tail-call. MachineInstr *Call = &*--MBB.instr_end(); @@ -9110,6 +9110,24 @@ void AArch64InstrInfo::buildOutlinedFrame( FI->setOutliningStyle("Thunk"); } + if (!InsertsReturn) { + // It ends with a tail call, so the outlined function might need to be told + // to authenticate LR first. + if (std::any_of(OF.Candidates.begin(), OF.Candidates.end(), + [](auto &Candidate) { + auto MF = Candidate.getMF(); + auto &Fn = MF->getFunction(); + return Fn.hasFnAttribute("ptrauth-auth-traps") && + Fn.hasFnAttribute("ptrauth-returns") && + MF->template getInfo() + ->hasStackFrame(); + })) { + MF.getFunction().addFnAttr("ptrauth-auth-traps"); + MF.getFunction().addFnAttr("ptrauth-returns"); + MF.getFunction().addFnAttr("outlined-function"); + } + } + bool IsLeafFunction = true; // Is there a call in the outlined range? diff --git a/llvm/test/CodeGen/AArch64/arm64e-auth-outlined-noframe.ll b/llvm/test/CodeGen/AArch64/arm64e-auth-outlined-noframe.ll new file mode 100644 index 0000000000000..de63e573c73f6 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-auth-outlined-noframe.ll @@ -0,0 +1,43 @@ +; RUN: llc -mtriple=arm64e-apple-ios %s -o - | FileCheck %s + +@var1 = external dso_local global [1024 x i8], align 8 +@var2 = external dso_local global [1024 x i8], align 8 + +define void @func1(i64 %offset) #0 { +; CHECK-LABEL: func1: +; CHECK-NOT: brk +; CHECK: b _OUTLINED_FUNCTION_0 + br i1 undef, label %t4, label %t3 + +t3: + ret void + +t4: + + %gep = getelementptr inbounds [1024 x i8], [1024 x i8]* @var1, i64 %offset + tail call fastcc void @take_pointer([1024 x i8]* %gep) + ret void +} + +; Neither caller ever stores x30, so it's known to be safe in the outlined function. +; CHECK-LABEL: OUTLINED_FUNCTION_0: +; CHECK-NOT: brk +; CHECK: b _take_pointer + +define void @func2(i64 %offset) #0 { + br i1 undef, label %t4, label %t3 + +t3: + ret void + +t4: + %gep = getelementptr inbounds [1024 x i8], [1024 x i8]* @var2, i64 %offset + tail call fastcc void @take_pointer([1024 x i8]* %gep) #7 + ret void +} + +declare void @foo() + +declare void @take_pointer([1024 x i8]*) + +attributes #0 = { minsize "ptrauth-auth-traps" "ptrauth-returns" } diff --git a/llvm/test/CodeGen/AArch64/arm64e-auth-outlined-tail-call.ll b/llvm/test/CodeGen/AArch64/arm64e-auth-outlined-tail-call.ll new file mode 100644 index 0000000000000..038481b758071 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64e-auth-outlined-tail-call.ll @@ -0,0 +1,45 @@ +; RUN: llc -mtriple=arm64e-apple-ios %s -o - | FileCheck %s + +@var1 = external dso_local global [1024 x i8], align 8 +@var2 = external dso_local global [1024 x i8], align 8 + +define void @func1(i64 %offset) #0 { +; CHECK-LABEL: func1: +; CHECK-NOT: brk +; CHECK: b _OUTLINED_FUNCTION_0 +; CHECK: pacibsp + br i1 undef, label %t4, label %t3 + +t3: + call void @foo() + ret void + +t4: + + %gep = getelementptr inbounds [1024 x i8], [1024 x i8]* @var1, i64 %offset + tail call fastcc void @take_pointer([1024 x i8]* %gep) + ret void +} + +; CHECK-LABEL: OUTLINED_FUNCTION_0: +; CHECK: brk +; CHECK: b _take_pointer + +define void @func2(i64 %offset) #0 { + br i1 undef, label %t4, label %t3 + +t3: + call void @foo() + ret void + +t4: + %gep = getelementptr inbounds [1024 x i8], [1024 x i8]* @var2, i64 %offset + tail call fastcc void @take_pointer([1024 x i8]* %gep) #7 + ret void +} + +declare void @foo() + +declare void @take_pointer([1024 x i8]*) + +attributes #0 = { minsize "ptrauth-auth-traps" "ptrauth-returns" } From c3249300548770c6ff31e8c8099e17d2956e3dd3 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 31 Jul 2023 07:43:42 -0700 Subject: [PATCH 16/58] [AArch64][PAC] Select auth+load into LDRA*. --- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 144 ++++ llvm/lib/Target/AArch64/AArch64Combine.td | 9 + .../Target/AArch64/AArch64ISelDAGToDAG.cpp | 122 +-- llvm/lib/Target/AArch64/AArch64InstrGISel.td | 17 + llvm/lib/Target/AArch64/AArch64InstrInfo.td | 36 + .../GISel/AArch64InstructionSelector.cpp | 71 ++ .../GISel/AArch64PostLegalizerLowering.cpp | 115 +++ .../CodeGen/AArch64/arm64e-ptrauth-load.ll | 301 -------- llvm/test/CodeGen/AArch64/ptrauth-load.ll | 716 ++++++++++++++++++ 9 files changed, 1184 insertions(+), 347 deletions(-) delete mode 100644 llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll create mode 100644 llvm/test/CodeGen/AArch64/ptrauth-load.ll diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index aaa313ee85cc1..8926c9ed2a577 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "AArch64.h" +#include "AArch64ExpandImm.h" #include "AArch64MCInstLower.h" #include "AArch64MachineFunctionInfo.h" #include "AArch64RegisterInfo.h" @@ -167,6 +168,9 @@ class AArch64AsmPrinter : public AsmPrinter { // adrp-add followed by PAC sign) void LowerMOVaddrPAC(const MachineInstr &MI); + // Emit the sequence for LDRA (auth + load from authenticated base). + void LowerPtrauthAuthLoad(const MachineInstr &MI); + /// tblgen'erated driver function for lowering simple MI->MC /// pseudo instructions. bool emitPseudoExpansionLowering(MCStreamer &OutStreamer, @@ -2119,6 +2123,141 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) { OutStreamer->emitLabel(EndSym); } +void AArch64AsmPrinter::LowerPtrauthAuthLoad(const MachineInstr &MI) { + unsigned InstsEmitted = 0; + bool IsPre = MI.getOpcode() == AArch64::LDRApre; + + unsigned DstReg = MI.getOperand(0).getReg(); + int64_t Offset = MI.getOperand(1).getImm(); + auto Key = (AArch64PACKey::ID)MI.getOperand(2).getImm(); + uint64_t Disc = MI.getOperand(3).getImm(); + unsigned AddrDisc = MI.getOperand(4).getReg(); + + unsigned DiscReg = AddrDisc; + if (Disc) { + assert(isUInt<16>(Disc) && "Integer discriminator is too wide"); + + // FIXME: consider using emitPtrauthDiscriminator() + if (AddrDisc != AArch64::XZR) { + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ORRXrs) + .addReg(AArch64::X17) + .addReg(AArch64::XZR) + .addReg(AddrDisc) + .addImm(0)); + ++InstsEmitted; + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::MOVKXi) + .addReg(AArch64::X17) + .addReg(AArch64::X17) + .addImm(Disc) + .addImm(/*shift=*/48)); + ++InstsEmitted; + } else { + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::MOVZXi) + .addReg(AArch64::X17) + .addImm(Disc) + .addImm(/*shift=*/0)); + ++InstsEmitted; + } + DiscReg = AArch64::X17; + } + + unsigned AUTOpc = getAUTOpcodeForKey(Key, DiscReg == AArch64::XZR); + auto MIB = MCInstBuilder(AUTOpc) + .addReg(AArch64::X16) + .addReg(AArch64::X16); + if (DiscReg != AArch64::XZR) + MIB.addReg(DiscReg); + + EmitToStreamer(*OutStreamer, MIB); + ++InstsEmitted; + + // We have a few options for offset folding: + // - 0 offset: LDRXui + // - no wb, uimm12s8 offset: LDRXui + // - no wb, simm9 offset: LDURXi + // - wb, simm9 offset: LDRXpre + // - no wb, any offset: expanded MOVImm + LDRXroX + // - wb, any offset: expanded MOVImm + ADD + LDRXui + if (!Offset || (!IsPre && isShiftedUInt<12, 3>(Offset))) { + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRXui) + .addReg(DstReg) + .addReg(AArch64::X16) + .addImm(Offset / 8)); + ++InstsEmitted; + } else if (!IsPre && Offset && isInt<9>(Offset)) { + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDURXi) + .addReg(DstReg) + .addReg(AArch64::X16) + .addImm(Offset)); + ++InstsEmitted; + } else if (IsPre && Offset && isInt<9>(Offset)) { + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRXpre) + .addReg(AArch64::X16) + .addReg(DstReg) + .addReg(AArch64::X16) + .addImm(Offset)); + ++InstsEmitted; + } else { + SmallVector ImmInsns; + AArch64_IMM::expandMOVImm(Offset, 64, ImmInsns); + + // X17 is dead at this point, use it as the offset register + for (auto &ImmI : ImmInsns) { + switch (ImmI.Opcode) { + default: llvm_unreachable("invalid ldra imm expansion opc!"); break; + + case AArch64::ORRXri: + EmitToStreamer(*OutStreamer, MCInstBuilder(ImmI.Opcode) + .addReg(AArch64::X17) + .addReg(AArch64::XZR) + .addImm(ImmI.Op2)); + ++InstsEmitted; + break; + case AArch64::MOVNXi: + case AArch64::MOVZXi: + EmitToStreamer(*OutStreamer, MCInstBuilder(ImmI.Opcode) + .addReg(AArch64::X17) + .addImm(ImmI.Op1) + .addImm(ImmI.Op2)); + ++InstsEmitted; + break; + case AArch64::MOVKXi: + EmitToStreamer(*OutStreamer, MCInstBuilder(ImmI.Opcode) + .addReg(AArch64::X17) + .addReg(AArch64::X17) + .addImm(ImmI.Op1) + .addImm(ImmI.Op2)); + ++InstsEmitted; + break; + } + } + + if (IsPre) { + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXrs) + .addReg(AArch64::X16) + .addReg(AArch64::X16) + .addReg(AArch64::X17) + .addImm(0)); + ++InstsEmitted; + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRXui) + .addReg(DstReg) + .addReg(AArch64::X16) + .addImm(/*Offset=*/0)); + ++InstsEmitted; + } else { + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRXroX) + .addReg(DstReg) + .addReg(AArch64::X16) + .addReg(AArch64::X17) + .addImm(0) + .addImm(0)); + ++InstsEmitted; + } + } + + assert(STI->getInstrInfo()->getInstSizeInBytes(MI) >= InstsEmitted * 4); +} + const MCExpr * AArch64AsmPrinter::lowerPtrAuthGlobalConstant(const GlobalPtrAuthInfo &PAI) { MCContext &Ctx = OutContext; @@ -2581,6 +2720,11 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { emitPtrauthAuthResign(MI); return; + case AArch64::LDRA: + case AArch64::LDRApre: + LowerPtrauthAuthLoad(*MI); + return; + case AArch64::BLRA: emitPtrauthBranch(MI); return; diff --git a/llvm/lib/Target/AArch64/AArch64Combine.td b/llvm/lib/Target/AArch64/AArch64Combine.td index 3f717c8a60050..43a081a156c6a 100644 --- a/llvm/lib/Target/AArch64/AArch64Combine.td +++ b/llvm/lib/Target/AArch64/AArch64Combine.td @@ -238,6 +238,14 @@ def form_truncstore : GICombineRule< (apply [{ applyFormTruncstore(*${root}, MRI, B, Observer, ${matchinfo}); }]) >; +def form_auth_load_matchdata : GIDefMatchData<"AuthLoadMatchInfo">; +def form_auth_load : GICombineRule< + (defs root:$root, form_auth_load_matchdata:$matchinfo), + (match (wip_match_opcode G_LOAD):$root, + [{ return matchFormAuthLoad(*${root}, MRI, Helper, ${matchinfo}); }]), + (apply [{ applyFormAuthLoad(*${root}, MRI, B, Helper, Observer, ${matchinfo}); }]) +>; + def fold_merge_to_zext : GICombineRule< (defs root:$d), (match (wip_match_opcode G_MERGE_VALUES):$d, @@ -298,6 +306,7 @@ def AArch64PostLegalizerLowering [shuffle_vector_lowering, vashr_vlshr_imm, icmp_lowering, build_vector_lowering, lower_vector_fcmp, form_truncstore, + form_auth_load, vector_sext_inreg_to_shift, unmerge_ext_to_unmerge, lower_mull, vector_unmerge_lowering, insertelt_nonconst]> { diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp index d6e7278fa4cc7..d842ea1ffe8cb 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp @@ -1686,9 +1686,9 @@ bool AArch64DAGToDAGISel::tryAuthLoad(SDNode *N) { assert(LD->getExtensionType() == ISD::NON_EXTLOAD && "invalid 64bit extload"); ISD::MemIndexedMode AM = LD->getAddressingMode(); - bool isPre = AM == ISD::PRE_INC; - if (!isPre && AM != ISD::UNINDEXED) + if (AM != ISD::PRE_INC && AM != ISD::UNINDEXED) return false; + bool IsPre = AM == ISD::PRE_INC; SDValue Chain = LD->getChain(); SDValue Ptr = LD->getBasePtr(); @@ -1696,7 +1696,7 @@ bool AArch64DAGToDAGISel::tryAuthLoad(SDNode *N) { SDValue Base = Ptr; int64_t OffsetVal = 0; - if (isPre) { + if (IsPre) { OffsetVal = cast(LD->getOffset())->getSExtValue(); } else if (CurDAG->isBaseWithConstantOffset(Base)) { // We support both 'base' and 'base + constant offset' modes. @@ -1707,33 +1707,30 @@ bool AArch64DAGToDAGISel::tryAuthLoad(SDNode *N) { Base = Base.getOperand(0); } - if (!isShiftedInt<10, 3>(OffsetVal)) - return false; - // The base must be of the form: - // (int_ptrauth_auth , da/db, 0) + // (int_ptrauth_auth , , ) + // with disc being either a constant int, or: + // (int_ptrauth_blend , ) if (Base.getOpcode() != ISD::INTRINSIC_WO_CHAIN) return false; unsigned IntID = cast(Base.getOperand(0))->getZExtValue(); if (IntID != Intrinsic::ptrauth_auth) return false; - unsigned IntKey = cast(Base.getOperand(2))->getZExtValue(); - if (!isNullConstant(Base.getOperand(3))) - return false; - // If the pointer is an address computation based on an intermediate auth - // that's used more than once, it's not worth folding the auth, as we can't - // writeback just the auth result (without the address computation). - // - // FIXME: we can turn it into an unchecked auth though. - if (OffsetVal && !Base.hasOneUse()) - return false; + unsigned KeyC = cast(Base.getOperand(2))->getZExtValue(); + bool IsDKey = KeyC == AArch64PACKey::DA || KeyC == AArch64PACKey::DB; + SDValue Disc = Base.getOperand(3); Base = Base.getOperand(1); + bool ZeroDisc = isNullConstant(Disc); + SDValue IntDisc, AddrDisc; + std::tie(IntDisc, AddrDisc) = + extractPtrauthBlendDiscriminators(Disc, CurDAG); + // If this is an indexed pre-inc load, we obviously need the writeback form. - bool needsWriteback = isPre; + bool needsWriteback = IsPre; // If not, but the base authenticated pointer has any other use, it's // beneficial to use the writeback form, to "writeback" the auth, even if // there is no base+offset addition. @@ -1761,45 +1758,78 @@ bool AArch64DAGToDAGISel::tryAuthLoad(SDNode *N) { return false; } - unsigned Opc = 0; - switch (IntKey) { - case AArch64PACKey::DA: - Opc = needsWriteback ? AArch64::LDRAAwriteback : AArch64::LDRAAindexed; - break; - case AArch64PACKey::DB: - Opc = needsWriteback ? AArch64::LDRABwriteback : AArch64::LDRABindexed; - break; - default: - return false; - } - + // We have 2 main isel alternatives: + // - LDRAA/LDRAB, writeback or indexed. Zero disc, small offsets, D key. + // - LDRA/LDRApre. Pointer needs to be in X16. SDLoc DL(N); - // The offset is encoded as scaled, for an element size of 8 bytes. - SDValue Offset = CurDAG->getTargetConstant(OffsetVal / 8, DL, MVT::i64); - SDValue Ops[] = { Base, Offset, Chain }; - SDNode *Res = needsWriteback ? - CurDAG->getMachineNode(Opc, DL, MVT::i64, MVT::i64, MVT::Other, Ops) : - CurDAG->getMachineNode(Opc, DL, MVT::i64, MVT::Other, Ops); - - if (isPre) { + MachineSDNode *Res = nullptr; + SDValue Writeback, ResVal, OutChain; + + // If the discriminator is zero and the offset fits, we can use LDRAA/LDRAB. + // Do that here to avoid needlessly constraining regalloc into using X16. + if (ZeroDisc && isShiftedInt<10, 3>(OffsetVal) && IsDKey) { + unsigned Opc = 0; + switch (KeyC) { + case AArch64PACKey::DA: + Opc = needsWriteback ? AArch64::LDRAAwriteback : AArch64::LDRAAindexed; + break; + case AArch64PACKey::DB: + Opc = needsWriteback ? AArch64::LDRABwriteback : AArch64::LDRABindexed; + break; + default: + llvm_unreachable("Invalid key for LDRAA/LDRAB"); + } + // The offset is encoded as scaled, for an element size of 8 bytes. + SDValue Offset = CurDAG->getTargetConstant(OffsetVal / 8, DL, MVT::i64); + SDValue Ops[] = {Base, Offset, Chain}; + Res = needsWriteback ? + CurDAG->getMachineNode(Opc, DL, MVT::i64, MVT::i64, MVT::Other, Ops) : + CurDAG->getMachineNode(Opc, DL, MVT::i64, MVT::Other, Ops); + if (needsWriteback) { + Writeback = SDValue(Res, 0); + ResVal = SDValue(Res, 1); + OutChain = SDValue(Res, 2); + } else { + ResVal = SDValue(Res, 0); + OutChain = SDValue(Res, 1); + } + } else { + // Otherwise, use the generalized LDRA pseudos. + unsigned Opc = needsWriteback ? AArch64::LDRApre : AArch64::LDRA; + + SDValue X16Copy = CurDAG->getCopyToReg(Chain, DL, AArch64::X16, + Base, SDValue()); + SDValue Offset = CurDAG->getTargetConstant(OffsetVal, DL, MVT::i64); + SDValue Key = CurDAG->getTargetConstant(KeyC, DL, MVT::i32); + SDValue Ops[] = {Offset, Key, IntDisc, AddrDisc, X16Copy.getValue(1)}; + Res = CurDAG->getMachineNode(Opc, DL, MVT::i64, MVT::Other, MVT::Glue, Ops); + if (needsWriteback) + Writeback = CurDAG->getCopyFromReg(SDValue(Res, 1), DL, AArch64::X16, + MVT::i64, SDValue(Res, 2)); + ResVal = SDValue(Res, 0); + OutChain = SDValue(Res, 1); + } + + if (IsPre) { // If the original load was pre-inc, the resulting LDRA is writeback. assert(needsWriteback && "preinc loads can't be selected into non-wb ldra"); - ReplaceUses(SDValue(N, 1), SDValue(Res, 0)); // writeback - ReplaceUses(SDValue(N, 0), SDValue(Res, 1)); // loaded value - ReplaceUses(SDValue(N, 2), SDValue(Res, 2)); // chain + ReplaceUses(SDValue(N, 1), Writeback); // writeback + ReplaceUses(SDValue(N, 0), ResVal); // loaded value + ReplaceUses(SDValue(N, 2), OutChain); // chain } else if (needsWriteback) { // If the original load was unindexed, but we emitted a writeback form, // we need to replace the uses of the original auth(signedbase)[+offset] // computation. - ReplaceUses(Ptr, SDValue(Res, 0)); // writeback - ReplaceUses(SDValue(N, 0), SDValue(Res, 1)); // loaded value - ReplaceUses(SDValue(N, 1), SDValue(Res, 2)); // chain + ReplaceUses(Ptr, Writeback); // writeback + ReplaceUses(SDValue(N, 0), ResVal); // loaded value + ReplaceUses(SDValue(N, 1), OutChain); // chain } else { // Otherwise, we selected a simple load to a simple non-wb ldra. assert(Ptr.hasOneUse() && "reused auth ptr should be folded into ldra"); - ReplaceUses(SDValue(N, 0), SDValue(Res, 0)); // loaded value - ReplaceUses(SDValue(N, 1), SDValue(Res, 1)); // chain + ReplaceUses(SDValue(N, 0), ResVal); // loaded value + ReplaceUses(SDValue(N, 1), OutChain); // chain } + CurDAG->RemoveDeadNode(N); return true; } diff --git a/llvm/lib/Target/AArch64/AArch64InstrGISel.td b/llvm/lib/Target/AArch64/AArch64InstrGISel.td index 2d2b2bee99ec4..1b544c4f8c19a 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrGISel.td +++ b/llvm/lib/Target/AArch64/AArch64InstrGISel.td @@ -25,6 +25,23 @@ def G_ADD_LOW : AArch64GenericInstruction { let hasSideEffects = 0; } +// Represents an auth-load instruction. Produced post-legalization from +// G_LOADs of ptrauth_auth intrinsics, with variants for keys/discriminators. +def G_LDRA : AArch64GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type1:$addr, i64imm:$offset, i32imm:$key, i64imm:$disc, type0:$addrdisc); + let hasSideEffects = 0; + let mayLoad = 1; +} + +// Represents a pre-inc writeback auth-load instruction. Similar to G_LDRA. +def G_LDRApre : AArch64GenericInstruction { + let OutOperandList = (outs type0:$dst, ptype1:$newaddr); + let InOperandList = (ins ptype1:$addr, i64imm:$offset, i32imm:$key, i64imm:$disc, type0:$addrdisc); + let hasSideEffects = 0; + let mayLoad = 1; +} + // Pseudo for a rev16 instruction. Produced post-legalization from // G_SHUFFLE_VECTORs with appropriate masks. def G_REV16 : AArch64GenericInstruction { diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 583d32b12a263..3a339886ce7de 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -1860,6 +1860,42 @@ let Predicates = [HasPAuth] in { let Size = 8; } + // LDRA pseudo: generalized LDRAA/Bindexed, allowing arbitrary discriminators, + // and wider offsets. + // This directly manipulates x16/x17, which are the only registers the OS + // guarantees are safe to use for sensitive operations. + // The loaded value is in $Rt. The signed pointer is in X16. + // $Rt could be GPR64 but is GPR64noip to help out regalloc: we imp-def 2/3rds + // of the difference between the two, and the 3rd reg (LR) is often reserved. + def LDRA : Pseudo<(outs GPR64noip:$Rt), + (ins i64imm:$Offset, i32imm:$Key, i64imm:$Disc, + GPR64noip:$AddrDisc), + []>, Sched<[]> { + let isCodeGenOnly = 1; + let hasSideEffects = 1; + let mayStore = 0; + let mayLoad = 1; + let Size = 48; + let Defs = [X16,X17]; + let Uses = [X16]; + } + + // Pre-indexed + writeback variant of LDRA. + // The signed pointer is in X16, and is written back, after being + // authenticated and offset, into X16. + def LDRApre : Pseudo<(outs GPR64noip:$Rt), + (ins i64imm:$Offset, i32imm:$Key, i64imm:$Disc, + GPR64noip:$AddrDisc), + []>, Sched<[]> { + let isCodeGenOnly = 1; + let hasSideEffects = 1; + let mayStore = 0; + let mayLoad = 1; + let Size = 48; + let Defs = [X16,X17]; + let Uses = [X16]; + } + // Size 12: 4 fixed + 8 variable to compute discriminator. // potentially + 8 to check LR. let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Size = 12, diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp index 894aa0e22bfdd..fde3ce8b380b8 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp @@ -226,6 +226,7 @@ class AArch64InstructionSelector : public InstructionSelector { bool selectTLSGlobalValue(MachineInstr &I, MachineRegisterInfo &MRI); bool selectPtrAuthGlobalValue(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectAuthLoad(MachineInstr &I, MachineRegisterInfo &MRI); bool selectReduction(MachineInstr &I, MachineRegisterInfo &MRI); bool selectMOPS(MachineInstr &I, MachineRegisterInfo &MRI); bool selectUSMovFromExtend(MachineInstr &I, MachineRegisterInfo &MRI); @@ -2853,6 +2854,10 @@ bool AArch64InstructionSelector::select(MachineInstr &I) { case TargetOpcode::G_PTRAUTH_GLOBAL_VALUE: return selectPtrAuthGlobalValue(I, MRI); + case AArch64::G_LDRA: + case AArch64::G_LDRApre: + return selectAuthLoad(I, MRI); + case TargetOpcode::G_ZEXTLOAD: case TargetOpcode::G_LOAD: case TargetOpcode::G_STORE: { @@ -6805,6 +6810,72 @@ bool AArch64InstructionSelector::selectPtrAuthGlobalValue( return true; } +bool AArch64InstructionSelector::selectAuthLoad( + MachineInstr &I, MachineRegisterInfo &MRI) { + bool Writeback = I.getOpcode() == AArch64::G_LDRApre; + + Register ValReg = I.getOperand(0).getReg(); + Register PtrReg = I.getOperand(1 + Writeback).getReg(); + int64_t Offset = I.getOperand(2 + Writeback).getImm(); + auto Key = static_cast(I.getOperand(3 + Writeback).getImm()); + uint64_t DiscImm = I.getOperand(4 + Writeback).getImm(); + Register AddrDisc = I.getOperand(5 + Writeback).getReg(); + + bool IsDKey = Key == AArch64PACKey::DA || Key == AArch64PACKey::DB; + bool ZeroDisc = AddrDisc == AArch64::NoRegister && !DiscImm; + + // If the discriminator is zero and the offset fits, we can use LDRAA/LDRAB. + // Do that here to avoid needlessly constraining regalloc into using X16. + if (ZeroDisc && isShiftedInt<10, 3>(Offset) && IsDKey) { + unsigned Opc = 0; + switch (Key) { + case AArch64PACKey::DA: + Opc = Writeback ? AArch64::LDRAAwriteback : AArch64::LDRAAindexed; + break; + case AArch64PACKey::DB: + Opc = Writeback ? AArch64::LDRABwriteback : AArch64::LDRABindexed; + break; + default: + llvm_unreachable("Invalid key for LDRAA/LDRAB"); + } + // The LDRAA/LDRAB offset immediate is scaled. + Offset /= 8; + if (Writeback) { + MIB.buildInstr(Opc, {I.getOperand(1).getReg(), ValReg}, {PtrReg, Offset}) + .constrainAllUses(TII, TRI, RBI); + RBI.constrainGenericRegister(I.getOperand(1).getReg(), + AArch64::GPR64spRegClass, MRI); + } else { + MIB.buildInstr(Opc, {ValReg}, {PtrReg, Offset}) + .constrainAllUses(TII, TRI, RBI); + } + I.eraseFromParent(); + return true; + } + + if (AddrDisc == AArch64::NoRegister) + AddrDisc = AArch64::XZR; + + // Otherwise, use the generalized LDRA pseudo. + MIB.buildCopy(AArch64::X16, PtrReg); + if (Writeback) { + MIB.buildInstr(AArch64::LDRApre, {ValReg}, + {Offset, uint64_t(Key), DiscImm, AddrDisc}) + .constrainAllUses(TII, TRI, RBI); + MIB.buildCopy(I.getOperand(1).getReg(), (Register)AArch64::X16); + RBI.constrainGenericRegister(I.getOperand(1).getReg(), + AArch64::GPR64RegClass, MRI); + } else { + MIB.buildInstr(AArch64::LDRA, {ValReg}, + {Offset, uint64_t(Key), DiscImm, AddrDisc}) + .constrainAllUses(TII, TRI, RBI); + } + + I.eraseFromParent(); + return true; +} + + void AArch64InstructionSelector::SelectTable(MachineInstr &I, MachineRegisterInfo &MRI, unsigned NumVec, unsigned Opc1, diff --git a/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerLowering.cpp b/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerLowering.cpp index 4a1977ba1a00f..3f85f0eb727c6 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerLowering.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerLowering.cpp @@ -1087,6 +1087,121 @@ void applyVectorSextInReg(MachineInstr &MI, MachineRegisterInfo &MRI, LegalizerHelper Helper(*MI.getMF(), Observer, B); Helper.lower(MI, 0, /* Unused hint type */ LLT()); } +struct AuthLoadMatchInfo { + Register Dst = 0; + Register Addr = 0; + int64_t Offset = 0; + unsigned Key = 0; + Register Disc = 0; + + bool Writeback = false; + Register NewAddr = 0; +}; + +static bool matchFormAuthLoad(MachineInstr &MI, MachineRegisterInfo &MRI, + CombinerHelper &Helper, + AuthLoadMatchInfo &MatchInfo) { + assert(MI.getOpcode() == TargetOpcode::G_LOAD); + if (!MI.getMF()->getSubtarget().hasPAuth()) + return false; + + MatchInfo.Dst = MI.getOperand(0).getReg(); + + LLT DstTy = MRI.getType(MatchInfo.Dst); + if (DstTy != LLT::scalar(64) && DstTy != LLT::pointer(0, 64)) + return false; + + Register LoadPtr = MI.getOperand(1).getReg(); + if (MRI.getType(LoadPtr) != LLT::pointer(0, 64)) + return false; + + // When matching the writeback variant, we may need to writeback either the + // ptr-typed (used by the G_LOAD) or int-typed (def'd by @llvm.ptrauth.auth) + // base (+offset, with auth) pointer. Try at each level, but at most once. + auto TryWriteback = [&](Register Ptr, MachineInstr &KnownUseMI) { + if (MRI.hasOneNonDBGUse(Ptr)) + return true; + if (MatchInfo.Writeback) + return false; + for (auto &UseMI : MRI.use_nodbg_instructions(Ptr)) + if (&KnownUseMI != &UseMI && !Helper.dominates(MI, UseMI)) + return false; + MatchInfo.Writeback = true; + MatchInfo.NewAddr = Ptr; + return true; + }; + + if (!TryWriteback(LoadPtr, MI)) + return false; + + // Try to match different variants of offset additions to find the base ptr. + Register BasePtr = AArch64::NoRegister; + + MachineInstr *LoadPtrDef = getDefIgnoringCopies(LoadPtr, MRI); + if (!LoadPtrDef) + return false; + + if (LoadPtrDef->getOpcode() == TargetOpcode::G_INTTOPTR) { + Register IntPtr = LoadPtrDef->getOperand(1).getReg(); + + // Check if the int-typed ptr is the one in need of writeback. + if (!TryWriteback(IntPtr, *LoadPtrDef)) + return false; + + if (!mi_match(IntPtr, MRI, m_any_of( + m_GAdd(m_Reg(BasePtr), m_ICst(MatchInfo.Offset)), + m_Reg(BasePtr)))) + return false; + + } else if (!mi_match(*LoadPtrDef, MRI, m_GPtrAdd(m_GIntToPtr(m_Reg(BasePtr)), + m_ICst(MatchInfo.Offset)))) { + return false; + } + + MachineInstr *AUT = getOpcodeDef(TargetOpcode::G_INTRINSIC, BasePtr, MRI); + if (!AUT || + cast(AUT)->getIntrinsicID() != Intrinsic::ptrauth_auth) + return false; + + Register RawPtr; + if (!mi_match(AUT->getOperand(2).getReg(), MRI, m_GPtrToInt(m_Reg(RawPtr)))) + return false; + + MatchInfo.Addr = RawPtr; + MatchInfo.Key = AUT->getOperand(3).getImm(); + MatchInfo.Disc = AUT->getOperand(4).getReg(); + return true; +} + +static bool applyFormAuthLoad(MachineInstr &MI, MachineRegisterInfo &MRI, + MachineIRBuilder &B, + CombinerHelper &Helper, + GISelChangeObserver &Observer, + AuthLoadMatchInfo &MatchInfo) { + MachineIRBuilder MIB(MI); + + Register AddrDisc; + uint16_t DiscImm; + std::tie(DiscImm, AddrDisc) = + extractPtrauthBlendDiscriminators(MatchInfo.Disc, MRI); + + if (MatchInfo.Writeback) { + MachineInstr &AddrDef = *MRI.getUniqueVRegDef(MatchInfo.NewAddr); + MIB.buildInstr(AArch64::G_LDRApre, {MatchInfo.Dst, MatchInfo.NewAddr}, + {MatchInfo.Addr, MatchInfo.Offset, (uint64_t)MatchInfo.Key, + (uint64_t)DiscImm, AddrDisc}) + .addMemOperand(*MI.memoperands_begin()); + AddrDef.eraseFromParent(); + } else { + MIB.buildInstr(AArch64::G_LDRA, {MatchInfo.Dst}, + {MatchInfo.Addr, MatchInfo.Offset, (uint64_t)MatchInfo.Key, + (uint64_t)DiscImm, AddrDisc}) + .addMemOperand(*MI.memoperands_begin()); + } + + MI.eraseFromParent(); + return true; +} /// Combine , unused = unmerge(G_EXT <2*N x t> v, undef, N) /// => unused, = unmerge v diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll deleted file mode 100644 index 2fe14beb3e4c0..0000000000000 --- a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-load.ll +++ /dev/null @@ -1,301 +0,0 @@ -; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py -; RUN: llc < %s -mtriple arm64e-apple-darwin -aarch64-enable-sink-fold=1 -verify-machineinstrs | FileCheck %s - -; FIXME: remove explicit sink-fold enable once it's stable. -; rdar://117833011 - -target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" - -define i64 @test_load_auth_da(i64* %ptr) { -; CHECK-LABEL: test_load_auth_da: -; CHECK: ; %bb.0: -; CHECK-NEXT: ldraa x0, [x0] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) - %tmp2 = inttoptr i64 %tmp1 to i64* - %tmp3 = load i64, i64* %tmp2 - ret i64 %tmp3 -} - -define i64 @test_load_auth_db(i64* %ptr) { -; CHECK-LABEL: test_load_auth_db: -; CHECK: ; %bb.0: -; CHECK-NEXT: ldrab x0, [x0] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) - %tmp2 = inttoptr i64 %tmp1 to i64* - %tmp3 = load i64, i64* %tmp2 - ret i64 %tmp3 -} - -; Offset. - -define i64 @test_load_auth_da_8(i64* %ptr) { -; CHECK-LABEL: test_load_auth_da_8: -; CHECK: ; %bb.0: -; CHECK-NEXT: ldraa x0, [x0, #8] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) - %tmp2 = add i64 %tmp1, 8 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - ret i64 %tmp4 -} - -define i64 @test_load_auth_da_m8(i64* %ptr) { -; CHECK-LABEL: test_load_auth_da_m8: -; CHECK: ; %bb.0: -; CHECK-NEXT: ldraa x0, [x0, #-8] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) - %tmp2 = add i64 %tmp1, -8 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - ret i64 %tmp4 -} - -define i64 @test_load_auth_db_4088(i64* %ptr) { -; CHECK-LABEL: test_load_auth_db_4088: -; CHECK: ; %bb.0: -; CHECK-NEXT: ldrab x0, [x0, #4088] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) - %tmp2 = add i64 %tmp1, 4088 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - ret i64 %tmp4 -} - -; Offset invalid cases. - -define i64 @test_load_auth_da_4(i64* %ptr) { -; CHECK-LABEL: test_load_auth_da_4: -; CHECK: ; %bb.0: -; CHECK-NEXT: mov x16, x0 -; CHECK-NEXT: autdza x16 -; CHECK-NEXT: ldur x0, [x16, #4] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) - %tmp2 = add i64 %tmp1, 4 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - ret i64 %tmp4 -} - -define i64 @test_load_auth_da_4096(i64* %ptr) { -; CHECK-LABEL: test_load_auth_da_4096: -; CHECK: ; %bb.0: -; CHECK-NEXT: mov x16, x0 -; CHECK-NEXT: autdza x16 -; CHECK-NEXT: ldr x0, [x16, #4096] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) - %tmp2 = add i64 %tmp1, 4096 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - ret i64 %tmp4 -} - -; Pre-indexed variant. - -define i64* @test_load_auth_da_8_pre(i64* %ptr, i64* %dst) { -; CHECK-LABEL: test_load_auth_da_8_pre: -; CHECK: ; %bb.0: -; CHECK-NEXT: ldraa x8, [x0, #8]! -; CHECK-NEXT: str x8, [x1] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) - %tmp2 = add i64 %tmp1, 8 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - store i64 %tmp4, i64* %dst - ret i64* %tmp3 -} - -define i64* @test_load_auth_db_248_pre(i64* %ptr, i64* %dst) { -; CHECK-LABEL: test_load_auth_db_248_pre: -; CHECK: ; %bb.0: -; CHECK-NEXT: ldrab x8, [x0, #248]! -; CHECK-NEXT: str x8, [x1] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) - %tmp2 = add i64 %tmp1, 248 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - store i64 %tmp4, i64* %dst - ret i64* %tmp3 -} - -define i64* @test_load_auth_db_m256_pre(i64* %ptr, i64* %dst) { -; CHECK-LABEL: test_load_auth_db_m256_pre: -; CHECK: ; %bb.0: -; CHECK-NEXT: ldrab x8, [x0, #-256]! -; CHECK-NEXT: str x8, [x1] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) - %tmp2 = add i64 %tmp1, -256 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - store i64 %tmp4, i64* %dst - ret i64* %tmp3 -} - -; "Pre-indexed" with index 0: writeback the auth result. - -define i64* @test_load_auth_da_0_pre(i64* %ptr, i64* %dst) { -; CHECK-LABEL: test_load_auth_da_0_pre: -; CHECK: ; %bb.0: -; CHECK-NEXT: ldraa x8, [x0, #0]! -; CHECK-NEXT: str x8, [x1] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) - %tmp2 = inttoptr i64 %tmp1 to i64* - %tmp3 = load i64, i64* %tmp2 - store i64 %tmp3, i64* %dst - ret i64* %tmp2 -} - -; "Pre-indexed" with index 0, with a potential cycle. - -define void @test_load_auth_da_0_pre_cycle(i64* %ptr, i64* %dst, i64* %dst2, i64* %dst3) { -; CHECK-LABEL: test_load_auth_da_0_pre_cycle: -; CHECK: ; %bb.0: -; CHECK-NEXT: mov x16, x0 -; CHECK-NEXT: autdza x16 -; CHECK-NEXT: str x16, [x2] -; CHECK-NEXT: ldr x8, [x16] -; CHECK-NEXT: str x8, [x1] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) - %tmp2 = inttoptr i64 %tmp1 to i64* - store i64 %tmp1, i64* %dst2 - %tmp3 = load i64, i64* %tmp2 - store i64 %tmp3, i64* %dst - ret void -} - -; Pre-indexed invalid offsets. - -define i64* @test_load_auth_db_4_pre(i64* %ptr, i64* %dst) { -; CHECK-LABEL: test_load_auth_db_4_pre: -; CHECK: ; %bb.0: -; CHECK-NEXT: mov x16, x0 -; CHECK-NEXT: autdzb x16 -; CHECK-NEXT: mov x0, x16 -; CHECK-NEXT: ldr x8, [x0, #4]! -; CHECK-NEXT: str x8, [x1] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) - %tmp2 = add i64 %tmp1, 4 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - store i64 %tmp4, i64* %dst - ret i64* %tmp3 -} - -define i64* @test_load_auth_db_4096_pre(i64* %ptr, i64* %dst) { -; CHECK-LABEL: test_load_auth_db_4096_pre: -; CHECK: ; %bb.0: -; CHECK-NEXT: mov x16, x0 -; CHECK-NEXT: autdzb x16 -; CHECK-NEXT: ldr x8, [x16, #4096] -; CHECK-NEXT: str x8, [x1] -; CHECK-NEXT: add x0, x16, #1, lsl #12 ; =4096 -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) - %tmp2 = add i64 %tmp1, 4096 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - store i64 %tmp4, i64* %dst - ret i64* %tmp3 -} - -define i64* @test_load_auth_db_256_pre(i64* %ptr, i64* %dst) { -; CHECK-LABEL: test_load_auth_db_256_pre: -; CHECK: ; %bb.0: -; CHECK-NEXT: ldrab x8, [x0, #256]! -; CHECK-NEXT: str x8, [x1] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) - %tmp2 = add i64 %tmp1, 256 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - store i64 %tmp4, i64* %dst - ret i64* %tmp3 -} - -define i64* @test_load_auth_db_m264_pre(i64* %ptr, i64* %dst) { -; CHECK-LABEL: test_load_auth_db_m264_pre: -; CHECK: ; %bb.0: -; CHECK-NEXT: ldrab x8, [x0, #-264]! -; CHECK-NEXT: str x8, [x1] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) - %tmp2 = add i64 %tmp1, -264 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - store i64 %tmp4, i64* %dst - ret i64* %tmp3 -} - -; Pre-indexed multiple-use of the auth. - -define i64* @test_load_auth_da_8_pre_use(i64* %ptr, i64* %dst, i64* %dst2) { -; CHECK-LABEL: test_load_auth_da_8_pre_use: -; CHECK: ; %bb.0: -; CHECK-NEXT: mov x16, x0 -; CHECK-NEXT: autdza x16 -; CHECK-NEXT: mov x0, x16 -; CHECK-NEXT: ldr x8, [x0, #8]! -; CHECK-NEXT: str x8, [x1] -; CHECK-NEXT: str x16, [x2] -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) - %tmp2 = add i64 %tmp1, 8 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - store i64 %tmp4, i64* %dst - store i64 %tmp1, i64* %dst2 - ret i64* %tmp3 -} - -; Pre-indexed multiple-use of the auth, invalid offset. - -define i64* @test_load_auth_da_256_pre_use(i64* %ptr, i64* %dst, i64* %dst2) { -; CHECK-LABEL: test_load_auth_da_256_pre_use: -; CHECK: ; %bb.0: -; CHECK-NEXT: mov x16, x0 -; CHECK-NEXT: autdza x16 -; CHECK-NEXT: ldr x8, [x16, #256] -; CHECK-NEXT: str x8, [x1] -; CHECK-NEXT: str x16, [x2] -; CHECK-NEXT: add x0, x16, #256 -; CHECK-NEXT: ret - %tmp0 = ptrtoint i64* %ptr to i64 - %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) - %tmp2 = add i64 %tmp1, 256 - %tmp3 = inttoptr i64 %tmp2 to i64* - %tmp4 = load i64, i64* %tmp3 - store i64 %tmp4, i64* %dst - store i64 %tmp1, i64* %dst2 - ret i64* %tmp3 -} - -declare i64 @llvm.ptrauth.auth(i64, i32, i64) diff --git a/llvm/test/CodeGen/AArch64/ptrauth-load.ll b/llvm/test/CodeGen/AArch64/ptrauth-load.ll new file mode 100644 index 0000000000000..6e7c42ea76799 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/ptrauth-load.ll @@ -0,0 +1,716 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple arm64e-apple-darwin -verify-machineinstrs -global-isel=0 | FileCheck %s +; RUN: llc < %s -mtriple arm64e-apple-darwin -verify-machineinstrs -global-isel=1 -global-isel-abort=1 | FileCheck %s + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + +; Basic: no discriminator, no offset. + +define i64 @test_da(ptr %ptr) { +; CHECK-LABEL: test_da: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldraa x0, [x0] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = inttoptr i64 %tmp1 to ptr + %tmp3 = load i64, ptr %tmp2 + ret i64 %tmp3 +} + +define i64 @test_db(ptr %ptr) { +; CHECK-LABEL: test_db: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldrab x0, [x0] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) + %tmp2 = inttoptr i64 %tmp1 to ptr + %tmp3 = load i64, ptr %tmp2 + ret i64 %tmp3 +} + +define i64 @test_ia(ptr %ptr) { +; CHECK-LABEL: test_ia: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autiza x16 +; CHECK-NEXT: ldr x0, [x16] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 0, i64 0) + %tmp2 = inttoptr i64 %tmp1 to ptr + %tmp3 = load i64, ptr %tmp2 + ret i64 %tmp3 +} + +define i64 @test_ib(ptr %ptr) { +; CHECK-LABEL: test_ib: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autizb x16 +; CHECK-NEXT: ldr x0, [x16] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 1, i64 0) + %tmp2 = inttoptr i64 %tmp1 to ptr + %tmp3 = load i64, ptr %tmp2 + ret i64 %tmp3 +} + +; No discriminator, interesting offsets. + +define i64 @test_da_8(ptr %ptr) { +; CHECK-LABEL: test_da_8: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldraa x0, [x0, #8] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 8 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + ret i64 %tmp4 +} + +define i64 @test_db_simm9max(ptr %ptr) { +; CHECK-LABEL: test_db_simm9max: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldrab x0, [x0, #4088] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, 4088 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + ret i64 %tmp4 +} + +define i64 @test_db_uimm12max(ptr %ptr) { +; CHECK-LABEL: test_db_uimm12max: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdzb x16 +; CHECK-NEXT: ldr x0, [x16, #32760] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, 32760 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + ret i64 %tmp4 +} + +define i64 @test_da_4(ptr %ptr) { +; CHECK-LABEL: test_da_4: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: ldur x0, [x16, #4] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 4 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + ret i64 %tmp4 +} + +define i64 @test_da_largeoff_12b(ptr %ptr) { +; CHECK-LABEL: test_da_largeoff_12b: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: mov x17, #32768 +; CHECK-NEXT: ldr x0, [x16, x17] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 32768 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + ret i64 %tmp4 +} + +define i64 @test_da_largeoff_32b(ptr %ptr) { +; CHECK-LABEL: test_da_largeoff_32b: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: mov x17, #2 +; CHECK-NEXT: movk x17, #1, lsl #32 +; CHECK-NEXT: ldr x0, [x16, x17] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 4294967298 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + ret i64 %tmp4 +} + +define i64 @test_da_m8(ptr %ptr) { +; CHECK-LABEL: test_da_m8: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldraa x0, [x0, #-8] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, -8 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + ret i64 %tmp4 +} + +define i64 @test_db_simm9min(ptr %ptr) { +; CHECK-LABEL: test_db_simm9min: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldrab x0, [x0, #-4096] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, -4096 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + ret i64 %tmp4 +} + +define i64 @test_da_neg_largeoff_12b(ptr %ptr) { +; CHECK-LABEL: test_da_neg_largeoff_12b: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: mov x17, #-32768 +; CHECK-NEXT: ldr x0, [x16, x17] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, -32768 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + ret i64 %tmp4 +} + +define i64 @test_da_neg_largeoff_32b(ptr %ptr) { +; CHECK-LABEL: test_da_neg_largeoff_32b: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: mov x17, #-3 +; CHECK-NEXT: movk x17, #65534, lsl #32 +; CHECK-NEXT: ldr x0, [x16, x17] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, -4294967299 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + ret i64 %tmp4 +} + +define i64 @test_da_disc_m256(ptr %ptr, i64 %disc) { +; CHECK-LABEL: test_da_disc_m256: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autda x16, x1 +; CHECK-NEXT: ldur x0, [x16, #-256] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 %disc) + %tmp2 = add i64 %tmp1, -256 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + ret i64 %tmp4 +} + +; No discriminator, interesting offsets, writeback. + +define ptr @test_da_wb(ptr %ptr, ptr %dst) { +; CHECK-LABEL: test_da_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldraa x8, [x0, #0]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = inttoptr i64 %tmp1 to ptr + %tmp3 = load i64, ptr %tmp2 + store i64 %tmp3, ptr %dst + ret ptr %tmp2 +} + +define ptr @test_da_8_wb(ptr %ptr, ptr %dst) { +; CHECK-LABEL: test_da_8_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldraa x8, [x0, #8]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 8 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + store i64 %tmp4, ptr %dst + ret ptr %tmp3 +} + +define ptr @test_da_simm9max_wb(ptr %ptr, ptr %dst) { +; CHECK-LABEL: test_da_simm9max_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldraa x8, [x0, #4088]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 4088 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + store i64 %tmp4, ptr %dst + ret ptr %tmp3 +} + +define ptr @test_da_uimm12max_wb(ptr %ptr, ptr %dst) { +; CHECK-LABEL: test_da_uimm12max_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: mov x17, #32760 +; CHECK-NEXT: add x16, x16, x17 +; CHECK-NEXT: ldr x8, [x16] +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 32760 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + store i64 %tmp4, ptr %dst + ret ptr %tmp3 +} + +define ptr @test_db_4_wb(ptr %ptr, ptr %dst) { +; CHECK-LABEL: test_db_4_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdzb x16 +; CHECK-NEXT: ldr x8, [x16, #4]! +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, 4 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + store i64 %tmp4, ptr %dst + ret ptr %tmp3 +} + +define ptr @test_da_largeoff_12b_wb(ptr %ptr, ptr %dst) { +; CHECK-LABEL: test_da_largeoff_12b_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: mov x17, #32768 +; CHECK-NEXT: add x16, x16, x17 +; CHECK-NEXT: ldr x8, [x16] +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 32768 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + store i64 %tmp4, ptr %dst + ret ptr %tmp3 +} + +define ptr @test_db_m256_wb(ptr %ptr, ptr %dst) { +; CHECK-LABEL: test_db_m256_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldrab x8, [x0, #-256]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, -256 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + store i64 %tmp4, ptr %dst + ret ptr %tmp3 +} + +define ptr @test_db_simm9min_wb(ptr %ptr, ptr %dst) { +; CHECK-LABEL: test_db_simm9min_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldrab x8, [x0, #-4096]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, -4096 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + store i64 %tmp4, ptr %dst + ret ptr %tmp3 +} + +define ptr @test_db_neg_largeoff_12b_wb(ptr %ptr, ptr %dst) { +; CHECK-LABEL: test_db_neg_largeoff_12b_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdzb x16 +; CHECK-NEXT: mov x17, #-32768 +; CHECK-NEXT: add x16, x16, x17 +; CHECK-NEXT: ldr x8, [x16] +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 3, i64 0) + %tmp2 = add i64 %tmp1, -32768 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + store i64 %tmp4, ptr %dst + ret ptr %tmp3 +} + +; Writeback, with a potential cycle. + +define void @test_da_wb_cycle(ptr %ptr, ptr %dst, ptr %dst2, ptr %dst3) { +; CHECK-LABEL: test_da_wb_cycle: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: str x16, [x2] +; CHECK-NEXT: ldr x8, [x16] +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = inttoptr i64 %tmp1 to ptr + store i64 %tmp1, ptr %dst2 + %tmp3 = load i64, ptr %tmp2 + store i64 %tmp3, ptr %dst + ret void +} + +; Writeback multiple-use of the auth. + +define ptr @test_da_8_wb_use(ptr %ptr, ptr %dst, ptr %dst2) { +; CHECK-LABEL: test_da_8_wb_use: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: ldraa x8, [x0, #8]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: str x16, [x2] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 8 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + store i64 %tmp4, ptr %dst + store i64 %tmp1, ptr %dst2 + ret ptr %tmp3 +} + +; Writeback multiple-use of the auth, invalid offset. + +define ptr @test_da_256_wb_use(ptr %ptr, ptr %dst, ptr %dst2) { +; CHECK-LABEL: test_da_256_wb_use: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autdza x16 +; CHECK-NEXT: ldraa x8, [x0, #256]! +; CHECK-NEXT: str x8, [x1] +; CHECK-NEXT: str x16, [x2] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 0) + %tmp2 = add i64 %tmp1, 256 + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + store i64 %tmp4, ptr %dst + store i64 %tmp1, ptr %dst2 + ret ptr %tmp3 +} + +; Integer discriminator, no offset. + +define i64 @test_da_constdisc(ptr %ptr) { +; CHECK-LABEL: test_da_constdisc: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, #12345 +; CHECK-NEXT: autda x16, x17 +; CHECK-NEXT: ldr x0, [x16] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 12345) + %tmp2 = inttoptr i64 %tmp1 to ptr + %tmp3 = load i64, ptr %tmp2 + ret i64 %tmp3 +} + +define i64 @test_ib_constdisc(ptr %ptr) { +; CHECK-LABEL: test_ib_constdisc: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, #12345 +; CHECK-NEXT: autib x16, x17 +; CHECK-NEXT: ldr x0, [x16] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 1, i64 12345) + %tmp2 = inttoptr i64 %tmp1 to ptr + %tmp3 = load i64, ptr %tmp2 + ret i64 %tmp3 +} + +; "Address" (register) discriminator, no offset. + +define i64 @test_da_addrdisc(ptr %ptr, i64 %disc) { +; CHECK-LABEL: test_da_addrdisc: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: autda x16, x1 +; CHECK-NEXT: ldr x0, [x16] +; CHECK-NEXT: ret + %tmp0 = ptrtoint ptr %ptr to i64 + %tmp1 = call i64 @llvm.ptrauth.auth(i64 %tmp0, i32 2, i64 %disc) + %tmp2 = inttoptr i64 %tmp1 to ptr + %tmp3 = load i64, ptr %tmp2 + ret i64 %tmp3 +} + +; Blend discriminator, no offset. + +define i64 @test_da_blend(ptr %ptr, i64 %disc) { +; CHECK-LABEL: test_da_blend: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, x1 +; CHECK-NEXT: movk x17, #12345, lsl #48 +; CHECK-NEXT: autda x16, x17 +; CHECK-NEXT: ldr x0, [x16] +; CHECK-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %disc, i64 12345) + %tmp1 = ptrtoint ptr %ptr to i64 + %tmp2 = call i64 @llvm.ptrauth.auth(i64 %tmp1, i32 2, i64 %tmp0) + %tmp3 = inttoptr i64 %tmp2 to ptr + %tmp4 = load i64, ptr %tmp3 + ret i64 %tmp4 +} + +; Blend discriminator, interesting offsets. + +define i64 @test_da_blend_8(ptr %ptr, i64 %disc) { +; CHECK-LABEL: test_da_blend_8: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, x1 +; CHECK-NEXT: movk x17, #12345, lsl #48 +; CHECK-NEXT: autda x16, x17 +; CHECK-NEXT: ldr x0, [x16, #8] +; CHECK-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %disc, i64 12345) + %tmp1 = ptrtoint ptr %ptr to i64 + %tmp2 = call i64 @llvm.ptrauth.auth(i64 %tmp1, i32 2, i64 %tmp0) + %tmp3 = add i64 %tmp2, 8 + %tmp4 = inttoptr i64 %tmp3 to ptr + %tmp5 = load i64, ptr %tmp4 + ret i64 %tmp5 +} + +define i64 @test_da_blend_uimm12max(ptr %ptr, i64 %disc) { +; CHECK-LABEL: test_da_blend_uimm12max: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, x1 +; CHECK-NEXT: movk x17, #12345, lsl #48 +; CHECK-NEXT: autda x16, x17 +; CHECK-NEXT: ldr x0, [x16, #32760] +; CHECK-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %disc, i64 12345) + %tmp1 = ptrtoint ptr %ptr to i64 + %tmp2 = call i64 @llvm.ptrauth.auth(i64 %tmp1, i32 2, i64 %tmp0) + %tmp3 = add i64 %tmp2, 32760 + %tmp4 = inttoptr i64 %tmp3 to ptr + %tmp5 = load i64, ptr %tmp4 + ret i64 %tmp5 +} + +define i64 @test_da_blend_largeoff_32b(ptr %ptr, i64 %disc) { +; CHECK-LABEL: test_da_blend_largeoff_32b: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, x1 +; CHECK-NEXT: movk x17, #12345, lsl #48 +; CHECK-NEXT: autda x16, x17 +; CHECK-NEXT: mov x17, #2 +; CHECK-NEXT: movk x17, #1, lsl #32 +; CHECK-NEXT: ldr x0, [x16, x17] +; CHECK-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %disc, i64 12345) + %tmp1 = ptrtoint ptr %ptr to i64 + %tmp2 = call i64 @llvm.ptrauth.auth(i64 %tmp1, i32 2, i64 %tmp0) + %tmp3 = add i64 %tmp2, 4294967298 + %tmp4 = inttoptr i64 %tmp3 to ptr + %tmp5 = load i64, ptr %tmp4 + ret i64 %tmp5 +} + +define i64 @test_da_blend_m4(ptr %ptr, i64 %disc) { +; CHECK-LABEL: test_da_blend_m4: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, x1 +; CHECK-NEXT: movk x17, #12345, lsl #48 +; CHECK-NEXT: autda x16, x17 +; CHECK-NEXT: ldur x0, [x16, #-4] +; CHECK-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %disc, i64 12345) + %tmp1 = ptrtoint ptr %ptr to i64 + %tmp2 = call i64 @llvm.ptrauth.auth(i64 %tmp1, i32 2, i64 %tmp0) + %tmp3 = add i64 %tmp2, -4 + %tmp4 = inttoptr i64 %tmp3 to ptr + %tmp5 = load i64, ptr %tmp4 + ret i64 %tmp5 +} + +define i64 @test_da_blend_simm9min(ptr %ptr, i64 %disc) { +; CHECK-LABEL: test_da_blend_simm9min: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, x1 +; CHECK-NEXT: movk x17, #12345, lsl #48 +; CHECK-NEXT: autda x16, x17 +; CHECK-NEXT: mov x17, #-4096 +; CHECK-NEXT: ldr x0, [x16, x17] +; CHECK-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %disc, i64 12345) + %tmp1 = ptrtoint ptr %ptr to i64 + %tmp2 = call i64 @llvm.ptrauth.auth(i64 %tmp1, i32 2, i64 %tmp0) + %tmp3 = add i64 %tmp2, -4096 + %tmp4 = inttoptr i64 %tmp3 to ptr + %tmp5 = load i64, ptr %tmp4 + ret i64 %tmp5 +} + +define i64 @test_da_blend_neg_largeoff_32b(ptr %ptr, i64 %disc) { +; CHECK-LABEL: test_da_blend_neg_largeoff_32b: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, x1 +; CHECK-NEXT: movk x17, #12345, lsl #48 +; CHECK-NEXT: autda x16, x17 +; CHECK-NEXT: mov x17, #-3 +; CHECK-NEXT: movk x17, #65534, lsl #32 +; CHECK-NEXT: ldr x0, [x16, x17] +; CHECK-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %disc, i64 12345) + %tmp1 = ptrtoint ptr %ptr to i64 + %tmp2 = call i64 @llvm.ptrauth.auth(i64 %tmp1, i32 2, i64 %tmp0) + %tmp3 = add i64 %tmp2, -4294967299 + %tmp4 = inttoptr i64 %tmp3 to ptr + %tmp5 = load i64, ptr %tmp4 + ret i64 %tmp5 +} + +; Blend discriminator, interesting offsets, writeback. + +define i64 @test_da_blend_8_wb(ptr %ptr, i64 %disc, ptr %dst) { +; CHECK-LABEL: test_da_blend_8_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, x1 +; CHECK-NEXT: movk x17, #12345, lsl #48 +; CHECK-NEXT: autda x16, x17 +; CHECK-NEXT: ldr x8, [x16, #8]! +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: str x8, [x2] +; CHECK-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %disc, i64 12345) + %tmp1 = ptrtoint ptr %ptr to i64 + %tmp2 = call i64 @llvm.ptrauth.auth(i64 %tmp1, i32 2, i64 %tmp0) + %tmp3 = add i64 %tmp2, 8 + %tmp4 = inttoptr i64 %tmp3 to ptr + %tmp5 = load i64, ptr %tmp4 + store i64 %tmp5, ptr %dst + ret i64 %tmp3 +} + +define i64 @test_da_blend_simm9umax_wb(ptr %ptr, i64 %disc, ptr %dst) { +; CHECK-LABEL: test_da_blend_simm9umax_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, x1 +; CHECK-NEXT: movk x17, #12345, lsl #48 +; CHECK-NEXT: autda x16, x17 +; CHECK-NEXT: ldr x8, [x16, #248]! +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: str x8, [x2] +; CHECK-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %disc, i64 12345) + %tmp1 = ptrtoint ptr %ptr to i64 + %tmp2 = call i64 @llvm.ptrauth.auth(i64 %tmp1, i32 2, i64 %tmp0) + %tmp3 = add i64 %tmp2, 248 + %tmp4 = inttoptr i64 %tmp3 to ptr + %tmp5 = load i64, ptr %tmp4 + store i64 %tmp5, ptr %dst + ret i64 %tmp3 +} + +define i64 @test_da_blend_simm9s8max_wb(ptr %ptr, i64 %disc, ptr %dst) { +; CHECK-LABEL: test_da_blend_simm9s8max_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, x1 +; CHECK-NEXT: movk x17, #12345, lsl #48 +; CHECK-NEXT: autda x16, x17 +; CHECK-NEXT: mov x17, #4088 +; CHECK-NEXT: add x16, x16, x17 +; CHECK-NEXT: ldr x8, [x16] +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: str x8, [x2] +; CHECK-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %disc, i64 12345) + %tmp1 = ptrtoint ptr %ptr to i64 + %tmp2 = call i64 @llvm.ptrauth.auth(i64 %tmp1, i32 2, i64 %tmp0) + %tmp3 = add i64 %tmp2, 4088 + %tmp4 = inttoptr i64 %tmp3 to ptr + %tmp5 = load i64, ptr %tmp4 + store i64 %tmp5, ptr %dst + ret i64 %tmp3 +} + +define i64 @test_da_blend_neg_largeoff_32b_wb(ptr %ptr, i64 %disc, ptr %dst) { +; CHECK-LABEL: test_da_blend_neg_largeoff_32b_wb: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x16, x0 +; CHECK-NEXT: mov x17, x1 +; CHECK-NEXT: movk x17, #12345, lsl #48 +; CHECK-NEXT: autda x16, x17 +; CHECK-NEXT: mov x17, #-3 +; CHECK-NEXT: movk x17, #65534, lsl #32 +; CHECK-NEXT: add x16, x16, x17 +; CHECK-NEXT: ldr x8, [x16] +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: str x8, [x2] +; CHECK-NEXT: ret + %tmp0 = call i64 @llvm.ptrauth.blend(i64 %disc, i64 12345) + %tmp1 = ptrtoint ptr %ptr to i64 + %tmp2 = call i64 @llvm.ptrauth.auth(i64 %tmp1, i32 2, i64 %tmp0) + %tmp3 = add i64 %tmp2, -4294967299 + %tmp4 = inttoptr i64 %tmp3 to ptr + %tmp5 = load i64, ptr %tmp4 + store i64 %tmp5, ptr %dst + ret i64 %tmp3 +} + +declare i64 @llvm.ptrauth.auth(i64, i32, i64) +declare i64 @llvm.ptrauth.blend(i64, i64) From c02689b3c9124cd7b5d7a15be46e23fd5eeb4f43 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 1 Jul 2024 17:43:19 -0700 Subject: [PATCH 17/58] [AArch64][PAC] Support return address authentication for frame LR. This includes a new pseudo-instruction, XPACIuntied, to be used when lowering @llvm.returnaddress: it avoids clobbering LR, thereby saving a stack frame when it's not otherwise needed. --- .../AArch64/AArch64ExpandPseudoInsts.cpp | 4 +- .../GISel/AArch64InstructionSelector.cpp | 24 ++ .../AArch64/GlobalISel/select-returnaddr.ll | 7 +- .../CodeGen/AArch64/arm64e-ptrauth-ret.ll | 221 ----------------- llvm/test/CodeGen/AArch64/ptrauth-ret.ll | 222 ++++++++++++++++++ 5 files changed, 251 insertions(+), 227 deletions(-) delete mode 100644 llvm/test/CodeGen/AArch64/arm64e-ptrauth-ret.ll create mode 100644 llvm/test/CodeGen/AArch64/ptrauth-ret.ll diff --git a/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp b/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp index e8a1be55d1d3f..abb53bf38b5cf 100644 --- a/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp @@ -1526,8 +1526,8 @@ bool AArch64ExpandPseudo::expandMI(MachineBasicBlock &MBB, // mov $x0, $x1 // XPACI $x0. MachineInstrBuilder DefMIB = - BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::ORRXrs)) - .addReg(LHS.getReg()) + BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::ORRXrs), + LHS.getReg()) .addReg(AArch64::XZR) .add(RHS) .addImm(0); diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp index fde3ce8b380b8..1faf18c101b80 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp @@ -6595,6 +6595,17 @@ bool AArch64InstructionSelector::selectIntrinsic(MachineInstr &I, MF, TII, AArch64::LR, AArch64::GPR64RegClass, I.getDebugLoc()); } + if (STI.isTargetDarwin()) { + // If we're doing LR signing, we need to fixup ReturnAddr: strip it. + // If not, on Darwin, we know we will never seen a frame with a signed LR. + if (MF.getFunction().hasFnAttribute("ptrauth-returns")) + MIB.buildInstr(AArch64::XPACIuntied, {DstReg}, {MFReturnAddr}); + else + MIB.buildCopy({DstReg}, {MFReturnAddr}); + I.eraseFromParent(); + return true; + } + if (STI.hasPAuth()) { MIB.buildInstr(AArch64::XPACI, {DstReg}, {MFReturnAddr}); } else { @@ -6622,6 +6633,19 @@ bool AArch64InstructionSelector::selectIntrinsic(MachineInstr &I, else { MFI.setReturnAddressIsTaken(true); + if (STI.isTargetDarwin()) { + // If we're doing LR signing, we need to fixup ReturnAddr: strip it. + // If not, on Darwin, we know we will never seen a frame with a signed LR. + if (MF.getFunction().hasFnAttribute("ptrauth-returns")) { + Register TmpReg = MRI.createVirtualRegister(&AArch64::GPR64RegClass); + MIB.buildInstr(AArch64::LDRXui, {TmpReg}, {FrameAddr}).addImm(1); + MIB.buildInstr(AArch64::XPACIuntied, {DstReg}, {TmpReg}); + } else { + MIB.buildInstr(AArch64::LDRXui, {DstReg}, {FrameAddr}).addImm(1); + } + I.eraseFromParent(); + return true; + } if (STI.hasPAuth()) { Register TmpReg = MRI.createVirtualRegister(&AArch64::GPR64RegClass); MIB.buildInstr(AArch64::LDRXui, {TmpReg}, {FrameAddr}).addImm(1); diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/select-returnaddr.ll b/llvm/test/CodeGen/AArch64/GlobalISel/select-returnaddr.ll index 2e17a72f7bd9d..e3b7b805099a4 100644 --- a/llvm/test/CodeGen/AArch64/GlobalISel/select-returnaddr.ll +++ b/llvm/test/CodeGen/AArch64/GlobalISel/select-returnaddr.ll @@ -1,4 +1,4 @@ -; RUN: llc -mtriple=arm64-apple-ios -global-isel -o - %s | FileCheck %s +; RUN: llc -mtriple=aarch64-- -global-isel -o - %s | FileCheck %s define ptr @rt0(i32 %x) nounwind readnone { entry: @@ -12,10 +12,9 @@ entry: define ptr @rt0_call_clobber(i32 %x) nounwind readnone { entry: ; CHECK-LABEL: rt0_call_clobber: -; CHECK: stp x20, x19, [sp, #-32]! -; CHECK: stp x29, x30, [sp, #16] +; CHECK: stp x30, x19, [sp, #-16]! ; CHECK: mov x19, x30 -; CHECK: bl _foo +; CHECK: bl foo ; CHECK: mov x30, x19 ; CHECK-NEXT: hint #7 ; CHECK-NEXT: mov x0, x30 diff --git a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-ret.ll b/llvm/test/CodeGen/AArch64/arm64e-ptrauth-ret.ll deleted file mode 100644 index d93ef83f2a2fa..0000000000000 --- a/llvm/test/CodeGen/AArch64/arm64e-ptrauth-ret.ll +++ /dev/null @@ -1,221 +0,0 @@ -; RUN: llc -mtriple arm64e-apple-darwin -asm-verbose=false -disable-post-ra -o - %s | FileCheck %s - -; CHECK-LABEL: _test: -; CHECK-NEXT: stp x20, x19, [sp, #-16]! -; CHECK-NEXT: ; InlineAsm Start -; CHECK-NEXT: ; InlineAsm End -; CHECK-NEXT: mov w0, #0 -; CHECK-NEXT: ldp x20, x19, [sp], #16 -; CHECK-NEXT: ret -define i32 @test() #0 { - call void asm sideeffect "", "~{x19}"() - ret i32 0 -} - -; CHECK-LABEL: _test_alloca: -; CHECK-NEXT: sub sp, sp, #32 -; CHECK-NEXT: mov x8, sp -; CHECK-NEXT: ; InlineAsm Start -; CHECK-NEXT: ; InlineAsm End -; CHECK-NEXT: mov w0, #0 -; CHECK-NEXT: add sp, sp, #32 -; CHECK-NEXT: ret -define i32 @test_alloca() #0 { - %p = alloca i8, i32 32 - call void asm sideeffect "", "r"(i8* %p) - ret i32 0 -} - -; CHECK-LABEL: _test_realign_alloca: -; CHECK-NEXT: pacibsp -; CHECK-NEXT: stp x29, x30, [sp, #-16]! -; CHECK-NEXT: mov x29, sp -; CHECK-NEXT: sub x9, sp, #112 -; CHECK-NEXT: and sp, x9, #0xffffffffffffff80 -; CHECK-NEXT: mov x8, sp -; CHECK-NEXT: ; InlineAsm Start -; CHECK-NEXT: ; InlineAsm End -; CHECK-NEXT: mov w0, #0 -; CHECK-NEXT: mov sp, x29 -; CHECK-NEXT: ldp x29, x30, [sp], #16 -; CHECK-NEXT: retab -define i32 @test_realign_alloca() #0 { - %p = alloca i8, i32 32, align 128 - call void asm sideeffect "", "r"(i8* %p) - ret i32 0 -} - -; CHECK-LABEL: _test_big_alloca: -; CHECK-NEXT: stp x28, x27, [sp, #-16]! -; CHECK-NEXT: sub sp, sp, #1024 -; CHECK-NEXT: mov x8, sp -; CHECK-NEXT: ; InlineAsm Start -; CHECK-NEXT: ; InlineAsm End -; CHECK-NEXT: mov w0, #0 -; CHECK-NEXT: add sp, sp, #1024 -; CHECK-NEXT: ldp x28, x27, [sp], #16 -; CHECK-NEXT: ret -define i32 @test_big_alloca() #0 { - %p = alloca i8, i32 1024 - call void asm sideeffect "", "r"(i8* %p) - ret i32 0 -} - -; CHECK-LABEL: _test_var_alloca: -; CHECK-NEXT: pacibsp -; CHECK-NEXT: stp x29, x30, [sp, #-16]! -; CHECK-NEXT: mov x29, sp -; CHECK-NEXT: mov x8, sp -; CHECK-NEXT: mov w9, w0 -; CHECK-NEXT: add x9, x9, #15 -; CHECK-NEXT: and x9, x9, #0x1fffffff0 -; CHECK-NEXT: sub x8, x8, x9 -; CHECK-NEXT: mov sp, x8 -; CHECK-NEXT: ; InlineAsm Start -; CHECK-NEXT: ; InlineAsm End -; CHECK-NEXT: mov w0, #0 -; CHECK-NEXT: mov sp, x29 -; CHECK-NEXT: ldp x29, x30, [sp], #16 -; CHECK-NEXT: retab -define i32 @test_var_alloca(i32 %s) #0 { - %p = alloca i8, i32 %s - call void asm sideeffect "", "r"(i8* %p) - ret i32 0 -} - -; CHECK-LABEL: _test_noframe_saved: -; CHECK-NEXT: pacibsp -; CHECK-NEXT: stp x28, x27, [sp, #-96]! -; CHECK-NEXT: stp x26, x25, [sp, #16] -; CHECK-NEXT: stp x24, x23, [sp, #32] -; CHECK-NEXT: stp x22, x21, [sp, #48] -; CHECK-NEXT: stp x20, x19, [sp, #64] -; CHECK-NEXT: stp x29, x30, [sp, #80] -; CHECK-NEXT: ldr w30, [x0] -; CHECK-NEXT: ; InlineAsm Start -; CHECK-NEXT: ; InlineAsm End -; CHECK-NEXT: mov x0, x30 -; CHECK-NEXT: ldp x29, x30, [sp, #80] -; CHECK-NEXT: ldp x20, x19, [sp, #64] -; CHECK-NEXT: ldp x22, x21, [sp, #48] -; CHECK-NEXT: ldp x24, x23, [sp, #32] -; CHECK-NEXT: ldp x26, x25, [sp, #16] -; CHECK-NEXT: ldp x28, x27, [sp], #96 -; CHECK-NEXT: retab -define i32 @test_noframe_saved(i32* %p) #0 { - %v = load i32, i32* %p - call void asm sideeffect "", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28}"() - ret i32 %v -} - -; CHECK-LABEL: _test_noframe: -; CHECK-NEXT: ret -define void @test_noframe() #0 { - ret void -} - -; CHECK-LABEL: _test_returnaddress_0: -; CHECK-NEXT: mov x0, x30 -; CHECK-NEXT: xpaci x0 -; CHECK-NEXT: ret -define i8* @test_returnaddress_0() #0 { - %r = call i8* @llvm.returnaddress(i32 0) - ret i8* %r -} - -; CHECK-LABEL: _test_returnaddress_1: -; CHECK-NEXT: pacibsp -; CHECK-NEXT: stp x29, x30, [sp, #-16]! -; CHECK-NEXT: mov x29, sp -; CHECK-NEXT: ldr x8, [x29] -; CHECK-NEXT: ldr x8, [x8, #8] -; CHECK-NEXT: mov x0, x8 -; CHECK-NEXT: xpaci x0 -; CHECK-NEXT: ldp x29, x30, [sp], #16 -; CHECK-NEXT: retab -define i8* @test_returnaddress_1() #0 { - %r = call i8* @llvm.returnaddress(i32 1) - ret i8* %r -} - -; CHECK-LABEL: _test_noframe_alloca: -; CHECK-NEXT: sub sp, sp, #16 -; CHECK-NEXT: add x8, sp, #15 -; CHECK-NEXT: ; InlineAsm Start -; CHECK-NEXT: ; InlineAsm End -; CHECK-NEXT: add sp, sp, #16 -; CHECK-NEXT: ret -define void @test_noframe_alloca() #0 { - %p = alloca i8, i32 1 - call void asm sideeffect "", "r"(i8* %p) - ret void -} - -; CHECK-LABEL: _test_call: -; CHECK-NEXT: pacibsp -; CHECK-NEXT: stp x29, x30, [sp, #-16]! -; CHECK-NEXT: bl _bar -; CHECK-NEXT: ldp x29, x30, [sp], #16 -; CHECK-NEXT: retab -define void @test_call() #0 { - call i32 @bar() - ret void -} - -; CHECK-LABEL: _test_call_alloca: -; CHECK-NEXT: pacibsp -; CHECK-NEXT: sub sp, sp, #32 -; CHECK-NEXT: stp x29, x30, [sp, #16] -; CHECK-NEXT: bl _bar -; CHECK-NEXT: ldp x29, x30, [sp, #16] -; CHECK-NEXT: add sp, sp, #32 -; CHECK-NEXT: retab -define void @test_call_alloca() #0 { - alloca i8 - call i32 @bar() - ret void -} - -; CHECK-LABEL: _test_call_shrinkwrapping: -; CHECK-NEXT: tbz w0, #0, [[RETBB:LBB[0-9_]+]] -; CHECK-NEXT: pacibsp -; CHECK-NEXT: stp x29, x30, [sp, #-16]! -; CHECK-NEXT: bl _bar -; CHECK-NEXT: ldp x29, x30, [sp], #16 -; CHECK-NEXT: autibsp -; CHECK-NEXT: [[RETBB]]: -; CHECK-NEXT: ret -define void @test_call_shrinkwrapping(i1 %c) #0 { - br i1 %c, label %tbb, label %fbb -tbb: - call i32 @bar() - br label %fbb -fbb: - ret void -} - -; CHECK-LABEL: _test_tailcall: -; CHECK-NEXT: pacibsp -; CHECK-NEXT: stp x29, x30, [sp, #-16]! -; CHECK-NEXT: bl _bar -; CHECK-NEXT: ldp x29, x30, [sp], #16 -; CHECK-NEXT: autibsp -; CHECK-NEXT: b _bar -define i32 @test_tailcall() #0 { - call i32 @bar() - %c = tail call i32 @bar() - ret i32 %c -} - -; CHECK-LABEL: _test_tailcall_noframe: -; CHECK-NEXT: b _bar -define i32 @test_tailcall_noframe() #0 { - %c = tail call i32 @bar() - ret i32 %c -} - -declare i32 @bar() - -declare i8* @llvm.returnaddress(i32) - -attributes #0 = { nounwind "ptrauth-returns" } diff --git a/llvm/test/CodeGen/AArch64/ptrauth-ret.ll b/llvm/test/CodeGen/AArch64/ptrauth-ret.ll new file mode 100644 index 0000000000000..f1b00d3987f2d --- /dev/null +++ b/llvm/test/CodeGen/AArch64/ptrauth-ret.ll @@ -0,0 +1,222 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple arm64e-apple-darwin -verify-machineinstrs -disable-post-ra -global-isel=0 -o - %s | FileCheck %s +; RUN: llc < %s -mtriple arm64e-apple-darwin -verify-machineinstrs -disable-post-ra -global-isel=1 -global-isel-abort=1 -o - %s | FileCheck %s + +define i32 @test() #0 { +; CHECK-LABEL: test: +; CHECK: ; %bb.0: +; CHECK-NEXT: stp x20, x19, [sp, #-16]! ; 16-byte Folded Spill +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: mov w0, #0 +; CHECK-NEXT: ldp x20, x19, [sp], #16 ; 16-byte Folded Reload +; CHECK-NEXT: ret + call void asm sideeffect "", "~{x19}"() + ret i32 0 +} + +define i32 @test_alloca() #0 { +; CHECK-LABEL: test_alloca: +; CHECK: ; %bb.0: +; CHECK-NEXT: sub sp, sp, #32 +; CHECK-NEXT: mov x8, sp +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: mov w0, #0 +; CHECK-NEXT: add sp, sp, #32 +; CHECK-NEXT: ret + %p = alloca i8, i32 32 + call void asm sideeffect "", "r"(i8* %p) + ret i32 0 +} + +define i32 @test_realign_alloca() #0 { +; CHECK-LABEL: test_realign_alloca: +; CHECK: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! ; 16-byte Folded Spill +; CHECK-NEXT: mov x29, sp +; CHECK-NEXT: sub x9, sp, #112 +; CHECK-NEXT: and sp, x9, #0xffffffffffffff80 +; CHECK-NEXT: mov x8, sp +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: mov w0, #0 +; CHECK-NEXT: mov sp, x29 +; CHECK-NEXT: ldp x29, x30, [sp], #16 ; 16-byte Folded Reload +; CHECK-NEXT: retab + %p = alloca i8, i32 32, align 128 + call void asm sideeffect "", "r"(i8* %p) + ret i32 0 +} + +define i32 @test_big_alloca() #0 { +; CHECK-LABEL: test_big_alloca: +; CHECK: ; %bb.0: +; CHECK-NEXT: stp x28, x27, [sp, #-16]! ; 16-byte Folded Spill +; CHECK-NEXT: sub sp, sp, #1024 +; CHECK-NEXT: mov x8, sp +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: mov w0, #0 +; CHECK-NEXT: add sp, sp, #1024 +; CHECK-NEXT: ldp x28, x27, [sp], #16 ; 16-byte Folded Reload +; CHECK-NEXT: ret + %p = alloca i8, i32 1024 + call void asm sideeffect "", "r"(i8* %p) + ret i32 0 +} + +define i32 @test_var_alloca(i32 %s) #0 { + %p = alloca i8, i32 %s + call void asm sideeffect "", "r"(i8* %p) + ret i32 0 +} + +define i32 @test_noframe_saved(i32* %p) #0 { +; CHECK-LABEL: test_noframe_saved: +; CHECK: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x28, x27, [sp, #-96]! ; 16-byte Folded Spill +; CHECK-NEXT: stp x26, x25, [sp, #16] ; 16-byte Folded Spill +; CHECK-NEXT: stp x24, x23, [sp, #32] ; 16-byte Folded Spill +; CHECK-NEXT: stp x22, x21, [sp, #48] ; 16-byte Folded Spill +; CHECK-NEXT: stp x20, x19, [sp, #64] ; 16-byte Folded Spill +; CHECK-NEXT: stp x29, x30, [sp, #80] ; 16-byte Folded Spill +; CHECK-NEXT: ldr w30, [x0] +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: mov x0, x30 +; CHECK-NEXT: ldp x29, x30, [sp, #80] ; 16-byte Folded Reload +; CHECK-NEXT: ldp x20, x19, [sp, #64] ; 16-byte Folded Reload +; CHECK-NEXT: ldp x22, x21, [sp, #48] ; 16-byte Folded Reload +; CHECK-NEXT: ldp x24, x23, [sp, #32] ; 16-byte Folded Reload +; CHECK-NEXT: ldp x26, x25, [sp, #16] ; 16-byte Folded Reload +; CHECK-NEXT: ldp x28, x27, [sp], #96 ; 16-byte Folded Reload +; CHECK-NEXT: retab + %v = load i32, i32* %p + call void asm sideeffect "", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28}"() + ret i32 %v +} + +define void @test_noframe() #0 { +; CHECK-LABEL: test_noframe: +; CHECK: ; %bb.0: +; CHECK-NEXT: ret + ret void +} + +define i8* @test_returnaddress_0() #0 { +; CHECK-LABEL: test_returnaddress_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: mov x0, x30 +; CHECK-NEXT: xpaci x0 +; CHECK-NEXT: ret + %r = call i8* @llvm.returnaddress(i32 0) + ret i8* %r +} + +define i8* @test_returnaddress_1() #0 { +; CHECK-LABEL: test_returnaddress_1: +; CHECK: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! ; 16-byte Folded Spill +; CHECK-NEXT: mov x29, sp +; CHECK-NEXT: ldr x8, [x29] +; CHECK-NEXT: ldr x8, [x8, #8] +; CHECK-NEXT: mov x0, x8 +; CHECK-NEXT: xpaci x0 +; CHECK-NEXT: ldp x29, x30, [sp], #16 ; 16-byte Folded Reload +; CHECK-NEXT: retab + %r = call i8* @llvm.returnaddress(i32 1) + ret i8* %r +} + +define void @test_noframe_alloca() #0 { +; CHECK-LABEL: test_noframe_alloca: +; CHECK: ; %bb.0: +; CHECK-NEXT: sub sp, sp, #16 +; CHECK-NEXT: add x8, sp, #15 +; CHECK-NEXT: ; InlineAsm Start +; CHECK-NEXT: ; InlineAsm End +; CHECK-NEXT: add sp, sp, #16 +; CHECK-NEXT: ret + %p = alloca i8, i32 1 + call void asm sideeffect "", "r"(i8* %p) + ret void +} + +define void @test_call() #0 { +; CHECK-LABEL: test_call: +; CHECK: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! ; 16-byte Folded Spill +; CHECK-NEXT: bl _bar +; CHECK-NEXT: ldp x29, x30, [sp], #16 ; 16-byte Folded Reload +; CHECK-NEXT: retab + call i32 @bar() + ret void +} + +define void @test_call_alloca() #0 { +; CHECK-LABEL: test_call_alloca: +; CHECK: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: sub sp, sp, #32 +; CHECK-NEXT: stp x29, x30, [sp, #16] ; 16-byte Folded Spill +; CHECK-NEXT: bl _bar +; CHECK-NEXT: ldp x29, x30, [sp, #16] ; 16-byte Folded Reload +; CHECK-NEXT: add sp, sp, #32 +; CHECK-NEXT: retab + alloca i8 + call i32 @bar() + ret void +} + +define void @test_call_shrinkwrapping(i1 %c) #0 { +; CHECK-LABEL: test_call_shrinkwrapping: +; CHECK: ; %bb.0: +; CHECK-NEXT: tbz w0, #0, LBB12_2 +; CHECK-NEXT: ; %bb.1: ; %tbb +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! ; 16-byte Folded Spill +; CHECK-NEXT: bl _bar +; CHECK-NEXT: ldp x29, x30, [sp], #16 ; 16-byte Folded Reload +; CHECK-NEXT: autibsp +; CHECK-NEXT: LBB12_2: ; %fbb +; CHECK-NEXT: ret + br i1 %c, label %tbb, label %fbb +tbb: + call i32 @bar() + br label %fbb +fbb: + ret void +} + +define i32 @test_tailcall() #0 { +; CHECK-LABEL: test_tailcall: +; CHECK: ; %bb.0: +; CHECK-NEXT: pacibsp +; CHECK-NEXT: stp x29, x30, [sp, #-16]! ; 16-byte Folded Spill +; CHECK-NEXT: bl _bar +; CHECK-NEXT: ldp x29, x30, [sp], #16 ; 16-byte Folded Reload +; CHECK-NEXT: autibsp +; CHECK-NEXT: b _bar + call i32 @bar() + %c = tail call i32 @bar() + ret i32 %c +} + +define i32 @test_tailcall_noframe() #0 { +; CHECK-LABEL: test_tailcall_noframe: +; CHECK: ; %bb.0: +; CHECK-NEXT: b _bar + %c = tail call i32 @bar() + ret i32 %c +} + +declare i32 @bar() + +declare i8* @llvm.returnaddress(i32) + +attributes #0 = { nounwind "ptrauth-returns" } From 5443db3583bde27a94e77d8718d6bb4d556a0030 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 27 Sep 2021 08:00:00 -0700 Subject: [PATCH 18/58] [AArch64][MachO] Support ptrauth ABI version. --- clang/include/clang/Basic/LangOptions.def | 1 + clang/include/clang/Driver/Options.td | 24 +++++++++---------- clang/lib/Driver/ToolChains/Darwin.cpp | 5 ++-- .../arch-arm64e-abi-versioning-defaults.c | 16 +++++++++++++ .../test/Driver/arch-arm64e-abi-versioning.c | 18 ++++++-------- clang/tools/driver/cc1as_main.cpp | 6 ++--- 6 files changed, 41 insertions(+), 29 deletions(-) create mode 100644 clang/test/Driver/arch-arm64e-abi-versioning-defaults.c diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 0e18e751a9d6e..f3d390d321b16 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -172,6 +172,7 @@ LANGOPT(PointerAuthVTPtrTypeDiscrimination, 1, 0, "incorporate type discriminati LANGOPT(PointerAuthInitFini, 1, 0, "sign function pointers in init/fini arrays") LANGOPT(PointerAuthIndirectGotos, 1, 0, "indirect gotos pointer authentication") LANGOPT(SoftPointerAuth , 1, 0, "software emulation of pointer authentication") + VALUE_LANGOPT(PointerAuthABIVersion, 32, 0, "pointer authentication ABI version") LANGOPT(PointerAuthKernelABIVersion, 1, 0, "controls whether the pointer auth abi version represents a kernel ABI") LANGOPT(PointerAuthABIVersionEncoded, 1, 0, "controls whether the pointer auth abi version should be encoded in the IR") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 587b267a925d6..5853eb70b1126 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4262,6 +4262,18 @@ let Group = f_Group in { def fno_ptrauth_soft : Flag<["-"], "fno-ptrauth-soft">; } +let Flags = [TargetSpecific] in { +defm ptrauth_intrinsics : OptInCC1FFlag<"ptrauth-intrinsics", "Enable pointer authentication intrinsics">; +defm ptrauth_calls : OptInCC1FFlag<"ptrauth-calls", "Enable signing and authentication of all indirect calls">; +defm ptrauth_returns : OptInCC1FFlag<"ptrauth-returns", "Enable signing and authentication of return addresses">; +defm ptrauth_auth_traps : OptInCC1FFlag<"ptrauth-auth-traps", "Enable traps on authentication failures">; +defm ptrauth_vtable_pointer_address_discrimination : + OptInCC1FFlag<"ptrauth-vtable-pointer-address-discrimination", "Enable address discrimination of vtable pointers">; +defm ptrauth_vtable_pointer_type_discrimination : + OptInCC1FFlag<"ptrauth-vtable-pointer-type-discrimination", "Enable type discrimination of vtable pointers">; +defm ptrauth_init_fini : OptInCC1FFlag<"ptrauth-init-fini", "Enable signing of function pointers in init/fini arrays">; +} + let Group = f_Group in { def fptrauth_abi_version_EQ : Joined<["-"], "fptrauth-abi-version=">, Visibility<[ClangOption, CC1Option, CC1AsOption]>, @@ -4276,18 +4288,6 @@ let Group = f_Group in { HelpText<"Disable Pointer Authentication kernel ABI versioning">; } -let Flags = [TargetSpecific] in { -defm ptrauth_intrinsics : OptInCC1FFlag<"ptrauth-intrinsics", "Enable pointer authentication intrinsics">; -defm ptrauth_calls : OptInCC1FFlag<"ptrauth-calls", "Enable signing and authentication of all indirect calls">; -defm ptrauth_returns : OptInCC1FFlag<"ptrauth-returns", "Enable signing and authentication of return addresses">; -defm ptrauth_auth_traps : OptInCC1FFlag<"ptrauth-auth-traps", "Enable traps on authentication failures">; -defm ptrauth_vtable_pointer_address_discrimination : - OptInCC1FFlag<"ptrauth-vtable-pointer-address-discrimination", "Enable address discrimination of vtable pointers">; -defm ptrauth_vtable_pointer_type_discrimination : - OptInCC1FFlag<"ptrauth-vtable-pointer-type-discrimination", "Enable type discrimination of vtable pointers">; -defm ptrauth_init_fini : OptInCC1FFlag<"ptrauth-init-fini", "Enable signing of function pointers in init/fini arrays">; -} - def fenable_matrix : Flag<["-"], "fenable-matrix">, Group, Visibility<[ClangOption, CC1Option]>, HelpText<"Enable matrix data type and related builtin functions">, diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 2f63f8844201a..bb70d7c4f1539 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -1289,9 +1289,8 @@ void DarwinClang::addClangTargetOptions( Darwin::addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadKind); - // On arm64e, enable pointer authentication (for the return address and - // indirect calls), as well as usage of the intrinsics. - if (getArchName() == "arm64e") { + // On arm64e, enable pointer authentication intrinsics. + if (getTriple().isArm64e()) { // The ptrauth ABI version is 0 by default, but can be overridden. static const constexpr unsigned DefaultPtrauthABIVersion = 0; diff --git a/clang/test/Driver/arch-arm64e-abi-versioning-defaults.c b/clang/test/Driver/arch-arm64e-abi-versioning-defaults.c new file mode 100644 index 0000000000000..0984bcf631a5e --- /dev/null +++ b/clang/test/Driver/arch-arm64e-abi-versioning-defaults.c @@ -0,0 +1,16 @@ +// Check the ABI version support defaults. + +// RUN: %clang -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix NOKERNELABIVERSION +// RUN: %clang -mkernel -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix KERNELABIVERSION +// RUN: %clang -fapple-kext -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix KERNELABIVERSION +// +// RUN: %clang -fno-ptrauth-kernel-abi-version -mkernel -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix NOKERNELABIVERSION +// RUN: %clang -mkernel -fno-ptrauth-kernel-abi-version -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix NOKERNELABIVERSION +// RUN: %clang -fno-ptrauth-kernel-abi-version -fapple-kext -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix NOKERNELABIVERSION +// RUN: %clang -fapple-kext -fno-ptrauth-kernel-abi-version -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix NOKERNELABIVERSION +// RUN: %clang -fno-ptrauth-kernel-abi-version -fptrauth-kernel-abi-version -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix NOKERNELABIVERSION +// RUN: %clang -fptrauth-kernel-abi-version -fno-ptrauth-kernel-abi-version -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix NOKERNELABIVERSION +// +// ABIVERSION-DEFAULT: "-fptrauth-abi-version=0" +// KERNELABIVERSION: "-fptrauth-kernel-abi-version" +// NOKERNELABIVERSION-NOT: fptrauth-kernel-abi-version diff --git a/clang/test/Driver/arch-arm64e-abi-versioning.c b/clang/test/Driver/arch-arm64e-abi-versioning.c index f3b8604a508ce..7a279ddbbd793 100644 --- a/clang/test/Driver/arch-arm64e-abi-versioning.c +++ b/clang/test/Driver/arch-arm64e-abi-versioning.c @@ -1,17 +1,13 @@ // Check the ABI version support. -// RUN: %clang -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix NOKERNELABIVERSION -// RUN: %clang -mkernel -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix KERNELABIVERSION -// RUN: %clang -fapple-kext -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix KERNELABIVERSION -// -// RUN: %clang -fptrauth-abi-version=5 -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix NOKERNELABIVERSION -// RUN: %clang -fptrauth-abi-version=5 -mkernel -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix KERNELABIVERSION -// RUN: %clang -fptrauth-abi-version=5 -fapple-kext -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix KERNELABIVERSION -// RUN: %clang -fptrauth-abi-version=5 -fptrauth-kernel-abi-version -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix KERNELABIVERSION +// RUN: %clang -fptrauth-abi-version=5 -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix NOKERNELABIVERSION +// RUN: %clang -fptrauth-abi-version=5 -mkernel -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix KERNELABIVERSION +// RUN: %clang -fptrauth-abi-version=5 -fapple-kext -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix KERNELABIVERSION +// RUN: %clang -fptrauth-abi-version=5 -fptrauth-kernel-abi-version -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix KERNELABIVERSION -// RUN: %clang -fno-ptrauth-abi-version -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix NOABIVERSION --check-prefix NOKERNELABIVERSION -// RUN: %clang -fptrauth-abi-version=5 -fno-ptrauth-abi-version -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix NOABIVERSION --check-prefix NOKERNELABIVERSION -// RUN: %clang -fno-ptrauth-abi-version -fptrauth-abi-version=5 -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix NOKERNELABIVERSION +// RUN: %clang -fno-ptrauth-abi-version -target arm64e-apple-ios -### 2>&1 | FileCheck %s --check-prefix NOABIVERSION --check-prefix NOKERNELABIVERSION +// RUoN: %clang -fptrauth-abi-version=5 -fno-ptrauth-abi-version -target arm64e-apple-ios -### 2>&1 | FileCheck %s --check-prefix NOABIVERSION --check-prefix NOKERNELABIVERSION +// RUoN: %clang -fno-ptrauth-abi-version -fptrauth-abi-version=5 -target arm64e-apple-ios -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix NOKERNELABIVERSION // ABIVERSION: "-fptrauth-abi-version=5" // ABIVERSION-DEFAULT: "-fptrauth-abi-version=0" diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp index e212aa149750f..b9af3fc3758e1 100644 --- a/clang/tools/driver/cc1as_main.cpp +++ b/clang/tools/driver/cc1as_main.cpp @@ -179,15 +179,15 @@ struct AssemblerInvocation { /// compilation llvm::VersionTuple DarwinTargetVariantSDKVersion; + /// The name of a file to use with \c .secure_log_unique directives. + std::string AsSecureLogFile; + /// The ptrauth ABI version targeted by the backend. unsigned PointerAuthABIVersion; /// Whether the ptrauth ABI version represents a kernel ABI. unsigned PointerAuthKernelABIVersion : 1; /// Whether the assembler should encode the ptrauth ABI version. unsigned PointerAuthABIVersionEncoded : 1; - - /// The name of a file to use with \c .secure_log_unique directives. - std::string AsSecureLogFile; /// @} public: From 602ddb2872954dfd66e2548399bf3248dff398e4 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Wed, 17 Apr 2024 16:33:45 -0700 Subject: [PATCH 19/58] [DWARF] Complete __ptrauth type qualifier support with auth mode. --- clang/lib/CodeGen/CGDebugInfo.cpp | 13 +++++++++---- llvm/include/llvm/IR/DIBuilder.h | 3 ++- llvm/include/llvm/IR/DebugInfoMetadata.h | 9 +++++++-- llvm/lib/AsmParser/LLParser.cpp | 8 +++++--- llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 3 +++ llvm/lib/IR/AsmWriter.cpp | 5 +++++ llvm/lib/IR/DIBuilder.cpp | 6 +++--- llvm/unittests/IR/MetadataTest.cpp | 11 ++++++++--- 8 files changed, 42 insertions(+), 16 deletions(-) diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 1e1e349c8dc9a..d4ca9d47021ca 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1024,13 +1024,18 @@ llvm::DIType *CGDebugInfo::CreateQualifiedType(QualType Ty, unsigned Key = Qc.getPointerAuth().getKey(); bool IsDiscr = Qc.getPointerAuth().isAddressDiscriminated(); unsigned ExtraDiscr = Qc.getPointerAuth().getExtraDiscriminator(); + bool IsaPointer = Qc.getPointerAuth().isIsaPointer(); + bool AuthenticatesNullValues = + Qc.getPointerAuth().authenticatesNullValues(); + unsigned AuthenticationMode = + (unsigned)Qc.getPointerAuth().getAuthenticationMode(); Qc.removePointerAuth(); assert(Qc.empty() && "Unknown type qualifier for debug info"); auto *FromTy = getOrCreateType(QualType(T, 0), Unit); - return DBuilder.createPtrAuthQualifiedType(FromTy, Key, IsDiscr, - ExtraDiscr, - /*IsaPointer=*/ false, - /*AuthenticatesNullValues=*/ false); + return DBuilder.createPtrAuthQualifiedType( + FromTy, Key, IsDiscr, ExtraDiscr, /*IsaPointer=*/IsaPointer, + /*AuthenticatesNullValues=*/AuthenticatesNullValues, + /*AuthenticationMode=*/AuthenticationMode); } else if (!Tag) { assert(Qc.empty() && "Unknown type qualifier for debug info"); return getOrCreateType(QualType(T, 0), Unit); diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h index 6e03c070ee765..20d4104d9ec74 100644 --- a/llvm/include/llvm/IR/DIBuilder.h +++ b/llvm/include/llvm/IR/DIBuilder.h @@ -280,7 +280,8 @@ namespace llvm { bool IsAddressDiscriminated, unsigned ExtraDiscriminator, bool IsaPointer, - bool authenticatesNullValues); + bool AuthenticatesNullValues, + unsigned AuthenticationMode); /// Create debugging information entry for a pointer to member. /// \param PointeeTy Type pointed to by this pointer. diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h index d30df75a30890..8a9344dace9a1 100644 --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -995,16 +995,20 @@ class DIDerivedType : public DIType { // - Bits 5..20: ExtraDiscriminator // - Bit 21: IsaPointer // - Bit 22: AuthenticatesNullValues + // - Bit 23..24 AuthenticationMode unsigned RawData; PtrAuthData(unsigned FromRawData) : RawData(FromRawData) {} PtrAuthData(unsigned Key, bool IsDiscr, unsigned Discriminator, - bool IsaPointer, bool AuthenticatesNullValues) { + bool IsaPointer, bool AuthenticatesNullValues, + unsigned AuthenticationMode) { assert(Key < 16); assert(Discriminator <= 0xffff); + assert(AuthenticationMode <= 3); RawData = (Key << 0) | (IsDiscr ? (1 << 4) : 0) | (Discriminator << 5) | (IsaPointer ? (1 << 21) : 0) | - (AuthenticatesNullValues ? (1 << 22) : 0); + (AuthenticatesNullValues ? (1 << 22) : 0) | + (AuthenticationMode << 23); } unsigned key() { return (RawData >> 0) & 0b1111; } @@ -1012,6 +1016,7 @@ class DIDerivedType : public DIType { unsigned extraDiscriminator() { return (RawData >> 5) & 0xffff; } bool isaPointer() { return (RawData >> 21) & 1; } bool authenticatesNullValues() { return (RawData >> 22) & 1; } + unsigned authenticationMode() { return (RawData >> 23) & 0b11;} }; private: diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index d72ed5a3c271b..117304226f4f3 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -5352,7 +5352,8 @@ bool LLParser::parseDIStringType(MDNode *&Result, bool IsDistinct) { /// dwarfAddressSpace: 3, ptrAuthKey: 1, /// ptrAuthIsAddressDiscriminated: true, /// ptrAuthExtraDiscriminator: 0x1234, -/// ptrAuthIsaPointer: 1, ptrAuthAuthenticatesNullValues:1 +/// ptrAuthIsaPointer: 1, ptrAuthAuthenticatesNullValues:1, +/// ptrAuthAuthenticationMode: 3 /// ) bool LLParser::parseDIDerivedType(MDNode *&Result, bool IsDistinct) { #define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \ @@ -5373,7 +5374,8 @@ bool LLParser::parseDIDerivedType(MDNode *&Result, bool IsDistinct) { OPTIONAL(ptrAuthIsAddressDiscriminated, MDBoolField, ); \ OPTIONAL(ptrAuthExtraDiscriminator, MDUnsignedField, (0, 0xffff)); \ OPTIONAL(ptrAuthIsaPointer, MDBoolField, ); \ - OPTIONAL(ptrAuthAuthenticatesNullValues, MDBoolField, ); + OPTIONAL(ptrAuthAuthenticatesNullValues, MDBoolField, ); \ + OPTIONAL(ptrAuthAuthenticationMode, MDUnsignedField, (3, 3)); PARSE_MD_FIELDS(); #undef VISIT_MD_FIELDS @@ -5385,7 +5387,7 @@ bool LLParser::parseDIDerivedType(MDNode *&Result, bool IsDistinct) { PtrAuthData.emplace( (unsigned)ptrAuthKey.Val, ptrAuthIsAddressDiscriminated.Val, (unsigned)ptrAuthExtraDiscriminator.Val, ptrAuthIsaPointer.Val, - ptrAuthAuthenticatesNullValues.Val); + ptrAuthAuthenticatesNullValues.Val, ptrAuthAuthenticationMode.Val); Result = GET_OR_DISTINCT(DIDerivedType, (Context, tag.Val, name.Val, file.Val, line.Val, diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp index 3edf5e39f0e3d..4414f6483a2ae 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -840,6 +840,9 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DIDerivedType *DTy) { addFlag(Buffer, dwarf::DW_AT_LLVM_ptrauth_isa_pointer); if (PtrAuthData->authenticatesNullValues()) addFlag(Buffer, dwarf::DW_AT_LLVM_ptrauth_authenticates_null_values); + if (PtrAuthData->authenticationMode() != 3) + addUInt(Buffer, dwarf::DW_AT_LLVM_ptrauth_authentication_mode, + dwarf::DW_FORM_data1, PtrAuthData->authenticationMode()); } } diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index c0150ec4eff3b..741d6f8a94e60 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -2184,6 +2184,11 @@ static void writeDIDerivedType(raw_ostream &Out, const DIDerivedType *N, Printer.printBool("ptrAuthIsaPointer", PtrAuthData->isaPointer()); Printer.printBool("ptrAuthAuthenticatesNullValues", PtrAuthData->authenticatesNullValues()); + if (PtrAuthData->authenticationMode() != 3) { + // Display non-default options + Printer.printInt("ptrAuthAuthenticationMode", + PtrAuthData->authenticationMode()); + } } Out << ")"; } diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp index 061a9f8e62ced..1d4c09b706997 100644 --- a/llvm/lib/IR/DIBuilder.cpp +++ b/llvm/lib/IR/DIBuilder.cpp @@ -302,14 +302,14 @@ DIDerivedType *DIBuilder::createQualifiedType(unsigned Tag, DIType *FromTy) { DIDerivedType *DIBuilder::createPtrAuthQualifiedType( DIType *FromTy, unsigned Key, bool IsAddressDiscriminated, - unsigned ExtraDiscriminator, bool IsaPointer, - bool AuthenticatesNullValues) { + unsigned ExtraDiscriminator, bool IsaPointer, bool AuthenticatesNullValues, + unsigned AuthenticationMode) { return DIDerivedType::get(VMContext, dwarf::DW_TAG_LLVM_ptrauth_type, "", nullptr, 0, nullptr, FromTy, 0, 0, 0, std::nullopt, std::optional( std::in_place, Key, IsAddressDiscriminated, ExtraDiscriminator, IsaPointer, - AuthenticatesNullValues), + AuthenticatesNullValues, AuthenticationMode), DINode::FlagZero); } diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp index 76cd48bf93dbc..113401ecbff3f 100644 --- a/llvm/unittests/IR/MetadataTest.cpp +++ b/llvm/unittests/IR/MetadataTest.cpp @@ -1869,8 +1869,8 @@ TEST_F(DIDerivedTypeTest, get) { DIType *BaseType = getBasicType("basic"); MDTuple *ExtraData = getTuple(); unsigned DWARFAddressSpace = 8; - DIDerivedType::PtrAuthData PtrAuthData(1, false, 1234, true, true); - DIDerivedType::PtrAuthData PtrAuthData2(1, false, 1234, true, false); + DIDerivedType::PtrAuthData PtrAuthData(1, false, 1234, true, true, 2); + DIDerivedType::PtrAuthData PtrAuthData2(1, false, 1234, true, false, 3); DINode::DIFlags Flags5 = static_cast(5); DINode::DIFlags Flags4 = static_cast(4); @@ -1880,6 +1880,9 @@ TEST_F(DIDerivedTypeTest, get) { auto *N1 = DIDerivedType::get(Context, dwarf::DW_TAG_LLVM_ptrauth_type, "", File, 1, Scope, N, 2, 3, 4, DWARFAddressSpace, PtrAuthData, Flags5, ExtraData); + auto *N2 = DIDerivedType::get(Context, dwarf::DW_TAG_LLVM_ptrauth_type, "", + File, 1, Scope, N, 2, 3, 4, DWARFAddressSpace, + PtrAuthData2, Flags5, ExtraData); EXPECT_EQ(dwarf::DW_TAG_pointer_type, N->getTag()); EXPECT_EQ("something", N->getName()); EXPECT_EQ(File, N->getFile()); @@ -1893,6 +1896,8 @@ TEST_F(DIDerivedTypeTest, get) { EXPECT_EQ(std::nullopt, N->getPtrAuthData()); EXPECT_EQ(PtrAuthData, N1->getPtrAuthData()); EXPECT_NE(PtrAuthData2, N1->getPtrAuthData()); + EXPECT_NE(PtrAuthData, N2->getPtrAuthData()); + EXPECT_EQ(PtrAuthData2, N2->getPtrAuthData()); EXPECT_EQ(5u, N->getFlags()); EXPECT_EQ(ExtraData, N->getExtraData()); EXPECT_EQ(N, DIDerivedType::get(Context, dwarf::DW_TAG_pointer_type, @@ -1978,7 +1983,7 @@ TEST_F(DIDerivedTypeTest, getWithLargeValues) { auto *N1 = DIDerivedType::get( Context, dwarf::DW_TAG_LLVM_ptrauth_type, "", File, 1, Scope, N, UINT64_MAX, UINT32_MAX - 1, UINT64_MAX - 2, UINT32_MAX - 3, - DIDerivedType::PtrAuthData(7, true, 0xffff, true, false), Flags, + DIDerivedType::PtrAuthData(7, true, 0xffff, true, false, 3), Flags, ExtraData); EXPECT_EQ(7U, N1->getPtrAuthData()->key()); EXPECT_EQ(true, N1->getPtrAuthData()->isAddressDiscriminated()); From b8bfae44ba6daa03fce80311157b825882bcc5ae Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 27 Sep 2021 08:00:00 -0700 Subject: [PATCH 20/58] [IR] Generalize EmitGEPOffset to not need an actual GEP inst. --- llvm/include/llvm/Analysis/Utils/Local.h | 82 ++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/llvm/include/llvm/Analysis/Utils/Local.h b/llvm/include/llvm/Analysis/Utils/Local.h index e1dbfd3e5f37c..aff803869a048 100644 --- a/llvm/include/llvm/Analysis/Utils/Local.h +++ b/llvm/include/llvm/Analysis/Utils/Local.h @@ -14,6 +14,11 @@ #ifndef LLVM_ANALYSIS_UTILS_LOCAL_H #define LLVM_ANALYSIS_UTILS_LOCAL_H +#include "llvm/ADT/StringRef.h" +#include "llvm/Analysis/ConstantFolding.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/GetElementPtrTypeIterator.h" + namespace llvm { class DataLayout; @@ -26,6 +31,83 @@ class Value; /// pointer). Return the result as a signed integer of intptr size. /// When NoAssumptions is true, no assumptions about index computation not /// overflowing is made. + +template +std::pair +EmitGEPOffset(IRBuilderTy *Builder, const DataLayout &DL, + generic_gep_type_iterator GTI, OpItTy IdxBegin, + OpItTy IdxEnd, Type *GEPType, StringRef GEPName, bool IsInBounds, + bool NoAssumptions = false) { + Type *IntIdxTy = DL.getIndexType(GEPType); + Value *Result = nullptr; + + // If the GEP is inbounds, we know that none of the addressing operations will + // overflow in a signed sense. + bool isInBounds = IsInBounds && !NoAssumptions; + + // Build a mask for high order bits. + unsigned IntPtrWidth = IntIdxTy->getScalarType()->getIntegerBitWidth(); + uint64_t PtrSizeMask = + std::numeric_limits::max() >> (64 - IntPtrWidth); + Type *IndexedType = nullptr; + + for (OpItTy i = IdxBegin, e = IdxEnd; i != e; ++i, ++GTI) { + Value *Op = *i; + IndexedType = GTI.getIndexedType(); + uint64_t Size = DL.getTypeAllocSize(IndexedType) & PtrSizeMask; + Value *Offset; + if (Constant *OpC = dyn_cast(Op)) { + if (OpC->isZeroValue()) + continue; + + // Handle a struct index, which adds its field offset to the pointer. + if (StructType *STy = GTI.getStructTypeOrNull()) { + uint64_t OpValue = OpC->getUniqueInteger().getZExtValue(); + IndexedType = STy->getElementType(OpValue); + Size = DL.getStructLayout(STy)->getElementOffset(OpValue); + if (!Size) + continue; + + Offset = ConstantInt::get(IntIdxTy, Size); + } else { + // Splat the constant if needed. + if (IntIdxTy->isVectorTy() && !OpC->getType()->isVectorTy()) + OpC = ConstantVector::getSplat( + cast(IntIdxTy)->getElementCount(), OpC); + + Constant *Scale = ConstantInt::get(IntIdxTy, Size); + Constant *OC = ConstantFoldIntegerCast(OpC, IntIdxTy, true, DL); + Offset = + ConstantExpr::getMul(OC, Scale, false /*NUW*/, isInBounds /*NSW*/); + } + } else { + // Splat the index if needed. + if (IntIdxTy->isVectorTy() && !Op->getType()->isVectorTy()) + Op = Builder->CreateVectorSplat( + cast(IntIdxTy)->getNumElements(), Op); + + // Convert to correct type. + if (Op->getType() != IntIdxTy) + Op = Builder->CreateIntCast(Op, IntIdxTy, true, Op->getName().str()+".c"); + if (Size != 1) { + // We'll let instcombine(mul) convert this to a shl if possible. + Op = Builder->CreateMul(Op, ConstantInt::get(IntIdxTy, Size), + GEPName.str() + ".idx", false /*NUW*/, + isInBounds /*NSW*/); + } + Offset = Op; + } + + if (Result) + Result = Builder->CreateAdd(Result, Offset, GEPName.str()+".offs", + false /*NUW*/, isInBounds /*NSW*/); + else + Result = Offset; + } + return std::make_pair(Result ? Result : Constant::getNullValue(IntIdxTy), + IndexedType); +} + Value *emitGEPOffset(IRBuilderBase *Builder, const DataLayout &DL, User *GEP, bool NoAssumptions = false); From ee4321f5ed5cc7e8e3df97287b3b20b1dff3e00d Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Wed, 15 May 2024 13:37:17 -0700 Subject: [PATCH 21/58] [clang][CodeGen] Set KnownNonNull in more places. --- clang/lib/CodeGen/CGExpr.cpp | 15 ++++++++++---- clang/lib/CodeGen/CGExprAgg.cpp | 3 +++ clang/lib/CodeGen/CGExprCXX.cpp | 9 +++++---- clang/lib/CodeGen/CGValue.h | 10 ++------- clang/lib/CodeGen/CodeGenFunction.cpp | 29 ++++++++++++++++++--------- clang/lib/CodeGen/CodeGenFunction.h | 4 +++- clang/lib/CodeGen/ItaniumCXXABI.cpp | 2 +- 7 files changed, 45 insertions(+), 27 deletions(-) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index fea1aa2198b1e..461ef325bca3c 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1944,6 +1944,9 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(Address Addr, bool Volatile, Addr = Addr.withPointer(Builder.CreateThreadLocalAddress(GV), NotKnownNonNull); + if (!CGM.getCodeGenOpts().NullPointerIsValid) + Addr = Addr.setKnownNonNull(); + if (const auto *ClangVecTy = Ty->getAs()) { // Boolean vectors use `iN` as storage type. if (ClangVecTy->isExtVectorBoolType()) { @@ -2093,6 +2096,9 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, Address Addr, Addr = Addr.withPointer(Builder.CreateThreadLocalAddress(GV), NotKnownNonNull); + if (!CGM.getCodeGenOpts().NullPointerIsValid) + Addr = Addr.setKnownNonNull(); + llvm::Type *SrcTy = Value->getType(); if (const auto *ClangVecTy = Ty->getAs()) { auto *VecTy = dyn_cast(SrcTy); @@ -2812,9 +2818,9 @@ CodeGenFunction::EmitLoadOfReference(LValue RefLVal, llvm::LoadInst *Load = Builder.CreateLoad(RefLVal.getAddress(), RefLVal.isVolatile()); CGM.DecorateInstructionWithTBAA(Load, RefLVal.getTBAAInfo()); - return makeNaturalAddressForPointer(Load, RefLVal.getType()->getPointeeType(), - CharUnits(), /*ForPointeeType=*/true, - PointeeBaseInfo, PointeeTBAAInfo); + return makeNaturalAddressForPointer( + Load, RefLVal.getType()->getPointeeType(), CharUnits(), + /*ForPointeeType=*/true, PointeeBaseInfo, PointeeTBAAInfo, KnownNonNull); } LValue CodeGenFunction::EmitLoadOfReferenceLValue(LValue RefLVal) { @@ -4752,7 +4758,8 @@ LValue CodeGenFunction::EmitLValueForLambdaField(const FieldDecl *Field, } } else { QualType LambdaTagType = getContext().getTagDeclType(Field->getParent()); - LambdaLV = MakeNaturalAlignAddrLValue(ThisValue, LambdaTagType); + LambdaLV = MakeNaturalAlignAddrLValue(ThisValue, LambdaTagType, + KnownNonNull); } return EmitLValueForField(LambdaLV, Field); } diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index c3c10e73ff05e..d70701f0856cf 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -253,6 +253,9 @@ class AggExprEmitter : public StmtVisitor { void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E) { LValue LV = CGF.EmitLValue(E); + if (!CGF.CGM.getCodeGenOpts().NullPointerIsValid) + LV = LV.setKnownNonNull(); + // If the type of the l-value is atomic, then do an atomic load. if (LV.getType()->isAtomicType() || CGF.LValueIsSuitableForInlineAtomic(LV)) { CGF.EmitAtomicLoad(LV, E->getExprLoc(), Dest); diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 8eb6ab7381acb..db4a70f5316f8 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -265,7 +265,7 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr( if (auto *OCE = dyn_cast(CE)) { if (OCE->isAssignmentOp()) { if (TrivialAssignment) { - TrivialAssignmentRHS = EmitLValue(CE->getArg(1)); + TrivialAssignmentRHS = EmitLValue(CE->getArg(1), KnownNonNull); } else { RtlArgs = &RtlArgStorage; EmitCallArgs(*RtlArgs, MD->getType()->castAs(), @@ -279,11 +279,12 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr( if (IsArrow) { LValueBaseInfo BaseInfo; TBAAAccessInfo TBAAInfo; - Address ThisValue = EmitPointerWithAlignment(Base, &BaseInfo, &TBAAInfo); + Address ThisValue = EmitPointerWithAlignment( + Base, &BaseInfo, &TBAAInfo, KnownNonNull); This = MakeAddrLValue(ThisValue, Base->getType()->getPointeeType(), BaseInfo, TBAAInfo); } else { - This = EmitLValue(Base); + This = EmitLValue(Base, KnownNonNull); } if (const CXXConstructorDecl *Ctor = dyn_cast(MD)) { @@ -316,7 +317,7 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr( // the RHS. LValue RHS = isa(CE) ? TrivialAssignmentRHS - : EmitLValue(*CE->arg_begin()); + : EmitLValue(*CE->arg_begin(), KnownNonNull); EmitAggregateAssign(This, RHS, CE->getType()); return RValue::get(This.getPointer(*this)); } diff --git a/clang/lib/CodeGen/CGValue.h b/clang/lib/CodeGen/CGValue.h index f1ba3cf95ae59..6ed94bd9c7592 100644 --- a/clang/lib/CodeGen/CGValue.h +++ b/clang/lib/CodeGen/CGValue.h @@ -233,9 +233,6 @@ class LValue { // this lvalue. bool Nontemporal : 1; - // The pointer is known not to be null. - bool IsKnownNonNull : 1; - LValueBaseInfo BaseInfo; TBAAAccessInfo TBAAInfo; @@ -263,7 +260,6 @@ class LValue { this->ImpreciseLifetime = false; this->Nontemporal = false; this->ThreadLocalRef = false; - this->IsKnownNonNull = false; this->BaseIvarExp = nullptr; } @@ -349,11 +345,9 @@ class LValue { LValueBaseInfo getBaseInfo() const { return BaseInfo; } void setBaseInfo(LValueBaseInfo Info) { BaseInfo = Info; } - KnownNonNull_t isKnownNonNull() const { - return (KnownNonNull_t)IsKnownNonNull; - } + KnownNonNull_t isKnownNonNull() const { return Addr.isKnownNonNull(); } LValue setKnownNonNull() { - IsKnownNonNull = true; + Addr.setKnownNonNull(); return *this; } diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 3d7981eea1905..c97cad73a0505 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -195,34 +195,45 @@ CodeGenFunction::CGFPOptionsRAII::~CGFPOptionsRAII() { CGF.Builder.setDefaultConstrainedRounding(OldRounding); } -static LValue MakeNaturalAlignAddrLValue(llvm::Value *V, QualType T, - bool ForPointeeType, - CodeGenFunction &CGF) { +static LValue +MakeNaturalAlignAddrLValue(llvm::Value *V, QualType T, bool ForPointeeType, + bool MightBeSigned, CodeGenFunction &CGF, + KnownNonNull_t IsKnownNonNull = NotKnownNonNull) { LValueBaseInfo BaseInfo; TBAAAccessInfo TBAAInfo; CharUnits Alignment = CGF.CGM.getNaturalTypeAlignment(T, &BaseInfo, &TBAAInfo, ForPointeeType); - Address Addr = Address(V, CGF.ConvertTypeForMem(T), Alignment); + Address Addr = + MightBeSigned + ? CGF.makeNaturalAddressForPointer(V, T, Alignment, false, nullptr, + nullptr, IsKnownNonNull) + : Address(V, CGF.ConvertTypeForMem(T), Alignment, IsKnownNonNull); return CGF.MakeAddrLValue(Addr, T, BaseInfo, TBAAInfo); } -LValue CodeGenFunction::MakeNaturalAlignAddrLValue(llvm::Value *V, QualType T) { - return ::MakeNaturalAlignAddrLValue(V, T, /*ForPointeeType*/ false, *this); +LValue +CodeGenFunction::MakeNaturalAlignAddrLValue(llvm::Value *V, QualType T, + KnownNonNull_t IsKnownNonNull) { + return ::MakeNaturalAlignAddrLValue(V, T, /*ForPointeeType*/ false, + /*IsSigned*/ true, *this, IsKnownNonNull); } LValue CodeGenFunction::MakeNaturalAlignPointeeAddrLValue(llvm::Value *V, QualType T) { - return ::MakeNaturalAlignAddrLValue(V, T, /*ForPointeeType*/ true, *this); + return ::MakeNaturalAlignAddrLValue(V, T, /*ForPointeeType*/ true, + /*IsSigned*/ true, *this); } LValue CodeGenFunction::MakeNaturalAlignRawAddrLValue(llvm::Value *V, QualType T) { - return ::MakeNaturalAlignAddrLValue(V, T, /*ForPointeeType*/ false, *this); + return ::MakeNaturalAlignAddrLValue(V, T, /*ForPointeeType*/ false, + /*IsSigned*/ false, *this); } LValue CodeGenFunction::MakeNaturalAlignPointeeRawAddrLValue(llvm::Value *V, QualType T) { - return ::MakeNaturalAlignAddrLValue(V, T, /*ForPointeeType*/ true, *this); + return ::MakeNaturalAlignAddrLValue(V, T, /*ForPointeeType*/ true, + /*IsSigned*/ false, *this); } llvm::Type *CodeGenFunction::ConvertTypeForMem(QualType T) { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index e9b257f752735..9b5fd1381e72f 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2728,7 +2728,9 @@ class CodeGenFunction : public CodeGenTypeCache { /// an l-value with the natural pointee alignment of T. LValue MakeNaturalAlignPointeeAddrLValue(llvm::Value *V, QualType T); - LValue MakeNaturalAlignAddrLValue(llvm::Value *V, QualType T); + LValue + MakeNaturalAlignAddrLValue(llvm::Value *V, QualType T, + KnownNonNull_t IsKnownNonNull = NotKnownNonNull); /// Same as MakeNaturalAlignPointeeAddrLValue except that the pointer is known /// to be unsigned. diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index c6c3f1e21b752..578ee2043a5b8 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -4926,7 +4926,7 @@ static void InitCatchParam(CodeGenFunction &CGF, // Cast that to the appropriate type. Address adjustedExn(CGF.Builder.CreateBitCast(rawAdjustedExn, PtrTy), - LLVMCatchTy, caughtExnAlignment); + LLVMCatchTy, caughtExnAlignment, KnownNonNull); // The copy expression is defined in terms of an OpaqueValueExpr. // Find it and map it to the adjusted expression. From d00e74217e7261c1560e8eed703106a877ae1c56 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 27 Sep 2021 08:00:00 -0700 Subject: [PATCH 22/58] [clang][CodeGen] Teach RawAddress/Address about ptrauth. --- clang/lib/CodeGen/Address.h | 43 ++++++++++++++++++++++++++--- clang/lib/CodeGen/CGBlocks.cpp | 4 +-- clang/lib/CodeGen/CGBuilder.h | 46 ++++++++++++++++++++++--------- clang/lib/CodeGen/CGCall.cpp | 13 +++++---- clang/lib/CodeGen/CGException.cpp | 4 +-- clang/lib/CodeGen/CGObjCMac.cpp | 2 +- 6 files changed, 85 insertions(+), 27 deletions(-) diff --git a/clang/lib/CodeGen/Address.h b/clang/lib/CodeGen/Address.h index 35ec370a139c9..3b677c9159e22 100644 --- a/clang/lib/CodeGen/Address.h +++ b/clang/lib/CodeGen/Address.h @@ -108,6 +108,22 @@ class RawAddress { /// Like RawAddress, an abstract representation of an aligned address, but the /// pointer contained in this class is possibly signed. +/// +/// This is designed to be an IR-level abstraction, carrying just the +/// information necessary to perform IR operations on an address like loads and +/// stores. In particular, it doesn't carry C type information or allow the +/// representation of things like bit-fields; clients working at that level +/// should generally be using `LValue`. +/// +/// An address may be either *raw*, meaning that it's an ordinary machine +/// pointer, or *signed*, meaning that the pointer carries an embedded +/// pointer-authentication signature. Representing signed pointers directly in +/// this abstraction allows the authentication to be delayed as long as possible +/// without forcing IRGen to use totally different code paths for signed and +/// unsigned values or to separately propagate signature information through +/// every API that manipulates addresses. Pointer arithmetic on signed addresses +/// (e.g. drilling down to a struct field) is accumulated into a separate offset +/// which is applied when the address is finally accessed. class Address { friend class CGBuilderTy; @@ -117,6 +133,10 @@ class Address { /// The expected IR type of the pointer. Carrying accurate element type /// information in Address makes it more convenient to work with Address /// values and allows frontend assertions to catch simple mistakes. + /// + /// When the address is a raw pointer, this is currently redundant with the + /// pointer's type, but for signed pointers it is useful if the pointer has + /// been offsetted or cast from the original type. llvm::Type *ElementType = nullptr; CharUnits Alignment; @@ -153,6 +173,11 @@ class Address { static Address invalid() { return Address(nullptr); } bool isValid() const { return Pointer.getPointer() != nullptr; } + llvm::Value *getPointerIfNotSigned() const { + assert(isValid() && "pointer isn't valid"); + return Pointer.getPointer(); + } + /// This function is used in situations where the caller is doing some sort of /// opaque "laundering" of the pointer. void replaceBasePointer(llvm::Value *P) { @@ -172,6 +197,8 @@ class Address { return Pointer.getPointer(); } + llvm::Value *getUnsignedPointer() const { return getBasePointer(); } + /// Return the type of the pointer value. llvm::PointerType *getType() const { return llvm::PointerType::get( @@ -211,6 +238,14 @@ class Address { return *this; } + /// Add a constant offset. + void addOffset(CharUnits V, llvm::Type *Ty, CGBuilderTy &Builder); + + /// Add a variable offset. + /// \param V An llvm value holding a variable offset. + void addOffset(llvm::Value *V, llvm::Type *Ty, CGBuilderTy &Builder, + CharUnits NewAlignment); + bool hasOffset() const { return Offset; } llvm::Value *getOffset() const { return Offset; } @@ -218,7 +253,7 @@ class Address { /// Return the pointer contained in this class after authenticating it and /// adding offset to it if necessary. llvm::Value *emitRawPointer(CodeGenFunction &CGF) const { - return getBasePointer(); + return getUnsignedPointer(); } /// Return address with different pointer, but same element type and @@ -249,9 +284,9 @@ class Address { }; inline RawAddress::RawAddress(Address Addr) - : PointerAndKnownNonNull(Addr.isValid() ? Addr.getBasePointer() : nullptr, - Addr.isValid() ? Addr.isKnownNonNull() - : NotKnownNonNull), + : PointerAndKnownNonNull( + Addr.isValid() ? Addr.getUnsignedPointer() : nullptr, + Addr.isValid() ? Addr.isKnownNonNull() : NotKnownNonNull), ElementType(Addr.isValid() ? Addr.getElementType() : nullptr), Alignment(Addr.isValid() ? Addr.getAlignment() : CharUnits::Zero()) {} diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index 698a1bb99b445..35756b495d653 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -2026,8 +2026,8 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { // it. It's not quite worth the annoyance to avoid creating it in the // first place. if (!needsEHCleanup(captureType.isDestructedType())) - if (auto *I = - cast_or_null(dstField.getBasePointer())) + if (auto *I = cast_or_null( + dstField.getPointerIfNotSigned())) I->eraseFromParent(); } break; diff --git a/clang/lib/CodeGen/CGBuilder.h b/clang/lib/CodeGen/CGBuilder.h index 0bc4fda62979c..752bfc6c0c058 100644 --- a/clang/lib/CodeGen/CGBuilder.h +++ b/clang/lib/CodeGen/CGBuilder.h @@ -15,6 +15,7 @@ #include "llvm/Analysis/Utils/Local.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" #include "llvm/IR/Type.h" namespace clang { @@ -56,7 +57,24 @@ class CGBuilderTy : public CGBuilderBaseTy { CodeGenFunction *getCGF() const { return getInserter().CGF; } llvm::Value *emitRawPointerFromAddress(Address Addr) const { - return Addr.getBasePointer(); + return Addr.getUnsignedPointer(); + } + + /// Helper function to compute a GEP's offset and add it to Addr. + Address addGEPOffset(Address Addr, ArrayRef IdxList, + CharUnits Align, bool IsInBounds, const Twine &Name) { + typedef ArrayRef::const_iterator IdxItTy; + typedef llvm::generic_gep_type_iterator GEPTypeIt; + const llvm::DataLayout &DL = BB->getParent()->getParent()->getDataLayout(); + GEPTypeIt GTI = GEPTypeIt::begin(Addr.getElementType(), IdxList.begin()); + IdxItTy IdxBegin = IdxList.begin(), IdxEnd = IdxList.end(); + llvm::Type *GEPType = Addr.getType(); + SmallString<12> Buffer; + StringRef GEPName = Name.toStringRef(Buffer); + std::pair OffsetAndType = llvm::EmitGEPOffset( + this, DL, GTI, IdxBegin, IdxEnd, GEPType, GEPName, IsInBounds, false); + Addr.addOffset(OffsetAndType.first, OffsetAndType.second, *this, Align); + return Addr; } template @@ -222,8 +240,8 @@ class CGBuilderTy : public CGBuilderBaseTy { const llvm::StructLayout *Layout = DL.getStructLayout(ElTy); auto Offset = CharUnits::fromQuantity(Layout->getElementOffset(Index)); - return Address(CreateStructGEP(Addr.getElementType(), Addr.getBasePointer(), - Index, Name), + return Address(CreateStructGEP(Addr.getElementType(), + Addr.getUnsignedPointer(), Index, Name), ElTy->getElementType(Index), Addr.getAlignment().alignmentAtOffset(Offset), Addr.isKnownNonNull()); @@ -245,7 +263,7 @@ class CGBuilderTy : public CGBuilderBaseTy { CharUnits::fromQuantity(DL.getTypeAllocSize(ElTy->getElementType())); return Address( - CreateInBoundsGEP(Addr.getElementType(), Addr.getBasePointer(), + CreateInBoundsGEP(Addr.getElementType(), Addr.getUnsignedPointer(), {getSize(CharUnits::Zero()), getSize(Index)}, Name), ElTy->getElementType(), Addr.getAlignment().alignmentAtOffset(Index * EltSize), @@ -263,10 +281,10 @@ class CGBuilderTy : public CGBuilderBaseTy { const llvm::DataLayout &DL = BB->getDataLayout(); CharUnits EltSize = CharUnits::fromQuantity(DL.getTypeAllocSize(ElTy)); - return Address( - CreateInBoundsGEP(ElTy, Addr.getBasePointer(), getSize(Index), Name), - ElTy, Addr.getAlignment().alignmentAtOffset(Index * EltSize), - Addr.isKnownNonNull()); + return Address(CreateInBoundsGEP(ElTy, Addr.getUnsignedPointer(), + getSize(Index), Name), + ElTy, Addr.getAlignment().alignmentAtOffset(Index * EltSize), + Addr.isKnownNonNull()); } /// Given @@ -280,9 +298,10 @@ class CGBuilderTy : public CGBuilderBaseTy { const llvm::DataLayout &DL = BB->getDataLayout(); CharUnits EltSize = CharUnits::fromQuantity(DL.getTypeAllocSize(ElTy)); - return Address(CreateGEP(ElTy, Addr.getBasePointer(), getSize(Index), Name), - Addr.getElementType(), - Addr.getAlignment().alignmentAtOffset(Index * EltSize)); + return Address( + CreateGEP(ElTy, Addr.getUnsignedPointer(), getSize(Index), Name), + Addr.getElementType(), + Addr.getAlignment().alignmentAtOffset(Index * EltSize)); } /// Create GEP with single dynamic index. The address alignment is reduced @@ -305,7 +324,7 @@ class CGBuilderTy : public CGBuilderBaseTy { const llvm::Twine &Name = "") { assert(Addr.getElementType() == TypeCache.Int8Ty); return Address( - CreateInBoundsGEP(Addr.getElementType(), Addr.getBasePointer(), + CreateInBoundsGEP(Addr.getElementType(), Addr.getUnsignedPointer(), getSize(Offset), Name), Addr.getElementType(), Addr.getAlignment().alignmentAtOffset(Offset), Addr.isKnownNonNull()); @@ -314,7 +333,8 @@ class CGBuilderTy : public CGBuilderBaseTy { Address CreateConstByteGEP(Address Addr, CharUnits Offset, const llvm::Twine &Name = "") { assert(Addr.getElementType() == TypeCache.Int8Ty); - return Address(CreateGEP(Addr.getElementType(), Addr.getBasePointer(), + + return Address(CreateGEP(Addr.getElementType(), Addr.getUnsignedPointer(), getSize(Offset), Name), Addr.getElementType(), Addr.getAlignment().alignmentAtOffset(Offset)); diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 6e25e9224749f..0cf220e0443a0 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3513,7 +3513,8 @@ static llvm::Value *tryRemoveRetainOfSelf(CodeGenFunction &CGF, llvm::LoadInst *load = dyn_cast(retainedValue->stripPointerCasts()); if (!load || load->isAtomic() || load->isVolatile() || - load->getPointerOperand() != CGF.GetAddrOfLocalVar(self).getBasePointer()) + load->getPointerOperand() != + CGF.GetAddrOfLocalVar(self).getPointerIfNotSigned()) return nullptr; // Okay! Burn it all down. This relies for correctness on the @@ -3550,7 +3551,8 @@ static llvm::Value *emitAutoreleaseOfResult(CodeGenFunction &CGF, /// Heuristically search for a dominating store to the return-value slot. static llvm::StoreInst *findDominatingStoreToReturnValue(CodeGenFunction &CGF) { - llvm::Value *ReturnValuePtr = CGF.ReturnValue.getBasePointer(); + // This function shouldn't be called when ReturnValue is signed. + llvm::Value *ReturnValuePtr = CGF.ReturnValue.getUnsignedPointer(); // Check if a User is a store which pointerOperand is the ReturnValue. // We are looking for stores to the ReturnValue, not for stores of the @@ -4138,7 +4140,8 @@ static bool isProvablyNull(llvm::Value *addr) { } static bool isProvablyNonNull(Address Addr, CodeGenFunction &CGF) { - return llvm::isKnownNonZero(Addr.getBasePointer(), CGF.CGM.getDataLayout()); + return llvm::isKnownNonZero(Addr.getUnsignedPointer(), + CGF.CGM.getDataLayout()); } /// Emit the actual writing-back of a writeback. @@ -4146,7 +4149,7 @@ static void emitWriteback(CodeGenFunction &CGF, const CallArgList::Writeback &writeback) { const LValue &srcLV = writeback.Source; Address srcAddr = srcLV.getAddress(); - assert(!isProvablyNull(srcAddr.getBasePointer()) && + assert(!isProvablyNull(srcAddr.getPointerIfNotSigned()) && "shouldn't have writeback for provably null argument"); llvm::BasicBlock *contBB = nullptr; @@ -4263,7 +4266,7 @@ static void emitWritebackArg(CodeGenFunction &CGF, CallArgList &args, CGF.ConvertTypeForMem(CRE->getType()->getPointeeType()); // If the address is a constant null, just pass the appropriate null. - if (isProvablyNull(srcAddr.getBasePointer())) { + if (isProvablyNull(srcAddr.getPointerIfNotSigned())) { args.add(RValue::get(llvm::ConstantPointerNull::get(destType)), CRE->getType()); return; diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index bb2ed237ee9f3..2de85963437f5 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -1835,8 +1835,8 @@ Address CodeGenFunction::recoverAddrOfEscapedLocal(CodeGenFunction &ParentCGF, llvm::Value *ParentFP) { llvm::CallInst *RecoverCall = nullptr; CGBuilderTy Builder(*this, AllocaInsertPt); - if (auto *ParentAlloca = - dyn_cast_or_null(ParentVar.getBasePointer())) { + if (auto *ParentAlloca = dyn_cast_or_null( + ParentVar.getPointerIfNotSigned())) { // Mark the variable escaped if nobody else referenced it and compute the // localescape index. auto InsertPair = ParentCGF.EscapedLocals.insert( diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 86f3ef934760d..2cab598f7bf20 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -4425,7 +4425,7 @@ void FragileHazards::emitHazardsInNewBlocks() { static void addIfPresent(llvm::DenseSet &S, Address V) { if (V.isValid()) - if (llvm::Value *Ptr = V.getBasePointer()) + if (llvm::Value *Ptr = V.getPointerIfNotSigned()) S.insert(Ptr); } From d68f2eae7eb2a3d42432dfcb00d86b18f4424ee0 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Tue, 2 Jul 2024 11:22:19 -0700 Subject: [PATCH 23/58] [clang] Implement function pointer type discrimination Give users an option to sign a function pointer using a non-zero discrimiantor based on the type of the destination. Co-authored-by: John McCall --- clang/include/clang/AST/ASTContext.h | 6 +- clang/include/clang/AST/Type.h | 3 + clang/include/clang/Basic/Features.def | 1 + clang/include/clang/Basic/LangOptions.def | 2 + clang/include/clang/CodeGen/CodeGenABITypes.h | 3 +- clang/include/clang/Driver/Options.td | 2 + clang/lib/AST/ASTContext.cpp | 261 +++++++++++++++++- clang/lib/CodeGen/CGExprConstant.cpp | 4 +- clang/lib/CodeGen/CGPointerAuth.cpp | 69 +++-- clang/lib/CodeGen/CodeGenModule.h | 4 + clang/lib/Driver/ToolChains/Clang.cpp | 3 + clang/lib/Frontend/CompilerInvocation.cpp | 10 +- ...ction-type-discriminator-wrapper-globals.c | 144 ++++++++++ .../ptrauth-function-type-discriminator.c | 138 +++++++++ clang/test/Preprocessor/ptrauth_feature.c | 34 ++- 15 files changed, 642 insertions(+), 42 deletions(-) create mode 100644 clang/test/CodeGen/ptrauth-function-type-discriminator-wrapper-globals.c create mode 100644 clang/test/CodeGen/ptrauth-function-type-discriminator.c diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index fc7f7a2e169a7..5c7377586da13 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1300,14 +1300,14 @@ class ASTContext : public RefCountedBase { /// space. QualType removeAddrSpaceQualType(QualType T) const; - /// Return the "other" type-specific discriminator for the given type. - uint16_t getPointerAuthTypeDiscriminator(QualType T); - /// Return the "other" discriminator used for the pointer auth schema used for /// vtable pointers in instances of the requested type. uint16_t getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD); + /// Return the "other" type-specific discriminator for the given type. + uint16_t getPointerAuthTypeDiscriminator(QualType T); + /// Apply Objective-C protocol qualifiers to the given type. /// \param allowOnPointerType specifies if we can apply protocol /// qualifiers on ObjCObjectPointerType. It can be set to true when diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 178ec2dcf6288..36f9d3c277905 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2530,6 +2530,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isFunctionNoProtoType() const { return getAs(); } bool isFunctionProtoType() const { return getAs(); } bool isPointerType() const; + bool isSignableType() const; bool isAnyPointerType() const; // Any C pointer or ObjC object pointer bool isCountAttributedType() const; bool isBlockPointerType() const; @@ -8019,6 +8020,8 @@ inline bool Type::isAnyPointerType() const { return isPointerType() || isObjCObjectPointerType(); } +inline bool Type::isSignableType() const { return isPointerType(); } + inline bool Type::isBlockPointerType() const { return isa(CanonicalType); } diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 561599c1ab600..05b6b2bef759f 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -117,6 +117,7 @@ FEATURE(ptrauth_vtable_pointer_address_discrimination, LangOpts.PointerAuthVTPtr FEATURE(ptrauth_vtable_pointer_type_discrimination, LangOpts.PointerAuthVTPtrTypeDiscrimination) FEATURE(ptrauth_member_function_pointer_type_discrimination, LangOpts.PointerAuthCalls) FEATURE(ptrauth_init_fini, LangOpts.PointerAuthInitFini) +FEATURE(ptrauth_function_pointer_type_discrimination, LangOpts.PointerAuthFunctionTypeDiscrimination) EXTENSION(swiftcc, PP.getTargetInfo().checkCallingConvention(CC_Swift) == clang::TargetInfo::CCCR_OK) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index f3d390d321b16..030ee75ddd76b 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -170,6 +170,8 @@ LANGOPT(PointerAuthAuthTraps, 1, 0, "pointer authentication failure traps") LANGOPT(PointerAuthVTPtrAddressDiscrimination, 1, 0, "incorporate address discrimination in authenticated vtable pointers") LANGOPT(PointerAuthVTPtrTypeDiscrimination, 1, 0, "incorporate type discrimination in authenticated vtable pointers") LANGOPT(PointerAuthInitFini, 1, 0, "sign function pointers in init/fini arrays") +BENIGN_LANGOPT(PointerAuthFunctionTypeDiscrimination, 1, 0, + "Use type discrimination when signing function pointers") LANGOPT(PointerAuthIndirectGotos, 1, 0, "indirect gotos pointer authentication") LANGOPT(SoftPointerAuth , 1, 0, "software emulation of pointer authentication") diff --git a/clang/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h index 7f4f6b3fce269..c959bb8747847 100644 --- a/clang/include/clang/CodeGen/CodeGenABITypes.h +++ b/clang/include/clang/CodeGen/CodeGenABITypes.h @@ -117,7 +117,8 @@ uint64_t computeStableStringHash(StringRef string); uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD); /// Return a type discriminator for the given function type. -uint16_t getPointerAuthTypeDiscriminator(CodeGenModule &CGM, QualType fnType); +uint16_t getPointerAuthTypeDiscriminator(CodeGenModule &CGM, + QualType FunctionType); /// Return a signed constant pointer. llvm::Constant *getConstantSignedPointer(CodeGenModule &CGM, diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 5853eb70b1126..46f4988809865 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4272,6 +4272,8 @@ defm ptrauth_vtable_pointer_address_discrimination : defm ptrauth_vtable_pointer_type_discrimination : OptInCC1FFlag<"ptrauth-vtable-pointer-type-discrimination", "Enable type discrimination of vtable pointers">; defm ptrauth_init_fini : OptInCC1FFlag<"ptrauth-init-fini", "Enable signing of function pointers in init/fini arrays">; +defm ptrauth_function_pointer_type_discrimination : OptInCC1FFlag<"ptrauth-function-pointer-type-discrimination", + "Enable type discrimination on C function pointers">; } let Group = f_Group in { diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index e993c51c8787a..3d006a982fd75 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3142,14 +3142,269 @@ ASTContext::getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD) { return llvm::getPointerAuthStableSipHash(Str); } +/// Encode a function type for use in the discriminator of a function pointer +/// type. We can't use the itanium scheme for this since C has quite permissive +/// rules for type compatibility that we need to be compatible with. +/// +/// Formally, this function associates every function pointer type T with an +/// encoded string E(T). Let the equivalence relation T1 ~ T2 be defined as +/// E(T1) == E(T2). E(T) is part of the ABI of values of type T. C type +/// compatibility requires equivalent treatment under the ABI, so +/// CCompatible(T1, T2) must imply E(T1) == E(T2), that is, CCompatible must be +/// a subset of ~. Crucially, however, it must be a proper subset because +/// CCompatible is not an equivalence relation: for example, int[] is compatible +/// with both int[1] and int[2], but the latter are not compatible with each +/// other. Therefore this encoding function must be careful to only distinguish +/// types if there is no third type with which they are both required to be +/// compatible. +static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx, + raw_ostream &OS, QualType QT) { + // FIXME: Consider address space qualifiers. + const Type *T = QT.getCanonicalType().getTypePtr(); + + // FIXME: Consider using the C++ type mangling when we encounter a construct + // that is incompatible with C. + + switch (T->getTypeClass()) { + case Type::Atomic: + return encodeTypeForFunctionPointerAuth( + Ctx, OS, cast(T)->getValueType()); + + case Type::LValueReference: + OS << "R"; + encodeTypeForFunctionPointerAuth(Ctx, OS, + cast(T)->getPointeeType()); + return; + case Type::RValueReference: + OS << "O"; + encodeTypeForFunctionPointerAuth(Ctx, OS, + cast(T)->getPointeeType()); + return; + + case Type::Pointer: + // C11 6.7.6.1p2: + // For two pointer types to be compatible, both shall be identically + // qualified and both shall be pointers to compatible types. + // FIXME: we should also consider pointee types. + OS << "P"; + return; + + case Type::ObjCObjectPointer: + case Type::BlockPointer: + OS << "P"; + return; + + case Type::Complex: + OS << "C"; + return encodeTypeForFunctionPointerAuth( + Ctx, OS, cast(T)->getElementType()); + + case Type::VariableArray: + case Type::ConstantArray: + case Type::IncompleteArray: + case Type::ArrayParameter: + // C11 6.7.6.2p6: + // For two array types to be compatible, both shall have compatible + // element types, and if both size specifiers are present, and are integer + // constant expressions, then both size specifiers shall have the same + // constant value [...] + // + // So since ElemType[N] has to be compatible ElemType[], we can't encode the + // width of the array. + OS << "A"; + return encodeTypeForFunctionPointerAuth( + Ctx, OS, cast(T)->getElementType()); + + case Type::ObjCInterface: + case Type::ObjCObject: + OS << ""; + return; + + case Type::Enum: + // C11 6.7.2.2p4: + // Each enumerated type shall be compatible with char, a signed integer + // type, or an unsigned integer type. + // + // So we have to treat enum types as integers. + OS << "i"; + return; + + case Type::FunctionNoProto: + case Type::FunctionProto: { + // C11 6.7.6.3p15: + // For two function types to be compatible, both shall specify compatible + // return types. Moreover, the parameter type lists, if both are present, + // shall agree in the number of parameters and in the use of the ellipsis + // terminator; corresponding parameters shall have compatible types. + // + // That paragraph goes on to describe how unprototyped functions are to be + // handled, which we ignore here. Unprototyped function pointers are hashed + // as though they were prototyped nullary functions since thats probably + // what the user meant. This behavior is non-conforming. + // FIXME: If we add a "custom discriminator" function type attribute we + // should encode functions as their discriminators. + OS << "F"; + const auto *FuncType = cast(T); + encodeTypeForFunctionPointerAuth(Ctx, OS, FuncType->getReturnType()); + if (const auto *FPT = dyn_cast(FuncType)) { + for (QualType Param : FPT->param_types()) { + Param = Ctx.getSignatureParameterType(Param); + encodeTypeForFunctionPointerAuth(Ctx, OS, Param); + } + if (FPT->isVariadic()) + OS << "z"; + } + OS << "E"; + return; + } + + case Type::MemberPointer: { + OS << "M"; + const auto *MPT = T->getAs(); + encodeTypeForFunctionPointerAuth(Ctx, OS, QualType(MPT->getClass(), 0)); + encodeTypeForFunctionPointerAuth(Ctx, OS, MPT->getPointeeType()); + return; + } + case Type::ExtVector: + case Type::Vector: + OS << "Dv" << Ctx.getTypeSizeInChars(T).getQuantity(); + break; + + // Don't bother discriminating based on these types. + case Type::Pipe: + case Type::BitInt: + case Type::ConstantMatrix: + OS << "?"; + return; + + case Type::Builtin: { + const auto *BTy = T->getAs(); + switch (BTy->getKind()) { +#define SIGNED_TYPE(Id, SingletonId) \ + case BuiltinType::Id: \ + OS << "i"; \ + return; +#define UNSIGNED_TYPE(Id, SingletonId) \ + case BuiltinType::Id: \ + OS << "i"; \ + return; +#define PLACEHOLDER_TYPE(Id, SingletonId) case BuiltinType::Id: +#define BUILTIN_TYPE(Id, SingletonId) +#include "clang/AST/BuiltinTypes.def" + llvm_unreachable("placeholder types should not appear here."); + + case BuiltinType::Half: + OS << "Dh"; + return; + case BuiltinType::Float: + OS << "f"; + return; + case BuiltinType::Double: + OS << "d"; + return; + case BuiltinType::LongDouble: + OS << "e"; + return; + case BuiltinType::Float16: + OS << "DF16_"; + return; + case BuiltinType::Float128: + OS << "g"; + return; + + case BuiltinType::Void: + OS << "v"; + return; + + case BuiltinType::ObjCId: + case BuiltinType::ObjCClass: + case BuiltinType::ObjCSel: + case BuiltinType::NullPtr: + OS << "P"; + return; + + // Don't bother discriminating based on OpenCL types. + case BuiltinType::OCLSampler: + case BuiltinType::OCLEvent: + case BuiltinType::OCLClkEvent: + case BuiltinType::OCLQueue: + case BuiltinType::OCLReserveID: + case BuiltinType::BFloat16: + case BuiltinType::VectorQuad: + case BuiltinType::VectorPair: + OS << "?"; + return; + + // Don't bother discriminating based on these seldom-used types. + case BuiltinType::Ibm128: + return; +#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ + case BuiltinType::Id: \ + return; +#include "clang/Basic/OpenCLImageTypes.def" +#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ + case BuiltinType::Id: \ + return; +#include "clang/Basic/OpenCLExtensionTypes.def" +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: \ + return; +#include "clang/Basic/AArch64SVEACLETypes.def" + case BuiltinType::Dependent: + llvm_unreachable("should never get here"); + case BuiltinType::AMDGPUBufferRsrc: + case BuiltinType::WasmExternRef: +#define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id: +#include "clang/Basic/RISCVVTypes.def" + llvm_unreachable("not yet implemented"); + } + } + case Type::Record: { + const RecordDecl *RD = T->getAs()->getDecl(); + const IdentifierInfo *II = RD->getIdentifier(); + if (!II) + if (const TypedefNameDecl *Typedef = RD->getTypedefNameForAnonDecl()) + II = Typedef->getDeclName().getAsIdentifierInfo(); + + if (!II) { + OS << ""; + return; + } + OS << II->getLength() << II->getName(); + return; + } + case Type::DeducedTemplateSpecialization: + case Type::Auto: +#define NON_CANONICAL_TYPE(Class, Base) case Type::Class: +#define DEPENDENT_TYPE(Class, Base) case Type::Class: +#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) case Type::Class: +#define ABSTRACT_TYPE(Class, Base) +#define TYPE(Class, Base) +#include "clang/AST/TypeNodes.inc" + llvm_unreachable("unexpected non-canonical or dependent type!"); + return; + } +} + uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) { assert(!T->isDependentType() && "cannot compute type discriminator of a dependent type"); + SmallString<256> Str; llvm::raw_svector_ostream Out(Str); - std::unique_ptr MC(createMangleContext()); - MC->mangleCanonicalTypeName(T, Out); - return getPointerAuthStringDiscriminator(*this, Str.c_str()); + + if (T->isFunctionPointerType() || T->isFunctionReferenceType()) + T = T->getPointeeType(); + + if (T->isFunctionType()) + encodeTypeForFunctionPointerAuth(*this, Out, T); + else { + T = T.getUnqualifiedType(); + std::unique_ptr MC(createMangleContext()); + MC->mangleCanonicalTypeName(T, Out); + } + + return llvm::getPointerAuthStableSipHash(Str); } QualType ASTContext::getObjCGCQualType(QualType T, diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 20af15caa61ee..28a7457446be7 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -2304,8 +2304,8 @@ llvm::Constant *ConstantLValueEmitter::emitPointerAuthPointer(const Expr *E) { // The assertions here are all checked by Sema. assert(Result.Val.isLValue()); - auto Base = Result.Val.getLValueBase().get(); - if (auto Decl = dyn_cast_or_null(Base)) { + const auto *Base = Result.Val.getLValueBase().get(); + if (const auto *Decl = dyn_cast_or_null(Base)) { assert(Result.Val.getLValueOffset().isZero()); return CGM.getRawFunctionPointer(Decl); } diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index 86c8f80d78c5b..890dc04dfc4f7 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -39,8 +39,7 @@ CodeGenModule::getPointerAuthOtherDiscriminator(const PointerAuthSchema &Schema, return nullptr; case PointerAuthSchema::Discrimination::Type: - assert(!Type.isNull() && - "type not provided for type-discriminated schema"); + assert(!Type.isNull() && "type not provided for type-discriminated schema"); return llvm::ConstantInt::get( IntPtrTy, getContext().getPointerAuthTypeDiscriminator(Type)); @@ -57,8 +56,8 @@ CodeGenModule::getPointerAuthOtherDiscriminator(const PointerAuthSchema &Schema, } uint16_t CodeGen::getPointerAuthTypeDiscriminator(CodeGenModule &CGM, - QualType functionType) { - return CGM.getContext().getPointerAuthTypeDiscriminator(functionType); + QualType FunctionType) { + return CGM.getContext().getPointerAuthTypeDiscriminator(FunctionType); } /// Compute an ABI-stable hash of the given string. @@ -93,7 +92,6 @@ CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) { assert(!Schema.isAddressDiscriminated() && "function pointers cannot use address-specific discrimination"); - llvm::Constant *Discriminator = nullptr; if (T->isFunctionPointerType() || T->isFunctionReferenceType()) T = T->getPointeeType(); @@ -123,24 +121,6 @@ CodeGenModule::getMemberFunctionPointerAuthInfo(QualType functionType) { /* authenticatesNullValues */ false, discriminator); } -/// Return the natural pointer authentication for values of the given -/// pointer type. -static CGPointerAuthInfo getPointerAuthInfoForType(CodeGenModule &CGM, - QualType type) { - assert(type->isPointerType()); - - // Function pointers use the function-pointer schema by default. - if (auto ptrTy = type->getAs()) { - auto functionType = ptrTy->getPointeeType(); - if (functionType->isFunctionType()) { - return CGM.getFunctionPointerAuthInfo(functionType); - } - } - - // Normal data pointers never use direct pointer authentication by default. - return CGPointerAuthInfo(); -} - CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo(PointerAuthQualifier qualifier, Address storageAddress) { @@ -170,6 +150,47 @@ CodeGenFunction::EmitPointerAuthInfo(PointerAuthQualifier qualifier, qualifier.authenticatesNullValues(), discriminator); } +/// Return the natural pointer authentication for values of the given +/// pointee type. +static CGPointerAuthInfo +getPointerAuthInfoForPointeeType(CodeGenModule &CGM, QualType PointeeType) { + if (PointeeType.isNull()) + return CGPointerAuthInfo(); + + // Function pointers use the function-pointer schema by default. + if (PointeeType->isFunctionType()) + return CGM.getFunctionPointerAuthInfo(PointeeType); + + // Normal data pointers never use direct pointer authentication by default. + return CGPointerAuthInfo(); +} + +CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForPointeeType(QualType T) { + return ::getPointerAuthInfoForPointeeType(*this, T); +} + +/// Return the natural pointer authentication for values of the given +/// pointer type. +static CGPointerAuthInfo getPointerAuthInfoForType(CodeGenModule &CGM, + QualType PointerType) { + assert(PointerType->isSignableType()); + + // Block pointers are currently not signed. + if (PointerType->isBlockPointerType()) + return CGPointerAuthInfo(); + + auto PointeeType = PointerType->getPointeeType(); + + if (PointeeType.isNull()) + return CGPointerAuthInfo(); + + return ::getPointerAuthInfoForPointeeType(CGM, PointeeType); +} + +CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForType(QualType T) { + return ::getPointerAuthInfoForType(*this, T); +} + static std::pair emitLoadOfOrigPointerRValue(CodeGenFunction &CGF, const LValue &lv, SourceLocation loc) { @@ -631,7 +652,7 @@ llvm::Constant *CodeGenModule::getFunctionPointer(GlobalDecl GD, FuncType = Context.getFunctionNoProtoType(Proto->getReturnType(), Proto->getExtInfo()); - return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType, FD); + return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType); } llvm::Constant * diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 7c3cb8c59c656..c17fef6ad2520 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -979,6 +979,10 @@ class CodeGenModule : public CodeGenTypeCache { CGPointerAuthInfo getMemberFunctionPointerAuthInfo(QualType functionType); + CGPointerAuthInfo getPointerAuthInfoForPointeeType(QualType type); + + CGPointerAuthInfo getPointerAuthInfoForType(QualType type); + bool shouldSignPointer(const PointerAuthSchema &Schema); llvm::Constant *getConstantSignedPointer(llvm::Constant *Pointer, const PointerAuthSchema &Schema, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 7d07d7533b2a2..5c43c14b27a7b 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1796,6 +1796,9 @@ void Clang::AddAArch64TargetArgs(const ArgList &Args, options::OPT_fno_ptrauth_vtable_pointer_type_discrimination); Args.addOptInFlag(CmdArgs, options::OPT_fptrauth_init_fini, options::OPT_fno_ptrauth_init_fini); + Args.addOptInFlag( + CmdArgs, options::OPT_fptrauth_function_pointer_type_discrimination, + options::OPT_fno_ptrauth_function_pointer_type_discrimination); } void Clang::AddLoongArchTargetArgs(const ArgList &Args, diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 2e0dd142e1f60..11f4fdd76c611 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1642,8 +1642,10 @@ void CompilerInvocation::setDefaultPointerAuthOptions( using Key = PointerAuthSchema::ARM8_3Key; using Discrimination = PointerAuthSchema::Discrimination; // If you change anything here, be sure to update . - Opts.FunctionPointers = - PointerAuthSchema(Key::ASIA, false, Discrimination::None); + Opts.FunctionPointers = PointerAuthSchema( + Key::ASIA, false, + LangOpts.PointerAuthFunctionTypeDiscrimination ? Discrimination::Type + : Discrimination::None); Opts.CXXVTablePointers = PointerAuthSchema( Key::ASDA, LangOpts.PointerAuthVTPtrAddressDiscrimination, @@ -3720,6 +3722,8 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts, GenerateArg(Consumer, OPT_fptrauth_vtable_pointer_type_discrimination); if (Opts.PointerAuthInitFini) GenerateArg(Consumer, OPT_fptrauth_init_fini); + if (Opts.PointerAuthFunctionTypeDiscrimination) + GenerateArg(Consumer, OPT_fptrauth_function_pointer_type_discrimination); if (Opts.PointerAuthIndirectGotos) GenerateArg(Consumer, OPT_fptrauth_indirect_gotos); @@ -3745,6 +3749,8 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, Opts.PointerAuthVTPtrTypeDiscrimination = Args.hasArg(OPT_fptrauth_vtable_pointer_type_discrimination); Opts.PointerAuthInitFini = Args.hasArg(OPT_fptrauth_init_fini); + Opts.PointerAuthFunctionTypeDiscrimination = + Args.hasArg(OPT_fptrauth_function_pointer_type_discrimination); Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos); Opts.SoftPointerAuth = Args.hasArg(OPT_fptrauth_soft); diff --git a/clang/test/CodeGen/ptrauth-function-type-discriminator-wrapper-globals.c b/clang/test/CodeGen/ptrauth-function-type-discriminator-wrapper-globals.c new file mode 100644 index 0000000000000..31278fb75416e --- /dev/null +++ b/clang/test/CodeGen/ptrauth-function-type-discriminator-wrapper-globals.c @@ -0,0 +1,144 @@ +// RUN: %clang_cc1 %s -mllvm -ptrauth-emit-wrapper-globals=1 -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKC +// RUN: %clang_cc1 -xc++ %s -mllvm -ptrauth-emit-wrapper-globals=1 -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefix=CHECK +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=1 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-pch %s -o %t.ast +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=1 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -x ast -o - %t.ast | FileCheck -check-prefix=CHECK --check-prefix=CHECKC %s + +#ifdef __cplusplus +extern "C" { +#endif + +void f(void); +void f2(int); +void (*fnptr)(void); +void *opaque; +unsigned long uintptr; + +// CHECK: @test_constant_null = global ptr null +void (*test_constant_null)(int) = 0; + +// CHECK: @f.ptrauth = private constant { {{.*}} } { ptr @f, i32 0, i64 0, i64 2712 } +// CHECK: @test_constant_cast = global ptr @f.ptrauth +void (*test_constant_cast)(int) = (void (*)(int))f; + +// CHECK: @f.ptrauth.1 = private constant { {{.*}} } { ptr @f, i32 0, i64 0, i64 0 } +// CHECK: @test_opaque = global ptr @f.ptrauth.1 +void *test_opaque = +#ifdef __cplusplus + (void *) +#endif + (void (*)(int))(double (*)(double))f; + +// CHECK: @test_intptr_t = global i64 ptrtoint (ptr @f.ptrauth.1 to i64) +unsigned long test_intptr_t = (unsigned long)f; + +// CHECK: @test_through_long = global ptr @f.ptrauth +void (*test_through_long)(int) = (void (*)(int))(long)f; + +// CHECK: @test_to_long = global i64 ptrtoint (ptr @f.ptrauth.1 to i64) +long test_to_long = (long)(double (*)())f; + +extern void external_function(void); +// CHECK: @fptr1 = global ptr @external_function.ptrauth +void (*fptr1)(void) = external_function; +// CHECK: @fptr2 = global ptr @external_function.ptrauth +void (*fptr2)(void) = &external_function; + +// CHECK: @external_function.ptrauth.2 = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 2, i64 0, i64 26 }, section "llvm.ptrauth" +// CHECK: @fptr3 = global ptr @external_function.ptrauth.2 +void (*fptr3)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, 26); + +// CHECK: @fptr4 = global ptr @external_function.ptrauth.3 +// CHECK: @external_function.ptrauth.3 = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 2, i64 ptrtoint (ptr @fptr4 to i64), i64 26 }, section "llvm.ptrauth" +void (*fptr4)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, __builtin_ptrauth_blend_discriminator(&fptr4, 26)); + +// CHECK: @returns_initially_incomplete.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @returns_initially_incomplete, i32 0, i64 0, i64 25106 }, section "llvm.ptrauth" +// CHECKC: @knr.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @knr, i32 0, i64 0, i64 18983 }, section "llvm.ptrauth" +// CHECKC: @redecl.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @redecl, i32 0, i64 0, i64 18983 }, section "llvm.ptrauth" +// CHECKC: @redecl.ptrauth.4 = private constant { ptr, i32, i64, i64 } { ptr @redecl, i32 0, i64 0, i64 2712 }, section "llvm.ptrauth" + +// CHECK-LABEL: define void @test_call() +void test_call() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 0, i64 18983) ] + fnptr(); +} + +// CHECK-LABEL: define ptr @test_function_pointer() +// CHECK: ret ptr @external_function.ptrauth +void (*test_function_pointer())(void) { + return external_function; +} + +struct InitiallyIncomplete; +extern struct InitiallyIncomplete returns_initially_incomplete(void); +// CHECK-LABEL: define void @use_while_incomplete() +void use_while_incomplete() { + // CHECK: [[VAR:%.*]] = alloca ptr, + // CHECK-NEXT: store ptr @returns_initially_incomplete.ptrauth, ptr [[VAR]] + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} +struct InitiallyIncomplete { int x; }; +// CHECK-LABEL: define void @use_while_complete() +void use_while_complete() { + // CHECK: [[VAR:%.*]] = alloca ptr, + // CHECK-NEXT: store ptr @returns_initially_incomplete.ptrauth, ptr [[VAR]] + // CHECK-NEXT: ret void + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} + + +#ifndef __cplusplus + +void knr(param) + int param; +{} + +// CHECKC-LABEL: define void @test_knr +void test_knr() { + void (*p)() = knr; + p(0); + + // CHECKC: [[P:%.*]] = alloca ptr + // CHECKC: store ptr @knr.ptrauth, ptr [[P]] + // CHECKC: [[LOAD:%.*]] = load ptr, ptr [[P]] + // CHECKC: call void [[LOAD]](i32 noundef 0) [ "ptrauth"(i32 0, i64 18983) ] +} + +// CHECKC-LABEL: define void @test_redeclaration +void test_redeclaration() { + void redecl(); + void (*ptr)() = redecl; + void redecl(int); + void (*ptr2)(int) = redecl; + ptr(); + ptr2(0); + + // CHECKC-NOT: call i64 @llvm.ptrauth.resign + // CHECKC: call void {{.*}}() [ "ptrauth"(i32 0, i64 18983) ] + // CHECKC: call void {{.*}}(i32 noundef 0) [ "ptrauth"(i32 0, i64 2712) ] +} + +void knr2(param) + int param; +{} + +// CHECKC-LABEL: define void @test_redecl_knr +void test_redecl_knr() { + void (*p)() = knr2; + p(); + + void knr2(int); + + void (*p2)(int) = knr2; + p2(0); + + // CHECKC-NOT: call i64 @llvm.ptrauth.resign + // CHECKC: call void {{.*}}() [ "ptrauth"(i32 0, i64 18983) ] + // CHECKC: call void {{.*}}(i32 noundef 0) [ "ptrauth"(i32 0, i64 2712) ] +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/clang/test/CodeGen/ptrauth-function-type-discriminator.c b/clang/test/CodeGen/ptrauth-function-type-discriminator.c new file mode 100644 index 0000000000000..3c7eaaf793d71 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-function-type-discriminator.c @@ -0,0 +1,138 @@ +// RUN: %clang_cc1 %s -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKC +// RUN: %clang_cc1 -xc++ %s -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefix=CHECK +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-pch %s -o %t.ast +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -x ast -o - %t.ast | FileCheck -check-prefix=CHECK --check-prefix=CHECKC %s + + +#ifdef __cplusplus +extern "C" { +#endif + +void f(void); +void f2(int); +void (*fnptr)(void); +void *opaque; +unsigned long uintptr; + +// CHECK: @test_constant_null = global ptr null +void (*test_constant_null)(int) = 0; + +// CHECK: @test_constant_cast = global ptr ptrauth (ptr @f, i32 0, i64 2712) +void (*test_constant_cast)(int) = (void (*)(int))f; + +// CHECK: @test_opaque = global ptr ptrauth (ptr @f, i32 0) +void *test_opaque = +#ifdef __cplusplus + (void *) +#endif + (void (*)(int))(double (*)(double))f; + +// CHECK: @test_intptr_t = global i64 ptrtoint (ptr ptrauth (ptr @f, i32 0) to i64) +unsigned long test_intptr_t = (unsigned long)f; + +// CHECK: @test_through_long = global ptr ptrauth (ptr @f, i32 0, i64 2712) +void (*test_through_long)(int) = (void (*)(int))(long)f; + +// CHECK: @test_to_long = global i64 ptrtoint (ptr ptrauth (ptr @f, i32 0) to i64) +long test_to_long = (long)(double (*)())f; + +extern void external_function(void); +// CHECK: @fptr1 = global ptr ptrauth (ptr @external_function, i32 0, i64 18983) +void (*fptr1)(void) = external_function; +// CHECK: @fptr2 = global ptr ptrauth (ptr @external_function, i32 0, i64 18983) +void (*fptr2)(void) = &external_function; + +// CHECK: @fptr3 = global ptr ptrauth (ptr @external_function, i32 2, i64 26) +void (*fptr3)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, 26); + +// CHECK: @fptr4 = global ptr ptrauth (ptr @external_function, i32 2, i64 26, ptr @fptr4) +void (*fptr4)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, __builtin_ptrauth_blend_discriminator(&fptr4, 26)); + +// CHECK-LABEL: define void @test_call() +void test_call() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 0, i64 18983) ] + fnptr(); +} + +// CHECK-LABEL: define ptr @test_function_pointer() +// CHECK: ret ptr ptrauth (ptr @external_function, i32 0, i64 18983) +void (*test_function_pointer())(void) { + return external_function; +} + +struct InitiallyIncomplete; +extern struct InitiallyIncomplete returns_initially_incomplete(void); +// CHECK-LABEL: define void @use_while_incomplete() +void use_while_incomplete() { + // CHECK: [[VAR:%.*]] = alloca ptr, + // CHECK-NEXT: store ptr ptrauth (ptr @returns_initially_incomplete, i32 0, i64 25106), ptr [[VAR]] + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} +struct InitiallyIncomplete { int x; }; +// CHECK-LABEL: define void @use_while_complete() +void use_while_complete() { + // CHECK: [[VAR:%.*]] = alloca ptr, + // CHECK-NEXT: store ptr ptrauth (ptr @returns_initially_incomplete, i32 0, i64 25106), ptr [[VAR]] + // CHECK-NEXT: ret void + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} + +#ifndef __cplusplus + +void knr(param) + int param; +{} + +// CHECKC-LABEL: define void @test_knr +void test_knr() { + void (*p)() = knr; + p(0); + + // CHECKC: [[P:%.*]] = alloca ptr + // CHECKC: store ptr ptrauth (ptr @knr, i32 0, i64 18983), ptr [[P]] + // CHECKC: [[LOAD:%.*]] = load ptr, ptr [[P]] + // CHECKC: call void [[LOAD]](i32 noundef 0) [ "ptrauth"(i32 0, i64 18983) ] +} + +// CHECKC-LABEL: define void @test_redeclaration +void test_redeclaration() { + void redecl(); + void (*ptr)() = redecl; + void redecl(int); + void (*ptr2)(int) = redecl; + ptr(); + ptr2(0); + + // CHECKC: store ptr ptrauth (ptr @redecl, i32 0, i64 18983), ptr %ptr + // CHECKC: store ptr ptrauth (ptr @redecl, i32 0, i64 2712), ptr %ptr2 + // CHECKC: call void {{.*}}() [ "ptrauth"(i32 0, i64 18983) ] + // CHECKC: call void {{.*}}(i32 noundef 0) [ "ptrauth"(i32 0, i64 2712) ] +} + +void knr2(param) + int param; +{} + +// CHECKC-LABEL: define void @test_redecl_knr +void test_redecl_knr() { + void (*p)() = knr2; + p(); + + // CHECKC: store ptr ptrauth (ptr @knr2, i32 0, i64 18983) + // CHECKC: call void {{.*}}() [ "ptrauth"(i32 0, i64 18983) ] + + void knr2(int); + + void (*p2)(int) = knr2; + p2(0); + + // CHECKC: store ptr ptrauth (ptr @knr2, i32 0, i64 2712) + // CHECKC: call void {{.*}}(i32 noundef 0) [ "ptrauth"(i32 0, i64 2712) ] +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/clang/test/Preprocessor/ptrauth_feature.c b/clang/test/Preprocessor/ptrauth_feature.c index 8621ab08edf5b..1c6dfcc436383 100644 --- a/clang/test/Preprocessor/ptrauth_feature.c +++ b/clang/test/Preprocessor/ptrauth_feature.c @@ -5,7 +5,7 @@ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI,NOFUNC // RUN: %clang_cc1 -E %s -triple=aarch64 \ // RUN: -fptrauth-calls \ @@ -13,7 +13,7 @@ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=NOINTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI +// RUN: FileCheck %s --check-prefixes=NOINTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI,NOFUNC // RUN: %clang_cc1 -E %s -triple=aarch64 \ // RUN: -fptrauth-intrinsics \ @@ -21,7 +21,7 @@ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,NOCALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI +// RUN: FileCheck %s --check-prefixes=INTRIN,NOCALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI,NOFUNC // RUN: %clang_cc1 -E %s -triple=aarch64 \ // RUN: -fptrauth-intrinsics \ @@ -29,7 +29,7 @@ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,NORETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,NORETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI,NOFUNC // RUN: %clang_cc1 -E %s -triple=aarch64 \ // RUN: -fptrauth-intrinsics \ @@ -37,7 +37,7 @@ // RUN: -fptrauth-returns \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,NOVPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,NOVPTR_ADDR_DISCR,VPTR_TYPE_DISCR,INITFINI,NOFUNC // RUN: %clang_cc1 -E %s -triple=aarch64 \ // RUN: -fptrauth-intrinsics \ @@ -45,7 +45,7 @@ // RUN: -fptrauth-returns \ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: -fptrauth-init-fini | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,INITFINI +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,NOVPTR_TYPE_DISCR,INITFINI,NOFUNC // RUN: %clang_cc1 -E %s -triple=aarch64 \ // RUN: -fptrauth-intrinsics \ @@ -53,7 +53,17 @@ // RUN: -fptrauth-returns \ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: -fptrauth-vtable-pointer-type-discrimination | \ -// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,NOINITFINI +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,NOINITFINI,NOFUNC + +// RUN: %clang_cc1 -E %s -triple=aarch64 \ +// RUN: -fptrauth-intrinsics \ +// RUN: -fptrauth-calls \ +// RUN: -fptrauth-returns \ +// RUN: -fptrauth-vtable-pointer-address-discrimination \ +// RUN: -fptrauth-vtable-pointer-type-discrimination \ +// RUN: -fptrauth-function-pointer-type-discrimination | \ +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,NOINITFINI,FUNC + #if __has_feature(ptrauth_intrinsics) // INTRIN: has_ptrauth_intrinsics @@ -119,3 +129,13 @@ void has_ptrauth_qualifier() {} // NOQUAL: no_ptrauth_qualifier void no_ptrauth_qualifier() {} #endif + +#include + +#if __has_feature(ptrauth_function_pointer_type_discrimination) +// FUNC: has_ptrauth_function_pointer_type_discrimination +void has_ptrauth_function_pointer_type_discrimination() {} +#else +// NOFUNC: no_ptrauth_function_pointer_type_discrimination +void no_ptrauth_function_pointer_type_discrimination() {} +#endif From 89e80a6b5a089c1d17907c40a170b5b354df35e5 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Thu, 27 Jun 2024 16:00:35 -0700 Subject: [PATCH 24/58] [clang] Resign type-discriminated function pointers on cast. --- clang/include/clang/AST/Type.h | 5 +- clang/lib/AST/ASTContext.cpp | 4 +- clang/lib/CodeGen/Address.h | 39 +- clang/lib/CodeGen/CGBuilder.h | 94 ++-- clang/lib/CodeGen/CGBuiltin.cpp | 3 +- clang/lib/CodeGen/CGCall.cpp | 6 +- clang/lib/CodeGen/CGExpr.cpp | 3 +- clang/lib/CodeGen/CGExprAgg.cpp | 4 +- clang/lib/CodeGen/CGExprConstant.cpp | 23 +- clang/lib/CodeGen/CGExprScalar.cpp | 29 +- clang/lib/CodeGen/CGPointerAuth.cpp | 457 +++++++++++++----- clang/lib/CodeGen/CGValue.h | 29 +- clang/lib/CodeGen/CodeGenFunction.cpp | 92 ++-- clang/lib/CodeGen/CodeGenFunction.h | 116 +++-- clang/lib/CodeGen/CodeGenModule.h | 3 + .../ptrauth-function-lvalue-cast-disc.c | 55 +++ ...-type-discriminator-cast-wrapper-globals.c | 87 ++++ ...ptrauth-function-type-discriminator-cast.c | 85 ++++ clang/test/CodeGen/ptrauth-wrapper-globals.c | 100 ++++ clang/test/CodeGen/ptrauth.c | 71 +-- clang/test/Preprocessor/ptrauth_feature.c | 10 + .../ptrauth-function-type-discriminatior.c | 65 +++ 22 files changed, 1065 insertions(+), 315 deletions(-) create mode 100644 clang/test/CodeGen/ptrauth-function-lvalue-cast-disc.c create mode 100644 clang/test/CodeGen/ptrauth-function-type-discriminator-cast-wrapper-globals.c create mode 100644 clang/test/CodeGen/ptrauth-function-type-discriminator-cast.c create mode 100644 clang/test/CodeGen/ptrauth-wrapper-globals.c create mode 100644 clang/test/Sema/ptrauth-function-type-discriminatior.c diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 36f9d3c277905..9d42b426a5e3b 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2531,6 +2531,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isFunctionProtoType() const { return getAs(); } bool isPointerType() const; bool isSignableType() const; + bool isSignablePointerType() const; bool isAnyPointerType() const; // Any C pointer or ObjC object pointer bool isCountAttributedType() const; bool isBlockPointerType() const; @@ -8020,7 +8021,9 @@ inline bool Type::isAnyPointerType() const { return isPointerType() || isObjCObjectPointerType(); } -inline bool Type::isSignableType() const { return isPointerType(); } +inline bool Type::isSignableType() const { return isSignablePointerType(); } + +inline bool Type::isSignablePointerType() const { return isPointerType(); } inline bool Type::isBlockPointerType() const { return isa(CanonicalType); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 3d006a982fd75..c1b1a13c1339a 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3396,9 +3396,9 @@ uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) { if (T->isFunctionPointerType() || T->isFunctionReferenceType()) T = T->getPointeeType(); - if (T->isFunctionType()) + if (T->isFunctionType()) { encodeTypeForFunctionPointerAuth(*this, Out, T); - else { + } else { T = T.getUnqualifiedType(); std::unique_ptr MC(createMangleContext()); MC->mangleCanonicalTypeName(T, Out); diff --git a/clang/lib/CodeGen/Address.h b/clang/lib/CodeGen/Address.h index 3b677c9159e22..1d2c453f25523 100644 --- a/clang/lib/CodeGen/Address.h +++ b/clang/lib/CodeGen/Address.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_LIB_CODEGEN_ADDRESS_H #define LLVM_CLANG_LIB_CODEGEN_ADDRESS_H +#include "CGPointerAuthInfo.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Type.h" #include "llvm/ADT/PointerIntPair.h" @@ -141,7 +142,11 @@ class Address { CharUnits Alignment; - /// Offset from the base pointer. + /// The ptrauth information needed to authenticate the base pointer. + CGPointerAuthInfo PtrAuthInfo; + + /// Offset from the base pointer. This is non-null only when the base + /// pointer is signed. llvm::Value *Offset = nullptr; llvm::Value *emitRawPointerSlow(CodeGenFunction &CGF) const; @@ -160,12 +165,14 @@ class Address { } Address(llvm::Value *BasePtr, llvm::Type *ElementType, CharUnits Alignment, - llvm::Value *Offset, KnownNonNull_t IsKnownNonNull = NotKnownNonNull) + CGPointerAuthInfo PtrAuthInfo, llvm::Value *Offset, + KnownNonNull_t IsKnownNonNull = NotKnownNonNull) : Pointer(BasePtr, IsKnownNonNull), ElementType(ElementType), - Alignment(Alignment), Offset(Offset) {} + Alignment(Alignment), PtrAuthInfo(PtrAuthInfo), Offset(Offset) {} Address(RawAddress RawAddr) - : Pointer(RawAddr.isValid() ? RawAddr.getPointer() : nullptr), + : Pointer(RawAddr.isValid() ? RawAddr.getPointer() : nullptr, + RawAddr.isValid() ? RawAddr.isKnownNonNull() : NotKnownNonNull), ElementType(RawAddr.isValid() ? RawAddr.getElementType() : nullptr), Alignment(RawAddr.isValid() ? RawAddr.getAlignment() : CharUnits::Zero()) {} @@ -175,7 +182,7 @@ class Address { llvm::Value *getPointerIfNotSigned() const { assert(isValid() && "pointer isn't valid"); - return Pointer.getPointer(); + return !isSigned() ? Pointer.getPointer() : nullptr; } /// This function is used in situations where the caller is doing some sort of @@ -197,7 +204,10 @@ class Address { return Pointer.getPointer(); } - llvm::Value *getUnsignedPointer() const { return getBasePointer(); } + llvm::Value *getUnsignedPointer() const { + assert(!isSigned() && "cannot call this function if pointer is signed"); + return getBasePointer(); + } /// Return the type of the pointer value. llvm::PointerType *getType() const { @@ -219,6 +229,9 @@ class Address { /// Return the IR name of the pointer value. llvm::StringRef getName() const { return Pointer.getPointer()->getName(); } + const CGPointerAuthInfo &getPointerAuthInfo() const { return PtrAuthInfo; } + void setPointerAuthInfo(const CGPointerAuthInfo &Info) { PtrAuthInfo = Info; } + // This function is called only in CGBuilderBaseTy::CreateElementBitCast. void setElementType(llvm::Type *Ty) { assert(hasOffset() && @@ -226,7 +239,8 @@ class Address { ElementType = Ty; } - /// Whether the pointer is known not to be null. + bool isSigned() const { return PtrAuthInfo.isSigned(); } + KnownNonNull_t isKnownNonNull() const { assert(isValid()); return (KnownNonNull_t)Pointer.getInt(); @@ -250,10 +264,15 @@ class Address { llvm::Value *getOffset() const { return Offset; } + Address getResignedAddress(const CGPointerAuthInfo &NewInfo, + CodeGenFunction &CGF) const; + /// Return the pointer contained in this class after authenticating it and /// adding offset to it if necessary. llvm::Value *emitRawPointer(CodeGenFunction &CGF) const { - return getUnsignedPointer(); + if (!isSigned()) + return getUnsignedPointer(); + return emitRawPointerSlow(CGF); } /// Return address with different pointer, but same element type and @@ -275,8 +294,8 @@ class Address { /// alignment. Address withElementType(llvm::Type *ElemTy) const { if (!hasOffset()) - return Address(getBasePointer(), ElemTy, getAlignment(), nullptr, - isKnownNonNull()); + return Address(getBasePointer(), ElemTy, getAlignment(), + getPointerAuthInfo(), nullptr, isKnownNonNull()); Address A(*this); A.ElementType = ElemTy; return A; diff --git a/clang/lib/CodeGen/CGBuilder.h b/clang/lib/CodeGen/CGBuilder.h index 752bfc6c0c058..48b3067494062 100644 --- a/clang/lib/CodeGen/CGBuilder.h +++ b/clang/lib/CodeGen/CGBuilder.h @@ -57,7 +57,10 @@ class CGBuilderTy : public CGBuilderBaseTy { CodeGenFunction *getCGF() const { return getInserter().CGF; } llvm::Value *emitRawPointerFromAddress(Address Addr) const { - return Addr.getUnsignedPointer(); + if (!Addr.isSigned()) + return Addr.getUnsignedPointer(); + assert(getCGF() && "CGF not set"); + return Addr.emitRawPointerSlow(*getCGF()); } /// Helper function to compute a GEP's offset and add it to Addr. @@ -208,8 +211,8 @@ class CGBuilderTy : public CGBuilderBaseTy { const llvm::Twine &Name = "") { if (!Addr.hasOffset()) return Address(CreateAddrSpaceCast(Addr.getBasePointer(), Ty, Name), - ElementTy, Addr.getAlignment(), nullptr, - Addr.isKnownNonNull()); + ElementTy, Addr.getAlignment(), Addr.getPointerAuthInfo(), + nullptr, Addr.isKnownNonNull()); // Eagerly force a raw address if these is an offset. return RawAddress( CreateAddrSpaceCast(Addr.emitRawPointer(*getCGF()), Ty, Name), @@ -240,11 +243,14 @@ class CGBuilderTy : public CGBuilderBaseTy { const llvm::StructLayout *Layout = DL.getStructLayout(ElTy); auto Offset = CharUnits::fromQuantity(Layout->getElementOffset(Index)); - return Address(CreateStructGEP(Addr.getElementType(), - Addr.getUnsignedPointer(), Index, Name), - ElTy->getElementType(Index), - Addr.getAlignment().alignmentAtOffset(Offset), - Addr.isKnownNonNull()); + if (!Addr.isSigned()) + return Address(CreateStructGEP(Addr.getElementType(), + Addr.getUnsignedPointer(), Index, Name), + ElTy->getElementType(Index), + Addr.getAlignment().alignmentAtOffset(Offset), + Addr.isKnownNonNull()); + Addr.addOffset(Offset, ElTy->getTypeAtIndex(Index), *this); + return Addr; } /// Given @@ -262,12 +268,15 @@ class CGBuilderTy : public CGBuilderBaseTy { CharUnits EltSize = CharUnits::fromQuantity(DL.getTypeAllocSize(ElTy->getElementType())); - return Address( - CreateInBoundsGEP(Addr.getElementType(), Addr.getUnsignedPointer(), - {getSize(CharUnits::Zero()), getSize(Index)}, Name), - ElTy->getElementType(), - Addr.getAlignment().alignmentAtOffset(Index * EltSize), - Addr.isKnownNonNull()); + if (!Addr.isSigned()) + return Address( + CreateInBoundsGEP(Addr.getElementType(), Addr.getUnsignedPointer(), + {getSize(CharUnits::Zero()), getSize(Index)}, Name), + ElTy->getElementType(), + Addr.getAlignment().alignmentAtOffset(Index * EltSize), + Addr.isKnownNonNull()); + Addr.addOffset(Index * EltSize, ElTy, *this); + return Addr; } /// Given @@ -281,10 +290,14 @@ class CGBuilderTy : public CGBuilderBaseTy { const llvm::DataLayout &DL = BB->getDataLayout(); CharUnits EltSize = CharUnits::fromQuantity(DL.getTypeAllocSize(ElTy)); - return Address(CreateInBoundsGEP(ElTy, Addr.getUnsignedPointer(), - getSize(Index), Name), - ElTy, Addr.getAlignment().alignmentAtOffset(Index * EltSize), - Addr.isKnownNonNull()); + if (!Addr.isSigned()) + return Address(CreateInBoundsGEP(ElTy, Addr.getUnsignedPointer(), + getSize(Index), Name), + ElTy, + Addr.getAlignment().alignmentAtOffset(Index * EltSize), + Addr.isKnownNonNull()); + Addr.addOffset(Index * EltSize, ElTy, *this); + return Addr; } /// Given @@ -298,10 +311,12 @@ class CGBuilderTy : public CGBuilderBaseTy { const llvm::DataLayout &DL = BB->getDataLayout(); CharUnits EltSize = CharUnits::fromQuantity(DL.getTypeAllocSize(ElTy)); - return Address( - CreateGEP(ElTy, Addr.getUnsignedPointer(), getSize(Index), Name), - Addr.getElementType(), - Addr.getAlignment().alignmentAtOffset(Index * EltSize)); + if (!Addr.isSigned()) + return Address( + CreateGEP(ElTy, Addr.getUnsignedPointer(), getSize(Index), Name), + ElTy, Addr.getAlignment().alignmentAtOffset(Index * EltSize)); + Addr.addOffset(Index * EltSize, ElTy, *this); + return Addr; } /// Create GEP with single dynamic index. The address alignment is reduced @@ -323,21 +338,28 @@ class CGBuilderTy : public CGBuilderBaseTy { Address CreateConstInBoundsByteGEP(Address Addr, CharUnits Offset, const llvm::Twine &Name = "") { assert(Addr.getElementType() == TypeCache.Int8Ty); - return Address( - CreateInBoundsGEP(Addr.getElementType(), Addr.getUnsignedPointer(), - getSize(Offset), Name), - Addr.getElementType(), Addr.getAlignment().alignmentAtOffset(Offset), - Addr.isKnownNonNull()); + + if (!Addr.isSigned()) + return Address( + CreateInBoundsGEP(Addr.getElementType(), Addr.getUnsignedPointer(), + getSize(Offset), Name), + Addr.getElementType(), Addr.getAlignment().alignmentAtOffset(Offset), + Addr.isKnownNonNull()); + Addr.addOffset(Offset, TypeCache.Int8Ty, *this); + return Addr; } Address CreateConstByteGEP(Address Addr, CharUnits Offset, const llvm::Twine &Name = "") { assert(Addr.getElementType() == TypeCache.Int8Ty); - return Address(CreateGEP(Addr.getElementType(), Addr.getUnsignedPointer(), - getSize(Offset), Name), - Addr.getElementType(), - Addr.getAlignment().alignmentAtOffset(Offset)); + if (!Addr.isSigned()) + return Address(CreateGEP(Addr.getElementType(), Addr.getUnsignedPointer(), + getSize(Offset), Name), + Addr.getElementType(), + Addr.getAlignment().alignmentAtOffset(Offset)); + Addr.addOffset(Offset, TypeCache.Int8Ty, *this); + return Addr; } using CGBuilderBaseTy::CreateConstInBoundsGEP2_32; @@ -364,10 +386,12 @@ class CGBuilderTy : public CGBuilderBaseTy { Address CreateInBoundsGEP(Address Addr, ArrayRef IdxList, llvm::Type *ElementType, CharUnits Align, const Twine &Name = "") { - return RawAddress(CreateInBoundsGEP(Addr.getElementType(), - emitRawPointerFromAddress(Addr), - IdxList, Name), - ElementType, Align, Addr.isKnownNonNull()); + if (!Addr.isSigned()) + return RawAddress(CreateInBoundsGEP(Addr.getElementType(), + emitRawPointerFromAddress(Addr), + IdxList, Name), + ElementType, Align, Addr.isKnownNonNull()); + return addGEPOffset(Addr, IdxList, Align, true, Name); } using CGBuilderBaseTy::CreateIsNull; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index ed37267efe715..99a98d6ff4db6 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2151,7 +2151,8 @@ RValue CodeGenFunction::emitBuiltinOSLogFormat(const CallExpr &E) { // Ignore argument 1, the format string. It is not currently used. CallArgList Args; - Args.add(RValue::get(BufAddr.emitRawPointer(*this)), Ctx.VoidPtrTy); + Args.add(RValue::get(getAsNaturalPointerTo(BufAddr, Ctx.VoidTy)), + Ctx.VoidPtrTy); for (const auto &Item : Layout.Items) { int Size = Item.getSizeByte(); diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 0cf220e0443a0..5992aacb8777c 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4140,8 +4140,8 @@ static bool isProvablyNull(llvm::Value *addr) { } static bool isProvablyNonNull(Address Addr, CodeGenFunction &CGF) { - return llvm::isKnownNonZero(Addr.getUnsignedPointer(), - CGF.CGM.getDataLayout()); + return !Addr.isSigned() && llvm::isKnownNonZero(Addr.getUnsignedPointer(), + CGF.CGM.getDataLayout()); } /// Emit the actual writing-back of a writeback. @@ -5110,7 +5110,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, SRetPtr = Address(CurFn->arg_begin() + IRFunctionArgs.getSRetArgNo(), ConvertTypeForMem(RetTy), CharUnits::fromQuantity(1)); } else if (!ReturnValue.isNull()) { - SRetPtr = ReturnValue.getAddress(); + SRetPtr = ReturnValue.getValue(); } else { SRetPtr = CreateMemTemp(RetTy, "tmp", &SRetAlloca); if (HaveInsertPoint() && ReturnValue.isUnused()) { diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 461ef325bca3c..38975b33b6530 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1311,7 +1311,8 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, if (CE->getCastKind() == CK_AddressSpaceConversion) Addr = CGF.Builder.CreateAddrSpaceCast( Addr, CGF.ConvertType(E->getType()), ElemTy); - return Addr; + return CGF.AuthPointerToPointerCast(Addr, CE->getSubExpr()->getType(), + CE->getType()); } break; diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index d70701f0856cf..14fee2eee8250 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -330,8 +330,8 @@ void AggExprEmitter::withReturnValueSlot( if (!UseTemp) return; - assert(Dest.isIgnored() || Dest.emitRawPointer(CGF) != - Src.getAggregatePointer(E->getType(), CGF)); + assert(Dest.getAddress().isSigned() || Dest.isIgnored() || + Dest.emitRawPointer(CGF) != Src.getAggregatePointer(E->getType(), CGF)); EmitFinalDestCopy(E->getType(), Src); if (!RequiresDestruction && LifetimeStartInst) { diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 28a7457446be7..153d28f341bf6 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -2110,17 +2110,30 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { if (D->hasAttr()) return CGM.GetWeakRefReference(D).getPointer(); - auto PtrAuthSign = [&](llvm::Constant *C) { + auto PtrAuthSign = [&](llvm::Constant *C, bool IsFunction) { if (auto pointerAuth = DestType.getPointerAuth()) { C = applyOffset(C); - C = tryEmitConstantSignedPointer(C, pointerAuth); + C = Emitter.tryEmitConstantSignedPointer(C, pointerAuth); return ConstantLValue(C, /*applied offset*/ true, /*signed*/ true); } - CGPointerAuthInfo AuthInfo = CGM.getFunctionPointerAuthInfo(DestType); + CGPointerAuthInfo AuthInfo; + + if (IsFunction) + AuthInfo = CGM.getFunctionPointerAuthInfo(DestType); + else { + // FIXME: getPointerAuthInfoForType should be able to return the pointer + // auth info of reference types. + if (auto *RT = DestType->getAs()) + DestType = CGM.getContext().getPointerType(RT->getPointeeType()); + // Don't emit a signed pointer if the destination is a function pointer + // type. + if (DestType->isSignableType() && !DestType->isFunctionPointerType()) + AuthInfo = CGM.getPointerAuthInfoForType(DestType); + } if (AuthInfo) { - if (hasNonZeroOffset()) + if (IsFunction && hasNonZeroOffset()) return ConstantLValue(nullptr); C = applyOffset(C); @@ -2134,7 +2147,7 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { }; if (const auto *FD = dyn_cast(D)) - return PtrAuthSign(CGM.getRawFunctionPointer(FD)); + return PtrAuthSign(CGM.getRawFunctionPointer(FD), true); if (const auto *VD = dyn_cast(D)) { // We can never refer to a variable with local storage. diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 792b7de0a815b..5c950d15106d6 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -2425,7 +2425,9 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { DestLV.setTBAAInfo(TBAAAccessInfo::getMayAliasInfo()); return EmitLoadOfLValue(DestLV, CE->getExprLoc()); } - return Builder.CreateBitCast(Src, DstTy); + + llvm::Value *Result = Builder.CreateBitCast(Src, DstTy); + return CGF.AuthPointerToPointerCast(Result, E->getType(), DestTy); } case CK_AddressSpaceConversion: { Expr::EvalResult Result; @@ -2575,6 +2577,8 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { if (DestTy.mayBeDynamicClass()) IntToPtr = Builder.CreateLaunderInvariantGroup(IntToPtr); } + + IntToPtr = CGF.AuthPointerToPointerCast(IntToPtr, E->getType(), DestTy); return IntToPtr; } case CK_PointerToIntegral: { @@ -2590,6 +2594,7 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { PtrExpr = Builder.CreateStripInvariantGroup(PtrExpr); } + PtrExpr = CGF.AuthPointerToPointerCast(PtrExpr, E->getType(), DestTy); return Builder.CreatePtrToInt(PtrExpr, ConvertType(DestTy)); } case CK_ToVoid: { @@ -2902,10 +2907,11 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, Builder.SetInsertPoint(opBB); atomicPHI = Builder.CreatePHI(value->getType(), 2); atomicPHI->addIncoming(value, startBB); - value = atomicPHI; + value = CGF.EmitPointerAuthAuth(type->getPointeeType(), atomicPHI); } else { value = EmitLoadOfLValue(LV, E->getExprLoc()); input = value; + value = CGF.EmitPointerAuthAuth(type->getPointeeType(), value); } // Special case of integer increment that we have to check first: bool++. @@ -3147,6 +3153,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, if (atomicPHI) { llvm::BasicBlock *curBlock = Builder.GetInsertBlock(); llvm::BasicBlock *contBB = CGF.createBasicBlock("atomic_cont", CGF.CurFn); + value = CGF.EmitPointerAuthSign(type->getPointeeType(), value); auto Pair = CGF.EmitAtomicCompareExchange( LV, RValue::get(atomicPHI), RValue::get(value), E->getExprLoc()); llvm::Value *old = CGF.EmitToMemory(Pair.first.getScalarVal(), type); @@ -3158,6 +3165,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, } // Store the updated result through the lvalue. + value = CGF.EmitPointerAuthSign(type->getPointeeType(), value); if (LV.isBitField()) { Value *Src = Previous ? Previous : value; CGF.EmitStoreThroughBitfieldLValue(RValue::get(value), LV, &value); @@ -4018,6 +4026,10 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF, return CGF.Builder.CreateBitCast(result, pointer->getType()); } + CGPointerAuthInfo PtrAuthInfo = CGF.CGM.getPointerAuthInfoForType(op.Ty); + if (PtrAuthInfo) + pointer = CGF.EmitPointerAuthAuth(PtrAuthInfo, pointer); + QualType elementType = pointerType->getPointeeType(); if (const VariableArrayType *vla = CGF.getContext().getAsVariableArrayType(elementType)) { @@ -4038,7 +4050,8 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF, elemTy, pointer, index, isSigned, isSubtraction, op.E->getExprLoc(), "add.ptr"); } - return pointer; + return PtrAuthInfo ? CGF.EmitPointerAuthSign(PtrAuthInfo, pointer) + : pointer; } // Explicitly handle GNU void* and function pointer arithmetic extensions. The @@ -4051,11 +4064,13 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF, elemTy = CGF.ConvertTypeForMem(elementType); if (CGF.getLangOpts().isSignedOverflowDefined()) - return CGF.Builder.CreateGEP(elemTy, pointer, index, "add.ptr"); + pointer = CGF.Builder.CreateGEP(elemTy, pointer, index, "add.ptr"); + else + pointer = CGF.EmitCheckedInBoundsGEP(elemTy, pointer, index, isSigned, + isSubtraction, op.E->getExprLoc(), + "add.ptr"); - return CGF.EmitCheckedInBoundsGEP( - elemTy, pointer, index, isSigned, isSubtraction, op.E->getExprLoc(), - "add.ptr"); + return PtrAuthInfo ? CGF.EmitPointerAuthSign(PtrAuthInfo, pointer) : pointer; } // Construct an fmuladd intrinsic to represent a fused mul-add of MulOp and diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index 890dc04dfc4f7..acddd03176223 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -103,23 +103,6 @@ CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) { Discriminator); } -CGPointerAuthInfo -CodeGenModule::getMemberFunctionPointerAuthInfo(QualType functionType) { - assert(functionType->getAs() && - "MemberPointerType expected"); - auto &schema = getCodeGenOpts().PointerAuth.CXXMemberFunctionPointers; - if (!schema) - return CGPointerAuthInfo(); - - assert(!schema.isAddressDiscriminated() && - "function pointers cannot use address-specific discrimination"); - - auto discriminator = - getPointerAuthOtherDiscriminator(schema, GlobalDecl(), functionType); - return CGPointerAuthInfo(schema.getKey(), schema.getAuthenticationMode(), - /* isIsaPointer */ false, - /* authenticatesNullValues */ false, discriminator); -} CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo(PointerAuthQualifier qualifier, @@ -293,74 +276,6 @@ CodeGenFunction::EmitPointerAuthUnqualify(PointerAuthQualifier curQualifier, isKnownNonNull); } -static bool isZeroConstant(llvm::Value *value) { - if (auto ci = dyn_cast(value)) - return ci->isZero(); - return false; -} - -llvm::Value * -CodeGenFunction::EmitPointerAuthResign(llvm::Value *value, QualType type, - const CGPointerAuthInfo &curAuthInfo, - const CGPointerAuthInfo &newAuthInfo, - bool isKnownNonNull) { - // Fast path: if neither schema wants a signature, we're done. - if (!curAuthInfo && !newAuthInfo) - return value; - - // If the value is obviously null, we're done. - auto null = - CGM.getNullPointer(cast(value->getType()), type); - if (value == null) { - return value; - } - - // If both schemas sign the same way, we're done. - if (curAuthInfo && newAuthInfo && - curAuthInfo.getKey() == newAuthInfo.getKey()) { - auto curD = curAuthInfo.getDiscriminator(); - auto newD = newAuthInfo.getDiscriminator(); - if (curD == newD || - (curD == nullptr && isZeroConstant(newD)) || - (newD == nullptr && isZeroConstant(curD))) - return value; - } - - llvm::BasicBlock *initBB = Builder.GetInsertBlock(); - llvm::BasicBlock *resignBB = nullptr, *contBB = nullptr; - - // Null pointers have to be mapped to null, and the ptrauth_resign - // intrinsic doesn't do that. - if (!isKnownNonNull && !llvm::isKnownNonZero(value, CGM.getDataLayout())) { - contBB = createBasicBlock("resign.cont"); - resignBB = createBasicBlock("resign.nonnull"); - - auto isNonNull = Builder.CreateICmpNE(value, null); - Builder.CreateCondBr(isNonNull, resignBB, contBB); - EmitBlock(resignBB); - } - - // Perform the auth/sign/resign operation. - if (!newAuthInfo) { - value = EmitPointerAuthAuth(curAuthInfo, value); - } else if (!curAuthInfo) { - value = EmitPointerAuthSign(newAuthInfo, value); - } else { - value = EmitPointerAuthResignCall(value, curAuthInfo, newAuthInfo); - } - - // Clean up with a phi if we branched before. - if (contBB) { - EmitBlock(contBB); - auto phi = Builder.CreatePHI(value->getType(), 2); - phi->addIncoming(null, initBB); - phi->addIncoming(value, resignBB); - value = phi; - } - - return value; -} - void CodeGenFunction::EmitPointerAuthCopy(PointerAuthQualifier qualifier, QualType type, Address destAddress, @@ -497,6 +412,130 @@ CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo( Schema.authenticatesNullValues(), Discriminator); } +Address CodeGenFunction::mergeAddressesInConditionalExpr( + Address LHS, Address RHS, llvm::BasicBlock *LHSBlock, + llvm::BasicBlock *RHSBlock, llvm::BasicBlock *MergeBlock, + QualType MergedType) { + CGPointerAuthInfo LHSInfo = LHS.getPointerAuthInfo(); + CGPointerAuthInfo RHSInfo = RHS.getPointerAuthInfo(); + + if (LHSInfo || RHSInfo) { + if (LHSInfo != RHSInfo || LHS.getOffset() != RHS.getOffset() || + LHS.getBasePointer()->getType() != RHS.getBasePointer()->getType()) { + // If the LHS and RHS have different signing information, offsets, or base + // pointer types, resign both sides and clear out the offsets. + CGPointerAuthInfo NewInfo = + CGM.getPointerAuthInfoForPointeeType(MergedType); + LHSBlock->getTerminator()->eraseFromParent(); + Builder.SetInsertPoint(LHSBlock); + LHS = LHS.getResignedAddress(NewInfo, *this); + Builder.CreateBr(MergeBlock); + LHSBlock = Builder.GetInsertBlock(); + RHSBlock->getTerminator()->eraseFromParent(); + Builder.SetInsertPoint(RHSBlock); + RHS = RHS.getResignedAddress(NewInfo, *this); + Builder.CreateBr(MergeBlock); + RHSBlock = Builder.GetInsertBlock(); + } + + assert(LHS.getPointerAuthInfo() == RHS.getPointerAuthInfo() && + LHS.getOffset() == RHS.getOffset() && + LHS.getBasePointer()->getType() == RHS.getBasePointer()->getType() && + "lhs and rhs must have the same signing information, offsets, and " + "base pointer types"); + } + + Builder.SetInsertPoint(MergeBlock); + llvm::PHINode *PtrPhi = Builder.CreatePHI(LHS.getType(), 2, "cond"); + PtrPhi->addIncoming(LHS.getBasePointer(), LHSBlock); + PtrPhi->addIncoming(RHS.getBasePointer(), RHSBlock); + LHS.replaceBasePointer(PtrPhi); + LHS.setAlignment(std::min(LHS.getAlignment(), RHS.getAlignment())); + return LHS; +} + +static bool isZeroConstant(llvm::Value *value) { + if (auto ci = dyn_cast(value)) + return ci->isZero(); + return false; +} + +static bool equalAuthPolicies(const CGPointerAuthInfo &left, + const CGPointerAuthInfo &right) { + if (left.isSigned() != right.isSigned()) + return false; + assert(left.isSigned() && right.isSigned() && + "should only be called with non-null auth policies"); + return left.getKey() == right.getKey() && + left.getAuthenticationMode() == right.getAuthenticationMode(); +} + +llvm::Value *CodeGenFunction::EmitPointerAuthResign( + llvm::Value *value, QualType type, const CGPointerAuthInfo &curAuthInfo, + const CGPointerAuthInfo &newAuthInfo, bool isKnownNonNull) { + // Fast path: if neither schema wants a signature, we're done. + if (!curAuthInfo && !newAuthInfo) + return value; + + llvm::Value *null = nullptr; + // If the value is obviously null, we're done. + if (auto pointerValue = dyn_cast(value->getType())) { + null = CGM.getNullPointer(pointerValue, type); + } else { + assert(value->getType()->isIntegerTy()); + null = llvm::ConstantInt::get(IntPtrTy, 0); + } + if (value == null) { + return value; + } + + // If both schemas sign the same way, we're done. + if (equalAuthPolicies(curAuthInfo, newAuthInfo)) { + auto curD = curAuthInfo.getDiscriminator(); + auto newD = newAuthInfo.getDiscriminator(); + if (curD == newD) + return value; + + if ((curD == nullptr && isZeroConstant(newD)) || + (newD == nullptr && isZeroConstant(curD))) + return value; + } + + llvm::BasicBlock *initBB = Builder.GetInsertBlock(); + llvm::BasicBlock *resignBB = nullptr, *contBB = nullptr; + + // Null pointers have to be mapped to null, and the ptrauth_resign + // intrinsic doesn't do that. + if (!isKnownNonNull && !llvm::isKnownNonZero(value, CGM.getDataLayout())) { + contBB = createBasicBlock("resign.cont"); + resignBB = createBasicBlock("resign.nonnull"); + + auto isNonNull = Builder.CreateICmpNE(value, null); + Builder.CreateCondBr(isNonNull, resignBB, contBB); + EmitBlock(resignBB); + } + + // Perform the auth/sign/resign operation. + if (!newAuthInfo) { + value = EmitPointerAuthAuth(curAuthInfo, value); + } else if (!curAuthInfo) { + value = EmitPointerAuthSign(newAuthInfo, value); + } else { + value = EmitPointerAuthResignCall(value, curAuthInfo, newAuthInfo); + } + + // Clean up with a phi if we branched before. + if (contBB) { + EmitBlock(contBB); + auto phi = Builder.CreatePHI(value->getType(), 2); + phi->addIncoming(null, initBB); + phi->addIncoming(value, resignBB); + value = phi; + } + + return value; +} + llvm::Constant * CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, llvm::Constant *StorageAddress, @@ -601,6 +640,16 @@ void CodeGenModule::destroyConstantSignedPointerCaches() { destroyCache(SignedThunkPointers); } +llvm::Constant *CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, + QualType PointeeType) { + CGPointerAuthInfo Info = getPointerAuthInfoForPointeeType(PointeeType); + if (!Info.shouldSign()) + return Pointer; + return getConstantSignedPointer( + Pointer, Info.getKey(), nullptr, + cast(Info.getDiscriminator())); +} + /// If applicable, sign a given constant function pointer with the ABI rules for /// functionType. llvm::Constant *CodeGenModule::getFunctionPointer(llvm::Constant *Pointer, @@ -655,40 +704,42 @@ llvm::Constant *CodeGenModule::getFunctionPointer(GlobalDecl GD, return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType); } -llvm::Constant * -CodeGenModule::getMemberFunctionPointer(llvm::Constant *pointer, - QualType functionType, - const FunctionDecl *FD) { - if (auto pointerAuth = getMemberFunctionPointerAuthInfo(functionType)) { - llvm::Constant **entry = nullptr; - if (FD) { - auto &cache = - getOrCreateCache(SignedThunkPointers); - entry = &cache[FD->getCanonicalDecl()]; - if (*entry) - return llvm::ConstantExpr::getBitCast(*entry, pointer->getType()); - } +CGPointerAuthInfo CodeGenModule::getMemberFunctionPointerAuthInfo(QualType FT) { + assert(FT->getAs() && "MemberPointerType expected"); + auto &Schema = getCodeGenOpts().PointerAuth.CXXMemberFunctionPointers; + if (!Schema) + return CGPointerAuthInfo(); - pointer = getConstantSignedPointer( - pointer, pointerAuth.getKey(), nullptr, - cast_or_null(pointerAuth.getDiscriminator())); + assert(!Schema.isAddressDiscriminated() && + "function pointers cannot use address-specific discrimination"); - if (entry) - *entry = pointer; - } + llvm::ConstantInt *Discriminator = + getPointerAuthOtherDiscriminator(Schema, GlobalDecl(), FT); + return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(), + /* IsIsaPointer */ false, + /* AuthenticatesNullValues */ false, Discriminator); +} - return pointer; +llvm::Constant *CodeGenModule::getMemberFunctionPointer(llvm::Constant *Pointer, + QualType FT, + const FunctionDecl *) { + if (CGPointerAuthInfo PointerAuth = getMemberFunctionPointerAuthInfo(FT)) + return getConstantSignedPointer( + Pointer, PointerAuth.getKey(), nullptr, + cast_or_null(PointerAuth.getDiscriminator())); + + return Pointer; } -llvm::Constant * -CodeGenModule::getMemberFunctionPointer(const FunctionDecl *FD, llvm::Type *Ty) { - QualType functionType = FD->getType(); - functionType = getContext().getMemberPointerType( - functionType, cast(FD)->getParent()->getTypeForDecl()); - return getMemberFunctionPointer(getRawFunctionPointer(FD, Ty), functionType, - FD); +llvm::Constant *CodeGenModule::getMemberFunctionPointer(const FunctionDecl *FD, + llvm::Type *Ty) { + QualType FT = FD->getType(); + FT = getContext().getMemberPointerType( + FT, cast(FD)->getParent()->getTypeForDecl()); + return getMemberFunctionPointer(getRawFunctionPointer(FD, Ty), FT); } + std::optional CodeGenModule::computeVTPointerAuthentication(const CXXRecordDecl *ThisClass) { auto DefaultAuthentication = getCodeGenOpts().PointerAuth.CXXVTablePointers; @@ -798,3 +849,185 @@ CodeGenModule::getVTablePointerAuthInfo(CodeGenFunction *CGF, /* IsIsaPointer */ false, /* AuthenticatesNullValues */ false, Discriminator); } + +llvm::Value *CodeGenFunction::AuthPointerToPointerCast(llvm::Value *ResultPtr, + QualType SourceType, + QualType DestType) { + CGPointerAuthInfo CurAuthInfo, NewAuthInfo; + if (SourceType->isSignableType()) + CurAuthInfo = getPointerAuthInfoForType(CGM, SourceType); + + if (DestType->isSignableType()) + NewAuthInfo = getPointerAuthInfoForType(CGM, DestType); + + if (!CurAuthInfo && !NewAuthInfo) + return ResultPtr; + + // If only one side of the cast is a function pointer, then we still need to + // resign to handle casts to/from opaque pointers. + if (!CurAuthInfo && DestType->isFunctionPointerType()) + CurAuthInfo = CGM.getFunctionPointerAuthInfo(SourceType); + + if (!NewAuthInfo && SourceType->isFunctionPointerType()) + NewAuthInfo = CGM.getFunctionPointerAuthInfo(DestType); + + return EmitPointerAuthResign(ResultPtr, DestType, CurAuthInfo, NewAuthInfo, + /*IsKnownNonNull=*/false); +} + +Address CodeGenFunction::AuthPointerToPointerCast(Address Ptr, + QualType SourceType, + QualType DestType) { + CGPointerAuthInfo CurAuthInfo, NewAuthInfo; + if (SourceType->isSignableType()) + CurAuthInfo = getPointerAuthInfoForType(CGM, SourceType); + + if (DestType->isSignableType()) + NewAuthInfo = getPointerAuthInfoForType(CGM, DestType); + + if (!CurAuthInfo && !NewAuthInfo) + return Ptr; + + if (!CurAuthInfo && DestType->isFunctionPointerType()) { + // When casting a non-signed pointer to a function pointer, just set the + // auth info on Ptr to the assumed schema. The pointer will be resigned to + // the effective type when used. + Ptr.setPointerAuthInfo(CGM.getFunctionPointerAuthInfo(SourceType)); + return Ptr; + } + + if (!NewAuthInfo && SourceType->isFunctionPointerType()) { + NewAuthInfo = CGM.getFunctionPointerAuthInfo(DestType); + Ptr = Ptr.getResignedAddress(NewAuthInfo, *this); + Ptr.setPointerAuthInfo(CGPointerAuthInfo()); + return Ptr; + } + + return Ptr; +} + +Address CodeGenFunction::EmitPointerAuthSign(Address Addr, + QualType PointeeType) { + CGPointerAuthInfo Info = getPointerAuthInfoForPointeeType(CGM, PointeeType); + llvm::Value *Ptr = EmitPointerAuthSign(Info, Addr.emitRawPointer(*this)); + return Address(Ptr, Addr.getElementType(), Addr.getAlignment()); +} + +Address CodeGenFunction::EmitPointerAuthAuth(Address Addr, + QualType PointeeType) { + CGPointerAuthInfo Info = getPointerAuthInfoForPointeeType(CGM, PointeeType); + llvm::Value *Ptr = EmitPointerAuthAuth(Info, Addr.emitRawPointer(*this)); + return Address(Ptr, Addr.getElementType(), Addr.getAlignment()); +} + +Address CodeGenFunction::getAsNaturalAddressOf(Address Addr, + QualType PointeeTy) { + CGPointerAuthInfo Info = + PointeeTy.isNull() ? CGPointerAuthInfo() + : CGM.getPointerAuthInfoForPointeeType(PointeeTy); + return Addr.getResignedAddress(Info, *this); +} + +Address Address::getResignedAddress(const CGPointerAuthInfo &NewInfo, + CodeGenFunction &CGF) const { + assert(isValid() && "pointer isn't valid"); + CGPointerAuthInfo CurInfo = getPointerAuthInfo(); + llvm::Value *Val; + + // Nothing to do if neither the current or the new ptrauth info needs signing. + if (!CurInfo.isSigned() && !NewInfo.isSigned()) + return Address(getUnsignedPointer(), getElementType(), getAlignment(), + isKnownNonNull()); + + assert(ElementType && "Effective type has to be set"); + + // If the current and the new ptrauth infos are the same and the offset is + // null, just cast the base pointer to the effective type. + if (CurInfo == NewInfo && !hasOffset()) + Val = getBasePointer(); + else { + if (Offset) { + assert(isSigned() && "signed pointer expected"); + // Authenticate the base pointer. + Val = CGF.EmitPointerAuthResign(getBasePointer(), QualType(), CurInfo, + CGPointerAuthInfo(), isKnownNonNull()); + + // Add offset to the authenticated pointer. + unsigned AS = cast(getBasePointer()->getType()) + ->getAddressSpace(); + Val = CGF.Builder.CreateBitCast(Val, + llvm::PointerType::get(CGF.Int8Ty, AS)); + Val = CGF.Builder.CreateGEP(CGF.Int8Ty, Val, Offset, "resignedgep"); + + // Sign the pointer using the new ptrauth info. + Val = CGF.EmitPointerAuthResign(Val, QualType(), CGPointerAuthInfo(), + NewInfo, isKnownNonNull()); + } else { + Val = CGF.EmitPointerAuthResign(getBasePointer(), QualType(), CurInfo, + NewInfo, isKnownNonNull()); + } + } + + Val = CGF.Builder.CreateBitCast(Val, getType()); + return Address(Val, getElementType(), getAlignment(), NewInfo, nullptr, + isKnownNonNull()); +} + +void Address::addOffset(CharUnits V, llvm::Type *Ty, CGBuilderTy &Builder) { + assert(isSigned() && + "shouldn't add an offset if the base pointer isn't signed"); + Alignment = Alignment.alignmentAtOffset(V); + llvm::Value *FixedOffset = + llvm::ConstantInt::get(Builder.getCGF()->IntPtrTy, V.getQuantity()); + addOffset(FixedOffset, Ty, Builder, Alignment); +} + +void Address::addOffset(llvm::Value *V, llvm::Type *Ty, CGBuilderTy &Builder, + CharUnits NewAlignment) { + assert(isSigned() && + "shouldn't add an offset if the base pointer isn't signed"); + ElementType = Ty; + Alignment = NewAlignment; + + if (!Offset) { + Offset = V; + return; + } + + Offset = Builder.CreateAdd(Offset, V, "add"); +} + +llvm::Value *Address::emitRawPointerSlow(CodeGenFunction &CGF) const { + return CGF.getAsNaturalPointerTo(*this, QualType()); +} + +llvm::Value *RValue::getAggregatePointer(QualType PointeeType, + CodeGenFunction &CGF) const { + return CGF.getAsNaturalPointerTo(getAggregateAddress(), PointeeType); +} + +llvm::Value *LValue::getPointer(CodeGenFunction &CGF) const { + assert(isSimple()); + return emitResignedPointer(getType(), CGF); +} + +llvm::Value *LValue::emitResignedPointer(QualType PointeeTy, + CodeGenFunction &CGF) const { + assert(isSimple()); + return CGF.getAsNaturalAddressOf(Addr, PointeeTy).getBasePointer(); +} + +llvm::Value *LValue::emitRawPointer(CodeGenFunction &CGF) const { + assert(isSimple()); + return Addr.isValid() ? Addr.emitRawPointer(CGF) : nullptr; +} + +llvm::Value *AggValueSlot::getPointer(QualType PointeeTy, + CodeGenFunction &CGF) const { + Address SignedAddr = CGF.getAsNaturalAddressOf(Addr, PointeeTy); + return SignedAddr.getBasePointer(); +} + +llvm::Value *AggValueSlot::emitRawPointer(CodeGenFunction &CGF) const { + return Addr.isValid() ? Addr.emitRawPointer(CGF) : nullptr; +} diff --git a/clang/lib/CodeGen/CGValue.h b/clang/lib/CodeGen/CGValue.h index 6ed94bd9c7592..27efcf6ac199b 100644 --- a/clang/lib/CodeGen/CGValue.h +++ b/clang/lib/CodeGen/CGValue.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_LIB_CODEGEN_CGVALUE_H #include "Address.h" +#include "CGPointerAuthInfo.h" #include "CodeGenTBAA.h" #include "EHScopeStack.h" #include "clang/AST/ASTContext.h" @@ -78,6 +79,8 @@ class RValue { return std::make_pair(Vals.first, Vals.second); } + bool isSignedAggregate() const { return AggregateAddr.isSigned(); } + /// getAggregateAddr() - Return the Value* of the address of the aggregate. Address getAggregateAddress() const { assert(isAggregate() && "Not an aggregate!"); @@ -85,9 +88,7 @@ class RValue { } llvm::Value *getAggregatePointer(QualType PointeeType, - CodeGenFunction &CGF) const { - return getAggregateAddress().getBasePointer(); - } + CodeGenFunction &CGF) const; static RValue getIgnored() { // FIXME: should we make this a more explicit state? @@ -317,6 +318,8 @@ class LValue { bool isNontemporal() const { return Nontemporal; } void setNontemporal(bool Value) { Nontemporal = Value; } + bool isPointerSigned() const { return Addr.isSigned(); } + bool isObjCWeak() const { return Quals.getObjCGCAttr() == Qualifiers::Weak; } @@ -352,19 +355,19 @@ class LValue { } // simple lvalue - llvm::Value *getPointer(CodeGenFunction &CGF) const { - assert(isSimple()); - return Addr.getBasePointer(); - } - llvm::Value *emitRawPointer(CodeGenFunction &CGF) const { - assert(isSimple()); - return Addr.isValid() ? Addr.emitRawPointer(CGF) : nullptr; - } + llvm::Value *getPointer(CodeGenFunction &CGF) const; + llvm::Value *emitResignedPointer(QualType PointeeTy, + CodeGenFunction &CGF) const; + llvm::Value *emitRawPointer(CodeGenFunction &CGF) const; Address getAddress() const { return Addr; } void setAddress(Address address) { Addr = address; } + CGPointerAuthInfo getPointerAuthInfo() const { + return Addr.getPointerAuthInfo(); + } + // vector elt lvalue Address getVectorAddress() const { assert(isVectorElt()); @@ -636,9 +639,7 @@ class AggValueSlot { llvm::Value *getPointer(QualType PointeeTy, CodeGenFunction &CGF) const; - llvm::Value *emitRawPointer(CodeGenFunction &CGF) const { - return Addr.isValid() ? Addr.emitRawPointer(CGF) : nullptr; - } + llvm::Value *emitRawPointer(CodeGenFunction &CGF) const; Address getAddress() const { return Addr; diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index c97cad73a0505..951542da53dbc 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -18,6 +18,7 @@ #include "CGDebugInfo.h" #include "CGHLSLRuntime.h" #include "CGOpenMPRuntime.h" +#include "CGRecordLayout.h" #include "CodeGenModule.h" #include "CodeGenPGO.h" #include "TargetInfo.h" @@ -558,8 +559,11 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) { ReturnBlock.getBlock()->eraseFromParent(); } if (ReturnValue.isValid()) { + // This only matters when ReturnValue isn't signed. ReturnValue is possibly + // signed only when the return is Indirect or InAlloca. In that case, a + // temporary alloca to store the return value isn't created. auto *RetAlloca = - dyn_cast(ReturnValue.emitRawPointer(*this)); + dyn_cast_or_null(ReturnValue.getPointerIfNotSigned()); if (RetAlloca && RetAlloca->use_empty()) { RetAlloca->eraseFromParent(); ReturnValue = Address::invalid(); @@ -3025,38 +3029,6 @@ llvm::DebugLoc CodeGenFunction::SourceLocToDebugLoc(SourceLocation Location) { return llvm::DebugLoc(); } -llvm::Value * -CodeGenFunction::EmitPointerAuthResignCall(llvm::Value *value, - const CGPointerAuthInfo &curAuth, - const CGPointerAuthInfo &newAuth) { - assert(curAuth && newAuth); - - // Convert the pointer to intptr_t before signing it. - auto origType = value->getType(); - value = Builder.CreatePtrToInt(value, IntPtrTy); - - auto curKey = Builder.getInt32(curAuth.getKey()); - auto newKey = Builder.getInt32(newAuth.getKey()); - - llvm::Value *curDiscriminator = curAuth.getDiscriminator(); - if (!curDiscriminator) curDiscriminator = Builder.getSize(0); - - llvm::Value *newDiscriminator = newAuth.getDiscriminator(); - if (!newDiscriminator) newDiscriminator = Builder.getSize(0); - - // call i64 @llvm.ptrauth.resign(i64 %pointer, - // i32 %curKey, i64 %curDiscriminator, - // i32 %newKey, i64 %newDiscriminator) - auto intrinsic = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_resign); - value = EmitRuntimeCall(intrinsic, - { value, curKey, curDiscriminator, - newKey, newDiscriminator }); - - // Convert back to the original type. - value = Builder.CreateIntToPtr(value, origType); - return value; -} - llvm::Value * CodeGenFunction::emitCondLikelihoodViaExpectIntrinsic(llvm::Value *Cond, Stmt::Likelihood LH) { @@ -3175,3 +3147,57 @@ CodeGenFunction::EmitPointerAuthAuth(const CGPointerAuthInfo &PointerAuth, return EmitPointerAuthCommon(*this, PointerAuth, Pointer, llvm::Intrinsic::ptrauth_auth); } + +llvm::Value *CodeGenFunction::EmitPointerAuthSign(QualType pointeeType, + llvm::Value *pointer) { + CGPointerAuthInfo pointerAuth = + CGM.getPointerAuthInfoForPointeeType(pointeeType); + return EmitPointerAuthSign(pointerAuth, pointer); +} + +llvm::Value *CodeGenFunction::EmitPointerAuthAuth(QualType pointeeType, + llvm::Value *pointer) { + CGPointerAuthInfo pointerAuth = + CGM.getPointerAuthInfoForPointeeType(pointeeType); + return EmitPointerAuthAuth(pointerAuth, pointer); +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthResignCall(llvm::Value *value, + const CGPointerAuthInfo &curAuth, + const CGPointerAuthInfo &newAuth) { + assert(curAuth && newAuth); + + if (curAuth.getAuthenticationMode() != + PointerAuthenticationMode::SignAndAuth || + newAuth.getAuthenticationMode() != + PointerAuthenticationMode::SignAndAuth) { + auto authedValue = EmitPointerAuthAuth(curAuth, value); + return EmitPointerAuthSign(newAuth, authedValue); + } + // Convert the pointer to intptr_t before signing it. + auto origType = value->getType(); + value = Builder.CreatePtrToInt(value, IntPtrTy); + + auto curKey = Builder.getInt32(curAuth.getKey()); + auto newKey = Builder.getInt32(newAuth.getKey()); + + llvm::Value *curDiscriminator = curAuth.getDiscriminator(); + if (!curDiscriminator) + curDiscriminator = Builder.getSize(0); + + llvm::Value *newDiscriminator = newAuth.getDiscriminator(); + if (!newDiscriminator) + newDiscriminator = Builder.getSize(0); + + // call i64 @llvm.ptrauth.resign(i64 %pointer, + // i32 %curKey, i64 %curDiscriminator, + // i32 %newKey, i64 %newDiscriminator) + auto intrinsic = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_resign); + value = EmitRuntimeCall( + intrinsic, {value, curKey, curDiscriminator, newKey, newDiscriminator}); + + // Convert back to the original type. + value = Builder.CreateIntToPtr(value, origType); + return value; +} diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 9b5fd1381e72f..60294fdb48f23 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -185,6 +185,11 @@ template <> struct DominatingValue
{ DominatingLLVMValue::saved_type BasePtr; llvm::Type *ElementType; CharUnits Alignment; + unsigned PtrAuthKey : 28; + PointerAuthenticationMode PtrAuthMode : 2; + bool IsIsaPointer : 1; + bool AuthenticatesNullValues : 1; + DominatingLLVMValue::saved_type PtrAuthDiscriminator; DominatingLLVMValue::saved_type Offset; llvm::PointerType *EffectiveType; }; @@ -193,16 +198,36 @@ template <> struct DominatingValue
{ if (DominatingLLVMValue::needsSaving(value.getBasePointer()) || DominatingLLVMValue::needsSaving(value.getOffset())) return true; + CGPointerAuthInfo info = value.getPointerAuthInfo(); + if (info.isSigned() && + DominatingLLVMValue::needsSaving(info.getDiscriminator())) + return true; return false; } static saved_type save(CodeGenFunction &CGF, type value) { + bool isSigned = value.getPointerAuthInfo().isSigned(); return {DominatingLLVMValue::save(CGF, value.getBasePointer()), - value.getElementType(), value.getAlignment(), - DominatingLLVMValue::save(CGF, value.getOffset()), value.getType()}; + value.getElementType(), + value.getAlignment(), + isSigned ? value.getPointerAuthInfo().getKey() : 0, + value.getPointerAuthInfo().getAuthenticationMode(), + value.getPointerAuthInfo().isIsaPointer(), + value.getPointerAuthInfo().authenticatesNullValues(), + isSigned ? DominatingLLVMValue::save( + CGF, value.getPointerAuthInfo().getDiscriminator()) + : DominatingLLVMValue::saved_type(), + DominatingLLVMValue::save(CGF, value.getOffset()), + value.getType()}; } static type restore(CodeGenFunction &CGF, saved_type value) { + CGPointerAuthInfo info; + if (value.PtrAuthMode != PointerAuthenticationMode::None) + info = CGPointerAuthInfo{ + value.PtrAuthKey, value.PtrAuthMode, value.IsIsaPointer, + value.AuthenticatesNullValues, + DominatingLLVMValue::restore(CGF, value.PtrAuthDiscriminator)}; return Address(DominatingLLVMValue::restore(CGF, value.BasePtr), - value.ElementType, value.Alignment, + value.ElementType, value.Alignment, info, DominatingLLVMValue::restore(CGF, value.Offset)); } }; @@ -2665,15 +2690,7 @@ class CodeGenFunction : public CodeGenTypeCache { llvm::BasicBlock *LHSBlock, llvm::BasicBlock *RHSBlock, llvm::BasicBlock *MergeBlock, - QualType MergedType) { - Builder.SetInsertPoint(MergeBlock); - llvm::PHINode *PtrPhi = Builder.CreatePHI(LHS.getType(), 2, "cond"); - PtrPhi->addIncoming(LHS.getBasePointer(), LHSBlock); - PtrPhi->addIncoming(RHS.getBasePointer(), RHSBlock); - LHS.replaceBasePointer(PtrPhi); - LHS.setAlignment(std::min(LHS.getAlignment(), RHS.getAlignment())); - return LHS; - } + QualType MergedType); /// Construct an address with the natural alignment of T. If a pointer to T /// is expected to be signed, the pointer passed to this function must have @@ -2687,7 +2704,8 @@ class CodeGenFunction : public CodeGenTypeCache { if (Alignment.isZero()) Alignment = CGM.getNaturalTypeAlignment(T, BaseInfo, TBAAInfo, ForPointeeType); - return Address(Ptr, ConvertTypeForMem(T), Alignment, nullptr, + return Address(Ptr, ConvertTypeForMem(T), Alignment, + CGM.getPointerAuthInfoForPointeeType(T), nullptr, IsKnownNonNull); } @@ -4424,17 +4442,47 @@ class CodeGenFunction : public CodeGenTypeCache { CXXDtorType Type, const CXXRecordDecl *RD); - llvm::Value *EmitPointerAuthResign(llvm::Value *pointer, - QualType pointerType, - const CGPointerAuthInfo &curAuthInfo, - const CGPointerAuthInfo &newAuthInfo, - bool isKnownNonNull); - llvm::Value *EmitPointerAuthResignCall(llvm::Value *pointer, - const CGPointerAuthInfo &curInfo, - const CGPointerAuthInfo &newInfo); + bool isPointerKnownNonNull(const Expr *E); + + /// Create the discriminator from the storage address and the entity hash. + llvm::Value *EmitPointerAuthBlendDiscriminator(llvm::Value *StorageAddress, + llvm::Value *Discriminator); + CGPointerAuthInfo EmitPointerAuthInfo(const PointerAuthSchema &Schema, + llvm::Value *StorageAddress, + GlobalDecl SchemaDecl, + QualType SchemaType); + + llvm::Value *EmitPointerAuthSign(QualType PointeeType, llvm::Value *Pointer); + llvm::Value *EmitPointerAuthSign(const CGPointerAuthInfo &Info, + llvm::Value *Pointer); + + llvm::Value *EmitPointerAuthAuth(QualType PointeeType, llvm::Value *Pointer); + llvm::Value *EmitPointerAuthAuth(const CGPointerAuthInfo &Info, + llvm::Value *Pointer); + + llvm::Value *EmitPointerAuthResign(llvm::Value *Pointer, QualType PointerType, + const CGPointerAuthInfo &CurAuthInfo, + const CGPointerAuthInfo &NewAuthInfo, + bool IsKnownNonNull); + llvm::Value *EmitPointerAuthResignCall(llvm::Value *Pointer, + const CGPointerAuthInfo &CurInfo, + const CGPointerAuthInfo &NewInfo); + + void EmitPointerAuthOperandBundle( + const CGPointerAuthInfo &Info, + SmallVectorImpl &Bundles); + + llvm::Value *AuthPointerToPointerCast(llvm::Value *ResultPtr, + QualType SourceType, QualType DestType); + Address AuthPointerToPointerCast(Address Ptr, QualType SourceType, + QualType DestType); + + Address EmitPointerAuthSign(Address Addr, QualType PointeeType); + Address EmitPointerAuthAuth(Address Addr, QualType PointeeType); CGPointerAuthInfo EmitPointerAuthInfo(PointerAuthQualifier qualifier, Address storageAddress); + llvm::Value *EmitPointerAuthQualify(PointerAuthQualifier qualifier, llvm::Value *pointer, QualType valueType, @@ -4448,34 +4496,18 @@ class CodeGenFunction : public CodeGenTypeCache { QualType pointerType, Address storageAddress, bool isKnownNonNull); + void EmitPointerAuthCopy(PointerAuthQualifier qualifier, QualType type, Address destField, Address srcField); - std::pair - EmitOrigPointerRValue(const Expr *E); + Address getAsNaturalAddressOf(Address Addr, QualType PointeeTy); llvm::Value *getAsNaturalPointerTo(Address Addr, QualType PointeeType) { - return Addr.getBasePointer(); + return getAsNaturalAddressOf(Addr, PointeeType).getBasePointer(); } - bool isPointerKnownNonNull(const Expr *E); - - /// Create the discriminator from the storage address and the entity hash. - llvm::Value *EmitPointerAuthBlendDiscriminator(llvm::Value *StorageAddress, - llvm::Value *Discriminator); - CGPointerAuthInfo EmitPointerAuthInfo(const PointerAuthSchema &Schema, - llvm::Value *StorageAddress, - GlobalDecl SchemaDecl, - QualType SchemaType); - llvm::Value *EmitPointerAuthSign(QualType PointeeType, llvm::Value *Pointer); - llvm::Value *EmitPointerAuthSign(const CGPointerAuthInfo &Info, - llvm::Value *Pointer); - llvm::Value *EmitPointerAuthAuth(const CGPointerAuthInfo &Info, - llvm::Value *Pointer); - - void EmitPointerAuthOperandBundle( - const CGPointerAuthInfo &Info, - SmallVectorImpl &Bundles); + std::pair + EmitOrigPointerRValue(const Expr *E); // Return the copy constructor name with the prefix "__copy_constructor_" // removed. diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index c17fef6ad2520..4ffe7cde66319 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -990,6 +990,9 @@ class CodeGenModule : public CodeGenTypeCache { GlobalDecl SchemaDecl, QualType SchemaType); + llvm::Constant *getConstantSignedPointer(llvm::Constant *Pointer, + QualType PointeeType); + llvm::Constant * getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, llvm::Constant *StorageAddress, diff --git a/clang/test/CodeGen/ptrauth-function-lvalue-cast-disc.c b/clang/test/CodeGen/ptrauth-function-lvalue-cast-disc.c new file mode 100644 index 0000000000000..6712a6cf417b9 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-function-lvalue-cast-disc.c @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 %s -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -emit-llvm -o- -fptrauth-function-pointer-type-discrimination | FileCheck %s + +typedef void (*fptr_t)(void); + +char *cptr; +void (*fptr)(void); + +// CHECK-LABEL: define void @test1 +void test1() { + // CHECK: [[LOAD:%.*]] = load ptr, ptr @cptr + // CHECK: [[TOINT:%.*]] = ptrtoint ptr [[LOAD]] to i64 + // CHECK: call i64 @llvm.ptrauth.resign(i64 [[TOINT]], i32 0, i64 0, i32 0, i64 18983) + // CHECK: call void {{.*}}() [ "ptrauth"(i32 0, i64 18983) ] + + (*(fptr_t)cptr)(); +} + +// CHECK-LABEL: define i8 @test2 +char test2() { + return *(char *)fptr; + + // CHECK: [[LOAD:%.*]] = load ptr, ptr @fptr + // CHECK: [[CMP:%.*]] = icmp ne ptr [[LOAD]], null + + // CHECK: [[TOINT:%.*]] = ptrtoint ptr [[LOAD]] to i64 + // CHECK: call i64 @llvm.ptrauth.resign(i64 [[TOINT]], i32 0, i64 18983, i32 0, i64 0) +} + +// CHECK-LABEL: define void @test4 +void test4() { + (*((fptr_t)(&*((char *)(&*(fptr_t)cptr)))))(); + + // CHECK: [[LOAD:%.*]] = load ptr, ptr @cptr + // CHECK-NEXT: [[CAST4:%.*]] = ptrtoint ptr [[LOAD]] to i64 + // CHECK-NEXT: [[RESIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[CAST4]], i32 0, i64 0, i32 0, i64 18983) + // CHECK-NEXT: [[CAST5:%.*]] = inttoptr i64 [[RESIGN]] to ptr + // CHECK-NEXT: call void [[CAST5]]() [ "ptrauth"(i32 0, i64 18983) ] +} + +void *vptr; +void test5() { + vptr = &*(char *)fptr; + + // CHECK: [[LOAD:%.*]] = load ptr, ptr @fptr + // CHECK-NEXT: [[CMP]] = icmp ne ptr [[LOAD]], null + // CHECK-NEXT: br i1 [[CMP]], label %[[NONNULL:.*]], label %[[CONT:.*]] + + // CHECK: [[NONNULL]]: + // CHECK: [[RESIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 {{.*}}, i32 0, i64 18983, i32 0, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[RESIGN]] to ptr + + // CHECK: [[CONT]]: + // CHECK: [[PHI:%.*]] = phi ptr [ null, {{.*}} ], [ [[CAST]], %[[NONNULL]] ] + // CHECK: store ptr [[PHI]], ptr @vptr +} diff --git a/clang/test/CodeGen/ptrauth-function-type-discriminator-cast-wrapper-globals.c b/clang/test/CodeGen/ptrauth-function-type-discriminator-cast-wrapper-globals.c new file mode 100644 index 0000000000000..d96b96af26d87 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-function-type-discriminator-cast-wrapper-globals.c @@ -0,0 +1,87 @@ +// RUN: %clang_cc1 %s -mllvm -ptrauth-emit-wrapper-globals=1 -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s +// RUN: %clang_cc1 -xc++ %s -mllvm -ptrauth-emit-wrapper-globals=1 -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,CHECKCXX + +#ifdef __cplusplus +extern "C" { +#endif + +void f(void); +void f2(int); +void (*fptr)(void); +void *opaque; +unsigned long uintptr; + +#ifdef __cplusplus +struct ptr_member { + void (*fptr_)(int) = 0; +}; +ptr_member pm; +void (*test_member)() = (void (*)())pm.fptr_; + +// CHECK: @f.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @f, i32 0, i64 0, i64 18983 }, section "llvm.ptrauth", align 8 + +// CHECKCXX-LABEL: define internal void @__cxx_global_var_init +// CHECKCXX: call i64 @llvm.ptrauth.resign(i64 {{.*}}, i32 0, i64 2712, i32 0, i64 18983) +#endif + +// CHECK-LABEL: define void @test_cast_to_opaque +void test_cast_to_opaque() { + opaque = (void *)f; + + // CHECK: [[RESIGN_VAL:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr @f.ptrauth to i64), i32 0, i64 18983, i32 0, i64 0) + // CHECK: [[RESIGN_PTR:%.*]] = inttoptr i64 [[RESIGN_VAL]] to ptr +} + +// CHECK-LABEL: define void @test_cast_from_opaque +void test_cast_from_opaque() { + fptr = (void (*)(void))opaque; + + // CHECK: [[LOAD:%.*]] = load ptr, ptr @opaque + // CHECK: [[CMP:%.*]] = icmp ne ptr [[LOAD]], null + // CHECK: br i1 [[CMP]], label %[[RESIGN_LAB:.*]], label + + // CHECK: [[RESIGN_LAB]]: + // CHECK: [[INT:%.*]] = ptrtoint ptr [[LOAD]] to i64 + // CHECK: [[RESIGN_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[INT]], i32 0, i64 0, i32 0, i64 18983) +} + +// CHECK-LABEL: define void @test_cast_to_intptr +void test_cast_to_intptr() { + uintptr = (unsigned long)fptr; + + // CHECK: [[ENTRY:.*]]: + // CHECK: [[LOAD:%.*]] = load ptr, ptr @fptr + // CHECK: [[CMP:%.*]] = icmp ne ptr [[LOAD]], null + // CHECK: br i1 [[CMP]], label %[[RESIGN_LAB:.*]], label %[[RESIGN_CONT:.*]] + + // CHECK: [[RESIGN_LAB]]: + // CHECK: [[INT:%.*]] = ptrtoint ptr [[LOAD]] to i64 + // CHECK: [[RESIGN_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[INT]], i32 0, i64 18983, i32 0, i64 0) + // CHECK: [[RESIGN:%.*]] = inttoptr i64 [[RESIGN_INT]] to ptr + // CHECK: br label %[[RESIGN_CONT]] + + // CHECK: [[RESIGN_CONT]]: + // CHECK: phi ptr [ null, %[[ENTRY]] ], [ [[RESIGN]], %[[RESIGN_LAB]] ] +} + +// CHECK-LABEL: define void @test_function_to_function_cast +void test_function_to_function_cast() { + void (*fptr2)(int) = (void (*)(int))fptr; + // CHECK: call i64 @llvm.ptrauth.resign(i64 {{.*}}, i32 0, i64 18983, i32 0, i64 2712) +} + +// CHECK-LABEL: define void @test_call_lvalue_cast +void test_call_lvalue_cast() { + (*(void (*)(int))f)(42); + + // CHECK: entry: + // CHECK-NEXT: [[RESIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr @f.ptrauth to i64), i32 0, i64 18983, i32 0, i64 2712) + // CHECK-NEXT: [[RESIGN_INT:%.*]] = inttoptr i64 [[RESIGN]] to ptr + // CHECK-NEXT: call void [[RESIGN_INT]](i32 noundef 42) [ "ptrauth"(i32 0, i64 2712) ] +} + + +#ifdef __cplusplus +} +#endif + diff --git a/clang/test/CodeGen/ptrauth-function-type-discriminator-cast.c b/clang/test/CodeGen/ptrauth-function-type-discriminator-cast.c new file mode 100644 index 0000000000000..7899c99763c96 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-function-type-discriminator-cast.c @@ -0,0 +1,85 @@ +// RUN: %clang_cc1 %s -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s +// RUN: %clang_cc1 -xc++ %s -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,CHECKCXX + +#ifdef __cplusplus +extern "C" { +#endif + +void f(void); +void f2(int); +void (*fptr)(void); +void *opaque; +unsigned long uintptr; + +#ifdef __cplusplus +struct ptr_member { + void (*fptr_)(int) = 0; +}; +ptr_member pm; +void (*test_member)() = (void (*)())pm.fptr_; + +// CHECKCXX-LABEL: define internal void @__cxx_global_var_init +// CHECKCXX: call i64 @llvm.ptrauth.resign(i64 {{.*}}, i32 0, i64 2712, i32 0, i64 18983) +#endif + + +// CHECK-LABEL: define void @test_cast_to_opaque +void test_cast_to_opaque() { + opaque = (void *)f; + + // CHECK: [[RESIGN_VAL:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @f, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 0, i64 0) + // CHECK: [[RESIGN_PTR:%.*]] = inttoptr i64 [[RESIGN_VAL]] to ptr +} + +// CHECK-LABEL: define void @test_cast_from_opaque +void test_cast_from_opaque() { + fptr = (void (*)(void))opaque; + + // CHECK: [[LOAD:%.*]] = load ptr, ptr @opaque + // CHECK: [[CMP:%.*]] = icmp ne ptr [[LOAD]], null + // CHECK: br i1 [[CMP]], label %[[RESIGN_LAB:.*]], label + + // CHECK: [[RESIGN_LAB]]: + // CHECK: [[INT:%.*]] = ptrtoint ptr [[LOAD]] to i64 + // CHECK: [[RESIGN_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[INT]], i32 0, i64 0, i32 0, i64 18983) +} + +// CHECK-LABEL: define void @test_cast_to_intptr +void test_cast_to_intptr() { + uintptr = (unsigned long)fptr; + + // CHECK: [[ENTRY:.*]]: + // CHECK: [[LOAD:%.*]] = load ptr, ptr @fptr + // CHECK: [[CMP:%.*]] = icmp ne ptr [[LOAD]], null + // CHECK: br i1 [[CMP]], label %[[RESIGN_LAB:.*]], label %[[RESIGN_CONT:.*]] + + // CHECK: [[RESIGN_LAB]]: + // CHECK: [[INT:%.*]] = ptrtoint ptr [[LOAD]] to i64 + // CHECK: [[RESIGN_INT:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[INT]], i32 0, i64 18983, i32 0, i64 0) + // CHECK: [[RESIGN:%.*]] = inttoptr i64 [[RESIGN_INT]] to ptr + // CHECK: br label %[[RESIGN_CONT]] + + // CHECK: [[RESIGN_CONT]]: + // CHECK: phi ptr [ null, %[[ENTRY]] ], [ [[RESIGN]], %[[RESIGN_LAB]] ] +} + +// CHECK-LABEL: define void @test_function_to_function_cast +void test_function_to_function_cast() { + void (*fptr2)(int) = (void (*)(int))fptr; + // CHECK: call i64 @llvm.ptrauth.resign(i64 {{.*}}, i32 0, i64 18983, i32 0, i64 2712) +} + +// CHECK-LABEL: define void @test_call_lvalue_cast +void test_call_lvalue_cast() { + (*(void (*)(int))f)(42); + + // CHECK: entry: + // CHECK-NEXT: [[RESIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @f, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 0, i64 2712) + // CHECK-NEXT: [[RESIGN_INT:%.*]] = inttoptr i64 [[RESIGN]] to ptr + // CHECK-NEXT: call void [[RESIGN_INT]](i32 noundef 42) [ "ptrauth"(i32 0, i64 2712) ] +} + + +#ifdef __cplusplus +} +#endif diff --git a/clang/test/CodeGen/ptrauth-wrapper-globals.c b/clang/test/CodeGen/ptrauth-wrapper-globals.c new file mode 100644 index 0000000000000..a68617a19a323 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-wrapper-globals.c @@ -0,0 +1,100 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck -check-prefix=CHECK -check-prefix=NOPCH %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-pch %s -o %t.ast +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -x ast -o - %t.ast | FileCheck -check-prefix=CHECK -check-prefix=PCH %s + +#define FNPTRKEY 0 + +void (*fnptr)(void); +long discriminator; + +extern void external_function(void); +// CHECK: [[EXTERNAL_FUNCTION:@.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @fptr1 = global ptr [[EXTERNAL_FUNCTION]] +void (*fptr1)(void) = external_function; +// CHECK: @fptr2 = global ptr [[EXTERNAL_FUNCTION]] +void (*fptr2)(void) = &external_function; + +// CHECK: [[SIGNED:@.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 2, i64 0, i64 26 }, section "llvm.ptrauth", align 8 +// CHECK: @fptr3 = global ptr [[SIGNED]] +void (*fptr3)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, 26); + +// CHECK: @fptr4 = global ptr [[SIGNED:@.*]], +// CHECK: [[SIGNED]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 2, i64 ptrtoint (ptr @fptr4 to i64), i64 26 }, section "llvm.ptrauth", align 8 +void (*fptr4)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, __builtin_ptrauth_blend_discriminator(&fptr4, 26)); + +// CHECK-LABEL: define void @test_call() +void test_call() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 0, i64 0) ] + fnptr(); +} + +// CHECK-LABEL: define void @test_direct_call() +void test_direct_call() { + // CHECK: call void @test_call(){{$}} + test_call(); +} + +void abort(); +// CHECK-LABEL: define void @test_direct_builtin_call() +void test_direct_builtin_call() { + // CHECK: call void @abort() {{#[0-9]+$}} + abort(); +} + +// CHECK-LABEL: define void @test_sign_unauthenticated_peephole() +void test_sign_unauthenticated_peephole() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: call void [[T0]](){{$}} + // CHECK-NEXT: ret void + __builtin_ptrauth_sign_unauthenticated(fnptr, FNPTRKEY, 0)(); +} + +// This peephole doesn't kick in because it's incorrect when ABI pointer +// authentication is enabled. +// CHECK-LABEL: define void @test_auth_peephole() +void test_auth_peephole() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: [[T1:%.*]] = load i64, ptr @discriminator, + // CHECK-NEXT: [[T2:%.*]] = ptrtoint ptr [[T0]] to i64 + // CHECK-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T2]], i32 0, i64 [[T1]]) + // CHECK-NEXT: [[T4:%.*]] = inttoptr i64 [[T3]] to ptr + // CHECK-NEXT: call void [[T4]]() [ "ptrauth"(i32 0, i64 0) ] + // CHECK-NEXT: ret void + __builtin_ptrauth_auth(fnptr, 0, discriminator)(); +} + +// CHECK-LABEL: define void @test_auth_and_resign_peephole() +void test_auth_and_resign_peephole() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: [[T1:%.*]] = load i64, ptr @discriminator, + // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 2, i64 [[T1]]) ] + // CHECK-NEXT: ret void + __builtin_ptrauth_auth_and_resign(fnptr, 2, discriminator, FNPTRKEY, 0)(); +} + +// CHECK-LABEL: define ptr @test_function_pointer() +// CHECK: [[EXTERNAL_FUNCTION]] +void (*test_function_pointer())(void) { + return external_function; +} + +// rdar://34562484 - Handle IR types changing in the caching mechanism. +struct InitiallyIncomplete; +extern struct InitiallyIncomplete returns_initially_incomplete(void); +// CHECK-LABEL: define void @use_while_incomplete() +void use_while_incomplete() { + // NOPCH: [[VAR:%.*]] = alloca ptr, + // NOPCH-NEXT: store ptr @returns_initially_incomplete.ptrauth, ptr [[VAR]], + // PCH: [[VAR:%.*]] = alloca ptr, + // PCH-NEXT: store ptr @returns_initially_incomplete.ptrauth, ptr [[VAR]], + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} +struct InitiallyIncomplete { int x; }; +// CHECK-LABEL: define void @use_while_complete() +void use_while_complete() { + // CHECK: [[VAR:%.*]] = alloca ptr, + // CHECK-NEXT: store ptr @returns_initially_incomplete.ptrauth, ptr [[VAR]], + // CHECK-NEXT: ret void + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} diff --git a/clang/test/CodeGen/ptrauth.c b/clang/test/CodeGen/ptrauth.c index a68617a19a323..a385d9bb92d6e 100644 --- a/clang/test/CodeGen/ptrauth.c +++ b/clang/test/CodeGen/ptrauth.c @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck -check-prefix=CHECK -check-prefix=NOPCH %s -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-pch %s -o %t.ast -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -x ast -o - %t.ast | FileCheck -check-prefix=CHECK -check-prefix=PCH %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck -check-prefix=CHECK -check-prefix=NOPCH %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-pch %s -o %t.ast +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -x ast -o - %t.ast | FileCheck -check-prefix=CHECK -check-prefix=PCH %s #define FNPTRKEY 0 @@ -8,24 +8,21 @@ void (*fnptr)(void); long discriminator; extern void external_function(void); -// CHECK: [[EXTERNAL_FUNCTION:@.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 -// CHECK: @fptr1 = global ptr [[EXTERNAL_FUNCTION]] +// CHECK: @fptr1 = global ptr ptrauth (ptr @external_function, i32 0, i64 18983) void (*fptr1)(void) = external_function; -// CHECK: @fptr2 = global ptr [[EXTERNAL_FUNCTION]] +// CHECK: @fptr2 = global ptr ptrauth (ptr @external_function, i32 0, i64 18983) void (*fptr2)(void) = &external_function; -// CHECK: [[SIGNED:@.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 2, i64 0, i64 26 }, section "llvm.ptrauth", align 8 -// CHECK: @fptr3 = global ptr [[SIGNED]] +// CHECK: @fptr3 = global ptr ptrauth (ptr @external_function, i32 2, i64 26) void (*fptr3)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, 26); -// CHECK: @fptr4 = global ptr [[SIGNED:@.*]], -// CHECK: [[SIGNED]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 2, i64 ptrtoint (ptr @fptr4 to i64), i64 26 }, section "llvm.ptrauth", align 8 +// CHECK: @fptr4 = global ptr ptrauth (ptr @external_function, i32 2, i64 26, ptr @fptr4) void (*fptr4)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, __builtin_ptrauth_blend_discriminator(&fptr4, 26)); // CHECK-LABEL: define void @test_call() void test_call() { // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, - // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 0, i64 0) ] + // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 0, i64 18983) ] fnptr(); } @@ -42,59 +39,39 @@ void test_direct_builtin_call() { abort(); } -// CHECK-LABEL: define void @test_sign_unauthenticated_peephole() -void test_sign_unauthenticated_peephole() { - // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, - // CHECK-NEXT: call void [[T0]](){{$}} - // CHECK-NEXT: ret void - __builtin_ptrauth_sign_unauthenticated(fnptr, FNPTRKEY, 0)(); -} - -// This peephole doesn't kick in because it's incorrect when ABI pointer -// authentication is enabled. -// CHECK-LABEL: define void @test_auth_peephole() -void test_auth_peephole() { - // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, - // CHECK-NEXT: [[T1:%.*]] = load i64, ptr @discriminator, - // CHECK-NEXT: [[T2:%.*]] = ptrtoint ptr [[T0]] to i64 - // CHECK-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T2]], i32 0, i64 [[T1]]) - // CHECK-NEXT: [[T4:%.*]] = inttoptr i64 [[T3]] to ptr - // CHECK-NEXT: call void [[T4]]() [ "ptrauth"(i32 0, i64 0) ] - // CHECK-NEXT: ret void - __builtin_ptrauth_auth(fnptr, 0, discriminator)(); -} - -// CHECK-LABEL: define void @test_auth_and_resign_peephole() -void test_auth_and_resign_peephole() { - // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, - // CHECK-NEXT: [[T1:%.*]] = load i64, ptr @discriminator, - // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 2, i64 [[T1]]) ] - // CHECK-NEXT: ret void - __builtin_ptrauth_auth_and_resign(fnptr, 2, discriminator, FNPTRKEY, 0)(); -} - // CHECK-LABEL: define ptr @test_function_pointer() -// CHECK: [[EXTERNAL_FUNCTION]] +// CHECK: ret ptr ptrauth (ptr @external_function, i32 0, i64 18983) void (*test_function_pointer())(void) { return external_function; } -// rdar://34562484 - Handle IR types changing in the caching mechanism. struct InitiallyIncomplete; extern struct InitiallyIncomplete returns_initially_incomplete(void); // CHECK-LABEL: define void @use_while_incomplete() void use_while_incomplete() { // NOPCH: [[VAR:%.*]] = alloca ptr, - // NOPCH-NEXT: store ptr @returns_initially_incomplete.ptrauth, ptr [[VAR]], + // NOPCH-NEXT: store ptr ptrauth (ptr @returns_initially_incomplete, i32 0, i64 25106), ptr [[VAR]] // PCH: [[VAR:%.*]] = alloca ptr, - // PCH-NEXT: store ptr @returns_initially_incomplete.ptrauth, ptr [[VAR]], + // PCH-NEXT: store ptr ptrauth (ptr @returns_initially_incomplete, i32 0, i64 25106), ptr [[VAR]] struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; } struct InitiallyIncomplete { int x; }; // CHECK-LABEL: define void @use_while_complete() void use_while_complete() { // CHECK: [[VAR:%.*]] = alloca ptr, - // CHECK-NEXT: store ptr @returns_initially_incomplete.ptrauth, ptr [[VAR]], + // CHECK-NEXT: store ptr ptrauth (ptr @returns_initially_incomplete, i32 0, i64 25106), ptr [[VAR]] // CHECK-NEXT: ret void struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; } + +// CHECK-LABEL: define void @test_memcpy_inline( +// CHECK-NOT: call{{.*}}memcpy + +extern inline __attribute__((__always_inline__)) +void *memcpy(void *d, const void *s, unsigned long) { + return 0; +} + +void test_memcpy_inline(char *d, char *s) { + memcpy(d, s, 4); +} diff --git a/clang/test/Preprocessor/ptrauth_feature.c b/clang/test/Preprocessor/ptrauth_feature.c index 1c6dfcc436383..bee3454388fef 100644 --- a/clang/test/Preprocessor/ptrauth_feature.c +++ b/clang/test/Preprocessor/ptrauth_feature.c @@ -65,6 +65,16 @@ // RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,NOINITFINI,FUNC +// RUN: %clang_cc1 -E %s -triple=aarch64 \ +// RUN: -fptrauth-intrinsics \ +// RUN: -fptrauth-calls \ +// RUN: -fptrauth-returns \ +// RUN: -fptrauth-vtable-pointer-address-discrimination \ +// RUN: -fptrauth-vtable-pointer-type-discrimination \ +// RUN: -fptrauth-function-pointer-type-discrimination | \ +// RUN: FileCheck %s --check-prefixes=INTRIN,CALLS,RETS,VPTR_ADDR_DISCR,VPTR_TYPE_DISCR,NOINITFINI,FUNC + + #if __has_feature(ptrauth_intrinsics) // INTRIN: has_ptrauth_intrinsics void has_ptrauth_intrinsics() {} diff --git a/clang/test/Sema/ptrauth-function-type-discriminatior.c b/clang/test/Sema/ptrauth-function-type-discriminatior.c new file mode 100644 index 0000000000000..0dacdf35f2038 --- /dev/null +++ b/clang/test/Sema/ptrauth-function-type-discriminatior.c @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 %s -triple arm64e-apple-ios13 -verify -xobjective-c -fblocks +// RUN: %clang_cc1 %s -triple arm64e-apple-ios13 -verify -xc +// RUN: %clang_cc1 %s -triple arm64e-apple-ios13 -verify -xc++ + +// expected-no-diagnostics + +#define discm(x) __builtin_ptrauth_type_discriminator(x) + +struct Complete {}; +struct Incomplete; + +#ifndef __cplusplus +enum EIncomplete; +#endif + +enum EComplete { enumerator }; + +_Static_assert(discm(void(void)) == 18983, ""); +_Static_assert(discm(void()) == discm(void(void)), ""); +_Static_assert(discm(void(int *)) == discm(void(float *)), ""); +_Static_assert(discm(void(int *)) == discm(void(struct Incomplete *)), ""); +_Static_assert(discm(void(struct Complete *)) == discm(void(struct Incomplete *)), ""); +_Static_assert(discm(void(int *)) != discm(void(int)), ""); +_Static_assert(discm(void(int)) != discm(void(int, ...)), ""); +_Static_assert(discm(_Atomic(int *)()) == discm(int *()), ""); +#ifndef __cplusplus +_Static_assert(discm(enum EIncomplete()) == discm(int()), ""); +#endif +_Static_assert(discm(enum EComplete()) == discm(int()), ""); +_Static_assert(discm(unsigned long()) == discm(int()), ""); +_Static_assert(discm(char()) == discm(int()), ""); +_Static_assert(discm(int(int (*)[10])) == discm(int(int (*)[9])), ""); +_Static_assert(discm(void (int[10])) == discm(void (int *)), ""); +_Static_assert(discm(void (int[*])) == discm(void (int *)), ""); +_Static_assert(discm(void (void ())) == discm(void (void (*))), ""); + +#ifndef __cplusplus +typedef struct {} foo; +struct foo {}; +_Static_assert(discm(void(foo)) == discm(void(struct foo)), ""); +#endif + +#ifdef __OBJC__ +@interface I @end +_Static_assert(discm(id()) == discm(I*()), ""); +_Static_assert(discm(id()) == discm(void*()), ""); +_Static_assert(discm(id()) == discm(Class()), ""); +_Static_assert(discm(void(^())()) == discm(id()), ""); +#endif + +#ifdef __cplusplus +_Static_assert(discm(void(Complete &)) != discm(void(Complete *)), ""); +_Static_assert(discm(void(Complete &)) != discm(void(Complete &&)), ""); +_Static_assert(discm(void(Incomplete &)) != discm(void(Incomplete &&)), ""); +/* Descend into array and function types when using references. */ +_Static_assert(discm(void(void (&)())) != discm(void (void (&)(int))), ""); +_Static_assert(discm(void(void (&)())) != discm(void (int (&)())), ""); +_Static_assert(discm(void(int (&)[10])) == discm(void(int (&)[9])), ""); +_Static_assert(discm(void(int (&)[10])) == discm(void(int (&)[])), ""); +_Static_assert(discm(void(int (&)[10])) != discm(void(float (&)[10])), ""); +#endif + +typedef __attribute__((ext_vector_type(4))) float vec4; +typedef __attribute__((ext_vector_type(16))) char char_vec16; +_Static_assert(discm(void (vec4)) == discm(void (char_vec16)), ""); From 28887e6238b7dc53bf536cd21125a750dbdbb163 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Tue, 2 Jul 2024 18:58:10 -0700 Subject: [PATCH 25/58] [clang] Define ptrauth_type_discriminator builtin. --- .../clang/Basic/DiagnosticSemaKinds.td | 6 ++-- clang/lib/AST/ItaniumMangle.cpp | 16 ++++----- clang/lib/Headers/ptrauth.h | 10 ++++-- clang/lib/Sema/SemaExpr.cpp | 27 +++++++++------ clang/test/AST/ast-dump-ptrauth-json.cpp | 2 +- clang/test/CodeGenCXX/mangle-fail.cpp | 2 +- clang/test/Sema/ptrauth-intrinsics-macro.c | 5 +++ .../SemaCXX/ptrauth-type-discriminator.cpp | 34 +++++++++++++++++++ clang/test/SemaCXX/ptrauth.cpp | 28 --------------- 9 files changed, 75 insertions(+), 55 deletions(-) create mode 100644 clang/test/SemaCXX/ptrauth-type-discriminator.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b1ec64d3cf731..ade927e23f68d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -944,9 +944,9 @@ def err_ptrauth_disabled_target : def err_ptrauth_string_not_literal : Error< "argument must be a string literal%select{| of char type}0">; -def err_ptrauth_type_disc_variably_modified : Error< - "cannot pass variably-modified type %0 to " - "'__builtin_ptrauth_type_discriminator'">; +def err_ptrauth_type_disc_undiscriminated : Error< + "cannot pass undiscriminated type %0 to " + "'__builtin_ptrauth_type_discriminator'">; // __ptrauth qualifier def err_ptrauth_qualifier_return : Error< diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 985ff996199ac..97eeaa6007b29 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -5174,14 +5174,6 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, Out << 'a'; MangleAlignofSizeofArg(); break; - 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_DataSizeOf: { DiagnosticsEngine &Diags = Context.getDiags(); unsigned DiagID = @@ -5190,6 +5182,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, diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h index 3e86b41e19124..db65e4ae55fdb 100644 --- a/clang/lib/Headers/ptrauth.h +++ b/clang/lib/Headers/ptrauth.h @@ -235,11 +235,13 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_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. This property is currently not true of other types. + 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. - The argument must be a type. A call to this function is an integer constant expression. */ -#define ptrauth_type_discriminator(__type) \ +#define ptrauth_type_discriminator(__type) \ __builtin_ptrauth_type_discriminator(__type) /* Compute a signature for the given pair of pointer-sized values. @@ -366,6 +368,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; \ diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 9c096444877c6..a58b6573627e0 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4098,17 +4098,6 @@ static bool CheckVecStepTraitOperandType(Sema &S, QualType T, return false; } -static bool CheckPtrAuthTypeDiscriminatorOperandType(Sema &S, QualType T, - SourceLocation Loc, - SourceRange ArgRange) { - if (T->isVariablyModifiedType()) { - S.Diag(Loc, diag::err_ptrauth_type_disc_variably_modified) << T << ArgRange; - return true; - } - - return false; -} - static bool CheckVectorElementsTraitOperandType(Sema &S, QualType T, SourceLocation Loc, SourceRange ArgRange) { @@ -4121,6 +4110,18 @@ static bool CheckVectorElementsTraitOperandType(Sema &S, QualType T, return false; } +static bool CheckPtrAuthTypeDiscriminatorOperandType(Sema &S, QualType T, + SourceLocation Loc, + SourceRange ArgRange) { + 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, @@ -4510,6 +4511,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)) diff --git a/clang/test/AST/ast-dump-ptrauth-json.cpp b/clang/test/AST/ast-dump-ptrauth-json.cpp index d7f11120007c3..125cda0cff53a 100644 --- a/clang/test/AST/ast-dump-ptrauth-json.cpp +++ b/clang/test/AST/ast-dump-ptrauth-json.cpp @@ -2,4 +2,4 @@ // CHECK: "name": "__builtin_ptrauth_type_discriminator", -int d = __builtin_ptrauth_type_discriminator(int); +int d = __builtin_ptrauth_type_discriminator(int()); diff --git a/clang/test/CodeGenCXX/mangle-fail.cpp b/clang/test/CodeGenCXX/mangle-fail.cpp index 7d842c53896db..cac95cabbab83 100644 --- a/clang/test/CodeGenCXX/mangle-fail.cpp +++ b/clang/test/CodeGenCXX/mangle-fail.cpp @@ -24,7 +24,7 @@ void func(S1 s1) { // expected-error } void testfunc1() { - func(S1()); + func(S1()); } // FIXME: There are several more cases we can't yet mangle. diff --git a/clang/test/Sema/ptrauth-intrinsics-macro.c b/clang/test/Sema/ptrauth-intrinsics-macro.c index 076835b0e8541..a43198feec3ab 100644 --- a/clang/test/Sema/ptrauth-intrinsics-macro.c +++ b/clang/test/Sema/ptrauth-intrinsics-macro.c @@ -69,6 +69,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); } diff --git a/clang/test/SemaCXX/ptrauth-type-discriminator.cpp b/clang/test/SemaCXX/ptrauth-type-discriminator.cpp new file mode 100644 index 0000000000000..4eefff168d8c0 --- /dev/null +++ b/clang/test/SemaCXX/ptrauth-type-discriminator.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++17 -Wno-vla -fsyntax-only -verify -fptrauth-intrinsics %s + +struct S { + virtual int foo(); +}; + +template +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 d = __builtin_ptrauth_type_discriminator(MemFnTy); + static_assert(d == 60844); + static_assert(__builtin_ptrauth_type_discriminator(int (S::*)()) == d); + static_assert(__builtin_ptrauth_type_discriminator(decltype(memFnPtr)) == d); + static_assert(__builtin_ptrauth_type_discriminator(decltype(memFnPtr2)) == d); + static_assert(__builtin_ptrauth_type_discriminator(decltype(&S::foo)) == d); + static_assert(dependentOperandDisc() == d); + static_assert(__builtin_ptrauth_type_discriminator(void (S::*)(int)) == 39121); + static_assert(__builtin_ptrauth_type_discriminator(void (S::*)(float)) == 52453); + static_assert(__builtin_ptrauth_type_discriminator(int (*())[s]) == 34128); + + 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'}} +} diff --git a/clang/test/SemaCXX/ptrauth.cpp b/clang/test/SemaCXX/ptrauth.cpp index 4afd83fb17faa..34a1e86249958 100644 --- a/clang/test/SemaCXX/ptrauth.cpp +++ b/clang/test/SemaCXX/ptrauth.cpp @@ -38,34 +38,6 @@ struct S2 { } }; -template -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 d = __builtin_ptrauth_type_discriminator(MemFnTy); - static_assert(d == 60844); - static_assert(__builtin_ptrauth_type_discriminator(int (S::*)()) == d); - static_assert(__builtin_ptrauth_type_discriminator(decltype(memFnPtr)) == d); - static_assert(__builtin_ptrauth_type_discriminator(decltype(memFnPtr2)) == d); - static_assert(__builtin_ptrauth_type_discriminator(decltype(&S::foo)) == d); - static_assert(dependentOperandDisc() == d); - static_assert(__builtin_ptrauth_type_discriminator(void (S::*)(int)) == 39121); - static_assert(__builtin_ptrauth_type_discriminator(void (S::*)(float)) == 52453); - static_assert(__builtin_ptrauth_type_discriminator(int *) == 42396); - - 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 variably-modified type 'decltype(vmarray)'}} -} - void test_incomplete_virtual_member_function_return_arg_type() { (void)&S::virtual0; // expected-error {{incomplete type 'Incomplete0}} expected-note {{cannot take an address of a virtual member function}} (void)&S::virtual1; // expected-error {{implicit instantiation of undefined template 'Incomplete1'}} expected-note {{cannot take an address of a virtual member function}} From fbb71315ab749994b3a13399d823a5e70f5010cc Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Fri, 24 May 2024 18:44:08 -0700 Subject: [PATCH 26/58] [clang] Define ptrauth_function_pointer_type_discriminator macro. --- clang/lib/Headers/ptrauth.h | 17 +++++++++++++++++ clang/test/Sema/ptrauth-intrinsics-macro.c | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h index db65e4ae55fdb..edb389225aa20 100644 --- a/clang/lib/Headers/ptrauth.h +++ b/clang/lib/Headers/ptrauth.h @@ -244,6 +244,20 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; #define ptrauth_type_discriminator(__type) \ __builtin_ptrauth_type_discriminator(__type) +/* Compute the constant discriminator used by Clang to sign pointers with the + given C function pointer type. + + A call to this function is an integer constant expression*/ +#if __has_feature(ptrauth_function_pointer_type_discrimination) +#define ptrauth_function_pointer_type_discriminator(__type) \ + __builtin_ptrauth_type_discriminator(__type) +#else +#define ptrauth_function_pointer_type_discriminator(__type) \ + ((ptrauth_extra_data_t)0) +#endif + + + /* Compute a signature for the given pair of pointer-sized values. The order of the arguments is significant. @@ -370,6 +384,9 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; #define ptrauth_type_discriminator(__type) ((ptrauth_extra_data_t)0) +#define ptrauth_function_pointer_type_discriminator(__type) \ + ((ptrauth_extra_data_t)0) + #define ptrauth_sign_generic_data(__value, __data) \ ({ \ (void)__value; \ diff --git a/clang/test/Sema/ptrauth-intrinsics-macro.c b/clang/test/Sema/ptrauth-intrinsics-macro.c index a43198feec3ab..033315d4e63c6 100644 --- a/clang/test/Sema/ptrauth-intrinsics-macro.c +++ b/clang/test/Sema/ptrauth-intrinsics-macro.c @@ -77,3 +77,10 @@ void test_type_discriminator(int *dp) { void test_sign_constant(int *dp) { dp = ptrauth_sign_constant(&dv, VALID_DATA_KEY, 0); } + +void test_function_pointer(int *dp, int (*fp)(int), int value) { + dp = ptrauth_sign_constant(&dv, VALID_DATA_KEY, 0); + fp = ptrauth_auth_function(fp, VALID_CODE_KEY, 0); + ptrauth_extra_data_t t0 = ptrauth_function_pointer_type_discriminator(int (*)(int)); + (void)t0; +} From c349c07a4a20001d7754d59065b215145a3f7bed Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Tue, 2 Jul 2024 19:21:55 -0700 Subject: [PATCH 27/58] [clang] Implement the __ptrauth type qualifier. With signed null, signed uintptr and isa pointer support. --- clang/include/clang/AST/ASTContext.h | 21 + clang/include/clang/AST/Type.h | 54 +- clang/include/clang/Basic/Attr.td | 6 +- clang/include/clang/Basic/AttrDocs.td | 1 + .../clang/Basic/DiagnosticSemaKinds.td | 59 ++- clang/include/clang/Basic/Features.def | 4 + clang/include/clang/Basic/LangOptions.h | 11 + clang/include/clang/Basic/TokenKinds.def | 1 + clang/include/clang/Sema/Sema.h | 13 +- clang/lib/AST/ASTContext.cpp | 59 ++- clang/lib/AST/DeclCXX.cpp | 2 +- clang/lib/AST/Expr.cpp | 2 +- clang/lib/AST/Type.cpp | 10 + clang/lib/AST/TypePrinter.cpp | 53 +- clang/lib/CodeGen/CGBlocks.h | 2 +- clang/lib/CodeGen/CGCall.cpp | 2 +- clang/lib/CodeGen/CGClass.cpp | 3 +- clang/lib/CodeGen/CGDebugInfo.cpp | 40 +- clang/lib/CodeGen/CGExpr.cpp | 3 +- clang/lib/CodeGen/CGExprAgg.cpp | 7 + clang/lib/CodeGen/CGExprCXX.cpp | 4 + clang/lib/CodeGen/CGExprConstant.cpp | 31 +- clang/lib/CodeGen/CGExprScalar.cpp | 2 +- clang/lib/CodeGen/CGNonTrivialStruct.cpp | 2 + clang/lib/CodeGen/CGObjC.cpp | 61 ++- clang/lib/CodeGen/CGPointerAuth.cpp | 317 +++++++----- clang/lib/CodeGen/CodeGenFunction.cpp | 86 +++ clang/lib/CodeGen/CodeGenFunction.h | 3 +- clang/lib/CodeGen/CodeGenModule.cpp | 6 +- clang/lib/CodeGen/CodeGenTypes.cpp | 2 + clang/lib/Format/FormatToken.h | 1 + clang/lib/Format/UnwrappedLineParser.cpp | 4 +- clang/lib/Headers/ptrauth.h | 1 + clang/lib/Parse/ParseDecl.cpp | 48 +- clang/lib/Sema/SemaCast.cpp | 3 +- clang/lib/Sema/SemaChecking.cpp | 72 ++- clang/lib/Sema/SemaDecl.cpp | 25 +- clang/lib/Sema/SemaDeclCXX.cpp | 12 +- clang/lib/Sema/SemaExpr.cpp | 7 +- clang/lib/Sema/SemaExprCXX.cpp | 4 +- clang/lib/Sema/SemaObjCProperty.cpp | 4 + clang/lib/Sema/SemaOverload.cpp | 2 +- clang/lib/Sema/SemaType.cpp | 310 +++++++++-- clang/lib/Sema/TreeTransform.h | 18 + clang/test/AST/ast-dump-ptrauth-json.cpp | 5 - .../ptrauth-authenticated-null-values.c | 468 +++++++++++++++++ clang/test/CodeGen/ptrauth-debuginfo.c | 61 ++- clang/test/CodeGen/ptrauth-in-c-struct.c | 22 +- clang/test/CodeGen/ptrauth-qualifier-blocks.c | 113 ++++ .../test/CodeGen/ptrauth-qualifier-function.c | 75 +++ .../CodeGen/ptrauth-qualifier-loadstore.c | 32 +- clang/test/CodeGen/ptrauth-qualifier.c | 71 +-- .../ptrauth-restricted-intptr-qualifier.c | 220 ++++++++ clang/test/CodeGen/ptrauth-stripping.c | 330 ++++++++++++ .../ptrauth-authenticated-null-values.cpp | 489 ++++++++++++++++++ clang/test/CodeGenObjC/ptrauth-class.m | 103 ++++ .../CodeGenObjC/ptrauth-property-backing.m | 78 +++ clang/test/Preprocessor/ptrauth_feature.c | 4 +- clang/test/Sema/atomic-ops-ptrauth.c | 117 +++++ clang/test/Sema/ptrauth-authenticated-null.c | 53 ++ .../test/Sema/ptrauth-authenticated-null.cpp | 43 ++ clang/test/Sema/ptrauth-qualifier.c | 67 ++- .../Sema/ptrauth-redeclared-on-null-key.c | 21 + .../ptrauth-restricted-intptr-qualifier.c | 43 ++ .../ptrauth-qualifier-constexpr-options.cpp | 67 +++ clang/test/SemaCXX/ptrauth-qualifier.cpp | 12 +- .../SemaCXX/ptrauth-template-parameters.cpp | 66 +++ clang/test/SemaObjC/ptrauth-pointers.m | 46 ++ clang/test/SemaObjC/ptrauth-qualifier.m | 92 ++++ 69 files changed, 3695 insertions(+), 381 deletions(-) delete mode 100644 clang/test/AST/ast-dump-ptrauth-json.cpp create mode 100644 clang/test/CodeGen/ptrauth-authenticated-null-values.c create mode 100644 clang/test/CodeGen/ptrauth-qualifier-blocks.c create mode 100644 clang/test/CodeGen/ptrauth-qualifier-function.c create mode 100644 clang/test/CodeGen/ptrauth-restricted-intptr-qualifier.c create mode 100644 clang/test/CodeGen/ptrauth-stripping.c create mode 100644 clang/test/CodeGenCXX/ptrauth-authenticated-null-values.cpp create mode 100644 clang/test/CodeGenObjC/ptrauth-class.m create mode 100644 clang/test/CodeGenObjC/ptrauth-property-backing.m create mode 100644 clang/test/Sema/atomic-ops-ptrauth.c create mode 100644 clang/test/Sema/ptrauth-authenticated-null.c create mode 100644 clang/test/Sema/ptrauth-authenticated-null.cpp create mode 100644 clang/test/Sema/ptrauth-redeclared-on-null-key.c create mode 100644 clang/test/Sema/ptrauth-restricted-intptr-qualifier.c create mode 100644 clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp create mode 100644 clang/test/SemaCXX/ptrauth-template-parameters.cpp create mode 100644 clang/test/SemaObjC/ptrauth-pointers.m create mode 100644 clang/test/SemaObjC/ptrauth-qualifier.m diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 5c7377586da13..b3913341ec92e 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -476,6 +476,8 @@ class ASTContext : public RefCountedBase { llvm::StringMap PrimaryModuleNameMap; llvm::DenseMap SameModuleLookupSet; + mutable llvm::DenseMap ContainsAuthenticatedNullTypes; + /// The include tree that is being built, if any. /// See \c FrontendOptions::CASIncludeTreeID. std::optional CASIncludeTreeID; @@ -1308,6 +1310,25 @@ class ASTContext : public RefCountedBase { /// Return the "other" type-specific discriminator for the given type. uint16_t getPointerAuthTypeDiscriminator(QualType T); + // Determine whether the type can have qualifier __ptrauth with key + // ptrauth_key_none. + bool canQualifyWithPtrAuthKeyNone(QualType T) const { + QualType PointeeType = T->getPointeeType(); + + if (PointeeType.isNull()) + return true; + + // Disallow the qualifier on function pointers. + if (PointeeType->isFunctionType()) + return false; + + return true; + } + + bool typeContainsAuthenticatedNull(QualType) const; + bool typeContainsAuthenticatedNull(const Type *) const; + std::optional tryTypeContainsAuthenticatedNull(QualType) const; + /// Apply Objective-C protocol qualifiers to the given type. /// \param allowOnPointerType specifies if we can apply protocol /// qualifiers on ObjCObjectPointerType. It can be set to true when diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 9d42b426a5e3b..e9ee059def02e 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -165,8 +165,11 @@ class PointerAuthQualifier { AuthenticatesNullValuesBits = 1, AuthenticatesNullValuesMask = ((1 << AuthenticatesNullValuesBits) - 1) << AuthenticatesNullValuesShift, - KeyShift = AuthenticatesNullValuesShift + AuthenticatesNullValuesBits, - KeyBits = 10, + RestrictedIntegralShift = AuthenticatesNullValuesShift + AuthenticatesNullValuesBits, + RestrictedIntegralBits = 1, + RestrictedIntegralMask = ((1 << RestrictedIntegralBits) - 1) << RestrictedIntegralShift, + KeyShift = RestrictedIntegralShift + RestrictedIntegralBits, + KeyBits = 9, KeyMask = ((1 << KeyBits) - 1) << KeyShift, DiscriminatorShift = KeyShift + KeyBits, DiscriminatorBits = 16, @@ -175,32 +178,36 @@ class PointerAuthQualifier { // bits: |0 |1 |2..3 |4 | // |Enabled|Address|AuthenticationMode|ISA pointer| - // bits: |5 |6..15| 16...31 | - // |AuthenticatesNull|Key |Discriminator| + // bits: |5 |6 |7..15| 16...31 | + // |AuthenticatesNull|RestrictedIntegral|Key |Discriminator| uint32_t Data = 0; // The following static assertions check that each of the 32 bits is present // exactly in one of the constants. static_assert((EnabledBits + AddressDiscriminatedBits + AuthenticationModeBits + IsaPointerBits + - AuthenticatesNullValuesBits + KeyBits + DiscriminatorBits) == + AuthenticatesNullValuesBits + RestrictedIntegralBits + + KeyBits + DiscriminatorBits) == 32, "PointerAuthQualifier should be exactly 32 bits"); static_assert((EnabledMask + AddressDiscriminatedMask + AuthenticationModeMask + IsaPointerMask + - AuthenticatesNullValuesMask + KeyMask + DiscriminatorMask) == + AuthenticatesNullValuesMask + RestrictedIntegralMask + + KeyMask + DiscriminatorMask) == 0xFFFFFFFF, "All masks should cover the entire bits"); static_assert((EnabledMask ^ AddressDiscriminatedMask ^ AuthenticationModeMask ^ IsaPointerMask ^ - AuthenticatesNullValuesMask ^ KeyMask ^ DiscriminatorMask) == + AuthenticatesNullValuesMask ^ RestrictedIntegralMask ^ + KeyMask ^ DiscriminatorMask) == 0xFFFFFFFF, "All masks should cover the entire bits"); PointerAuthQualifier(unsigned Key, bool IsAddressDiscriminated, unsigned ExtraDiscriminator, PointerAuthenticationMode AuthenticationMode, - bool IsIsaPointer, bool AuthenticatesNullValues) + bool IsIsaPointer, bool AuthenticatesNullValues, + bool IsRestrictedIntegral) : Data(EnabledMask | (IsAddressDiscriminated ? llvm::to_underlying(AddressDiscriminatedMask) @@ -210,7 +217,8 @@ class PointerAuthQualifier { << AuthenticationModeShift) | (ExtraDiscriminator << DiscriminatorShift) | (IsIsaPointer << IsaPointerShift) | - (AuthenticatesNullValues << AuthenticatesNullValuesShift)) { + (AuthenticatesNullValues << AuthenticatesNullValuesShift) | + (IsRestrictedIntegral << RestrictedIntegralShift)) { assert(Key <= KeyNoneInternal); assert(ExtraDiscriminator <= MaxDiscriminator); assert((Data == 0) == @@ -234,13 +242,13 @@ class PointerAuthQualifier { static PointerAuthQualifier Create(unsigned Key, bool IsAddressDiscriminated, unsigned ExtraDiscriminator, PointerAuthenticationMode AuthenticationMode, bool IsIsaPointer, - bool AuthenticatesNullValues) { + bool AuthenticatesNullValues, bool IsRestrictedIntegral) { if (Key == PointerAuthKeyNone) Key = KeyNoneInternal; assert(Key <= KeyNoneInternal && "out-of-range key value"); return PointerAuthQualifier(Key, IsAddressDiscriminated, ExtraDiscriminator, AuthenticationMode, IsIsaPointer, - AuthenticatesNullValues); + AuthenticatesNullValues, IsRestrictedIntegral); } bool isPresent() const { @@ -287,6 +295,10 @@ class PointerAuthQualifier { return hasKeyNone() ? PointerAuthQualifier() : *this; } + bool isRestrictedIntegral() const { + return (Data & RestrictedIntegralMask) >> RestrictedIntegralShift; + } + friend bool operator==(PointerAuthQualifier Lhs, PointerAuthQualifier Rhs) { return Lhs.Data == Rhs.Data; } @@ -828,17 +840,18 @@ class Qualifiers { static_assert(sizeof(PointerAuthQualifier) == sizeof(uint32_t), "PointerAuthQualifier must be 32 bits"); + static constexpr uint64_t PtrAuthShift = 32; + static constexpr uint64_t PtrAuthMask = uint64_t(0xffffffff) << PtrAuthShift; + static constexpr uint64_t UMask = 0x8; static constexpr uint64_t UShift = 3; static constexpr uint64_t GCAttrMask = 0x30; static constexpr uint64_t GCAttrShift = 4; static constexpr uint64_t LifetimeMask = 0x1C0; static constexpr uint64_t LifetimeShift = 6; - static constexpr uint64_t AddressSpaceShift = 9; - static constexpr uint64_t PtrAuthShift = 32; - static constexpr uint64_t PtrAuthMask = uint64_t(0xffffffff) << PtrAuthShift; static constexpr uint64_t AddressSpaceMask = ~(CVRMask | UMask | GCAttrMask | LifetimeMask | PtrAuthMask); + static constexpr uint64_t AddressSpaceShift = 9; }; class QualifiersAndAtomic { @@ -1477,7 +1490,7 @@ class QualType { } bool hasAddressDiscriminatedPointerAuth() const { - if (auto ptrauth = getPointerAuth()) + if (auto ptrauth = getPointerAuth().withoutKeyNone()) return ptrauth.isAddressDiscriminated(); return false; } @@ -2530,8 +2543,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isFunctionNoProtoType() const { return getAs(); } bool isFunctionProtoType() const { return getAs(); } bool isPointerType() const; - bool isSignableType() const; + bool isSignableType(const ASTContext &Ctx) const; bool isSignablePointerType() const; + bool isSignableIntegerType(const ASTContext &Ctx) const; bool isAnyPointerType() const; // Any C pointer or ObjC object pointer bool isCountAttributedType() const; bool isBlockPointerType() const; @@ -8021,9 +8035,13 @@ inline bool Type::isAnyPointerType() const { return isPointerType() || isObjCObjectPointerType(); } -inline bool Type::isSignableType() const { return isSignablePointerType(); } +inline bool Type::isSignableType(const ASTContext &Ctx) const { + return isSignablePointerType() || isSignableIntegerType(Ctx); +} -inline bool Type::isSignablePointerType() const { return isPointerType(); } +inline bool Type::isSignablePointerType() const { + return isPointerType() || isObjCClassType() || isObjCQualifiedClassType(); +} inline bool Type::isBlockPointerType() const { return isa(CanonicalType); diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 368af0326c354..41defca695c61 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3326,10 +3326,12 @@ def ObjCRequiresPropertyDefs : InheritableAttr { } def PointerAuth : TypeAttr { - let Spellings = [CustomKeyword<"__ptrauth">]; + let Spellings = [CustomKeyword<"__ptrauth">, + CustomKeyword<"__ptrauth_restricted_intptr">]; let Args = [IntArgument<"Key">, BoolArgument<"AddressDiscriminated", 1>, - IntArgument<"ExtraDiscriminator", 1>]; + IntArgument<"ExtraDiscriminator", 1>, + StringArgument<"AuthenticationMode", 1>]; let Documentation = [PtrAuthDocs]; } diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 2cb587920300d..622f5b8206320 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1760,6 +1760,7 @@ Also see the documentation for `@available def PtrAuthDocs : Documentation { let Category = DocCatVariable; + let Heading = "__ptrauth, __ptrauth_restricted_intptr"; let Content = [{ The ``__ptrauth`` qualifier allows the programmer to directly control how pointers are signed when they are stored in a particular variable. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ade927e23f68d..033c2b66d5260 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -948,31 +948,55 @@ 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 " + "argument types are incomplete">; +def note_ptrauth_virtual_function_incomplete_arg_ret_type : + Note<"%0 is incomplete">; + // __ptrauth qualifier -def err_ptrauth_qualifier_return : Error< - "return types may not be qualified with __ptrauth; type is %0">; -def err_ptrauth_qualifier_param : Error< - "parameter types may not be qualified with __ptrauth; type is %0">; +def err_ptrauth_qualifier_invalid : Error< + "%select{return types|parameter types|properties}2 may not be qualified with %select{__ptrauth|__ptrauth_restricted_intptr}1; type is %0">; def err_ptrauth_qualifier_cast : Error< "cast types may not be qualified with __ptrauth; type is %0">; +def err_ptrauth_restricted_intptr_qualifier_pointer : Error< + "__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is %0">; def err_ptrauth_qualifier_nonpointer : Error< "__ptrauth qualifier may only be applied to pointer types; type here is %0">; def err_ptrauth_qualifier_redundant : Error< - "type %0 is already __ptrauth-qualified">; + "type %0 is already %1-qualified">; +def err_ptrauth_qualifier_signed_pointer_type : Error< + "signed pointer types may not be qualified with __ptrauth(ptrauth_key_none); type is %0">; def err_ptrauth_qualifier_bad_arg_count : Error< - "__ptrauth qualifier must take between 1 and 3 arguments">; -def err_ptrauth_qualifier_arg_not_ice : Error< + "%0 qualifier must take between 1 and 4 arguments">; +def err_ptrauth_arg_not_ice : Error< "argument to __ptrauth must be an integer constant expression">; -def err_ptrauth_qualifier_address_discrimination_invalid : Error< +def err_ptrauth_address_discrimination_invalid : Error< "address discrimination flag for __ptrauth must be 0 or 1; value is %0">; -def err_ptrauth_qualifier_extra_discriminator_invalid : Error< - "extra discriminator for __ptrauth must be between 0 and %1; value is %0">; - -def note_ptrauth_virtual_function_pointer_incomplete_arg_ret : - Note<"cannot take an address of a virtual member function if its return or " - "argument types are incomplete">; -def note_ptrauth_virtual_function_incomplete_arg_ret_type : - Note<"%0 is incomplete">; +def err_ptrauth_extra_discriminator_invalid : Error< + "extra discriminator for __ptrauth must be between " + "0 and %1; value is %0">; +def note_ptrauth_evaluating_options : Note< + "options parameter evaluated to '%0'">; + +def err_ptrauth_non_string_authentication_option : Error< + "__ptrauth options must be a string of comma separated flags, found '%0'">; +def err_ptrauth_unknown_authentication_option : Error< + "unknown __ptrauth authentication option '%0'">; +def err_ptrauth_repeated_authentication_option : Error< + "repeated __ptrauth authentication %select{mode|option}0">; +def note_ptrauth_previous_authentication_option : Note< + "previous __ptrauth authentication %select{mode|option}0">; +def err_ptrauth_empty_authentication_option : Error< + "empty __ptrauth authentication option">; +def err_ptrauth_invalid_option_character : Error< + "invalid character in __ptrauth options">; +def err_ptrauth_invalid_option_missing_comma : Error< + "missing comma between __ptrauth options" +>; +def err_ptrauth_invalid_authenticated_null_global : Error< + "%select{static locals|globals}0 with authenticated null values are currently unsupported" +>; /// main() @@ -8969,6 +8993,9 @@ def err_atomic_op_needs_non_const_atomic : Error< def err_atomic_op_needs_non_const_pointer : Error< "address argument to atomic operation must be a pointer to non-const " "type (%0 invalid)">; +def err_atomic_op_needs_non_address_discriminated_pointer : Error< + "address argument to %select{atomic|__sync}0 operation must be a pointer to a non address discriminated " + "type (%1 invalid)">; def err_atomic_op_needs_trivial_copy : Error< "address argument to atomic operation must be a pointer to a " "trivially-copyable type (%0 invalid)">; diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 05b6b2bef759f..a9d9e881bbde6 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -111,6 +111,10 @@ FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns) FEATURE(ptrauth_indirect_gotos, LangOpts.PointerAuthIndirectGotos) FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo)) FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics) +FEATURE(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics) +FEATURE(ptrauth_qualifier_authentication_mode, LangOpts.PointerAuthIntrinsics) +FEATURE(ptrauth_restricted_intptr_qualifier, LangOpts.PointerAuthIntrinsics) +FEATURE(ptrauth_objc_signable_class, true) FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls) FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns) FEATURE(ptrauth_vtable_pointer_address_discrimination, LangOpts.PointerAuthVTPtrAddressDiscrimination) diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 91f1c2f2e6239..72eddb243ea32 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -64,6 +64,17 @@ enum class PointerAuthenticationMode : unsigned { SignAndAuth }; +static constexpr llvm::StringLiteral PointerAuthenticationOptionStrip = "strip"; +static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndStrip = + "sign-and-strip"; +static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndAuth = + "sign-and-auth"; +static constexpr llvm::StringLiteral + PointerAuthenticationOptionAuthenticatesNullValues = + "authenticates-null-values"; +static constexpr llvm::StringLiteral PointerAuthenticationOptionIsaPointer = + "isa-pointer"; + /// Bitfields of LangOptions, split out from LangOptions in order to ensure that /// this large collection of bitfields is a trivial class type. class LangOptionsBase { diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 66c17e91e7a01..128971047fcb3 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -339,6 +339,7 @@ KEYWORD(__func__ , KEYALL) KEYWORD(__objc_yes , KEYALL) KEYWORD(__objc_no , KEYALL) KEYWORD(__ptrauth , KEYALL) +KEYWORD(__ptrauth_restricted_intptr , KEYALL) // C++ 2.11p1: Keywords. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 9d4a41fe83715..f5afa45e239c2 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3456,7 +3456,18 @@ class Sema final : public SemaBase { bool IsMemberSpecialization); void diagnosePointerAuthDisabled(SourceLocation loc, SourceRange range); - bool checkConstantPointerAuthKey(Expr *keyExpr, unsigned &key); + bool checkConstantPointerAuthKey(Expr *keyExpr, int &key); + + enum PointerAuthDiscArgKind { + // Address discrimination argument of __ptrauth. + PADAK_AddrDiscPtrAuth, + + // Extra discriminator argument of __ptrauth. + PADAK_ExtraDiscPtrAuth, + }; + + bool checkPointerAuthDiscriminatorArg(Expr *arg, PointerAuthDiscArgKind kind, + unsigned &intVal); /// Diagnose function specifiers on a declaration of an identifier that /// does not identify a function. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index c1b1a13c1339a..086998cf16bb7 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3407,6 +3407,63 @@ uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) { return llvm::getPointerAuthStableSipHash(Str); } +std::optional +ASTContext::tryTypeContainsAuthenticatedNull(QualType T) const { + if (!LangOpts.PointerAuthIntrinsics) + return false; + + auto Existing = ContainsAuthenticatedNullTypes.find(T); + if (Existing != ContainsAuthenticatedNullTypes.end()) + return Existing->second; + if (T->isPointerType() || T->isIntegerType()) { + auto Auth = T.getPointerAuth().withoutKeyNone(); + bool Result = Auth && Auth.authenticatesNullValues(); + ContainsAuthenticatedNullTypes[T] = Result; + return Result; + } + if (auto recordType = T->getAsRecordDecl()) { + if (!recordType->getDefinition() || recordType->isInvalidDecl()) + return std::nullopt; + for (auto field : recordType->fields()) { + auto Result = tryTypeContainsAuthenticatedNull(field->getType()); + if (Result && *Result) { + ContainsAuthenticatedNullTypes[T] = true; + return true; + } + if (!Result) + return std::nullopt; + } + } + + if (const auto *CXXRecord = T->getAsCXXRecordDecl()) { + for (const auto &Base : CXXRecord->bases()) { + auto Result = tryTypeContainsAuthenticatedNull(Base.getType()); + if (Result && *Result) { + ContainsAuthenticatedNullTypes[T] = true; + return true; + } + if (!Result) + return std::nullopt; + } + } + + if (auto *ArrayTy = dyn_cast_or_null(T.getTypePtr())) { + bool Result = typeContainsAuthenticatedNull(ArrayTy->getElementType()); + ContainsAuthenticatedNullTypes[T] = Result; + return Result; + } + ContainsAuthenticatedNullTypes[T] = false; + return false; +} + +bool ASTContext::typeContainsAuthenticatedNull(QualType T) const { + return *tryTypeContainsAuthenticatedNull(T); +} + +bool ASTContext::typeContainsAuthenticatedNull(const Type *Ty) const { + return typeContainsAuthenticatedNull(QualType(Ty, 0)); +} + QualType ASTContext::getObjCGCQualType(QualType T, Qualifiers::GC GCAttr) const { QualType CanT = getCanonicalType(T); @@ -11055,7 +11112,7 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, if (LQuals.getCVRQualifiers() != RQuals.getCVRQualifiers() || LQuals.getAddressSpace() != RQuals.getAddressSpace() || LQuals.getObjCLifetime() != RQuals.getObjCLifetime() || - LQuals.getPointerAuth() != RQuals.getPointerAuth() || + !LQuals.getPointerAuth().isEquivalent(RQuals.getPointerAuth()) || LQuals.hasUnaligned() != RQuals.hasUnaligned()) return {}; diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 6bca824207795..0e86c24981e6c 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1072,7 +1072,7 @@ void CXXRecordDecl::addedMember(Decl *D) { // If a class has an address-discriminated signed pointer member, it is a // non-POD type and its copy constructor, move constructor, copy assignment // operator, move assignment operator are non-trivial. - if (PointerAuthQualifier Q = T.getPointerAuth()) { + if (PointerAuthQualifier Q = T.getPointerAuth().withoutKeyNone()) { if (Q.isAddressDiscriminated()) { struct DefinitionData &Data = data(); Data.PlainOldData = false; diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 6af1b5683e08b..505387e2ddd20 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3431,7 +3431,7 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, } case ImplicitValueInitExprClass: case NoInitExprClass: - return true; + return !Ctx.typeContainsAuthenticatedNull(this->getType()); case ParenExprClass: return cast(this)->getSubExpr() ->isConstantInitializer(Ctx, IsForRef, Culprit); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index a0453c0639d90..49fcca253a277 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2705,6 +2705,10 @@ static bool isTriviallyCopyableTypeImpl(const QualType &type, if (type.hasNonTrivialObjCLifetime()) return false; + QualType::PrimitiveCopyKind PCK = type.isNonTrivialToPrimitiveCopy(); + if (PCK != QualType::PCK_Trivial && PCK != QualType::PCK_VolatileTrivial) + return false; + // C++11 [basic.types]p9 - See Core 2094 // Scalar types, trivially copyable class types, arrays of such types, and // cv-qualified versions of these types are collectively @@ -4899,6 +4903,12 @@ AttributedType::stripOuterNullability(QualType &T) { return std::nullopt; } +bool Type::isSignableIntegerType(const ASTContext &Ctx) const { + if (!isIntegralType(Ctx) || isEnumeralType()) + return false; + return Ctx.getTypeSize(this) == Ctx.getTypeSize(Ctx.VoidPtrTy); +} + bool Type::isBlockCompatibleObjCPointerType(ASTContext &ctx) const { const auto *objcPtr = getAs(); if (!objcPtr) diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 4a23e164ad11b..537cea0ccce26 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2440,7 +2440,7 @@ std::string PointerAuthQualifier::getAsString(const PrintingPolicy &P) const { SmallString<64> Buf; llvm::raw_svector_ostream StrOS(Buf); print(StrOS, P); - return std::string(StrOS.str()); + return StrOS.str().str(); } bool PointerAuthQualifier::isEmptyWhenPrinted(const PrintingPolicy &P) const { @@ -2450,9 +2450,54 @@ bool PointerAuthQualifier::isEmptyWhenPrinted(const PrintingPolicy &P) const { void PointerAuthQualifier::print(raw_ostream &OS, const PrintingPolicy &P) const { if (!isPresent()) return; - OS << "__ptrauth(" << getKey() << "," - << unsigned(isAddressDiscriminated()) << "," - << getExtraDiscriminator() << ")"; + SmallString<128> Buf; + llvm::raw_svector_ostream StrOS(Buf); + StrOS << ",\""; + bool hasOptions = false; + switch (getAuthenticationMode()) { + case PointerAuthenticationMode::None: + return; + case PointerAuthenticationMode::Strip: + StrOS << PointerAuthenticationOptionStrip; + hasOptions = true; + break; + case PointerAuthenticationMode::SignAndStrip: + StrOS << PointerAuthenticationOptionSignAndStrip; + hasOptions = true; + break; + default: + break; + } + if (isIsaPointer()) { + if (hasOptions) + StrOS << ","; + StrOS << PointerAuthenticationOptionIsaPointer; + hasOptions = true; + } + + if (authenticatesNullValues()) { + if (hasOptions) + StrOS << ","; + StrOS << PointerAuthenticationOptionAuthenticatesNullValues; + hasOptions = true; + } + + StringRef optionString; + if (hasOptions) { + StrOS << "\""; + optionString = StrOS.str(); + } + + if (isRestrictedIntegral()) + OS << "__ptrauth_restricted_intptr("; + else + OS << "__ptrauth("; + if (getKey() == KeyNoneInternal) + OS << "ptrauth_key_none"; + else + OS << getKey(); + OS << "," << unsigned(isAddressDiscriminated()) << "," + << getExtraDiscriminator() << optionString << ")"; } std::string Qualifiers::getAsString() const { diff --git a/clang/lib/CodeGen/CGBlocks.h b/clang/lib/CodeGen/CGBlocks.h index de1ad99a18cfa..b2e850046ab9a 100644 --- a/clang/lib/CodeGen/CGBlocks.h +++ b/clang/lib/CodeGen/CGBlocks.h @@ -145,8 +145,8 @@ class BlockByrefInfo { /// entity that's captured by a block. enum class BlockCaptureEntityKind { None, - AddressDiscriminatedPointerAuth, CXXRecord, // Copy or destroy + AddressDiscriminatedPointerAuth, ARCWeak, ARCStrong, NonTrivialCStruct, diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 5992aacb8777c..7714a3c5969b4 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4739,7 +4739,7 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, if (HasAggregateEvalKind && isa(E) && cast(E)->getCastKind() == CK_LValueToRValue && - !type.isNonTrivialToPrimitiveCopy() && !type->isArrayParameterType()) { + !type->isArrayParameterType() && !type.isNonTrivialToPrimitiveCopy()) { LValue L = EmitLValue(cast(E)->getSubExpr()); assert(L.isSimple()); args.addUncopiedAggregate(L, type); diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index c142ccdd798fa..af1c87350046d 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -929,7 +929,8 @@ namespace { Qualifiers Qual = F->getType().getQualifiers(); if (Qual.hasVolatile() || Qual.hasObjCLifetime()) return false; - if (PointerAuthQualifier Q = F->getType().getPointerAuth()) + if (PointerAuthQualifier Q = + F->getType().getPointerAuth().withoutKeyNone()) if (Q.isAddressDiscriminated()) return false; return true; diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index d4ca9d47021ca..2c165b7730946 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1020,25 +1020,27 @@ llvm::DIType *CGDebugInfo::CreateQualifiedType(QualType Ty, // We will create one Derived type for one qualifier and recurse to handle any // additional ones. llvm::dwarf::Tag Tag = getNextQualifier(Qc); - if (!Tag && Qc.getPointerAuth().isPresent()) { - unsigned Key = Qc.getPointerAuth().getKey(); - bool IsDiscr = Qc.getPointerAuth().isAddressDiscriminated(); - unsigned ExtraDiscr = Qc.getPointerAuth().getExtraDiscriminator(); - bool IsaPointer = Qc.getPointerAuth().isIsaPointer(); - bool AuthenticatesNullValues = - Qc.getPointerAuth().authenticatesNullValues(); - unsigned AuthenticationMode = - (unsigned)Qc.getPointerAuth().getAuthenticationMode(); - Qc.removePointerAuth(); - assert(Qc.empty() && "Unknown type qualifier for debug info"); - auto *FromTy = getOrCreateType(QualType(T, 0), Unit); - return DBuilder.createPtrAuthQualifiedType( - FromTy, Key, IsDiscr, ExtraDiscr, /*IsaPointer=*/IsaPointer, - /*AuthenticatesNullValues=*/AuthenticatesNullValues, - /*AuthenticationMode=*/AuthenticationMode); - } else if (!Tag) { - assert(Qc.empty() && "Unknown type qualifier for debug info"); - return getOrCreateType(QualType(T, 0), Unit); + if (!Tag) { + if (Qc.getPointerAuth().withoutKeyNone()) { + unsigned Key = Qc.getPointerAuth().getKey(); + bool IsDiscr = Qc.getPointerAuth().isAddressDiscriminated(); + unsigned ExtraDiscr = Qc.getPointerAuth().getExtraDiscriminator(); + bool IsaPointer = Qc.getPointerAuth().isIsaPointer(); + bool AuthenticatesNullValues = + Qc.getPointerAuth().authenticatesNullValues(); + unsigned AuthenticationMode = + (unsigned)Qc.getPointerAuth().getAuthenticationMode(); + Qc.removePointerAuth(); + assert(Qc.empty() && "Unknown type qualifier for debug info"); + auto *FromTy = getOrCreateType(QualType(T, 0), Unit); + return DBuilder.createPtrAuthQualifiedType( + FromTy, Key, IsDiscr, ExtraDiscr, /*IsaPointer=*/IsaPointer, + /*AuthenticatesNullValues=*/AuthenticatesNullValues, + /*AuthenticationMode=*/AuthenticationMode); + } else { + assert(Qc.empty() && "Unknown type qualifier for debug info"); + return getOrCreateType(QualType(T, 0), Unit); + } } auto *FromTy = getOrCreateType(Qc.apply(CGM.getContext(), T), Unit); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 38975b33b6530..b915632a1ded2 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4692,7 +4692,8 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { if (IsBaseCXXThis) SkippedChecks.set(SanitizerKind::Alignment, true); if (IsBaseCXXThis || isa(BaseExpr) || - isa(Addr.emitRawPointer(*this))) + llvm::isa_and_nonnull( + Addr.getPointerIfNotSigned())) SkippedChecks.set(SanitizerKind::Null, true); EmitTypeCheck(TCK_MemberAccess, E->getExprLoc(), Addr, PtrTy, /*Alignment=*/CharUnits::Zero(), SkippedChecks); diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 14fee2eee8250..d78cc753b5c96 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -1612,6 +1612,13 @@ void AggExprEmitter::EmitNullInitializationToLValue(LValue lv) { CGF.EmitStoreThroughBitfieldLValue(RValue::get(null), lv); } else { assert(lv.isSimple()); + if (auto auth = lv.getType().getPointerAuth()) { + if (auth.authenticatesNullValues()) { + auto authInfo = CGF.EmitPointerAuthInfo(auth, lv.getAddress()); + null = CGF.EmitPointerAuthSign(authInfo, null); + } + } + CGF.EmitStoreOfScalar(null, lv, /* isInitialization */ true); } } else { diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index db4a70f5316f8..8d1f5eb07c13c 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -511,6 +511,7 @@ static void EmitNullBaseClassInitialization(CodeGenFunction &CGF, if (Base->isEmpty()) return; + auto originalPtr = DestPtr; DestPtr = DestPtr.withElementType(CGF.Int8Ty); const ASTRecordLayout &Layout = CGF.getContext().getASTRecordLayout(Base); @@ -590,6 +591,9 @@ static void EmitNullBaseClassInitialization(CodeGenFunction &CGF, CGF.Builder.getInt8(0), StoreSizeVal); } } + auto type = QualType(Base->getTypeForDecl(), 0); + if (CGF.getContext().typeContainsAuthenticatedNull(type)) + CGF.EmitNullInitializersForAuthenticatedNullFields(originalPtr, type); } void diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 153d28f341bf6..c270a2a32ffe0 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1922,6 +1922,11 @@ llvm::Constant *ConstantEmitter::tryEmitPrivate(const Expr *E, QualType destType) { assert(!destType->isVoidType() && "can't emit a void constant"); + // We don't yet support constant initializers for values with authenticated + // null values. + if (CGM.getContext().typeContainsAuthenticatedNull(destType)) + return nullptr; + if (!destType->isReferenceType()) if (llvm::Constant *C = ConstExprEmitter(*this).Visit(E, destType)) return C; @@ -2029,6 +2034,12 @@ class ConstantLValueEmitter : public ConstStmtVisitorgetPointeeType()); // Don't emit a signed pointer if the destination is a function pointer // type. - if (DestType->isSignableType() && !DestType->isFunctionPointerType()) + if (DestType->isSignableType(CGM.getContext()) && + !DestType->isFunctionPointerType()) AuthInfo = CGM.getPointerAuthInfoForType(DestType); } @@ -2140,7 +2155,7 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { C = CGM.getConstantSignedPointer( C, AuthInfo.getKey(), nullptr, cast_or_null(AuthInfo.getDiscriminator())); - return ConstantLValue(C, /*applied offset*/ true); + return ConstantLValue(C, /*applied offset*/ true, /*signed*/ true); } return ConstantLValue(C); @@ -2153,11 +2168,12 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { // We can never refer to a variable with local storage. if (!VD->hasLocalStorage()) { if (VD->isFileVarDecl() || VD->hasExternalStorage()) - return CGM.GetAddrOfGlobalVar(VD); + return PtrAuthSign(CGM.GetAddrOfGlobalVar(VD), false); if (VD->isLocalVarDecl()) { - return CGM.getOrCreateStaticVarDecl( + llvm::Constant *C = CGM.getOrCreateStaticVarDecl( *VD, CGM.getLLVMLinkageVarDefinition(VD)); + return PtrAuthSign(C, false); } } } @@ -2390,6 +2406,9 @@ llvm::Constant *ConstantEmitter::tryEmitPrivate(const APValue &Value, case APValue::LValue: return ConstantLValueEmitter(*this, Value, DestType).tryEmit(); case APValue::Int: + if (DestType.getPointerAuth().withoutKeyNone() && Value.getInt() != 0) { + return nullptr; + } return llvm::ConstantInt::get(CGM.getLLVMContext(), Value.getInt()); case APValue::FixedPoint: return llvm::ConstantInt::get(CGM.getLLVMContext(), diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 5c950d15106d6..da1f2d4e970dc 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -2222,7 +2222,7 @@ static bool isLValueKnownNonNull(CodeGenFunction &CGF, const Expr *E) { } bool CodeGenFunction::isPointerKnownNonNull(const Expr *E) { - assert(E->getType()->isPointerType()); + assert(E->getType()->isSignableType(getContext())); E = E->IgnoreParens(); diff --git a/clang/lib/CodeGen/CGNonTrivialStruct.cpp b/clang/lib/CodeGen/CGNonTrivialStruct.cpp index 49c2ce3e8261c..63e2d44a792e7 100644 --- a/clang/lib/CodeGen/CGNonTrivialStruct.cpp +++ b/clang/lib/CodeGen/CGNonTrivialStruct.cpp @@ -272,6 +272,8 @@ struct GenBinaryFuncName : CopyStructVisitor, IsMove>, PointerAuthQualifier PtrAuth = FT.getPointerAuth(); this->appendStr(llvm::to_string(PtrAuth.getKey()) + "_"); this->appendStr(llvm::to_string(PtrAuth.getExtraDiscriminator()) + "_"); + if (PtrAuth.authenticatesNullValues()) + this->appendStr("anv_"); CharUnits FieldOffset = CurStructOffset + this->getFieldOffset(FD); this->appendStr(llvm::to_string(FieldOffset.getQuantity())); } diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index 88efa8b92c903..177bbce2f6bd5 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -1191,8 +1191,16 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, // Perform an atomic load. This does not impose ordering constraints. Address ivarAddr = LV.getAddress(); ivarAddr = ivarAddr.withElementType(bitcastType); - llvm::LoadInst *load = Builder.CreateLoad(ivarAddr, "load"); - load->setAtomic(llvm::AtomicOrdering::Unordered); + llvm::LoadInst *loadInst = Builder.CreateLoad(ivarAddr, "load"); + loadInst->setAtomic(llvm::AtomicOrdering::Unordered); + llvm::Value *load = loadInst; + if (auto qualifier = ivar->getType().getPointerAuth()) { + CGPointerAuthInfo srcInfo = EmitPointerAuthInfo(qualifier, ivarAddr); + CGPointerAuthInfo targetInfo = + CGM.getPointerAuthInfoForType(getterMethod->getReturnType()); + load = EmitPointerAuthResign(load, ivar->getType(), srcInfo, targetInfo, + /* isKnownNonNull */ false); + } // Store that value into the return address. Doing this with a // bitcast is likely to produce some pretty ugly IR, but it's not @@ -1214,6 +1222,14 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, case PropertyImplStrategy::GetSetProperty: { llvm::FunctionCallee getPropertyFn = CGM.getObjCRuntime().GetPropertyGetFunction(); + if (ivar->getType().getPointerAuth()) { + // This currently cannot be hit, but if we ever allow objc pointers + // to be signed, this will become possible. Reaching here would require + // a copy, weak, etc property backed by an authenticated pointer. + CGM.ErrorUnsupported(propImpl, + "Obj-C getter requiring pointer authentication"); + return; + } if (!getPropertyFn) { CGM.ErrorUnsupported(propImpl, "Obj-C getter requiring atomic copy"); return; @@ -1269,7 +1285,9 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, LValue LV = EmitLValueForIvar(TypeOfSelfObject(), LoadObjCSelf(), ivar, 0); QualType ivarType = ivar->getType(); - switch (getEvaluationKind(ivarType)) { + auto evaluationKind = getEvaluationKind(ivarType); + assert(!ivarType.getPointerAuth() || evaluationKind == TEK_Scalar); + switch (evaluationKind) { case TEK_Complex: { ComplexPairTy pair = EmitLoadOfComplex(LV, SourceLocation()); EmitStoreOfComplex(pair, MakeAddrLValue(ReturnValue, ivarType), @@ -1287,6 +1305,11 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, case TEK_Scalar: { llvm::Value *value; if (propType->isReferenceType()) { + if (ivarType.getPointerAuth()) { + CGM.ErrorUnsupported(propImpl, + "Obj-C getter for authenticated reference type"); + return; + } value = LV.getAddress().emitRawPointer(*this); } else { // We want to load and autoreleaseReturnValue ARC __weak ivars. @@ -1300,11 +1323,23 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, // Otherwise we want to do a simple load, suppressing the // final autorelease. } else { - value = EmitLoadOfLValue(LV, SourceLocation()).getScalarVal(); + if (auto qualifier = ivar->getType().getPointerAuth()) { + Address ivarAddr = LV.getAddress(); + llvm::LoadInst *loadInst = Builder.CreateLoad(ivarAddr, "load"); + llvm::Value *load = loadInst; + auto srcInfo = EmitPointerAuthInfo(qualifier, ivarAddr); + auto targetInfo = + CGM.getPointerAuthInfoForType(getterMethod->getReturnType()); + load = EmitPointerAuthResign(load, ivarType, srcInfo, targetInfo, + /* isKnownNonNull */ false); + value = load; + } else { + value = EmitLoadOfLValue(LV, SourceLocation()).getScalarVal(); + } AutoreleaseResult = false; } - value = Builder.CreateBitCast( + value = Builder.CreateBitOrPointerCast( value, ConvertType(GetterMethodDecl->getReturnType())); } @@ -1453,6 +1488,7 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, return; } + QualType propertyType = propImpl->getPropertyDecl()->getType(); // Just use the setter expression if Sema gave us one and it's // non-trivial. if (!hasTrivialSetExpr(propImpl)) { @@ -1490,6 +1526,13 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, llvm::Value *load = Builder.CreateLoad(argAddr); + if (auto qualifier = ivar->getType().getPointerAuth()) { + CGPointerAuthInfo srcInfo = CGM.getPointerAuthInfoForType(propertyType); + CGPointerAuthInfo targetInfo = EmitPointerAuthInfo(qualifier, ivarAddr); + load = EmitPointerAuthResign(load, ivar->getType(), srcInfo, targetInfo, + /* isKnownNonNull */ false); + } + // Perform an atomic store. There are no memory ordering requirements. llvm::StoreInst *store = Builder.CreateStore(load, ivarAddr); store->setAtomic(llvm::AtomicOrdering::Unordered); @@ -1498,7 +1541,13 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, case PropertyImplStrategy::GetSetProperty: case PropertyImplStrategy::SetPropertyAndExpressionGet: { - + if (ivar->getType().getPointerAuth()) { + // As with the getter case above this cannot currently be hit, but we + // include it to prevent us from ever producing incorrect code. + CGM.ErrorUnsupported(propImpl, + "Obj-C setter requiring pointer authentication"); + return; + } llvm::FunctionCallee setOptimizedPropertyFn = nullptr; llvm::FunctionCallee setPropertyFn = nullptr; if (UseOptimizedSetter(CGM)) { diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index acddd03176223..d6ff3265ecd4e 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -109,6 +109,8 @@ CodeGenFunction::EmitPointerAuthInfo(PointerAuthQualifier qualifier, Address storageAddress) { assert(qualifier && "don't call this if you don't know that the qualifier is present"); + if (qualifier.hasKeyNone()) + return CGPointerAuthInfo(); llvm::Value *discriminator = nullptr; if (unsigned extra = qualifier.getExtraDiscriminator()) { @@ -156,7 +158,10 @@ CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForPointeeType(QualType T) { /// pointer type. static CGPointerAuthInfo getPointerAuthInfoForType(CodeGenModule &CGM, QualType PointerType) { - assert(PointerType->isSignableType()); + assert(PointerType->isSignableType(CGM.getContext())); + + if (PointerType.getPointerAuth().hasKeyNone()) + return CGPointerAuthInfo(); // Block pointers are currently not signed. if (PointerType->isBlockPointerType()) @@ -174,127 +179,6 @@ CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForType(QualType T) { return ::getPointerAuthInfoForType(*this, T); } -static std::pair -emitLoadOfOrigPointerRValue(CodeGenFunction &CGF, const LValue &lv, - SourceLocation loc) { - auto value = CGF.EmitLoadOfScalar(lv, loc); - CGPointerAuthInfo authInfo; - if (auto ptrauth = lv.getQuals().getPointerAuth()) { - authInfo = CGF.EmitPointerAuthInfo(ptrauth, lv.getAddress()); - } else { - authInfo = getPointerAuthInfoForType(CGF.CGM, lv.getType()); - } - return { value, authInfo }; -} - -std::pair -CodeGenFunction::EmitOrigPointerRValue(const Expr *E) { - assert(E->getType()->isPointerType()); - - E = E->IgnoreParens(); - if (auto load = dyn_cast(E)) { - if (load->getCastKind() == CK_LValueToRValue) { - E = load->getSubExpr()->IgnoreParens(); - - // We're semantically required to not emit loads of certain DREs naively. - if (auto refExpr = dyn_cast(const_cast(E))) { - if (auto result = tryEmitAsConstant(refExpr)) { - // Fold away a use of an intermediate variable. - if (!result.isReference()) - return { result.getValue(), - getPointerAuthInfoForType(CGM, refExpr->getType()) }; - - // Fold away a use of an intermediate reference. - auto lv = result.getReferenceLValue(*this, refExpr); - return emitLoadOfOrigPointerRValue(*this, lv, refExpr->getLocation()); - } - } - - // Otherwise, load and use the pointer - auto lv = EmitCheckedLValue(E, CodeGenFunction::TCK_Load); - return emitLoadOfOrigPointerRValue(*this, lv, E->getExprLoc()); - } - } - - // Emit direct references to functions without authentication. - if (auto DRE = dyn_cast(E)) { - if (auto FD = dyn_cast(DRE->getDecl())) { - return { CGM.getRawFunctionPointer(FD), CGPointerAuthInfo() }; - } - } else if (auto ME = dyn_cast(E)) { - if (auto FD = dyn_cast(ME->getMemberDecl())) { - EmitIgnoredExpr(ME->getBase()); - return { CGM.getRawFunctionPointer(FD), CGPointerAuthInfo() }; - } - } - - // Fallback: just use the normal rules for the type. - auto value = EmitScalarExpr(E); - return { value, getPointerAuthInfoForType(CGM, E->getType()) }; -} - -llvm::Value * -CodeGenFunction::EmitPointerAuthQualify(PointerAuthQualifier destQualifier, - const Expr *E, - Address destStorageAddress) { - assert(destQualifier); - - auto src = EmitOrigPointerRValue(E); - auto value = src.first; - auto curAuthInfo = src.second; - - auto destAuthInfo = EmitPointerAuthInfo(destQualifier, destStorageAddress); - return EmitPointerAuthResign(value, E->getType(), curAuthInfo, destAuthInfo, - isPointerKnownNonNull(E)); -} - -llvm::Value * -CodeGenFunction::EmitPointerAuthQualify(PointerAuthQualifier destQualifier, - llvm::Value *value, - QualType pointerType, - Address destStorageAddress, - bool isKnownNonNull) { - assert(destQualifier); - - auto curAuthInfo = getPointerAuthInfoForType(CGM, pointerType); - auto destAuthInfo = EmitPointerAuthInfo(destQualifier, destStorageAddress); - return EmitPointerAuthResign(value, pointerType, curAuthInfo, destAuthInfo, - isKnownNonNull); -} - -llvm::Value * -CodeGenFunction::EmitPointerAuthUnqualify(PointerAuthQualifier curQualifier, - llvm::Value *value, - QualType pointerType, - Address curStorageAddress, - bool isKnownNonNull) { - assert(curQualifier); - - auto curAuthInfo = EmitPointerAuthInfo(curQualifier, curStorageAddress); - auto destAuthInfo = getPointerAuthInfoForType(CGM, pointerType); - return EmitPointerAuthResign(value, pointerType, curAuthInfo, destAuthInfo, - isKnownNonNull); -} - -void CodeGenFunction::EmitPointerAuthCopy(PointerAuthQualifier qualifier, - QualType type, - Address destAddress, - Address srcAddress) { - assert(qualifier); - - llvm::Value *value = Builder.CreateLoad(srcAddress); - - // If we're using address-discrimination, we have to re-sign the value. - if (qualifier.isAddressDiscriminated()) { - auto srcPtrAuth = EmitPointerAuthInfo(qualifier, srcAddress); - auto destPtrAuth = EmitPointerAuthInfo(qualifier, destAddress); - value = EmitPointerAuthResign(value, type, srcPtrAuth, destPtrAuth, - /*is known nonnull*/ false); - } - - Builder.CreateStore(value, destAddress); -} - /// We use an abstract, side-allocated cache for signed function pointers /// because (1) most compiler invocations will not need this cache at all, /// since they don't use signed function pointers, and (2) the @@ -454,6 +338,108 @@ Address CodeGenFunction::mergeAddressesInConditionalExpr( return LHS; } +static std::pair +emitLoadOfOrigPointerRValue(CodeGenFunction &CGF, const LValue &lv, + SourceLocation loc) { + auto value = CGF.EmitLoadOfScalar(lv, loc); + CGPointerAuthInfo authInfo; + if (auto ptrauth = lv.getQuals().getPointerAuth()) { + authInfo = CGF.EmitPointerAuthInfo(ptrauth, lv.getAddress()); + } else { + authInfo = getPointerAuthInfoForType(CGF.CGM, lv.getType()); + } + return { value, authInfo }; +} + +std::pair +CodeGenFunction::EmitOrigPointerRValue(const Expr *E) { + assert(E->getType()->isSignableType(getContext())); + + E = E->IgnoreParens(); + if (auto load = dyn_cast(E)) { + if (load->getCastKind() == CK_LValueToRValue) { + E = load->getSubExpr()->IgnoreParens(); + + // We're semantically required to not emit loads of certain DREs naively. + if (auto refExpr = dyn_cast(const_cast(E))) { + if (auto result = tryEmitAsConstant(refExpr)) { + // Fold away a use of an intermediate variable. + if (!result.isReference()) + return { result.getValue(), + getPointerAuthInfoForType(CGM, refExpr->getType()) }; + + // Fold away a use of an intermediate reference. + auto lv = result.getReferenceLValue(*this, refExpr); + return emitLoadOfOrigPointerRValue(*this, lv, refExpr->getLocation()); + } + } + + // Otherwise, load and use the pointer + auto lv = EmitCheckedLValue(E, CodeGenFunction::TCK_Load); + return emitLoadOfOrigPointerRValue(*this, lv, E->getExprLoc()); + } + } + + // Emit direct references to functions without authentication. + if (auto DRE = dyn_cast(E)) { + if (auto FD = dyn_cast(DRE->getDecl())) { + return { CGM.getRawFunctionPointer(FD), CGPointerAuthInfo() }; + } + } else if (auto ME = dyn_cast(E)) { + if (auto FD = dyn_cast(ME->getMemberDecl())) { + EmitIgnoredExpr(ME->getBase()); + return { CGM.getRawFunctionPointer(FD), CGPointerAuthInfo() }; + } + } + + // Fallback: just use the normal rules for the type. + auto value = EmitScalarExpr(E); + return { value, getPointerAuthInfoForType(CGM, E->getType()) }; +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthQualify(PointerAuthQualifier destQualifier, + const Expr *E, + Address destStorageAddress) { + assert(destQualifier); + + auto src = EmitOrigPointerRValue(E); + auto value = src.first; + auto curAuthInfo = src.second; + + auto destAuthInfo = EmitPointerAuthInfo(destQualifier, destStorageAddress); + return EmitPointerAuthResign(value, E->getType(), curAuthInfo, destAuthInfo, + isPointerKnownNonNull(E)); +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthQualify(PointerAuthQualifier destQualifier, + llvm::Value *value, + QualType pointerType, + Address destStorageAddress, + bool isKnownNonNull) { + assert(destQualifier); + + auto curAuthInfo = getPointerAuthInfoForType(CGM, pointerType); + auto destAuthInfo = EmitPointerAuthInfo(destQualifier, destStorageAddress); + return EmitPointerAuthResign(value, pointerType, curAuthInfo, destAuthInfo, + isKnownNonNull); +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthUnqualify(PointerAuthQualifier curQualifier, + llvm::Value *value, + QualType pointerType, + Address curStorageAddress, + bool isKnownNonNull) { + assert(curQualifier); + + auto curAuthInfo = EmitPointerAuthInfo(curQualifier, curStorageAddress); + auto destAuthInfo = getPointerAuthInfoForType(CGM, pointerType); + return EmitPointerAuthResign(value, pointerType, curAuthInfo, destAuthInfo, + isKnownNonNull); +} + static bool isZeroConstant(llvm::Value *value) { if (auto ci = dyn_cast(value)) return ci->isZero(); @@ -466,8 +452,10 @@ static bool equalAuthPolicies(const CGPointerAuthInfo &left, return false; assert(left.isSigned() && right.isSigned() && "should only be called with non-null auth policies"); - return left.getKey() == right.getKey() && - left.getAuthenticationMode() == right.getAuthenticationMode(); + return left.authenticatesNullValues() == right.authenticatesNullValues() && + left.getKey() == right.getKey() && + left.getAuthenticationMode() == right.getAuthenticationMode() && + left.isIsaPointer() == right.isIsaPointer(); } llvm::Value *CodeGenFunction::EmitPointerAuthResign( @@ -485,7 +473,8 @@ llvm::Value *CodeGenFunction::EmitPointerAuthResign( assert(value->getType()->isIntegerTy()); null = llvm::ConstantInt::get(IntPtrTy, 0); } - if (value == null) { + if (value == null && + (newAuthInfo && !newAuthInfo.authenticatesNullValues())) { return value; } @@ -504,14 +493,48 @@ llvm::Value *CodeGenFunction::EmitPointerAuthResign( llvm::BasicBlock *initBB = Builder.GetInsertBlock(); llvm::BasicBlock *resignBB = nullptr, *contBB = nullptr; - // Null pointers have to be mapped to null, and the ptrauth_resign - // intrinsic doesn't do that. - if (!isKnownNonNull && !llvm::isKnownNonZero(value, CGM.getDataLayout())) { + // Null pointers have to be mapped to null unless explicitly opted out + bool maybeNull = + !isKnownNonNull && !llvm::isKnownNonZero(value, CGM.getDataLayout()); + bool curAuthenticatesNull = + curAuthInfo && curAuthInfo.authenticatesNullValues(); + bool newAuthenticatesNull = + newAuthInfo && newAuthInfo.authenticatesNullValues(); + bool authRequiresNullCheck = false; + if (curAuthInfo) { + if (newAuthInfo) { + authRequiresNullCheck = !curAuthenticatesNull || !newAuthenticatesNull; + } else + authRequiresNullCheck = !curAuthenticatesNull; + } else { + authRequiresNullCheck = !newAuthenticatesNull; + } + + llvm::BasicBlock *nullValueSource = initBB; + if (maybeNull && authRequiresNullCheck) { contBB = createBasicBlock("resign.cont"); resignBB = createBasicBlock("resign.nonnull"); + auto toTest = value; + if (curAuthenticatesNull) { + assert((!newAuthInfo || curAuthenticatesNull != newAuthenticatesNull) && + "This path should only happen when changing null signing mode"); - auto isNonNull = Builder.CreateICmpNE(value, null); - Builder.CreateCondBr(isNonNull, resignBB, contBB); + toTest = EmitPointerAuthAuth(curAuthInfo, value); + } + auto isNonNull = Builder.CreateICmpNE(toTest, null); + llvm::BasicBlock *signedNullBlock = nullptr; + llvm::BasicBlock *trueContinueBlock = contBB; + if (newAuthenticatesNull) { + signedNullBlock = createBasicBlock("resign.null"); + trueContinueBlock = signedNullBlock; + } + Builder.CreateCondBr(isNonNull, resignBB, trueContinueBlock); + if (signedNullBlock) { + EmitBlock(signedNullBlock); + null = EmitPointerAuthSign(newAuthInfo, null); + nullValueSource = signedNullBlock; + Builder.CreateBr(contBB); + } EmitBlock(resignBB); } @@ -528,7 +551,7 @@ llvm::Value *CodeGenFunction::EmitPointerAuthResign( if (contBB) { EmitBlock(contBB); auto phi = Builder.CreatePHI(value->getType(), 2); - phi->addIncoming(null, initBB); + phi->addIncoming(null, nullValueSource); phi->addIncoming(value, resignBB); value = phi; } @@ -536,6 +559,25 @@ llvm::Value *CodeGenFunction::EmitPointerAuthResign( return value; } +void CodeGenFunction::EmitPointerAuthCopy(PointerAuthQualifier qualifier, + QualType type, + Address destAddress, + Address srcAddress) { + assert(qualifier); + llvm::Value *value = Builder.CreateLoad(srcAddress); + + // If we're using address-discrimination, we have to re-sign the value. + if (qualifier.isAddressDiscriminated()) { + auto srcPtrAuth = EmitPointerAuthInfo(qualifier, srcAddress); + auto destPtrAuth = EmitPointerAuthInfo(qualifier, destAddress); + value = EmitPointerAuthResign(value, type, srcPtrAuth, destPtrAuth, + /*is known nonnull*/ false); + } + + Builder.CreateStore(value, destAddress); +} + + llvm::Constant * CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, llvm::Constant *StorageAddress, @@ -618,6 +660,8 @@ llvm::Constant *CodeGenModule::getConstantSignedPointer( llvm::Constant *StorageAddress, GlobalDecl SchemaDecl, QualType SchemaType) { assert(shouldSignPointer(Schema)); + // We ignore schema.isIsaPointer() for global decls as + // the assumption is that we won't emit tagged isas during codegen llvm::ConstantInt *OtherDiscriminator = getPointerAuthOtherDiscriminator(Schema, SchemaDecl, SchemaType); @@ -803,7 +847,8 @@ CodeGenModule::computeVTPointerAuthentication(const CXXRecordDecl *ThisClass) { return PointerAuthQualifier::Create(Key, AddressDiscriminated, Discriminator, PointerAuthenticationMode::SignAndAuth, /* IsIsaPointer */ false, - /* AuthenticatesNullValues */ false); + /* AuthenticatesNullValues */ false, + /* IsRestrictedIntegral */ false); } std::optional @@ -854,10 +899,10 @@ llvm::Value *CodeGenFunction::AuthPointerToPointerCast(llvm::Value *ResultPtr, QualType SourceType, QualType DestType) { CGPointerAuthInfo CurAuthInfo, NewAuthInfo; - if (SourceType->isSignableType()) + if (SourceType->isSignableType(getContext())) CurAuthInfo = getPointerAuthInfoForType(CGM, SourceType); - if (DestType->isSignableType()) + if (DestType->isSignableType(getContext())) NewAuthInfo = getPointerAuthInfoForType(CGM, DestType); if (!CurAuthInfo && !NewAuthInfo) @@ -879,10 +924,10 @@ Address CodeGenFunction::AuthPointerToPointerCast(Address Ptr, QualType SourceType, QualType DestType) { CGPointerAuthInfo CurAuthInfo, NewAuthInfo; - if (SourceType->isSignableType()) + if (SourceType->isSignableType(getContext())) CurAuthInfo = getPointerAuthInfoForType(CGM, SourceType); - if (DestType->isSignableType()) + if (DestType->isSignableType(getContext())) NewAuthInfo = getPointerAuthInfoForType(CGM, DestType); if (!CurAuthInfo && !NewAuthInfo) diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 951542da53dbc..5a33b258373b4 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -2110,6 +2110,87 @@ static void emitNonZeroVLAInit(CodeGenFunction &CGF, QualType baseType, CGF.EmitBlock(contBB); } +void CodeGenFunction::EmitNullInitializersForAuthenticatedNullFields( + Address storageAddress, QualType Ty) { + assert(getContext().typeContainsAuthenticatedNull(Ty)); + if (auto arrayType = getContext().getAsArrayType(Ty)) { + auto elementType = arrayType->getElementType(); + auto llvmArrayType = cast(storageAddress.getElementType()); + CharUnits elementSize = getContext().getTypeSizeInChars(elementType); + CharUnits elementAlign = + storageAddress.getAlignment().alignmentOfArrayElement(elementSize); + llvm::Value *zero = llvm::ConstantInt::get(SizeTy, 0); + llvm::Value *indices[] = {zero, zero}; + llvm::Value *one = llvm::ConstantInt::get(SizeTy, 1); + llvm::Value *count = + llvm::ConstantInt::get(SizeTy, llvmArrayType->getNumElements()); + llvm::Value *ptr = storageAddress.emitRawPointer(*this); + llvm::Value *element = Builder.CreateInBoundsGEP( + llvmArrayType, ptr, indices, "array_authenticated_null_init.start"); + llvm::Value *end = Builder.CreateInBoundsGEP( + llvmArrayType->getElementType(), element, + count, "array_authenticated_null_init.end"); + + llvm::BasicBlock *entryBB = Builder.GetInsertBlock(); + llvm::BasicBlock *bodyBB = + createBasicBlock("array_authenticated_null_init.body"); + EmitBlock(bodyBB); + llvm::PHINode *currentElement = Builder.CreatePHI( + element->getType(), 2, "array_authenticated_null_init.cur"); + currentElement->addIncoming(element, entryBB); + auto elementAddress = + Address(currentElement, llvmArrayType->getElementType(), elementAlign); + EmitNullInitializersForAuthenticatedNullFields(elementAddress, elementType); + llvm::Value *nextElement = Builder.CreateInBoundsGEP( + llvmArrayType->getElementType(), currentElement, one, + "array_authenticated_null_init.next"); + llvm::Value *done = Builder.CreateICmpEQ( + nextElement, end, "array_authenticated_null_init.done"); + llvm::BasicBlock *endBB = + createBasicBlock("array_authenticated_null_init.end"); + Builder.CreateCondBr(done, endBB, bodyBB); + currentElement->addIncoming(nextElement, Builder.GetInsertBlock()); + + EmitBlock(endBB); + return; + } + auto record = Ty->getAs(); + if (!record) { + assert(Ty.getPointerAuth().authenticatesNullValues() && + "Incorrectly selected non-null-signed field"); + assert((Ty->isPointerType() || Ty->isIntegerType()) && + "Invalid type for ptrauth"); + auto info = EmitPointerAuthInfo(Ty.getPointerAuth(), storageAddress); + llvm::Constant *NullConstant = CGM.EmitNullConstant(Ty); + auto signedValue = EmitPointerAuthSign(info, NullConstant); + EmitStoreOfScalar(signedValue, storageAddress, false, Ty); + return; + } + auto &layout = CGM.getTypes().getCGRecordLayout(record->getDecl()); + if (auto cxxRecord = record->getAsCXXRecordDecl()) { + for (const auto &base : cxxRecord->bases()) { + auto baseType = base.getType(); + if (!getContext().typeContainsAuthenticatedNull(baseType)) + continue; + const CXXRecordDecl *baseRecord = + cast(baseType->castAs()->getDecl()); + auto baseIndex = base.isVirtual() + ? layout.getVirtualBaseIndex(baseRecord) + : layout.getNonVirtualBaseLLVMFieldNo(baseRecord); + auto baseAddress = Builder.CreateStructGEP(storageAddress, baseIndex); + EmitNullInitializersForAuthenticatedNullFields(baseAddress, baseType); + } + } + for (auto field : record->getDecl()->fields()) { + auto fieldType = field->getType(); + if (!getContext().typeContainsAuthenticatedNull(fieldType)) + continue; + auto fieldIndex = layout.getLLVMFieldNo(field); + auto fieldAddress = Builder.CreateStructGEP(storageAddress, fieldIndex); + EmitNullInitializersForAuthenticatedNullFields(fieldAddress, fieldType); + } +} + void CodeGenFunction::EmitNullInitialization(Address DestPtr, QualType Ty) { // Ignore empty classes in C++. @@ -2120,6 +2201,7 @@ CodeGenFunction::EmitNullInitialization(Address DestPtr, QualType Ty) { } } + auto originalDestPtr = DestPtr; if (DestPtr.getElementType() != Int8Ty) DestPtr = DestPtr.withElementType(Int8Ty); @@ -2172,6 +2254,10 @@ CodeGenFunction::EmitNullInitialization(Address DestPtr, QualType Ty) { // Get and call the appropriate llvm.memcpy overload. Builder.CreateMemCpy(DestPtr, SrcPtr, SizeVal, false); + + if (getContext().typeContainsAuthenticatedNull(Ty)) + EmitNullInitializersForAuthenticatedNullFields(originalDestPtr, Ty); + return; } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 60294fdb48f23..7d2dbf75d9692 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3026,7 +3026,8 @@ class CodeGenFunction : public CodeGenTypeCache { /// null, If the type contains data member pointers, they will be initialized /// to -1 in accordance with the Itanium C++ ABI. void EmitNullInitialization(Address DestPtr, QualType Ty); - + void EmitNullInitializersForAuthenticatedNullFields(Address storageAddress, + QualType Ty); /// Emits a call to an LLVM variable-argument intrinsic, either /// \c llvm.va_start or \c llvm.va_end. /// \param ArgValue A reference to the \c va_list as emitted by either diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 2d758f9ed06d6..aac32d1471963 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -5418,7 +5418,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, // exists. A use may still exists, however, so we still may need // to do a RAUW. assert(!ASTTy->isIncompleteType() && "Unexpected incomplete type"); - Init = EmitNullConstant(D->getType()); + auto type = D->getType(); + auto pointerAuth = type.getPointerAuth(); + if (pointerAuth && pointerAuth.authenticatesNullValues()) + ErrorUnsupported(D, "static initializer with authenticated null values"); + Init = EmitNullConstant(type); } else { initializedGlobalDecl = GlobalDecl(D); emitter.emplace(*this); diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp index d823c336e39bf..f5c432f88b037 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -799,6 +799,8 @@ bool CodeGenTypes::isPointerZeroInitializable(QualType T) { } bool CodeGenTypes::isZeroInitializable(QualType T) { + if (CGM.getContext().typeContainsAuthenticatedNull(T)) + return false; if (T->getAs()) return Context.getTargetNullPointerValue(T) == 0; diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 4ffd745bf9307..ad8559abf070d 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -792,6 +792,7 @@ struct FormatToken { tok::kw__Atomic, #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) tok::kw___##Trait, #include "clang/Basic/TransformTypeTraits.def" + tok::kw___ptrauth, tok::kw___ptrauth_restricted_intptr, tok::kw_requires); } diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index d406a531a5c0c..3b4d754a5678d 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -3979,7 +3979,9 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr) { // it is often token-pasted. // An [[attribute]] can be before the identifier. while (FormatTok->isOneOf(tok::identifier, tok::coloncolon, tok::hashhash, - tok::kw_alignas, tok::l_square) || + tok::kw_alignas, tok::l_square, + tok::kw___ptrauth, + tok::kw___ptrauth_restricted_intptr) || FormatTok->isAttribute() || ((Style.Language == FormatStyle::LK_Java || Style.isJavaScript()) && FormatTok->isOneOf(tok::period, tok::comma))) { diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h index edb389225aa20..d02c6189a4c45 100644 --- a/clang/lib/Headers/ptrauth.h +++ b/clang/lib/Headers/ptrauth.h @@ -11,6 +11,7 @@ #define __PTRAUTH_H typedef enum { + ptrauth_key_none = -1, ptrauth_key_asia = 0, ptrauth_key_asib = 1, ptrauth_key_asda = 2, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 9ccece4115cc4..a1560455f9c1c 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3345,12 +3345,26 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs, } } +void Parser::DistributeCLateParsedAttrs(Decl *Dcl, + LateParsedAttrList *LateAttrs) { + if (!LateAttrs) + return; + + if (Dcl) { + for (auto *LateAttr : *LateAttrs) { + if (LateAttr->Decls.empty()) + LateAttr->addDecl(Dcl); + } + } +} + /// type-qualifier: -/// '__ptrauth' '(' constant-expression +/// ('__ptrauth' | '__ptrauth_restricted_intptr') '(' constant-expression /// (',' constant-expression)[opt] /// (',' constant-expression)[opt] ')' void Parser::ParsePtrauthQualifier(ParsedAttributes &attrs) { - assert(Tok.is(tok::kw___ptrauth)); + assert(Tok.is(tok::kw___ptrauth) || + Tok.is(tok::kw___ptrauth_restricted_intptr)); IdentifierInfo *kwName = Tok.getIdentifierInfo(); SourceLocation kwLoc = ConsumeToken(); @@ -3379,20 +3393,6 @@ void Parser::ParsePtrauthQualifier(ParsedAttributes &attrs) { /*IsRegularKeywordAttribute=*/false)); } - -void Parser::DistributeCLateParsedAttrs(Decl *Dcl, - LateParsedAttrList *LateAttrs) { - if (!LateAttrs) - return; - - if (Dcl) { - for (auto *LateAttr : *LateAttrs) { - if (LateAttr->Decls.empty()) - LateAttr->addDecl(Dcl); - } - } -} - /// Bounds attributes (e.g., counted_by): /// AttrName '(' expression ')' void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName, @@ -4256,6 +4256,7 @@ void Parser::ParseDeclarationSpecifiers( // __ptrauth qualifier. case tok::kw___ptrauth: + case tok::kw___ptrauth_restricted_intptr: ParsePtrauthQualifier(DS.getAttributes()); continue; @@ -5958,6 +5959,7 @@ bool Parser::isTypeSpecifierQualifier() { case tok::kw___pascal: case tok::kw___unaligned: case tok::kw___ptrauth: + case tok::kw___ptrauth_restricted_intptr: case tok::kw__Nonnull: case tok::kw__Nullable: @@ -6248,6 +6250,7 @@ bool Parser::isDeclarationSpecifier( case tok::kw___pascal: case tok::kw___unaligned: case tok::kw___ptrauth: + case tok::kw___ptrauth_restricted_intptr: case tok::kw__Nonnull: case tok::kw__Nullable: @@ -6502,12 +6505,6 @@ void Parser::ParseTypeQualifierListOpt( ParseOpenCLQualifiers(DS.getAttributes()); break; - // __ptrauth qualifier. - case tok::kw___ptrauth: - ParsePtrauthQualifier(DS.getAttributes()); - EndLoc = PrevTokLocation; - continue; - case tok::kw_groupshared: case tok::kw_in: case tok::kw_inout: @@ -6516,6 +6513,13 @@ void Parser::ParseTypeQualifierListOpt( ParseHLSLQualifiers(DS.getAttributes()); continue; + // __ptrauth qualifier. + case tok::kw___ptrauth: + case tok::kw___ptrauth_restricted_intptr: + ParsePtrauthQualifier(DS.getAttributes()); + EndLoc = PrevTokLocation; + continue; + case tok::kw___unaligned: isInvalid = DS.SetTypeQual(DeclSpec::TQ_unaligned, Loc, PrevSpec, DiagID, getLangOpts()); diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index eef2393c41b86..474c7de3a3bda 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -73,7 +73,8 @@ namespace { // value of the expression to the unqualified, non-atomic version of // the named type. if (!S.Context.getLangOpts().ObjC && !DestType->isRecordType() && - !DestType->isArrayType() && !DestType.getPointerAuth()) { + !DestType->isArrayType() && + !DestType.getPointerAuth().withoutKeyNone()) { DestType = DestType.getAtomicUnqualifiedType(); } diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index c1b49ed4c6dc8..f2546dcf58d37 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1515,11 +1515,11 @@ static bool checkPointerAuthKey(Sema &S, Expr *&Arg) { if (Arg->isValueDependent()) return false; - unsigned KeyValue; + int KeyValue; return S.checkConstantPointerAuthKey(Arg, KeyValue); } -bool Sema::checkConstantPointerAuthKey(Expr *Arg, unsigned &Result) { +bool Sema::checkConstantPointerAuthKey(Expr *Arg, int &Result) { // Attempt to constant-evaluate the expression. std::optional KeyValue = Arg->getIntegerConstantExpr(Context); if (!KeyValue) { @@ -1529,7 +1529,8 @@ bool Sema::checkConstantPointerAuthKey(Expr *Arg, unsigned &Result) { } // Ask the target to validate the key parameter. - if (!Context.getTargetInfo().validatePointerAuthKey(*KeyValue)) { + if (static_cast(KeyValue->getExtValue()) != PointerAuthKeyNone && + !Context.getTargetInfo().validatePointerAuthKey(*KeyValue)) { llvm::SmallString<32> Value; { llvm::raw_svector_ostream Str(Value); @@ -1541,10 +1542,58 @@ bool Sema::checkConstantPointerAuthKey(Expr *Arg, unsigned &Result) { return true; } - Result = KeyValue->getZExtValue(); + Result = KeyValue->getExtValue(); return false; } +bool Sema::checkPointerAuthDiscriminatorArg(Expr *arg, + PointerAuthDiscArgKind kind, + unsigned &intVal) { + if (!arg) { + intVal = 0; + return true; + } + + std::optional result = arg->getIntegerConstantExpr(Context); + if (!result) { + Diag(arg->getExprLoc(), diag::err_ptrauth_arg_not_ice); + return false; + } + + unsigned max; + bool isAddrDiscArg = false; + + switch (kind) { + case PADAK_AddrDiscPtrAuth: + max = 1; + isAddrDiscArg = true; + break; + case PADAK_ExtraDiscPtrAuth: + max = PointerAuthQualifier::MaxDiscriminator; + break; + }; + + if (*result < 0 || *result > max) { + llvm::SmallString<32> value; + { + llvm::raw_svector_ostream str(value); + str << *result; + } + + if (isAddrDiscArg) + Diag(arg->getExprLoc(), diag::err_ptrauth_address_discrimination_invalid) + << value; + else + Diag(arg->getExprLoc(), diag::err_ptrauth_extra_discriminator_invalid) + << value << max; + + return false; + }; + + intVal = result->getZExtValue(); + return true; +} + static std::pair findConstantBaseAndOffset(Sema &S, Expr *E) { // Must evaluate as a pointer. @@ -3801,6 +3850,14 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, return ExprError(); } + auto PointerAuth = AtomTy.getPointerAuth().withoutKeyNone(); + if (PointerAuth && PointerAuth.isAddressDiscriminated()) { + Diag(ExprRange.getBegin(), + diag::err_atomic_op_needs_non_address_discriminated_pointer) + << 0 << Ptr->getType() << Ptr->getSourceRange(); + return ExprError(); + } + // For an arithmetic operation, the implied arithmetic must be well-formed. if (Form == Arithmetic) { // GCC does not enforce these rules for GNU atomics, but we do to help catch @@ -4167,6 +4224,13 @@ ExprResult Sema::BuiltinAtomicOverloaded(ExprResult TheCallResult) { << FirstArg->getType() << 0 << FirstArg->getSourceRange(); return ExprError(); } + auto pointerAuth = ValType.getPointerAuth().withoutKeyNone(); + if (pointerAuth && pointerAuth.isAddressDiscriminated()) { + Diag(FirstArg->getBeginLoc(), + diag::err_atomic_op_needs_non_address_discriminated_pointer) + << 1 << ValType << FirstArg->getSourceRange(); + return ExprError(); + } if (ValType.isConstQualified()) { Diag(DRE->getBeginLoc(), diag::err_atomic_builtin_cannot_be_const) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 935da5826c2b0..3cadda56d7ed9 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14316,6 +14316,16 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { Diag(var->getLocation(), diag::err_constexpr_var_requires_const_init) << var; + if (GlobalStorage) { + auto supported = Context.tryTypeContainsAuthenticatedNull(var->getType()); + if (supported && *supported) { + Diag(var->getLocation(), + diag::err_ptrauth_invalid_authenticated_null_global) + << var->isFileVarDecl(); + var->setInvalidDecl(); + } + } + // Check whether the initializer is sufficiently constant. if ((getLangOpts().CPlusPlus || (getLangOpts().C23 && var->isConstexpr())) && !type->isDependentType() && Init && !Init->isValueDependent() && @@ -15083,7 +15093,8 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, // __ptrauth is forbidden on parameters. if (T.getPointerAuth()) { - Diag(NameLoc, diag::err_ptrauth_qualifier_param) << T; + Diag(NameLoc, diag::err_ptrauth_qualifier_invalid) + << T << (int)!T->isSignablePointerType() << 1; New->setInvalidDecl(); } @@ -19025,12 +19036,16 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, if (const auto *RT = FT->getAs()) { if (RT->getDecl()->getArgPassingRestrictions() == RecordArgPassingKind::CanNeverPassInRegs) - Record->setArgPassingRestrictions(RecordArgPassingKind::CanNeverPassInRegs); + Record->setArgPassingRestrictions( + RecordArgPassingKind::CanNeverPassInRegs); } else if (FT.getQualifiers().getObjCLifetime() == Qualifiers::OCL_Weak) { - Record->setArgPassingRestrictions(RecordArgPassingKind::CanNeverPassInRegs); - } else if (PointerAuthQualifier Q = FT.getPointerAuth()) { + Record->setArgPassingRestrictions( + RecordArgPassingKind::CanNeverPassInRegs); + } else if (PointerAuthQualifier Q = + FT.getPointerAuth().withoutKeyNone()) { if (Q.isAddressDiscriminated()) - Record->setArgPassingRestrictions(RecordArgPassingKind::CanNeverPassInRegs); + Record->setArgPassingRestrictions( + RecordArgPassingKind::CanNeverPassInRegs); } } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index b3ab9b5c9a863..4ce12e989a67a 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -9423,20 +9423,20 @@ bool SpecialMemberDeletionInfo::shouldDeleteForVariantPtrAuthMember( FieldDecl *FD, QualType FieldType) { // Copy/move constructors/assignment operators are deleted if the field has an // address-discriminated ptrauth qualifier. - PointerAuthQualifier Q = FieldType.getPointerAuth(); + PointerAuthQualifier Q = FieldType.getPointerAuth().withoutKeyNone(); if (!Q || !Q.isAddressDiscriminated()) return false; - if (CSM == CXXSpecialMemberKind::DefaultConstructor || CSM == CXXSpecialMemberKind::Destructor) + if (CSM == CXXSpecialMemberKind::DefaultConstructor || + CSM == CXXSpecialMemberKind::Destructor) return false; if (Diagnose) { auto *ParentClass = cast(FD->getParent()); - S.Diag(FD->getLocation(), - diag::note_deleted_special_member_class_subobject) - << llvm::to_underlying(getEffectiveCSM()) << ParentClass << /*IsField*/true - << FD << 4 << /*IsDtorCallInCtor*/false << 2; + S.Diag(FD->getLocation(), diag::note_deleted_special_member_class_subobject) + << llvm::to_underlying(getEffectiveCSM()) << ParentClass + << /*IsField*/ true << FD << 4 << /*IsDtorCallInCtor*/ false << 2; } return true; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index a58b6573627e0..ca835c779b947 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7945,7 +7945,7 @@ static QualType checkConditionalPointerCompatibility(Sema &S, ExprResult &LHS, lhQual.removeCVRQualifiers(); rhQual.removeCVRQualifiers(); - if (lhQual.getPointerAuth() != rhQual.getPointerAuth()) { + if (!lhQual.getPointerAuth().isEquivalent(rhQual.getPointerAuth())) { S.Diag(Loc, diag::err_typecheck_cond_incompatible_ptrauth) << LHSTy << RHSTy << LHS.get()->getSourceRange() @@ -8869,7 +8869,7 @@ checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType, ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; // Treat pointer-auth mismatches as fatal. - else if (lhq.getPointerAuth() != rhq.getPointerAuth()) + else if (!lhq.getPointerAuth().isEquivalent(rhq.getPointerAuth())) ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; // For GCC/MS compatibility, other qualifier mismatches are treated @@ -16691,6 +16691,9 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, } else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) { DiagKind = diag::err_typecheck_incompatible_ownership; break; + } else if (!lhq.getPointerAuth().isEquivalent(rhq.getPointerAuth())) { + DiagKind = diag::err_typecheck_incompatible_ptrauth; + break; } llvm_unreachable("unknown error case for discarding qualifiers!"); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 6b49fa19b3c06..277f1623b29c5 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7185,8 +7185,8 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, else return QualType(); - if (Q1.getPointerAuth() == Q2.getPointerAuth()) - Quals.setPointerAuth(Q1.getPointerAuth()); + if (Q1.getPointerAuth().isEquivalent(Q2.getPointerAuth())) + Quals.setPointerAuth(Q1.getPointerAuth().withoutKeyNone()); else return QualType(); diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index f1495125b1979..bfbab618922e9 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -182,6 +182,10 @@ Decl *SemaObjC::ActOnProperty(Scope *S, SourceLocation AtLoc, 0); TypeSourceInfo *TSI = SemaRef.GetTypeForDeclarator(FD.D); QualType T = TSI->getType(); + if (T.getPointerAuth().isPresent()) { + Diag(AtLoc, diag::err_ptrauth_qualifier_invalid) + << T << (int)!T->isSignablePointerType() << 2; + } if (!getOwnershipRule(Attributes)) { Attributes |= deducePropertyOwnershipFromType(SemaRef, T); } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index f9dedff95efe7..93fcce81ded22 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -11243,7 +11243,7 @@ static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, return; } - if (FromQs.getPointerAuth() != ToQs.getPointerAuth()) { + if (!FromQs.getPointerAuth().isEquivalent(ToQs.getPointerAuth())) { S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_ptrauth) << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc << FromTy diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 5e5638e125512..59dd86f2decd6 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2522,7 +2522,8 @@ bool Sema::CheckFunctionReturnType(QualType T, SourceLocation Loc) { // __ptrauth is illegal on a function return type. if (T.getPointerAuth()) { - Diag(Loc, diag::err_ptrauth_qualifier_return) << T; + Diag(Loc, diag::err_ptrauth_qualifier_invalid) + << T << (int)!T->isSignablePointerType() << 0; return true; } @@ -2626,13 +2627,14 @@ QualType Sema::BuildFunctionType(QualType T, Diag(Loc, diag::err_parameters_retval_cannot_have_fp16_type) << 0 << FixItHint::CreateInsertion(Loc, "*"); Invalid = true; - } else if (ParamType.getPointerAuth()) { - // __ptrauth is illegal on a function return type. - Diag(Loc, diag::err_ptrauth_qualifier_param) << T; - Invalid = true; } else if (ParamType->isWebAssemblyTableType()) { Diag(Loc, diag::err_wasm_table_as_function_parameter); Invalid = true; + } else if (ParamType.getPointerAuth()) { + // __ptrauth is illegal on a function return type. + Diag(Loc, diag::err_ptrauth_qualifier_invalid) + << T << (int)!T->isSignablePointerType() << 1; + Invalid = true; } // C++2a [dcl.fct]p4: @@ -4879,7 +4881,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // __ptrauth is illegal on a function return type. if (T.getPointerAuth()) { - S.Diag(DeclType.Loc, diag::err_ptrauth_qualifier_return) << T; + S.Diag(DeclType.Loc, diag::err_ptrauth_qualifier_invalid) + << T << (int)!T->isSignablePointerType() << 0; } if (LangOpts.OpenCL) { @@ -8224,11 +8227,92 @@ static void HandleNeonVectorTypeAttr(QualType &CurType, const ParsedAttr &Attr, CurType = S.Context.getVectorType(CurType, numElts, VecKind); } +namespace { + +struct PointerAuthenticationOption { + bool missingComma = false; + bool containsInvalidCharacter = false; + SourceRange range; + std::string option; +}; + +} // anonymous namespace + +static SmallVector +parsePtrAuthOptions(ASTContext &Ctx, StringLiteral *options, + CharUnits startingOffset) { + uint32_t tokenStartIndex = + startingOffset.getQuantity() / options->getCharByteWidth(); + SmallVector result; + bool missingComma = false; + auto optionsLength = options->getLength(); + while (tokenStartIndex < optionsLength) { + while (tokenStartIndex < optionsLength && + options->getCodeUnit(tokenStartIndex) == ' ') + tokenStartIndex++; + uint32_t tokenEndIndex = tokenStartIndex; + SmallVector buffer; + bool sawInvalidCharacter = false; + while (tokenEndIndex < optionsLength) { + auto character = options->getCodeUnit(tokenEndIndex); + if (character == ',' || (isascii(character) && isWhitespace(character))) { + break; + } + + buffer.push_back(character); + if (!isascii(character)) + sawInvalidCharacter = true; + tokenEndIndex++; + } + + auto startLocation = + options->getLocationOfByte(tokenStartIndex, Ctx.getSourceManager(), + Ctx.getLangOpts(), Ctx.getTargetInfo()); + auto endLocation = + options->getLocationOfByte(tokenEndIndex, Ctx.getSourceManager(), + Ctx.getLangOpts(), Ctx.getTargetInfo()); + auto optionRange = SourceRange(startLocation, endLocation); + PointerAuthenticationOption option{ + .missingComma = missingComma, + .containsInvalidCharacter = sawInvalidCharacter, + .range = optionRange, + .option = std::string(buffer.data(), buffer.size())}; + result.push_back(option); + tokenStartIndex = tokenEndIndex; + while (tokenStartIndex < optionsLength && + isascii(options->getCodeUnit(tokenStartIndex)) && + isWhitespace(options->getCodeUnit(tokenStartIndex))) + tokenStartIndex++; + missingComma = true; + if (tokenStartIndex < optionsLength && + options->getCodeUnit(tokenStartIndex) == ',') { + missingComma = false; + tokenStartIndex++; + continue; + } + } + // Handle a trailing comma + if (!missingComma && !result.empty()) { + auto location = + options->getLocationOfByte(tokenStartIndex, Ctx.getSourceManager(), + Ctx.getLangOpts(), Ctx.getTargetInfo()); + auto optionRange = SourceRange(location, location); + PointerAuthenticationOption option{.missingComma = false, + .containsInvalidCharacter = false, + .range = optionRange}; + result.push_back(option); + } + + return result; +} + /// Handle the __ptrauth qualifier. -static void HandlePtrAuthQualifier(QualType &type, const ParsedAttr &attr, - Sema &S) { - if (attr.getNumArgs() < 1 || attr.getNumArgs() > 3) { - S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_bad_arg_count); +static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &type, + const ParsedAttr &attr, Sema &S) { + auto attributeName = attr.getAttrName()->getName(); + if (attr.getNumArgs() < 1 || attr.getNumArgs() > 4) { + S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_bad_arg_count) + << attributeName; attr.setInvalid(); return; } @@ -8239,8 +8323,10 @@ static void HandlePtrAuthQualifier(QualType &type, const ParsedAttr &attr, attr.getNumArgs() >= 2 ? attr.getArgAsExpr(1) : nullptr; Expr *extraDiscriminatorArg = attr.getNumArgs() >= 3 ? attr.getArgAsExpr(2) : nullptr; + Expr *authenticationOptionsArg = + attr.getNumArgs() >= 4 ? attr.getArgAsExpr(3) : nullptr; - unsigned key; + int key; if (S.checkConstantPointerAuthKey(keyArg, key)) { attr.setInvalid(); return; @@ -8248,66 +8334,195 @@ static void HandlePtrAuthQualifier(QualType &type, const ParsedAttr &attr, assert(key <= PointerAuthQualifier::MaxKey && "ptrauth key is out of range"); bool isInvalid = false; - auto checkArg = [&](Expr *arg, unsigned argIndex) -> unsigned { - if (!arg) return 0; + unsigned isAddressDiscriminated, extraDiscriminator; + isInvalid |= !S.checkPointerAuthDiscriminatorArg(isAddressDiscriminatedArg, + Sema::PADAK_AddrDiscPtrAuth, + isAddressDiscriminated); + isInvalid |= !S.checkPointerAuthDiscriminatorArg( + extraDiscriminatorArg, Sema::PADAK_ExtraDiscPtrAuth, extraDiscriminator); + + std::optional authenticationMode = std::nullopt; + SourceRange authenticationModeRange; + bool isIsaPointer = false; + SourceRange isaPointerRange; + bool authenticatesNullValues = false; + SourceRange authenticatesNullValuesRange; + std::string evaluatedString; + CharUnits optionsOffset = CharUnits::Zero(); + if (authenticationOptionsArg) { + bool evaluatedExpression = false; + auto optionsStringLiteral = + dyn_cast(authenticationOptionsArg); + if (!optionsStringLiteral && !authenticationOptionsArg->containsErrors()) { + Expr::EvalResult Eval; + bool Result = authenticationOptionsArg->EvaluateAsRValue(Eval, Ctx); + if (Result && Eval.Val.isLValue()) { + auto *BaseExpr = Eval.Val.getLValueBase().dyn_cast(); + optionsStringLiteral = + dyn_cast(const_cast(BaseExpr)); + optionsOffset = Eval.Val.getLValueOffset(); + if (optionsStringLiteral) { + evaluatedExpression = true; + evaluatedString = optionsStringLiteral->getString().drop_front( + optionsOffset.getQuantity()); + } + } + } - std::optional result = arg->getIntegerConstantExpr(S.Context); - if (!result) { - isInvalid = true; - S.Diag(arg->getExprLoc(), diag::err_ptrauth_qualifier_arg_not_ice); - return 0; + if (!optionsStringLiteral) { + S.Diag(authenticationOptionsArg->getExprLoc(), + diag::err_ptrauth_non_string_authentication_option) + << authenticationOptionsArg; + attr.setInvalid(); + return; } - unsigned max = - (argIndex == 1 ? 1 : PointerAuthQualifier::MaxDiscriminator); - if (*result < 0 || *result > max) { - llvm::SmallString<32> value; { - llvm::raw_svector_ostream str(value); - str << *result; - } + auto options = + parsePtrAuthOptions(Ctx, optionsStringLiteral, optionsOffset); + auto optionsRange = authenticationOptionsArg->getSourceRange(); + auto diagnoseOptionError = [&](const PointerAuthenticationOption &opt, + auto diagnostic, + std::optional mode = std::nullopt) { + auto diagLocation = + evaluatedExpression ? optionsRange.getBegin() : opt.range.getBegin(); + auto highlightRange = evaluatedExpression ? optionsRange : opt.range; + auto diag = S.Diag(diagLocation, diagnostic); + if (mode) + diag << *mode; + diag << highlightRange; + isInvalid = true; + }; + for (auto option : options) { + if (option.missingComma) + diagnoseOptionError(option, + diag::err_ptrauth_invalid_option_missing_comma); - if (argIndex == 1) { - S.Diag(arg->getExprLoc(), - diag::err_ptrauth_qualifier_address_discrimination_invalid) - << value; - } else { - S.Diag(arg->getExprLoc(), - diag::err_ptrauth_qualifier_extra_discriminator_invalid) - << value << max; + if (option.containsInvalidCharacter) + diagnoseOptionError(option, diag::err_ptrauth_invalid_option_character); + + if (option.option.empty()) { + diagnoseOptionError(option, + diag::err_ptrauth_empty_authentication_option); + continue; } + auto explicitAuthenticationMode = + llvm::StringSwitch>( + option.option) + .Case(PointerAuthenticationOptionStrip, + PointerAuthenticationMode::Strip) + .Case(PointerAuthenticationOptionSignAndStrip, + PointerAuthenticationMode::SignAndStrip) + .Case(PointerAuthenticationOptionSignAndAuth, + PointerAuthenticationMode::SignAndAuth) + .Default(std::nullopt); + if (explicitAuthenticationMode) { + if (authenticationMode) { + diagnoseOptionError( + option, diag::err_ptrauth_repeated_authentication_option, 0); + if (!evaluatedExpression) { + S.Diag(authenticationModeRange.getBegin(), + diag::note_ptrauth_previous_authentication_option) + << 0 << authenticationModeRange; + } + } + authenticationMode = *explicitAuthenticationMode; + authenticationModeRange = option.range; + continue; + } + if (option.option == PointerAuthenticationOptionIsaPointer) { + if (isIsaPointer) { + diagnoseOptionError( + option, diag::err_ptrauth_repeated_authentication_option, 1); + if (!evaluatedExpression) { + S.Diag(isaPointerRange.getBegin(), + diag::note_ptrauth_previous_authentication_option) + << 1 << isaPointerRange; + } + } + isIsaPointer = true; + isaPointerRange = option.range; + continue; + } + if (option.option == PointerAuthenticationOptionAuthenticatesNullValues) { + if (authenticatesNullValues) { + diagnoseOptionError( + option, diag::err_ptrauth_repeated_authentication_option, 1); + if (!evaluatedExpression) { + S.Diag(authenticatesNullValuesRange.getBegin(), + diag::note_ptrauth_previous_authentication_option) + << 1 << authenticatesNullValuesRange; + } + } + authenticatesNullValues = true; + authenticatesNullValuesRange = option.range; + continue; + } + + S.Diag(evaluatedExpression ? optionsRange.getBegin() + : option.range.getBegin(), + diag::err_ptrauth_unknown_authentication_option) + << option.option; isInvalid = true; } - return result->getZExtValue(); - }; - bool isAddressDiscriminated = checkArg(isAddressDiscriminatedArg, 1); - unsigned extraDiscriminator = checkArg(extraDiscriminatorArg, 2); + if (isInvalid && evaluatedExpression) { + S.Diag(authenticationOptionsArg->getBeginLoc(), + diag::note_ptrauth_evaluating_options) + << authenticationOptionsArg->getSourceRange() << evaluatedString; + } + } + if (isInvalid) { attr.setInvalid(); return; } - if (!type->isPointerType()) { - S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_nonpointer) << type; - attr.setInvalid(); - return; + if (!authenticationMode) + authenticationMode = PointerAuthenticationMode::SignAndAuth; + + bool isRestrictedIntegral = false; + if (attr.getAttrName()->getName() == "__ptrauth_restricted_intptr") { + isRestrictedIntegral = true; + if (!type->isSignableIntegerType(Ctx) && !type->isDependentType()) { + S.Diag(attr.getLoc(), + diag::err_ptrauth_restricted_intptr_qualifier_pointer) + << type; + attr.setInvalid(); + return; + } + } else { + if (!type->isSignablePointerType() && !type->isDependentType()) { + S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_nonpointer) << type; + attr.setInvalid(); + return; + } } if (type.getPointerAuth()) { - S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_redundant) << type; + S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_redundant) + << type << attr.getAttrName()->getName(); attr.setInvalid(); return; } if (!S.getLangOpts().PointerAuthIntrinsics) { - S.diagnosePointerAuthDisabled(attr.getLoc(), attr.getRange()); + S.Diag(attr.getLoc(), diag::err_ptrauth_disabled) << attr.getRange(); attr.setInvalid(); return; } + assert((!isAddressDiscriminatedArg || isAddressDiscriminated <= 1) && + "address discriminator arg should be either 0 or 1"); PointerAuthQualifier qual = PointerAuthQualifier::Create( - key, isAddressDiscriminated, extraDiscriminator, - /*AuthenticationMode*/ PointerAuthenticationMode::SignAndAuth, - /*IsIsaPointer*/ false, /*AuthenticatesNullValues*/ true); + key, isAddressDiscriminated, extraDiscriminator, *authenticationMode, + isIsaPointer, authenticatesNullValues, isRestrictedIntegral); + + if (qual.hasKeyNone() && !S.Context.canQualifyWithPtrAuthKeyNone(type)) { + S.Diag(attr.getLoc(), diag::err_ptrauth_qualifier_signed_pointer_type) + << type; + attr.setInvalid(); + return; + } + type = S.Context.getPointerAuthType(type, qual); } @@ -8736,7 +8951,8 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, attr.setUsedAsTypeAttr(); break; case ParsedAttr::AT_PointerAuth: - HandlePtrAuthQualifier(type, attr, state.getSema()); + HandlePtrAuthQualifier(state.getSema().Context, type, attr, + state.getSema()); attr.setUsedAsTypeAttr(); break; case ParsedAttr::AT_LifetimeBound: diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 1ebe53b43fbef..87a93d2019086 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -5094,6 +5094,24 @@ QualType TreeTransform::RebuildQualifiedType(QualType T, return QualType(); } + auto LocalPointerAuth = Quals.getPointerAuth(); + if (LocalPointerAuth.isPresent()) { + if (T.getPointerAuth().isPresent()) { + SemaRef.Diag(Loc, diag::err_ptrauth_qualifier_redundant) + << TL.getType() << (LocalPointerAuth.isRestrictedIntegral() ? "__ptrauth_restricted_intptr" : "__ptrauth"); + return QualType(); + } + if (Quals.getPointerAuth().isRestrictedIntegral()) { + if (!T->isSignableIntegerType(SemaRef.getASTContext()) && + !T->isDependentType()) { + SemaRef.Diag(Loc, diag::err_ptrauth_restricted_intptr_qualifier_pointer) << T; + return QualType(); + } + } else if (!T->isSignablePointerType() && !T->isDependentType()) { + SemaRef.Diag(Loc, diag::err_ptrauth_qualifier_nonpointer) << T; + return QualType(); + } + } // C++ [dcl.fct]p7: // [When] adding cv-qualifications on top of the function type [...] the // cv-qualifiers are ignored. diff --git a/clang/test/AST/ast-dump-ptrauth-json.cpp b/clang/test/AST/ast-dump-ptrauth-json.cpp deleted file mode 100644 index 125cda0cff53a..0000000000000 --- a/clang/test/AST/ast-dump-ptrauth-json.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// 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()); diff --git a/clang/test/CodeGen/ptrauth-authenticated-null-values.c b/clang/test/CodeGen/ptrauth-authenticated-null-values.c new file mode 100644 index 0000000000000..edace11a8b0a2 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-authenticated-null-values.c @@ -0,0 +1,468 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -O0 -o - | FileCheck %s + +typedef void *__ptrauth(2, 0, 0, "authenticates-null-values") authenticated_null; +typedef void *__ptrauth(2, 1, 0, "authenticates-null-values") authenticated_null_addr_disc; +typedef void *__ptrauth(2, 0, 0) unauthenticated_null; +typedef void *__ptrauth(2, 1, 0) unauthenticated_null_addr_disc; + +int test_global; + +// CHECK: define void @f0(ptr noundef [[AUTH1_ARG:%.*]], ptr noundef [[AUTH2_ARG:%.*]]) +void f0(authenticated_null *auth1, authenticated_null *auth2) { + *auth1 = *auth2; + // CHECK: [[AUTH1_ARG]].addr = alloca ptr + // CHECK: [[AUTH2_ARG]].addr = alloca ptr + // CHECK: store ptr [[AUTH1_ARG]], ptr [[AUTH1_ARG]].addr + // CHECK: store ptr [[AUTH2_ARG]], ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH1_ADDR:%.*]] = load ptr, ptr [[AUTH1_ARG]].addr + // CHECK: [[AUTH2_ADDR:%.*]] = load ptr, ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH2_VALUE:%.*]] = load ptr, ptr [[AUTH2_ADDR]] + // CHECK: store ptr [[AUTH2_VALUE]], ptr [[AUTH1_ADDR]] +} + +// CHECK: define void @f1(ptr noundef [[AUTH1_ARG:%.*]], ptr noundef [[AUTH2_ARG:%.*]]) +void f1(unauthenticated_null *auth1, authenticated_null *auth2) { + *auth1 = *auth2; + // CHECK: [[ENTRY:.*]]: + // CHECK: [[AUTH1_ARG]].addr = alloca ptr + // CHECK: [[AUTH2_ARG]].addr = alloca ptr + // CHECK: store ptr [[AUTH1_ARG]], ptr [[AUTH1_ARG]].addr + // CHECK: store ptr [[AUTH2_ARG]], ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH1_ADDR:%.*]] = load ptr, ptr [[AUTH1_ARG]].addr + // CHECK: [[AUTH2_ADDR:%.*]] = load ptr, ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH2:%.*]] = load ptr, ptr [[AUTH2_ADDR]] + // CHECK: [[CAST_VALUE:%.*]] = ptrtoint ptr %2 to i64 + // CHECK: [[AUTHED_VALUE:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VALUE]], i32 2, i64 0) + // CHECK: [[TRUE_VALUE:%.*]] = inttoptr i64 [[AUTHED_VALUE]] to ptr + // CHECK: [[COMPARISON:%.*]] = icmp ne ptr [[TRUE_VALUE]], null + // CHECK: br i1 [[COMPARISON]], label %resign.nonnull, label %resign.cont + // CHECK: resign.nonnull: + // CHECK: %7 = ptrtoint ptr %2 to i64 + // CHECK: %8 = call i64 @llvm.ptrauth.resign(i64 %7, i32 2, i64 0, i32 2, i64 0) + // CHECK: %9 = inttoptr i64 %8 to ptr + // CHECK: br label %resign.cont + // CHECK: resign.cont: + // CHECK: [[RESULT:%.*]] = phi ptr [ null, %entry ], [ %9, %resign.nonnull ] + // CHECK: store ptr [[RESULT]], ptr [[AUTH1_ADDR]] +} + +// CHECK: define void @f2(ptr noundef [[AUTH1_ARG:%.*]], ptr noundef [[AUTH2_ARG:%.*]]) +void f2(authenticated_null *auth1, unauthenticated_null *auth2) { + *auth1 = *auth2; + // CHECK: [[ENTRY:.*]]: + // CHECK: [[AUTH1_ARG]].addr = alloca ptr + // CHECK: [[AUTH2_ARG]].addr = alloca ptr + // CHECK: store ptr [[AUTH1_ARG]], ptr [[AUTH1_ARG]].addr + // CHECK: store ptr [[AUTH2_ARG]], ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH1_ADDR:%.*]] = load ptr, ptr [[AUTH1_ARG]].addr + // CHECK: [[AUTH2_ADDR:%.*]] = load ptr, ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH2:%.*]] = load ptr, ptr [[AUTH2_ADDR]] + // CHECK: [[COMPARE:%.*]] = icmp ne ptr [[AUTH2]], null + // CHECK: br i1 [[COMPARE]], label %[[NON_NULL:resign.*]], label %[[NULL:resign.*]] + // CHECK: [[NULL]]: + // CHECK: [[SIGNED_ZERO:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[SIGNED_NULL:%.*]] = inttoptr i64 [[SIGNED_ZERO]] to ptr + // CHECK: br label %[[CONT:resign.*]] + // CHECK: [[NON_NULL]]: + // CHECK: [[AUTH2_CAST:%.*]] = ptrtoint ptr [[AUTH2]] to i64 + // CHECK: [[AUTH2_AUTHED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[AUTH2_CAST]], i32 2, i64 0, i32 2, i64 0) + // CHECK: [[AUTH2:%.*]] = inttoptr i64 [[AUTH2_AUTHED]] to ptr + // CHECK: br label %[[CONT]] + + // CHECK: [[CONT]]: + // CHECK: [[RESULT:%.*]] = phi ptr [ [[SIGNED_NULL]], %[[NULL]] ], [ [[AUTH2]], %[[NON_NULL]] ] + // CHECK: store ptr [[RESULT]], ptr [[AUTH1_ADDR]] +} + +// CHECK: define void @f3(ptr noundef [[AUTH1:%.*]], ptr noundef [[I:%.*]]) +void f3(authenticated_null *auth1, void *i) { + *auth1 = i; + // CHECK: [[AUTH1_ADDR:%.*]] = alloca ptr + // CHECK: [[I_ADDR:%.*]] = alloca ptr + // CHECK: store ptr [[AUTH1]], ptr [[AUTH1_ADDR]] + // CHECK: store ptr [[I]], ptr [[I_ADDR]] + // CHECK: [[AUTH1:%.*]] = load ptr, ptr [[AUTH1_ADDR]] + // CHECK: [[I:%.*]] = load ptr, ptr [[I_ADDR]] + // CHECK: [[CAST_I:%.*]] = ptrtoint ptr [[I]] to i64 + // CHECK: [[SIGNED_CAST_I:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CAST_I]], i32 2, i64 0) + // CHECK: [[SIGNED_I:%.*]] = inttoptr i64 [[SIGNED_CAST_I]] to ptr + // CHECK: store ptr [[SIGNED_I]], ptr [[AUTH1]] +} + +// CHECK: define void @f4(ptr noundef [[AUTH1:%.*]]) +void f4(authenticated_null *auth1) { + *auth1 = 0; + // CHECK: [[AUTH1_ADDR:%.*]] = alloca ptr + // CHECK: store ptr [[AUTH1]], ptr [[AUTH1_ADDR]] + // CHECK: [[AUTH1:%.*]] = load ptr, ptr [[AUTH1_ADDR]] + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[RESULT:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK: store ptr [[RESULT]], ptr [[AUTH1]] +} + +// CHECK: define void @f5(ptr noundef [[AUTH1:%.*]]) +void f5(authenticated_null *auth1) { + *auth1 = &test_global; + // CHECK: [[AUTH1_ADDR:%.*]] = alloca ptr + // CHECK: store ptr [[AUTH1]], ptr [[AUTH1_ADDR]] + // CHECK: [[AUTH1:%.*]] = load ptr, ptr [[AUTH1_ADDR]] + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @test_global to i64), i32 2, i64 0) + // CHECK: [[RESULT:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK: store ptr [[RESULT]], ptr [[AUTH1]] +} + +// CHECK: define i32 @f6(ptr noundef [[AUTH1:%.*]]) +int f6(authenticated_null *auth1) { + return !!*auth1; + // CHECK: [[AUTH1_ADDR:%.*]] = alloca ptr + // CHECK: store ptr [[AUTH1]], ptr [[AUTH1_ADDR]] + // CHECK: [[AUTH1:%.*]] = load ptr, ptr [[AUTH1_ADDR]] + // CHECK: [[AUTH1_V:%.*]] = load ptr, ptr [[AUTH1]] + // CHECK: [[CAST_AUTH1:%.*]] = ptrtoint ptr [[AUTH1_V]] to i64 + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_AUTH1]], i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[AUTHED]] to ptr + // CHECK: [[TOBOOL:%.*]] = icmp ne ptr [[CAST]], null + // CHECK: [[LNOT:%.*]] = xor i1 [[TOBOOL]], true + // CHECK: [[LNOT1:%.*]] = xor i1 [[LNOT]], true + // CHECK: [[LNOT_EXT:%.*]] = zext i1 [[LNOT1]] to i32 + // CHECK: ret i32 [[LNOT_EXT]] +} + +// CHECK: define void @f7(ptr noundef [[AUTH1_ARG:%.*]], ptr noundef [[AUTH2_ARG:%.*]]) +void f7(authenticated_null_addr_disc *auth1, authenticated_null_addr_disc *auth2) { + *auth1 = *auth2; + // CHECK: [[AUTH1_ARG]].addr = alloca ptr + // CHECK: [[AUTH2_ARG]].addr = alloca ptr + // CHECK: store ptr [[AUTH1_ARG]], ptr [[AUTH1_ARG]].addr + // CHECK: store ptr [[AUTH2_ARG]], ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH1_ADDR:%.*]] = load ptr, ptr [[AUTH1_ARG]].addr + // CHECK: [[AUTH2_ADDR:%.*]] = load ptr, ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH2_VALUE:%.*]] = load ptr, ptr [[AUTH2_ADDR]] + // CHECK: [[CAST_AUTH2_ADDR:%.*]] = ptrtoint ptr [[AUTH2_ADDR]] to i64 + // CHECK: [[CAST_AUTH1_ADDR:%.*]] = ptrtoint ptr [[AUTH1_ADDR]] to i64 + // CHECK: [[CAST_AUTH2:%.*]] = ptrtoint ptr [[AUTH2_VALUE]] to i64 + // CHECK: [[RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[CAST_AUTH2]], i32 2, i64 [[CAST_AUTH2_ADDR]], i32 2, i64 [[CAST_AUTH1_ADDR]]) + // CHECK: [[CAST_RESIGNED_VALUE:%.*]] = inttoptr i64 [[RESIGNED]] to ptr + // CHECK: store ptr [[CAST_RESIGNED_VALUE]], ptr [[AUTH1_ADDR]] +} + +struct S0 { + int i; + authenticated_null p; +}; + +void f8() { + struct S0 t = {.i = 1, .p = 0}; + // CHECK: define void @f8() + // CHECK: [[T:%.*]] = alloca %struct.S0 + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.S0, ptr [[T]], i32 0, i32 0 + // CHECK: store i32 1, ptr [[I]] + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S0, ptr [[T]], i32 0, i32 1 + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] +} + +void f9() { + struct S0 t = {}; + // CHECK: define void @f9() + // CHECK: [[T:%.*]] = alloca %struct.S0 + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.S0, ptr [[T]], i32 0, i32 0 + // CHECK: store i32 0, ptr [[I]] + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S0, ptr [[T]], i32 0, i32 1 + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] +} + +void f10() { + struct S0 t = {.i = 12}; + // CHECK: define void @f10() + // CHECK: [[T:%.*]] = alloca %struct.S0 + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.S0, ptr [[T]], i32 0, i32 0 + // CHECK: store i32 12, ptr [[I]] + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S0, ptr [[T]], i32 0, i32 1 + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] +} + +struct S1 { + authenticated_null p; + authenticated_null_addr_disc q; +}; + +void f11() { + struct S1 t = {.p = (void *)1}; + // CHECK-LABEL: define void @f11() + // CHECK: [[T:%.*]] = alloca %struct.S1 + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S1, ptr [[T]], i32 0, i32 0 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 1, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] + // CHECK: [[Q:%.*]] = getelementptr inbounds %struct.S1, ptr [[T]], i32 0, i32 1 + // CHECK: [[ADDR_DISC:%.*]] = ptrtoint ptr [[Q]] to i64 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 [[ADDR_DISC]]) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[Q]] +} + +void f12() { + struct S1 t = {.p = (void *)1, .q = (void *)0}; + // CHECK-LABEL: define void @f12() + // CHECK: [[T:%.*]] = alloca %struct.S1 + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S1, ptr [[T]], i32 0, i32 0 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 1, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] + // CHECK: [[Q:%.*]] = getelementptr inbounds %struct.S1, ptr [[T]], i32 0, i32 1 + // CHECK: [[ADDR_DISC:%.*]] = ptrtoint ptr [[Q]] to i64 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 [[ADDR_DISC]]) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[Q]] +} +void f13() { + struct S1 t = {.q = (void *)1}; + // CHECK: define void @f13() + // CHECK: [[T:%.*]] = alloca %struct.S1 + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S1, ptr [[T]], i32 0, i32 0 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] + // CHECK: [[Q:%.*]] = getelementptr inbounds %struct.S1, ptr [[T]], i32 0, i32 1 + // CHECK: [[ADDR_DISC:%.*]] = ptrtoint ptr [[Q]] to i64 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 1, i32 2, i64 [[ADDR_DISC]]) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[Q]] +} + +struct S2 { + int i; + struct S1 s1; +}; + +void f14() { + struct S2 t = {}; + // CHECK-LABEL: define void @f14 + // CHECK: [[T:%.*]] = alloca %struct.S2 + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.S2, ptr [[T]], i32 0, i32 0 + // CHECK: store i32 0, ptr [[I]] + // CHECK: [[S1:%.*]] = getelementptr inbounds %struct.S2, ptr [[T]], i32 0, i32 1 + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S1, ptr [[S1]], i32 0, i32 0 + // CHECK: [[SIGNED_INT:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[SIGNED:%.*]] = inttoptr i64 [[SIGNED_INT]] to ptr + // CHECK: store ptr [[SIGNED]], ptr [[P]] + // CHECK: [[Q:%.*]] = getelementptr inbounds %struct.S1, ptr [[S1]], i32 0, i32 1 + // CHECK: [[ADDR_DISC:%.*]] = ptrtoint ptr [[Q]] to i64 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 [[ADDR_DISC]]) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[Q]] +} + +void f15() { + struct S2 t = {.s1 = {}}; + // CHECK-LABEL: define void @f15 + // CHECK: [[T:%.*]] = alloca %struct.S2 + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.S2, ptr [[T]], i32 0, i32 0 + // CHECK: store i32 0, ptr [[I]] + // CHECK: [[S1:%.*]] = getelementptr inbounds %struct.S2, ptr [[T]], i32 0, i32 1 + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S1, ptr [[S1]], i32 0, i32 0 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] + // CHECK: [[Q:%.*]] = getelementptr inbounds %struct.S1, ptr [[S1]], i32 0, i32 1 + // CHECK: [[ADDR_DISC:%.*]] = ptrtoint ptr [[Q]] to i64 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 [[ADDR_DISC]]) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[Q]] +} + +void f16() { + struct S2 t = {.i = 13}; + // CHECK-LABEL: define void @f16 + // CHECK: [[T:%.*]] = alloca %struct.S2 + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.S2, ptr [[T]], i32 0, i32 0 + // CHECK: store i32 13, ptr [[I]] + // CHECK: [[S1:%.*]] = getelementptr inbounds %struct.S2, ptr [[T]], i32 0, i32 1 + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S1, ptr [[S1]], i32 0, i32 0 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] + // CHECK: [[Q:%.*]] = getelementptr inbounds %struct.S1, ptr [[S1]], i32 0, i32 1 + // CHECK: [[ADDR_DISC:%.*]] = ptrtoint ptr [[Q]] to i64 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 [[ADDR_DISC]]) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[Q]] +} + +void f17(struct S2 a, struct S2 b) { + a = b; + // CHECK-LABEL: define void @f17 + // CHECK: call void @__copy_assignment_8_8_t0w4_S_t8w8_pa2_0_anv_16 + + // CHECK-LABEL: define linkonce_odr hidden void @__copy_assignment_8_8_t0w4_S_t8w8_pa2_0_anv_16 + // CHECK: %dst.addr = alloca ptr + // CHECK: %src.addr = alloca ptr + // CHECK: store ptr %dst, ptr %dst.addr + // CHECK: store ptr %src, ptr %src.addr + // CHECK: %0 = load ptr, ptr %dst.addr + // CHECK: %1 = load ptr, ptr %src.addr + // CHECK: %2 = load i32, ptr %1 + // CHECK: store i32 %2, ptr %0 + // CHECK: %3 = getelementptr inbounds i8, ptr %0, i64 8 + // CHECK: %4 = getelementptr inbounds i8, ptr %1, i64 8 + // CHECK: call void @__copy_assignment_8_8_t0w8_pa2_0_anv_8 + + // CHECK-LABEL: define linkonce_odr hidden void @__copy_assignment_8_8_t0w8_pa2_0_anv_8 + // CHECK: %dst.addr = alloca ptr + // CHECK: %src.addr = alloca ptr + // CHECK: store ptr %dst, ptr %dst.addr + // CHECK: store ptr %src, ptr %src.addr + // CHECK: %0 = load ptr, ptr %dst.addr + // CHECK: %1 = load ptr, ptr %src.addr + // CHECK: %2 = load i64, ptr %1 + // CHECK: store i64 %2, ptr %0 + // CHECK: %3 = getelementptr inbounds i8, ptr %0, i64 8 + // CHECK: %4 = getelementptr inbounds i8, ptr %1, i64 8 + // CHECK: %5 = load ptr, ptr %4 + // CHECK: %6 = ptrtoint ptr %4 to i64 + // CHECK: %7 = ptrtoint ptr %3 to i64 + // CHECK: %8 = ptrtoint ptr %5 to i64 + // CHECK: %9 = call i64 @llvm.ptrauth.resign(i64 %8, i32 2, i64 %6, i32 2, i64 %7) + // CHECK: %10 = inttoptr i64 %9 to ptr + // CHECK: store ptr %10, ptr %3 +} + +struct S1_nonull_auth { + unauthenticated_null p; + unauthenticated_null_addr_disc q; +}; + +void f18(struct S1_nonull_auth a, struct S1_nonull_auth b) { + a = b; + // All we're doing here is making sure that we don't coalesce + // copy functions for pointer authenticated types that only + // differ in the handling of null values + // CHECK-LABEL: define void @f18 + // CHECK: call void @__copy_assignment_8_8_t0w8_pa2_0_8 +} + +void f19() { + authenticated_null_addr_disc addr[5] = {0}; + + // CHECK-LABEL: define void @f19 + // CHECK: %addr = alloca [5 x ptr] + // CHECK: %0 = ptrtoint ptr %addr to i64 + // CHECK: %1 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 %0) + // CHECK: %2 = inttoptr i64 %1 to ptr + // CHECK: store ptr %2, ptr %addr + // CHECK: %arrayinit.start = getelementptr inbounds ptr, ptr %addr, i64 1 + // CHECK: %arrayinit.end = getelementptr inbounds ptr, ptr %addr, i64 5 + // CHECK: br label %arrayinit.body + // CHECK: arrayinit.body: + // CHECK: %arrayinit.cur = phi ptr [ %arrayinit.start, %entry ], [ %arrayinit.next, %arrayinit.body ] + // CHECK: %3 = ptrtoint ptr %arrayinit.cur to i64 + // CHECK: %4 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 %3) + // CHECK: %5 = inttoptr i64 %4 to ptr + // CHECK: store ptr %5, ptr %arrayinit.cur + // CHECK: %arrayinit.next = getelementptr inbounds ptr, ptr %arrayinit.cur, i64 1 + // CHECK: %arrayinit.done = icmp eq ptr %arrayinit.next, %arrayinit.end + // CHECK: br i1 %arrayinit.done, label %arrayinit.end1, label %arrayinit.body + // CHECK: arrayinit.end1: +} + +void f21() { + struct S2 addr[5] = {{}}; +} + +void f22() { + struct S2 addr[5] = {0}; +} + +void f23() { + struct S1 addr[5] = {}; + // CHECK-LABEL: define void @f23() + // CHECK: %addr = alloca [5 x %struct.S1] + // CHECK: arrayinit.end = getelementptr inbounds %struct.S1, ptr %addr, i64 5 + // CHECK: br label %arrayinit.body + // CHECK: arrayinit.cur = phi ptr [ %addr, %entry ], [ %arrayinit.next, %arrayinit.body ] + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %arrayinit.cur, ptr align 8 @6, i64 16, i1 false) + // CHECK: 0 = getelementptr inbounds %struct.S1, ptr %arrayinit.cur, i32 0, i32 0 + // CHECK: 1 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: 2 = inttoptr i64 %1 to ptr + // CHECK: store ptr %2, ptr %0 + // CHECK: 3 = getelementptr inbounds %struct.S1, ptr %arrayinit.cur, i32 0, i32 1 + // CHECK: 4 = ptrtoint ptr %3 to i64 + // CHECK: 5 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 %4) + // CHECK: 6 = inttoptr i64 %5 to ptr + // CHECK: store ptr %6, ptr %3 + // CHECK: arrayinit.next = getelementptr inbounds %struct.S1, ptr %arrayinit.cur, i64 1 + // CHECK: arrayinit.done = icmp eq ptr %arrayinit.next, %arrayinit.end + // CHECK: br i1 %arrayinit.done, label %arrayinit.end1, label %arrayinit.body +} + +void f24() { + struct S2 addr[5] = {}; + // CHECK-LABEL: define void @f24() + // CHECK: %addr = alloca [5 x %struct.S2] + // CHECK: %arrayinit.end = getelementptr inbounds %struct.S2, ptr %addr, i64 5 + // CHECK: br label %arrayinit.body + + // CHECK: arrayinit.body: + // CHECK: %arrayinit.cur = phi ptr [ %addr, %entry ], [ %arrayinit.next, %arrayinit.body ] + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %arrayinit.cur, ptr align 8 @7, i64 24, i1 false) + // CHECK: %0 = getelementptr inbounds %struct.S2, ptr %arrayinit.cur, i32 0, i32 1 + // CHECK: %1 = getelementptr inbounds %struct.S1, ptr %0, i32 0, i32 0 + // CHECK: %2 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: %3 = inttoptr i64 %2 to ptr + // CHECK: store ptr %3, ptr %1 + // CHECK: %4 = getelementptr inbounds %struct.S1, ptr %0, i32 0, i32 1 + // CHECK: %5 = ptrtoint ptr %4 to i64 + // CHECK: %6 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 %5) + // CHECK: %7 = inttoptr i64 %6 to ptr + // CHECK: store ptr %7, ptr %4 + // CHECK: %arrayinit.next = getelementptr inbounds %struct.S2, ptr %arrayinit.cur, i64 1 + // CHECK: %arrayinit.done = icmp eq ptr %arrayinit.next, %arrayinit.end + // CHECK: br i1 %arrayinit.done, label %arrayinit.end1, label %arrayinit.body + // CHECK: arrayinit.end1: +} + +struct S3 { + int *__ptrauth(2, 0, 0, "authenticates-null-values") f0; +}; + +void f25() { + struct S3 s; + // CHECK-LABEL: define void @f25() + // CHECK: [[S:%.*]] = alloca %struct.S3 + // CHECK: ret void + // CHECK: } +} + +struct S4 { + int i; +}; +void f26() { + struct S3 addr[2][3][4] = {}; + // CHECK-LABEL: define void @f26() + // CHECK: arrayinit.body: + // CHECK: %arrayinit.cur = phi ptr [ %addr, %entry ], [ %arrayinit.next, %array_authenticated_null_init.end8 ] + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %arrayinit.cur, ptr align 8 @8, i64 96, i1 false) + // CHECK: %array_authenticated_null_init.start = getelementptr inbounds [3 x [4 x %struct.S3]], ptr %arrayinit.cur, i64 0, i64 0 + // CHECK: %array_authenticated_null_init.end = getelementptr inbounds [4 x %struct.S3], ptr %array_authenticated_null_init.start, i64 3 + // CHECK: br label %array_authenticated_null_init.body + // CHECK: array_authenticated_null_init.body: + // CHECK: %array_authenticated_null_init.cur = phi ptr [ %array_authenticated_null_init.start, %arrayinit.body ], [ %array_authenticated_null_init.next6, %array_authenticated_null_init.end5 ] + // CHECK: %array_authenticated_null_init.start1 = getelementptr inbounds [4 x %struct.S3], ptr %array_authenticated_null_init.cur, i64 0, i64 0 + // CHECK: %array_authenticated_null_init.end2 = getelementptr inbounds %struct.S3, ptr %array_authenticated_null_init.start1, i64 4 + // CHECK: br label %array_authenticated_null_init.body3 + // CHECK: array_authenticated_null_init.body3: + // CHECK: %array_authenticated_null_init.cur4 = phi ptr [ %array_authenticated_null_init.start1, %array_authenticated_null_init.body ], [ %array_authenticated_null_init.next, %array_authenticated_null_init.body3 ] + // CHECK: %0 = getelementptr inbounds %struct.S3, ptr %array_authenticated_null_init.cur4, i32 0, i32 0 + // CHECK: %1 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: %2 = inttoptr i64 %1 to ptr + // CHECK: store ptr %2, ptr %0 + // CHECK: %array_authenticated_null_init.next = getelementptr inbounds %struct.S3, ptr %array_authenticated_null_init.cur4, i64 1 + // CHECK: %array_authenticated_null_init.done = icmp eq ptr %array_authenticated_null_init.next, %array_authenticated_null_init.end2 + // CHECK: br i1 %array_authenticated_null_init.done, label %array_authenticated_null_init.end5, label %array_authenticated_null_init.body3 +} diff --git a/clang/test/CodeGen/ptrauth-debuginfo.c b/clang/test/CodeGen/ptrauth-debuginfo.c index d643a6aeb2441..02137a1b512a1 100644 --- a/clang/test/CodeGen/ptrauth-debuginfo.c +++ b/clang/test/CodeGen/ptrauth-debuginfo.c @@ -5,11 +5,21 @@ // Constant initializers for data pointers. extern int external_int; +int *__ptrauth(1, 0, 1234) g1 = &external_int; +int *__ptrauth(1, 0, 1235, "isa-pointer") g2 = &external_int; +// CHECK: !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, +// CHECK-SAME: ptrAuthKey: 1, +// CHECK-SAME: ptrAuthIsAddressDiscriminated: false, +// CHECK-SAME: ptrAuthExtraDiscriminator: 1235, +// CHECK-SAME: ptrAuthIsaPointer: true, +// CHECK-SAME: ptrAuthAuthenticatesNullValues: false) + // CHECK: !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, // CHECK-SAME: ptrAuthKey: 1, // CHECK-SAME: ptrAuthIsAddressDiscriminated: false, // CHECK-SAME: ptrAuthExtraDiscriminator: 1234, -int * __ptrauth(1,0,1234) g1 = &external_int; +// CHECK-SAME: ptrAuthIsaPointer: false, +// CHECK-SAME: ptrAuthAuthenticatesNullValues: false) struct A { int value; @@ -17,10 +27,53 @@ struct A { struct A *createA(void); void f() { - __block struct A * __ptrauth(1, 1, 1) ptr = createA(); - ^{ ptr->value; }(); + __block struct A *__ptrauth(1, 1, 1236) ptr = createA(); + ^{ + (void)ptr->value; + }(); +} +// CHECK: !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, +// CHECK-SAME: ptrAuthKey: 1, +// CHECK-SAME: ptrAuthIsAddressDiscriminated: true, +// CHECK-SAME: ptrAuthExtraDiscriminator: 1236, +// CHECK-SAME: ptrAuthIsaPointer: false, +// CHECK-SAME: ptrAuthAuthenticatesNullValues: false) + +void f2() { + __block struct A *__ptrauth(1, 1, 1237, "isa-pointer") ptr = createA(); + ^{ + (void)ptr->value; + }(); +} +// CHECK: !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, +// CHECK-SAME: ptrAuthKey: 1, +// CHECK-SAME: ptrAuthIsAddressDiscriminated: true, +// CHECK-SAME: ptrAuthExtraDiscriminator: 1237, +// CHECK-SAME: ptrAuthIsaPointer: true, +// CHECK-SAME: ptrAuthAuthenticatesNullValues: false) + +void f3() { + __block struct A *__ptrauth(1, 1, 1238, "isa-pointer, authenticates-null-values") ptr = createA(); + ^{ + (void)ptr->value; + }(); +} +// CHECK: !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, +// CHECK-SAME: ptrAuthKey: 1, +// CHECK-SAME: ptrAuthIsAddressDiscriminated: true, +// CHECK-SAME: ptrAuthExtraDiscriminator: 1238, +// CHECK-SAME: ptrAuthIsaPointer: true, +// CHECK-SAME: ptrAuthAuthenticatesNullValues: true) + +void f4() { + __block struct A *__ptrauth(1, 1, 1239, "authenticates-null-values") ptr = createA(); + ^{ + (void)ptr->value; + }(); } // CHECK: !DIDerivedType(tag: DW_TAG_LLVM_ptrauth_type, // CHECK-SAME: ptrAuthKey: 1, // CHECK-SAME: ptrAuthIsAddressDiscriminated: true, -// CHECK-SAME: ptrAuthExtraDiscriminator: 1, +// CHECK-SAME: ptrAuthExtraDiscriminator: 1239, +// CHECK-SAME: ptrAuthIsaPointer: false, +// CHECK-SAME: ptrAuthAuthenticatesNullValues: true) diff --git a/clang/test/CodeGen/ptrauth-in-c-struct.c b/clang/test/CodeGen/ptrauth-in-c-struct.c index 4db00e7186ee7..1c1181c2c1955 100644 --- a/clang/test/CodeGen/ptrauth-in-c-struct.c +++ b/clang/test/CodeGen/ptrauth-in-c-struct.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fblocks -fptrauth-calls -fptrauth-returns -fptrauth-intrinsics -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fblocks -fptrauth-calls -fptrauth-returns -fptrauth-intrinsics -emit-llvm -o - %s | FileCheck %s #define AQ1_50 __ptrauth(1,1,50) #define AQ2_30 __ptrauth(2,1,30) @@ -27,6 +27,8 @@ typedef struct { SA getSA(void); void calleeSA(SA); +int g0; + // CHECK: define void @test_copy_constructor_SA(ptr noundef %{{.*}}) // CHECK: call void @__copy_constructor_8_8_t0w4_pa1_50_8( @@ -150,3 +152,21 @@ void test_copy_constructor_SI(SI *s) { void test_parameter_SI(SI a) { } + +// CHECK-LABEL: define void @test_array( +// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_SA]], ptr %{{.*}}, i32 0, i32 1 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[F1]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V0]], i64 50) +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @g0 to i64), i32 1, i64 %[[V1]]) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: store ptr %[[V3]], ptr %[[F1]], align 8 +// CHECK: %[[F12:.*]] = getelementptr inbounds %[[STRUCT_SA]], ptr %{{.*}}, i32 0, i32 1 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[F12]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V4]], i64 50) +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @g0 to i64), i32 1, i64 %[[V5]]) +// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr +// CHECK: store ptr %[[V7]], ptr %[[F12]], align 8 + +void test_array(void) { + const SA a[] = {{0, &g0}, {1, &g0}}; +} diff --git a/clang/test/CodeGen/ptrauth-qualifier-blocks.c b/clang/test/CodeGen/ptrauth-qualifier-blocks.c new file mode 100644 index 0000000000000..f2d9c680d5147 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-qualifier-blocks.c @@ -0,0 +1,113 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -fblocks -emit-llvm %s -o - | FileCheck %s + +struct A { + int value; +}; +struct A *createA(void); + +void use_block(int (^)(void)); + +// CHECK-LABEL: define void @test_block_nonaddress_capture( +void test_block_nonaddress_capture() { + // CHECK: [[VAR:%.*]] = alloca ptr, + // CHECK: [[BLOCK:%.*]] = alloca + // flags - no copy/dispose required + // CHECK: store i32 1073741824, ptr + // CHECK: [[CAPTURE:%.*]] = getelementptr inbounds {{.*}} [[BLOCK]], i32 0, i32 5 + // CHECK: [[LOAD:%.*]] = load ptr, ptr [[VAR]], + // CHECK: store ptr [[LOAD]], ptr [[CAPTURE]] + struct A * __ptrauth(1, 0, 15) ptr = createA(); + use_block(^{ return ptr->value; }); +} +// CHECK-LABEL: define internal i32 @__test_block_nonaddress_capture_block_invoke +// CHECK: call i64 @llvm.ptrauth.auth(i64 {{%.*}}, i32 1, i64 15) + +// CHECK-LABEL: define void @test_block_address_capture( +void test_block_address_capture() { + // CHECK: [[VAR:%.*]] = alloca ptr, + // CHECK: [[BLOCK:%.*]] = alloca + // flags - copy/dispose required + // CHECK: store i32 1107296256, ptr + // CHECK: [[CAPTURE:%.*]] = getelementptr inbounds {{.*}} [[BLOCK]], i32 0, i32 5 + // CHECK: [[LOAD:%.*]] = load ptr, ptr [[VAR]], + // CHECK: [[T0:%.*]] = ptrtoint ptr [[VAR]] to i64 + // CHECK: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 30) + // CHECK: [[T0:%.*]] = ptrtoint ptr [[CAPTURE]] to i64 + // CHECK: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 30) + // CHECK: [[T0:%.*]] = icmp ne ptr [[LOAD]], null + // CHECK: br i1 [[T0]] + // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 + // CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) + // CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr + // CHECK: [[T0:%.*]] = phi + // CHECK: store ptr [[T0]], ptr [[CAPTURE]] + struct A * __ptrauth(1, 1, 30) ptr = createA(); + use_block(^{ return ptr->value; }); +} +// CHECK-LABEL: define internal i32 @__test_block_address_capture_block_invoke +// CHECK: call i64 @llvm.ptrauth.auth(i64 {{%.*}}, i32 1, i64 {{%.*}}) + +// CHECK: linkonce_odr hidden void @__copy_helper_block_8_32p1d30( +// CHECK: [[OLDSLOT:%.*]] = getelementptr inbounds {{.*}} {{.*}}, i32 0, i32 5 +// CHECK: [[NEWSLOT:%.*]] = getelementptr inbounds {{.*}} {{.*}}, i32 0, i32 5 +// CHECK: [[LOAD:%.*]] = load ptr, ptr [[OLDSLOT]], +// CHECK: [[T0:%.*]] = ptrtoint ptr [[OLDSLOT]] to i64 +// CHECK: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 30) +// CHECK: [[T0:%.*]] = ptrtoint ptr [[NEWSLOT]] to i64 +// CHECK: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 30) +// CHECK: [[T0:%.*]] = icmp ne ptr [[LOAD]], null +// CHECK: br i1 [[T0]] +// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 +// CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr +// CHECK: [[T0:%.*]] = phi +// CHECK: store ptr [[T0]], ptr [[NEWSLOT]] + +// CHECK-LABEL: define void @test_block_nonaddress_byref_capture( +void test_block_nonaddress_byref_capture() { + // flags - no copy/dispose required for byref + // CHECK: store i32 0, + // CHECK: call ptr @createA() + // flags - copy/dispose required for block (because it captures byref) + // CHECK: store i32 1107296256, + __block struct A * __ptrauth(1, 0, 45) ptr = createA(); + use_block(^{ return ptr->value; }); +} + +// CHECK-LABEL: define void @test_block_address_byref_capture( +void test_block_address_byref_capture() { + // CHECK: [[BYREF:%.*]] = alloca [[BYREF_T:.*]], align + // CHECK: [[BLOCK:%.*]] = alloca + // flags - byref requires copy/dispose + // CHECK: store i32 33554432, + // CHECK: store i32 48, + // CHECK: [[COPY_HELPER_FIELD:%.*]] = getelementptr inbounds [[BYREF_T]], ptr [[BYREF]], i32 0, i32 4 + // CHECK: [[T0:%.*]] = ptrtoint ptr [[COPY_HELPER_FIELD]] to i64 + // CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @__Block_byref_object_copy_ to i64), i32 0, i64 [[T0]]) + // CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr + // CHECK: store ptr [[T2]], ptr [[COPY_HELPER_FIELD]], align + // CHECK: [[DISPOSE_HELPER_FIELD:%.*]] = getelementptr inbounds [[BYREF_T]], ptr [[BYREF]], i32 0, i32 5 + // CHECK: [[T0:%.*]] = ptrtoint ptr [[DISPOSE_HELPER_FIELD]] to i64 + // CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @__Block_byref_object_dispose_ to i64), i32 0, i64 [[T0]]) + // CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr + // CHECK: store ptr [[T2]], ptr [[DISPOSE_HELPER_FIELD]], align + // flags - copy/dispose required + // CHECK: store i32 1107296256, ptr + __block struct A * __ptrauth(1, 1, 60) ptr = createA(); + use_block(^{ return ptr->value; }); +} +// CHECK-LABEL: define internal void @__Block_byref_object_copy_ +// CHECK: [[NEWSLOT:%.*]] = getelementptr inbounds {{.*}} {{.*}}, i32 0, i32 6 +// CHECK: [[OLDSLOT:%.*]] = getelementptr inbounds {{.*}} {{.*}}, i32 0, i32 6 +// CHECK: [[LOAD:%.*]] = load ptr, ptr [[OLDSLOT]], +// CHECK: [[T0:%.*]] = ptrtoint ptr [[OLDSLOT]] to i64 +// CHECK: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 60) +// CHECK: [[T0:%.*]] = ptrtoint ptr [[NEWSLOT]] to i64 +// CHECK: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 60) +// CHECK: [[T0:%.*]] = icmp ne ptr [[LOAD]], null +// CHECK: br i1 [[T0]] +// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 +// CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]]) +// CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr +// CHECK: [[T0:%.*]] = phi +// CHECK: store ptr [[T0]], ptr [[NEWSLOT]] diff --git a/clang/test/CodeGen/ptrauth-qualifier-function.c b/clang/test/CodeGen/ptrauth-qualifier-function.c new file mode 100644 index 0000000000000..f6cde0cd96e62 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-qualifier-function.c @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 %s -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s +// RUN: %clang_cc1 -xc++ %s -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s + +#ifdef __cplusplus +extern "C" { +#endif + +void (*fptr)(void); +void (* __ptrauth(0, 0, 42) f2ptr_42_discm)(int); + +// CHECK-LABEL: define void @test_assign_to_qualified +void test_assign_to_qualified() { + f2ptr_42_discm = (void (*)(int))fptr; + + // CHECK: [[ENTRY:.*]]:{{$}} + // CHECK: [[FPTR:%.*]] = load ptr, ptr @fptr + // CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[FPTR]], null + // CHECK-NEXT: br i1 [[CMP]], label %[[RESIGN1:.*]], label %[[JOIN1:.*]] + + // CHECK: [[RESIGN1]]: + // CHECK-NEXT: [[FPTR2:%.*]] = ptrtoint ptr [[FPTR]] to i64 + // CHECK-NEXT: [[FPTR4:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[FPTR2]], i32 0, i64 18983, i32 0, i64 2712) + // CHECK-NEXT: [[FPTR5:%.*]] = inttoptr i64 [[FPTR4]] to ptr + // CHECK-NEXT: br label %[[JOIN1]] + + // CHECK: [[JOIN1]]: + // CHECK-NEXT: [[FPTR6:%.*]] = phi ptr [ null, %[[ENTRY]] ], [ [[FPTR5]], %[[RESIGN1]] ] + // CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[FPTR6]], null + // CHECK-NEXT: br i1 [[CMP]], label %[[RESIGN2:.*]], label %[[JOIN2:.*]] + + // CHECK: [[RESIGN2]]: + // CHECK-NEXT: [[FPTR7:%.*]] = ptrtoint ptr [[FPTR6]] to i64 + // CHECK-NEXT: [[FPTR8:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[FPTR7]], i32 0, i64 2712, i32 0, i64 42) + // CHECK-NEXT: [[FPTR9:%.*]] = inttoptr i64 [[FPTR8]] to ptr + // CHECK-NEXT: br label %[[JOIN2]] + + // CHECK: [[JOIN2]] + // CHECK-NEXT: [[FPTR10:%.*]] = phi ptr [ null, %[[JOIN1]] ], [ [[FPTR9]], %[[RESIGN2]] ] + // CHECK-NEXT store void (i32)* [[FPTR10]], void (i32)** @f2ptr_42_discm +} + +// CHECK-LABEL: define void @test_assign_from_qualified +void test_assign_from_qualified() { + fptr = (void (*)(void))f2ptr_42_discm; + + // CHECK: [[ENTRY:.*]]:{{$}} + // CHECK: [[FPTR:%.*]] = load ptr, ptr @f2ptr_42_discm + // CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[FPTR]], null + // CHECK-NEXT: br i1 [[CMP]], label %[[RESIGN1:.*]], label %[[JOIN1:.*]] + + // CHECK: [[RESIGN1]]: + // CHECK-NEXT: [[FPTR1:%.*]] = ptrtoint ptr [[FPTR]] to i64 + // CHECK-NEXT: [[FPTR2:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[FPTR1]], i32 0, i64 42, i32 0, i64 2712) + // CHECK-NEXT: [[FPTR3:%.*]] = inttoptr i64 [[FPTR2]] to ptr + // CHECK-NEXT: br label %[[JOIN1]] + + // CHECK: [[JOIN1]]: + // CHECK-NEXT: [[FPTR4:%.*]] = phi ptr [ null, %[[ENTRY]] ], [ [[FPTR3]], %[[RESIGN1]] ] + // CHECK-NEXT: [[CMP:%.*]] = icmp ne ptr [[FPTR4]], null + // CHECK-NEXT: br i1 [[CMP]], label %[[RESIGN2:.*]], label %[[JOIN2:.*]] + + // CHECK: [[RESIGN2]]: + // CHECK-NEXT: [[FPTR6:%.*]] = ptrtoint ptr [[FPTR4]] to i64 + // CHECK-NEXT: [[FPTR7:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[FPTR6]], i32 0, i64 2712, i32 0, i64 18983) + // CHECK-NEXT: [[FPTR8:%.*]] = inttoptr i64 [[FPTR7]] to ptr + // CHECK-NEXT: br label %[[JOIN2]] + + // CHECK: [[JOIN2]] + // CHECK-NEXT: [[FPTR9:%.*]] = phi ptr [ null, %[[JOIN1]] ], [ [[FPTR8]], %[[RESIGN2]] ] + // CHECK-NEXT store void ()* [[FPTR10]], void ()** @f2ptr_42_discm +} + +#ifdef __cplusplus +} +#endif diff --git a/clang/test/CodeGen/ptrauth-qualifier-loadstore.c b/clang/test/CodeGen/ptrauth-qualifier-loadstore.c index 66863a9dbb59b..84d2a173f0092 100644 --- a/clang/test/CodeGen/ptrauth-qualifier-loadstore.c +++ b/clang/test/CodeGen/ptrauth-qualifier-loadstore.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s #define IQ __ptrauth(1,0,50) #define AQ __ptrauth(1,1,50) @@ -414,11 +414,11 @@ void test_load_data_a() { // CHECK-LABEL: define void @test_store_function_i_constant() void test_store_function_i_constant() { // CHECK: [[V:%.*]] = alloca ptr, -// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint ({{.*}} @external_func.ptrauth to i64), i32 0, i64 0, i32 1, i64 50) +// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @external_func, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 1, i64 50) // CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr // CHECK-NEXT: store ptr [[T0]], ptr [[V]], func_t * IQ iqpf = &external_func; -// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint ({{.*}} @external_func.ptrauth to i64), i32 0, i64 0, i32 1, i64 50) +// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @external_func, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 1, i64 50) // CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr // CHECK-NEXT: store ptr [[T0]], ptr [[V]], iqpf = &external_func; @@ -431,7 +431,7 @@ void test_store_function_iu() { // CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null // CHECK-NEXT: br i1 [[T0]], // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 -// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 0, i32 1, i64 50) +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 18983, i32 1, i64 50) // CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr // CHECK-NEXT: br label // CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] @@ -441,7 +441,7 @@ void test_store_function_iu() { // CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null // CHECK-NEXT: br i1 [[T0]], // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 -// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 0, i32 1, i64 50) +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 18983, i32 1, i64 50) // CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr // CHECK-NEXT: br label // CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] @@ -487,7 +487,7 @@ void test_store_function_ia() { // CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[RESULT]], null // CHECK-NEXT: br i1 [[T0]], // CHECK: [[T0:%.*]] = ptrtoint ptr [[RESULT]] to i64 -// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 0, i64 0) +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 0, i64 18983) // CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr // CHECK-NEXT: br label // CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] @@ -538,7 +538,7 @@ void test_load_function_i() { // CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null // CHECK-NEXT: br i1 [[T0]], // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 -// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 0, i64 0) +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 0, i64 18983) // CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr // CHECK-NEXT: br label // CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] @@ -548,7 +548,7 @@ void test_load_function_i() { // CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null // CHECK-NEXT: br i1 [[T0]], // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 -// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 0, i64 0) +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 0, i64 18983) // CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr // CHECK-NEXT: br label // CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] @@ -558,7 +558,7 @@ void test_load_function_i() { // CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null // CHECK-NEXT: br i1 [[T0]], // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 -// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 0, i64 0) +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 50, i32 0, i64 18983) // CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr // CHECK-NEXT: br label // CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] @@ -573,13 +573,13 @@ void test_store_function_a_constant() { // CHECK: [[V:%.*]] = alloca ptr, // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64 // CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50) -// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint ({{.*}} @external_func.ptrauth to i64), i32 0, i64 0, i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @external_func, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 1, i64 [[NEWDISC]]) // CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr // CHECK-NEXT: store ptr [[T0]], ptr [[V]], func_t * AQ aqpf = &external_func; // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64 // CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50) -// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint ({{.*}} @external_func.ptrauth to i64), i32 0, i64 0, i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @external_func, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 1, i64 [[NEWDISC]]) // CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr // CHECK-NEXT: store ptr [[T0]], ptr [[V]], aqpf = &external_func; @@ -594,7 +594,7 @@ void test_store_function_au() { // CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null // CHECK-NEXT: br i1 [[T0]], // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 -// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 0, i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 18983, i32 1, i64 [[NEWDISC]]) // CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr // CHECK-NEXT: br label // CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] @@ -606,7 +606,7 @@ void test_store_function_au() { // CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null // CHECK-NEXT: br i1 [[T0]], // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 -// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 0, i32 1, i64 [[NEWDISC]]) +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 0, i64 18983, i32 1, i64 [[NEWDISC]]) // CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr // CHECK-NEXT: br label // CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] @@ -713,7 +713,7 @@ void test_load_function_a() { // CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null // CHECK-NEXT: br i1 [[T0]], // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 -// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 0, i64 0) +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 0, i64 18983) // CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr // CHECK-NEXT: br label // CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] @@ -724,7 +724,7 @@ void test_load_function_a() { // CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null // CHECK-NEXT: br i1 [[T0]], // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 -// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 0, i64 0) +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 0, i64 18983) // CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr // CHECK-NEXT: br label // CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] @@ -735,7 +735,7 @@ void test_load_function_a() { // CHECK-NEXT: [[T0:%.*]] = icmp ne ptr [[LOAD]], null // CHECK-NEXT: br i1 [[T0]], // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64 -// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 0, i64 0) +// CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 0, i64 18983) // CHECK-NEXT: [[SIGNED:%.*]] = inttoptr i64 [[T1]] to ptr // CHECK-NEXT: br label // CHECK: [[T0:%.*]] = phi ptr [ null, {{.*}} ], [ [[SIGNED]], {{.*}} ] diff --git a/clang/test/CodeGen/ptrauth-qualifier.c b/clang/test/CodeGen/ptrauth-qualifier.c index 718ed723a9298..93f6afab2e94b 100644 --- a/clang/test/CodeGen/ptrauth-qualifier.c +++ b/clang/test/CodeGen/ptrauth-qualifier.c @@ -1,14 +1,16 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s + +#include + +#include // Constant initializers for data pointers. extern int external_int; -// CHECK: [[PTRAUTH_G1:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 0, i64 56 }, section "llvm.ptrauth" -// CHECK: @g1 = global ptr [[PTRAUTH_G1]] +// CHECK: @g1 = global ptr ptrauth (ptr @external_int, i32 1, i64 56) int * __ptrauth(1,0,56) g1 = &external_int; -// CHECK: [[PTRAUTH_G2:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr @g2 to i64), i64 1272 }, section "llvm.ptrauth" -// CHECK: @g2 = global ptr [[PTRAUTH_G2]] +// CHECK: @g2 = global ptr ptrauth (ptr @external_int, i32 1, i64 1272, ptr @g2) int * __ptrauth(1,1,1272) g2 = &external_int; // CHECK: @g3 = global ptr null @@ -18,10 +20,13 @@ int * __ptrauth(1,1,871) g3 = 0; // CHECK: @g4 = global ptr inttoptr (i64 1230 to ptr) int * __ptrauth(1,1,1902) g4 = (int*) 1230; -// CHECK: [[PTRAUTH_GA0:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr @ga to i64), i64 712 }, section "llvm.ptrauth" -// CHECK: [[PTRAUTH_GA1:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr getelementptr inbounds ([3 x ptr], ptr @ga, i32 0, i32 1) to i64), i64 712 }, section "llvm.ptrauth" -// CHECK: [[PTRAUTH_GA2:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr getelementptr inbounds ([3 x ptr], ptr @ga, i32 0, i32 2) to i64), i64 712 }, section "llvm.ptrauth" -// CHECK: @ga = global [3 x ptr] [ptr [[PTRAUTH_GA0]], ptr [[PTRAUTH_GA1]], ptr [[PTRAUTH_GA2]]] +// CHECK: @g_none = global ptr @external_int, align 8 +int * __ptrauth(ptrauth_key_none,1,1902) g_none = &external_int; + +// CHECK: @ga = global [3 x ptr] [ +// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 712, ptr @ga), +// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 712, ptr getelementptr inbounds ([3 x ptr], ptr @ga, i32 0, i32 1)), +// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 712, ptr getelementptr inbounds ([3 x ptr], ptr @ga, i32 0, i32 2))] int * __ptrauth(1,1,712) ga[3] = { &external_int, &external_int, &external_int }; struct A { @@ -29,10 +34,11 @@ struct A { int * __ptrauth(1,0,9182) f1; int * __ptrauth(1,0,783) f2; }; -// CHECK: [[PTRAUTH_GS0:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 0, i64 431 }, section "llvm.ptrauth" -// CHECK: [[PTRAUTH_GS1:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 0, i64 9182 }, section "llvm.ptrauth" -// CHECK: [[PTRAUTH_GS2:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 0, i64 783 }, section "llvm.ptrauth" -// CHECK: @gs1 = global %struct.A { ptr [[PTRAUTH_GS0]], ptr [[PTRAUTH_GS1]], ptr [[PTRAUTH_GS2]] } + +// CHECK: @gs1 = global %struct.A { +// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 431), +// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 9182), +// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 783) } struct A gs1 = { &external_int, &external_int, &external_int }; struct B { @@ -40,28 +46,27 @@ struct B { int * __ptrauth(1,1,23674) f1; int * __ptrauth(1,1,163) f2; }; -// CHECK: [[PTRAUTH_GS0:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr @gs2 to i64), i64 1276 }, section "llvm.ptrauth" -// CHECK: [[PTRAUTH_GS1:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 1) to i64), i64 23674 }, section "llvm.ptrauth" -// CHECK: [[PTRAUTH_GS2:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 2) to i64), i64 163 }, section "llvm.ptrauth" -// CHECK: @gs2 = global %struct.B { ptr [[PTRAUTH_GS0]], ptr [[PTRAUTH_GS1]], ptr [[PTRAUTH_GS2]] } + +// CHECK: @gs2 = global %struct.B { +// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 1276, ptr @gs2), +// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 23674, ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 1)), +// CHECK-SAME: ptr ptrauth (ptr @external_int, i32 1, i64 163, ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 2)) } struct B gs2 = { &external_int, &external_int, &external_int }; // Constant initializers for function pointers. extern void external_function(void); typedef void (*fpt)(void); -// CHECK: [[PTRAUTH_F1:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 0, i64 56 }, section "llvm.ptrauth" -// CHECK: @f1 = global ptr [[PTRAUTH_F1]] +// CHECK: @f1 = global ptr ptrauth (ptr @external_function, i32 1, i64 56) fpt __ptrauth(1,0,56) f1 = &external_function; -// CHECK: [[PTRAUTH_F2:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr @f2 to i64), i64 1272 }, section "llvm.ptrauth" -// CHECK: @f2 = global ptr [[PTRAUTH_F2]] +// CHECK: @f2 = global ptr ptrauth (ptr @external_function, i32 1, i64 1272, ptr @f2) fpt __ptrauth(1,1,1272) f2 = &external_function; -// CHECK: [[PTRAUTH_FA0:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr @fa to i64), i64 712 }, section "llvm.ptrauth" -// CHECK: [[PTRAUTH_FA1:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr getelementptr inbounds ([3 x ptr], ptr @fa, i32 0, i32 1) to i64), i64 712 }, section "llvm.ptrauth" -// CHECK: [[PTRAUTH_FA2:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr getelementptr inbounds ([3 x ptr], ptr @fa, i32 0, i32 2) to i64), i64 712 }, section "llvm.ptrauth" -// CHECK: @fa = global [3 x ptr] [ptr [[PTRAUTH_FA0]], ptr [[PTRAUTH_FA1]], ptr [[PTRAUTH_FA2]]] +// CHECK: @fa = global [3 x ptr] [ +// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 712, ptr @fa), +// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 712, ptr getelementptr inbounds ([3 x ptr], ptr @fa, i32 0, i32 1)), +// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 712, ptr getelementptr inbounds ([3 x ptr], ptr @fa, i32 0, i32 2))] fpt __ptrauth(1,1,712) fa[3] = { &external_function, &external_function, &external_function }; struct C { @@ -69,10 +74,10 @@ struct C { fpt __ptrauth(1,0,9182) f1; fpt __ptrauth(1,0,783) f2; }; -// CHECK: [[PTRAUTH_FS0:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 0, i64 431 }, section "llvm.ptrauth" -// CHECK: [[PTRAUTH_FS1:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 0, i64 9182 }, section "llvm.ptrauth" -// CHECK: [[PTRAUTH_FS2:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 0, i64 783 }, section "llvm.ptrauth" -// CHECK: @fs1 = global %struct.C { ptr [[PTRAUTH_FS0]], ptr [[PTRAUTH_FS1]], ptr [[PTRAUTH_FS2]] } +// CHECK: @fs1 = global %struct.C { +// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 431), +// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 9182), +// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 783) } struct C fs1 = { &external_function, &external_function, &external_function }; struct D { @@ -80,8 +85,8 @@ struct D { fpt __ptrauth(1,1,23674) f1; fpt __ptrauth(1,1,163) f2; }; -// CHECK: [[PTRAUTH_FS0:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr @fs2 to i64), i64 1276 }, section "llvm.ptrauth" -// CHECK: [[PTRAUTH_FS1:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr getelementptr inbounds (%struct.D, ptr @fs2, i32 0, i32 1) to i64), i64 23674 }, section "llvm.ptrauth" -// CHECK: [[PTRAUTH_FS2:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr getelementptr inbounds (%struct.D, ptr @fs2, i32 0, i32 2) to i64), i64 163 }, section "llvm.ptrauth" -// CHECK: @fs2 = global %struct.D { ptr [[PTRAUTH_FS0]], ptr [[PTRAUTH_FS1]], ptr [[PTRAUTH_FS2]] } +// CHECK: @fs2 = global %struct.D { +// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 1276, ptr @fs2), +// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 23674, ptr getelementptr inbounds (%struct.D, ptr @fs2, i32 0, i32 1)), +// CHECK-SAME: ptr ptrauth (ptr @external_function, i32 1, i64 163, ptr getelementptr inbounds (%struct.D, ptr @fs2, i32 0, i32 2)) } struct D fs2 = { &external_function, &external_function, &external_function }; diff --git a/clang/test/CodeGen/ptrauth-restricted-intptr-qualifier.c b/clang/test/CodeGen/ptrauth-restricted-intptr-qualifier.c new file mode 100644 index 0000000000000..36ecc481d4cd1 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-restricted-intptr-qualifier.c @@ -0,0 +1,220 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -O0 -o - | FileCheck %s +#if __has_feature(ptrauth_restricted_intptr_qualifier) + +__INTPTR_TYPE__ __ptrauth_restricted_intptr(1, 0, 56) g1 = 0; +// CHECK: @g1 = global i64 0 +__INTPTR_TYPE__ __ptrauth_restricted_intptr(1, 1, 1272) g2 = 0; +// CHECK: @g2 = global i64 0 +extern __UINTPTR_TYPE__ test_int; +__UINTPTR_TYPE__ __ptrauth_restricted_intptr(3, 1, 23) g3 = (__UINTPTR_TYPE__)&test_int; +// CHECK: @test_int = external global i64 +// CHECK: @g3 = global i64 ptrtoint (ptr ptrauth (ptr @test_int, i32 3, i64 23, ptr @g3) to i64) + +__INTPTR_TYPE__ __ptrauth_restricted_intptr(1, 1, 712) ga[3] = {0,0,(__UINTPTR_TYPE__)&test_int}; + +// CHECK: @ga = global [3 x i64] [i64 0, i64 0, i64 ptrtoint (ptr ptrauth (ptr @test_int, i32 1, i64 712, ptr getelementptr inbounds ([3 x i64], ptr @ga, i32 0, i32 2)) to i64)] + +struct A { + __INTPTR_TYPE__ __ptrauth_restricted_intptr(1, 0, 431) f0; + __INTPTR_TYPE__ __ptrauth_restricted_intptr(1, 0, 9182) f1; + __INTPTR_TYPE__ __ptrauth_restricted_intptr(1, 0, 783) f2; +}; + +struct A gs1 = {0, 0, (__UINTPTR_TYPE__)&test_int}; +// CHECK: @gs1 = global %struct.A { i64 0, i64 0, i64 ptrtoint (ptr ptrauth (ptr @test_int, i32 1, i64 783) to i64) } + +struct B { + __INTPTR_TYPE__ __ptrauth_restricted_intptr(1, 1, 1276) f0; + __INTPTR_TYPE__ __ptrauth_restricted_intptr(1, 1, 23674) f1; + __INTPTR_TYPE__ __ptrauth_restricted_intptr(1, 1, 163) f2; +}; + +struct B gs2 = {0, 0, (__UINTPTR_TYPE__)&test_int}; +// CHECK: @gs2 = global %struct.B { i64 0, i64 0, i64 ptrtoint (ptr ptrauth (ptr @test_int, i32 1, i64 163, ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 2)) to i64) } + +// CHECK-LABEL: i64 @test_read_globals +__INTPTR_TYPE__ test_read_globals() { + __INTPTR_TYPE__ result = g1 + g2 + g3; + // CHECK: [[A:%.*]] = load i64, ptr @g1 + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[A]], i32 1, i64 56) + // CHECK: [[B:%.*]] = load i64, ptr @g2 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @g2 to i64), i64 1272) + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[B]], i32 1, i64 [[BLENDED]]) + // CHECK: [[VALUE:%.*]] = load i64, ptr @g3 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @g3 to i64), i64 23) + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 3, i64 [[BLENDED]]) + + for (int i = 0; i < 3; i++) { + result += ga[i]; + } + // CHECK: for.cond: + // CHECK: [[TEMP:%.*]] = load i32, ptr [[IDX_ADDR:%.*]] + + // CHECK: for.body: + // CHECK: [[IDX:%.*]] = load i32, ptr [[IDX_ADDR]] + // CHECK: [[IDXPROM:%.*]] = sext i32 [[IDX]] to i64 + // CHECK: [[ARRAYIDX:%.*]] = getelementptr inbounds [3 x i64], ptr @ga, i64 0, i64 [[IDXPROM]] + // CHECK: [[VALUE:%.*]] = load i64, ptr [[ARRAYIDX]] + // CHECK: [[CASTIDX:%.*]] = ptrtoint ptr [[ARRAYIDX]] to i64 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTIDX]], i64 712) + // CHECK: resign.nonnull6: + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]]) + // CHECK: resign.cont7 + + result += gs1.f0 + gs1.f1 + gs1.f2; + // CHECK: resign.cont10: + // CHECK: [[ADDR:%.*]] = load i64, ptr getelementptr inbounds (%struct.A, ptr @gs1, i32 0, i32 1 + // CHECK: resign.nonnull11: + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[ADDR]], i32 1, i64 9182) + // CHECK: resign.cont12: + // CHECK: [[ADDR:%.*]] = load i64, ptr getelementptr inbounds (%struct.A, ptr @gs1, i32 0, i32 2) + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[ADDR]], i32 1, i64 783) + result += gs2.f0 + gs2.f1 + gs2.f2; + // CHECK: [[ADDR:%.*]] = load i64, ptr @gs2 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @gs2 to i64), i64 1276) + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[ADDR]], i32 1, i64 [[BLENDED]]) + // CHECK: [[ADDR:%.*]] = load i64, ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 1) + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 1) to i64), i64 23674) + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[ADDR]], i32 1, i64 [[BLENDED]]) + // CHECK: [[ADDR:%.*]] = load i64, ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 2) + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 2) to i64), i64 163) + + return result; +} + +// CHECK-LABEL: void @test_write_globals +void test_write_globals(int i, __INTPTR_TYPE__ j) { + g1 = i; + g2 = j; + g3 = 0; + ga[0] = i; + ga[1] = j; + ga[2] = 0; + gs1.f0 = i; + gs1.f1 = j; + gs1.f2 = 0; + gs2.f0 = i; + gs2.f1 = j; + gs2.f2 = 0; +} + +// CHECK-LABEL: define void @test_set_A +void test_set_A(struct A *a, __INTPTR_TYPE__ x, int y) { + a->f0 = x; + // CHECK: [[XADDR:%.*]] = load i64, ptr %x.addr + // CHECK: [[SIGNED_X:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[XADDR]], i32 1, i64 431) + a->f1 = y; + // CHECK: [[Y:%.*]] = load i32, ptr %y.addr + // CHECK: [[CONV:%.*]] = sext i32 [[Y]] to i64 + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CONV]], i32 1, i64 9182) + a->f2 = 0; + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr + // CHECK: [[F2:%.*]] = getelementptr inbounds %struct.A, ptr [[A]], i32 0, i32 2 + // CHECK: store i64 0, ptr [[F2]] +} + +// CHECK-LABEL: define void @test_set_B +void test_set_B(struct B *b, __INTPTR_TYPE__ x, int y) { + b->f0 = x; + // CHECK: [[X:%.*]] = load i64, ptr %x.addr + // CHECK: [[F0_ADDR:%.*]] = ptrtoint ptr %f0 to i64 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[F0_ADDR]], i64 1276) + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[X]], i32 1, i64 [[BLENDED]]) + b->f1 = y; + // CHECK: [[B:%.*]] = load ptr, ptr %b.addr + // CHECK: [[F1_ADDR:%.*]] = getelementptr inbounds %struct.B, ptr [[B]], i32 0, i32 1 + // CHECK: [[Y:%.*]] = load i32, ptr %y.addr, align 4 + // CHECK: [[CONV:%.*]] = sext i32 [[Y]] to i64 + // CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[F1_ADDR]] to i64 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 23674) + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CONV]], i32 1, i64 [[BLENDED]]) + b->f2 = 0; + // CHECK: [[B:%.*]] = load ptr, ptr %b.addr + // CHECK: [[F2_ADDR:%.*]] = getelementptr inbounds %struct.B, ptr [[B]], i32 0, i32 2 + // CHECK: store i64 0, ptr [[F2_ADDR]] +} + +// CHECK-LABEL: define i64 @test_get_A +__INTPTR_TYPE__ test_get_A(struct A *a) { + return a->f0 + a->f1 + a->f2; + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr + // CHECK: [[F0_ADDR:%.*]] = getelementptr inbounds %struct.A, ptr [[A]], i32 0, i32 0 + // CHECK: [[F0:%.*]] = load i64, ptr [[F0_ADDR]] + // CHECK: [[AUTH:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[F0]], i32 1, i64 431) + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr + // CHECK: [[F1_ADDR:%.*]] = getelementptr inbounds %struct.A, ptr [[A]], i32 0, i32 1 + // CHECK: [[F1:%.*]] = load i64, ptr [[F1_ADDR]] + // CHECK: [[AUTH:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[F1]], i32 1, i64 9182) + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr + // CHECK: [[F2_ADDR:%.*]] = getelementptr inbounds %struct.A, ptr [[A]], i32 0, i32 2 + // CHECK: [[F2:%.*]] = load i64, ptr [[F2_ADDR]] + // CHECK: [[AUTH:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[F2]], i32 1, i64 783) +} + +// CHECK-LABEL: define i64 @test_get_B +__INTPTR_TYPE__ test_get_B(struct B *b) { + return b->f0 + b->f1 + b->f2; + // CHECK: [[B:%.*]] = load ptr, ptr %b.addr + // CHECK: [[F0:%.*]] = getelementptr inbounds %struct.B, ptr [[B]], i32 0, i32 0 + // CHECK: [[VALUE:%.*]] = load i64, ptr [[F0]] + // CHECK: [[CASTF0:%.*]] = ptrtoint ptr %f0 to i64 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTF0]], i64 1276) + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]]) + // CHECK: [[B:%.*]] = load ptr, ptr %b.addr + // CHECK: [[ADDR:%.*]] = getelementptr inbounds %struct.B, ptr [[B]], i32 0, i32 1 + // CHECK: [[VALUE:%.*]] = load i64, ptr [[ADDR]] + // CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[ADDR]] to i64 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 23674) + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]]) + // CHECK: [[B:%.*]] = load ptr, ptr %b.addr + // CHECK: [[ADDR:%.*]] = getelementptr inbounds %struct.B, ptr [[B]], i32 0, i32 2 + // CHECK: [[VALUE:%.*]] = load i64, ptr [[ADDR]] + // CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[ADDR]] to i64 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 163) + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]]) +} + +// CHECK-LABEL: define void @test_resign +void test_resign(struct A* a, const struct B *b) { + a->f0 = b->f0; + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr, align 8 + // CHECK: [[F0:%.*]] = getelementptr inbounds %struct.A, ptr [[A]], i32 0, i32 0 + // CHECK: [[B:%.*]] = load ptr, ptr %b.addr, align 8 + // CHECK: [[F01:%.*]] = getelementptr inbounds %struct.B, ptr [[B]], i32 0, i32 0 + // CHECK: [[F01VALUE:%.*]] = load i64, ptr [[F01]] + // CHECK: [[CASTF01:%.*]] = ptrtoint ptr %f01 to i64 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTF01]], i64 1276) + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[F01VALUE]], i32 1, i64 [[BLENDED]], i32 1, i64 431) +} + +// CHECK-LABEL: define i64 @other_test +__INTPTR_TYPE__ other_test(__INTPTR_TYPE__ i) { + __INTPTR_TYPE__ __ptrauth_restricted_intptr(1, 1, 42) j = 0; + // CHECK: [[J_ADDR:%.*]] = ptrtoint ptr %j to i64 + // CHECK: store i64 0, ptr %j + __INTPTR_TYPE__ __ptrauth_restricted_intptr(1, 1, 43) k = 1234; + // CHECK: [[ADDR:%.*]] = ptrtoint ptr %k to i64 + // CHECK: [[JBLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[ADDR]], i64 43) + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 1234, i32 1, i64 [[JBLENDED]]) + __INTPTR_TYPE__ __ptrauth_restricted_intptr(1, 1, 44) l = i; + // CHECK: [[I:%.*]] = load i64, ptr %i.addr + // CHECK: [[ADDR:%.*]] = ptrtoint ptr %l to i64 + // CHECK: [[LBLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[ADDR]], i64 44) + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[I]], i32 1, i64 [[LBLENDED]]) + asm volatile ("" ::: "memory"); + return j + k + l; + // CHECK: [[VALUE:%.*]] = load i64, ptr %j + // CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr %j to i64 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 42) + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]]) + // CHECK: [[VALUE:%.*]] = load i64, ptr %k + // CHECK: [[CASTK:%.*]] = ptrtoint ptr %k to i64 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTK]], i64 43) + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]]) + // CHECK: [[VALUE:%.*]] = load i64, ptr %l + // CHECK: [[CASTL:%.*]] = ptrtoint ptr %l to i64 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTL]], i64 44) + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]]) +} + +#endif diff --git a/clang/test/CodeGen/ptrauth-stripping.c b/clang/test/CodeGen/ptrauth-stripping.c new file mode 100644 index 0000000000000..5ed931085cdeb --- /dev/null +++ b/clang/test/CodeGen/ptrauth-stripping.c @@ -0,0 +1,330 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s +#if __has_feature(ptrauth_qualifier_authentication_mode) + +typedef void *NonePointer; +typedef void *__ptrauth(1, 1, 101, "strip") StripPointer; +typedef void *__ptrauth(1, 1, 102, "sign-and-strip") SignAndStripPointer; +typedef void *__ptrauth(1, 1, 103, "sign-and-auth") SignAndAuthPointer; +typedef __UINT64_TYPE__ NoneIntptr; +typedef __UINT64_TYPE__ __ptrauth_restricted_intptr(1, 0, 105, "strip") StripIntptr; +typedef __UINT64_TYPE__ __ptrauth_restricted_intptr(1, 0, 106, "sign-and-strip") SignAndStripIntptr; +typedef __UINT64_TYPE__ __ptrauth_restricted_intptr(1, 0, 107, "sign-and-auth") SignAndAuthIntptr; + +NonePointer globalNonePointer = "foo0"; +StripPointer globalStripPointer = "foo1"; +SignAndStripPointer globalSignAndStripPointer = "foo2"; +SignAndAuthPointer globalSignAndAuthPointer = "foo3"; +NoneIntptr globalNoneIntptr = (__UINT64_TYPE__)&globalNonePointer; +StripIntptr globalStripIntptr = (__UINT64_TYPE__)&globalStripPointer; +SignAndStripIntptr globalSignAndStripIntptr = (__UINT64_TYPE__)&globalSignAndStripPointer; +SignAndAuthIntptr globalSignAndAuthIntptr = (__UINT64_TYPE__)&globalSignAndAuthPointer; + +// CHECK: @.str = private unnamed_addr constant [5 x i8] c"foo0\00", align 1 +// CHECK: @globalNonePointer = global ptr @.str, align 8 +// CHECK: @.str.1 = private unnamed_addr constant [5 x i8] c"foo1\00", align 1 +// CHECK: @globalStripPointer = global ptr @.str.1, align 8 +// CHECK: @.str.2 = private unnamed_addr constant [5 x i8] c"foo2\00", align 1 +// CHECK: @globalSignAndStripPointer = global ptr ptrauth (ptr @.str.2, i32 1, i64 102, ptr @globalSignAndStripPointer), align 8 +// CHECK: @.str.3 = private unnamed_addr constant [5 x i8] c"foo3\00", align 1 +// CHECK: @globalSignAndAuthPointer = global ptr ptrauth (ptr @.str.3, i32 1, i64 103, ptr @globalSignAndAuthPointer), align 8 +// CHECK: @globalNoneIntptr = global i64 ptrtoint (ptr @globalNonePointer to i64), align 8 +// CHECK: @globalStripIntptr = global i64 ptrtoint (ptr ptrauth (ptr @globalStripPointer, i32 1, i64 105) to i64), align 8 +// CHECK: @globalSignAndStripIntptr = global i64 ptrtoint (ptr ptrauth (ptr @globalSignAndStripPointer, i32 1, i64 106) to i64), align 8 +// CHECK: @globalSignAndAuthIntptr = global i64 ptrtoint (ptr ptrauth (ptr @globalSignAndAuthPointer, i32 1, i64 107) to i64), align 8 + +typedef struct { + NonePointer ptr; + NoneIntptr i; +} NoneStruct; +typedef struct { + StripPointer ptr; + StripIntptr i; +} StripStruct; +typedef struct { + SignAndStripPointer ptr; + SignAndStripIntptr i; +} SignAndStripStruct; +typedef struct { + SignAndAuthPointer ptr; + SignAndAuthIntptr i; +} SignAndAuthStruct; + +// CHECK-LABEL: define [2 x i64] @testNone +NoneStruct testNone(NoneStruct *a, NoneStruct *b, NoneStruct c) { + globalNonePointer += 1; + // CHECK: [[GLOBALP:%.*]] = load ptr, ptr @globalNonePointer + // CHECK: [[GLOBALPP:%.*]] = getelementptr inbounds i8, ptr [[GLOBALP]], i64 1 + // CHECK: store ptr [[GLOBALPP]], ptr @globalNonePointer + globalNoneIntptr += 1; + // CHECK: [[GLOBALI:%.*]] = load i64, ptr @globalNoneIntptr + // CHECK: [[GLOBALIP:%.*]] = add i64 [[GLOBALI]], 1 + // CHECK: store i64 [[GLOBALIP]], ptr @globalNoneIntptr + a->ptr += 1; + // CHECK: [[PTR:%.*]] = load ptr, ptr %a.addr, align 8 + // CHECK: [[PTR_PTR:%.*]] = getelementptr inbounds %struct.NoneStruct, ptr [[PTR]], i32 0, i32 0 + // CHECK: [[PTR:%.*]] = load ptr, ptr [[PTR_PTR]], align 8 + // CHECK: [[AP:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 1 + // CHECK: store ptr [[AP]], ptr [[PTR_PTR]], align 8 + a->i += 1; + // CHECK: [[PTR:%.*]] = load ptr, ptr %a.addr, align 8 + // CHECK: [[I_PTR:%.*]] = getelementptr inbounds %struct.NoneStruct, ptr [[PTR]], i32 0, i32 1 + // CHECK: [[I:%.*]] = load i64, ptr [[I_PTR]], align 8 + // CHECK: [[IP:%.*]] = add i64 [[I]], 1 + // CHECK: store i64 [[IP]], ptr [[I_PTR]], align 8 + *b = *a; + // CHECK: [[B_ADDR:%.*]] = load ptr, ptr %b.addr, align 8 + // CHECK: [[A_ADDR:%.*]] = load ptr, ptr %a.addr, align 8 + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[B_ADDR]], ptr align 8 [[A_ADDR]], i64 16, i1 false) + return c; +} + +// CHECK-LABEL: @testStrip1 +void testStrip1() { + globalStripPointer += 1; + // CHECK: [[GLOBALP:%.*]] = load ptr, ptr @globalStripPointer + // CHECK: [[GLOBALPI:%.*]] = ptrtoint ptr [[GLOBALP]] to i64 + // CHECK: {{%.*}} = call i64 @llvm.ptrauth.strip(i64 [[GLOBALPI]], i32 1) +} +// CHECK-LABEL: @testStrip2 +void testStrip2(StripStruct *a) { + a->ptr += 1; + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr + // CHECK: [[PTR:%.*]] = getelementptr inbounds %struct.StripStruct, ptr [[A]], i32 0, i32 0 + // CHECK: [[APTR:%.*]] = load ptr, ptr [[PTR]] + // CHECK: [[APTRI:%.*]] = ptrtoint ptr [[APTR]] to i64 + // CHECK: {{%.*}} = call i64 @llvm.ptrauth.strip(i64 [[APTRI]], i32 1) +} +// CHECK-LABEL: @testStrip3 +void testStrip3(StripStruct *a) { + a->i += 1; + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.StripStruct, ptr [[A]], i32 0, i32 1 + // CHECK: [[I64:%.*]] = load i64, ptr [[I]] + // CHECK: {{%.*}} = call i64 @llvm.ptrauth.strip(i64 [[I64]], i32 1) +} +// CHECK-LABEL: @testStrip4 +void testStrip4(StripStruct *a, StripStruct *b) { + *b = *a; + // CHECK: call void @__copy_assignment_8_8_pa1_101_0_t8w8(ptr %0, ptr %1) +} + +// CHECK-LABEL: @testStrip5 +StripStruct testStrip5(StripStruct a) { + return a; + // CHECK: call void @__copy_constructor_8_8_pa1_101_0_t8w8(ptr %agg.result, ptr %a) +} + +// CHECK-LABEL: @testSignAndStrip1 +void testSignAndStrip1(void) { + globalSignAndStripPointer += 1; + // CHECK: [[GP:%.*]] = load ptr, ptr @globalSignAndStripPointer + // CHECK: [[GPI:%.*]] = ptrtoint ptr [[GP]] to i64 + // CHECK: [[STRIPPED:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[GPI]], i32 1) + // CHECK: [[STRIPPEDP:%.*]] = inttoptr i64 [[STRIPPED]] to ptr + // CHECK: [[PHI:%.*]] = phi ptr [ null, %entry ], [ [[STRIPPEDP]], %resign.nonnull ] + // CHECK: [[ADDPTR:%.*]] = getelementptr inbounds i8, ptr [[PHI]], i64 1 + // CHECK: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @globalSignAndStripPointer to i64), i64 102) + // CHECK: [[ADDPTRI:%.*]] = ptrtoint ptr [[ADDPTR]] to i64 + // CHECK: {{%.*}} = call i64 @llvm.ptrauth.sign(i64 [[ADDPTRI]], i32 1, i64 [[DISC]]) +} + +// CHECK-LABEL: @testSignAndStrip2 +void testSignAndStrip2(SignAndStripStruct *a) { + a->ptr += 1; + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr + // CHECK: %ptr = getelementptr inbounds %struct.SignAndStripStruct, ptr [[A]], i32 0, i32 0 + // CHECK: [[APTR:%.*]] = load ptr, ptr %ptr + // CHECK: [[APTRI:%.*]] = ptrtoint ptr [[APTR]] to i64 + // CHECK: [[STRIPPED:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[APTRI]], i32 1) + // CHECK: [[STRIPPEDP:%.*]] = inttoptr i64 [[STRIPPED]] to ptr + // CHECK: [[PHI:%.*]] = phi ptr [ null, %entry ], [ [[STRIPPEDP]], %resign.nonnull ] + // CHECK: %add.ptr = getelementptr inbounds i8, ptr [[PHI]], i64 1 + // CHECK: [[PTRI:%.*]] = ptrtoint ptr %ptr to i64 + // CHECK: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[PTRI]], i64 102) + // CHECK: [[APTRI:%.*]] = ptrtoint ptr %add.ptr to i64 + // CHECK: call i64 @llvm.ptrauth.sign(i64 [[APTRI]], i32 1, i64 [[DISC]]) +} + +// CHECK-LABEL: @testSignAndStrip3 +void testSignAndStrip3(SignAndStripStruct *a) { + a->i += 1; + // CHECK: [[A:%.*]] = load ptr, ptr %a.addr + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.SignAndStripStruct, ptr [[A]], i32 0, i32 1 + // CHECK: [[I64:%.*]] = load i64, ptr [[I]] + // CHECK: [[STRIPPED:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[I64]], i32 1) + // CHECK: [[PHI:%.*]] = phi i64 [ 0, %entry ], [ [[STRIPPED]], %resign.nonnull ] + // CHECK: %add = add i64 [[PHI]], 1 + // CHECK: {{%.*}} = call i64 @llvm.ptrauth.sign(i64 %add, i32 1, i64 106) +} + +// CHECK-LABEL: @testSignAndStrip4 +void testSignAndStrip4(SignAndStripStruct *a, SignAndStripStruct *b) { + *b = *a; + // CHECK: call void @__copy_assignment_8_8_pa1_102_0_t8w8(ptr %0, ptr %1) +} + +// CHECK-LABEL: @testSignAndStrip5 +SignAndStripStruct testSignAndStrip5(SignAndStripStruct a) { + return a; + // CHECK: call void @__copy_constructor_8_8_pa1_102_0_t8w8(ptr %agg.result, ptr %a) +} + +// CHECK-LABEL: @testSignAndAuth1 +void testSignAndAuth1() { + globalSignAndAuthPointer += 1; + // CHECK: %0 = load ptr, ptr @globalSignAndAuthPointer + // CHECK: %1 = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @globalSignAndAuthPointer to i64), i64 103) + // CHECK: %3 = ptrtoint ptr %0 to i64 + // CHECK: %4 = call i64 @llvm.ptrauth.auth(i64 %3, i32 1, i64 %1) + // CHECK: %5 = inttoptr i64 %4 to ptr + // CHECK: %6 = phi ptr [ null, %entry ], [ %5, %resign.nonnull ] + // CHECK: %add.ptr = getelementptr inbounds i8, ptr %6, i64 1 + // CHECK: %7 = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @globalSignAndAuthPointer to i64), i64 103) + // CHECK: %8 = ptrtoint ptr %add.ptr to i64 + // CHECK: %9 = call i64 @llvm.ptrauth.sign(i64 %8, i32 1, i64 %7) +} + +// CHECK-LABEL: @testSignAndAuth2 +void testSignAndAuth2(SignAndAuthStruct *a) { + a->i += 1; + // CHECK: %0 = load ptr, ptr %a.addr + // CHECK: %i = getelementptr inbounds %struct.SignAndAuthStruct, ptr %0, i32 0, i32 1 + // CHECK: %1 = load i64, ptr %i + // CHECK: %3 = call i64 @llvm.ptrauth.auth(i64 %1, i32 1, i64 107) + // CHECK: %4 = phi i64 [ 0, %entry ], [ %3, %resign.nonnull ] + // CHECK: %add = add i64 %4, 1 + // CHECK: %6 = call i64 @llvm.ptrauth.sign(i64 %add, i32 1, i64 107) + // CHECK: %7 = phi i64 [ 0, %resign.cont ], [ %6, %resign.nonnull1 ] +} + +// CHECK-LABEL: @testSignAndAuth3 +void testSignAndAuth3(SignAndAuthStruct *a) { + a->ptr += 1; + // CHECK: %0 = load ptr, ptr %a.addr + // CHECK: %ptr = getelementptr inbounds %struct.SignAndAuthStruct, ptr %0, i32 0, i32 0 + // CHECK: %1 = load ptr, ptr %ptr + // CHECK: %2 = ptrtoint ptr %ptr to i64 + // CHECK: %3 = call i64 @llvm.ptrauth.blend(i64 %2, i64 103) + // CHECK: %5 = ptrtoint ptr %1 to i64 + // CHECK: %6 = call i64 @llvm.ptrauth.auth(i64 %5, i32 1, i64 %3) + // CHECK: %7 = inttoptr i64 %6 to ptr + // CHECK: %8 = phi ptr [ null, %entry ], [ %7, %resign.nonnull ] + // CHECK: %add.ptr = getelementptr inbounds i8, ptr %8, i64 1 + // CHECK: %9 = ptrtoint ptr %ptr to i64 + // CHECK: %10 = call i64 @llvm.ptrauth.blend(i64 %9, i64 103) + // CHECK: %11 = ptrtoint ptr %add.ptr to i64 + // CHECK: %12 = call i64 @llvm.ptrauth.sign(i64 %11, i32 1, i64 %10) +} + +// CHECK-LABEL: @testSignAndAuth4 +void testSignAndAuth4(SignAndAuthStruct *a, SignAndAuthStruct *b) { + *b = *a; + // CHECK: call void @__copy_assignment_8_8_pa1_103_0_t8w8(ptr %0, ptr %1) +} + +// CHECK-LABEL: @testSignAndAuth5 +SignAndAuthStruct testSignAndAuth5(SignAndAuthStruct a) { + return a; + // CHECK: call void @__copy_constructor_8_8_pa1_103_0_t8w8(ptr %agg.result, ptr %a) +} + +// CHECK-LABEL: @testCoercions1 +void testCoercions1(StripStruct *a, SignAndStripStruct *b) { + a->ptr = b->ptr; + // CHECK: %0 = load ptr, ptr %a.addr + // CHECK: %ptr = getelementptr inbounds %struct.StripStruct, ptr %0, i32 0, i32 0 + // CHECK: %1 = load ptr, ptr %b.addr + // CHECK: %ptr1 = getelementptr inbounds %struct.SignAndStripStruct, ptr %1, i32 0, i32 0 + // CHECK: %2 = load ptr, ptr %ptr1 + // CHECK: %3 = ptrtoint ptr %ptr1 to i64 + // CHECK: %4 = call i64 @llvm.ptrauth.blend(i64 %3, i64 102) + // CHECK: %8 = ptrtoint ptr %2 to i64 + // CHECK: %9 = call i64 @llvm.ptrauth.strip(i64 %8, i32 1) +} + +// CHECK-LABEL: @testCoercions2 +void testCoercions2(StripStruct *a, SignAndAuthStruct *b) { + b->ptr = a->ptr; + // CHECK: store ptr %a, ptr %a.addr + // CHECK: store ptr %b, ptr %b.addr + // CHECK: %0 = load ptr, ptr %b.addr + // CHECK: %ptr = getelementptr inbounds %struct.SignAndAuthStruct, ptr %0, i32 0, i32 0 + // CHECK: %1 = load ptr, ptr %a.addr + // CHECK: %ptr1 = getelementptr inbounds %struct.StripStruct, ptr %1, i32 0, i32 0 + // CHECK: %2 = load ptr, ptr %ptr1 + // CHECK: %3 = ptrtoint ptr %ptr1 to i64 + // CHECK: %4 = call i64 @llvm.ptrauth.blend(i64 %3, i64 101) + // CHECK: %5 = ptrtoint ptr %ptr to i64 + // CHECK: %6 = call i64 @llvm.ptrauth.blend(i64 %5, i64 103) + // CHECK: %7 = icmp ne ptr %2, null + // CHECK: %8 = ptrtoint ptr %2 to i64 + // CHECK: %9 = call i64 @llvm.ptrauth.strip(i64 %8, i32 1) + // CHECK: %10 = inttoptr i64 %9 to ptr + // CHECK: %11 = ptrtoint ptr %10 to i64 + // CHECK: %12 = call i64 @llvm.ptrauth.sign(i64 %11, i32 1, i64 %6) +} + +// CHECK-LABEL: @testCoercions3 +void testCoercions3(SignAndStripStruct *a, SignAndAuthStruct *b) { + a->ptr = b->ptr; + // CHECK: %0 = load ptr, ptr %a.addr + // CHECK: %ptr = getelementptr inbounds %struct.SignAndStripStruct, ptr %0, i32 0, i32 0 + // CHECK: %1 = load ptr, ptr %b.addr + // CHECK: %ptr1 = getelementptr inbounds %struct.SignAndAuthStruct, ptr %1, i32 0, i32 0 + // CHECK: %2 = load ptr, ptr %ptr1 + // CHECK: %3 = ptrtoint ptr %ptr1 to i64 + // CHECK: %4 = call i64 @llvm.ptrauth.blend(i64 %3, i64 103) + // CHECK: %5 = ptrtoint ptr %ptr to i64 + // CHECK: %6 = call i64 @llvm.ptrauth.blend(i64 %5, i64 102) + // CHECK: %8 = ptrtoint ptr %2 to i64 + // CHECK: %9 = call i64 @llvm.ptrauth.auth(i64 %8, i32 1, i64 %4) + // CHECK: %10 = inttoptr i64 %9 to ptr + // CHECK: %11 = ptrtoint ptr %10 to i64 + // CHECK: %12 = call i64 @llvm.ptrauth.sign(i64 %11, i32 1, i64 %6) + // CHECK: %13 = inttoptr i64 %12 to ptr + // CHECK: %14 = phi ptr [ null, %entry ], [ %13, %resign.nonnull ] +} + +// CHECK-LABEL: @testCoercions4 +void testCoercions4(StripStruct *a, SignAndStripStruct *b) { + a->i = b->i; + // CHECK: store ptr %a, ptr %a.addr + // CHECK: store ptr %b, ptr %b.addr + // CHECK: %0 = load ptr, ptr %a.addr + // CHECK: %i = getelementptr inbounds %struct.StripStruct, ptr %0, i32 0, i32 1 + // CHECK: %1 = load ptr, ptr %b.addr + // CHECK: %i1 = getelementptr inbounds %struct.SignAndStripStruct, ptr %1, i32 0, i32 1 + // CHECK: %2 = load i64, ptr %i1 + // CHECK: %4 = call i64 @llvm.ptrauth.strip(i64 %2, i32 1) + // CHECK: %5 = phi i64 [ 0, %entry ], [ %4, %resign.nonnull ] +} + +// CHECK-LABEL: @testCoercions5 +void testCoercions5(StripStruct *a, SignAndAuthStruct *b) { + b->i = a->i; + // CHECK: %0 = load ptr, ptr %b.addr + // CHECK: %i = getelementptr inbounds %struct.SignAndAuthStruct, ptr %0, i32 0, i32 1 + // CHECK: %1 = load ptr, ptr %a.addr + // CHECK: %i1 = getelementptr inbounds %struct.StripStruct, ptr %1, i32 0, i32 1 + // CHECK: %2 = load i64, ptr %i1 + // CHECK: %4 = call i64 @llvm.ptrauth.strip(i64 %2, i32 1) + // CHECK: %5 = call i64 @llvm.ptrauth.sign(i64 %4, i32 1, i64 107) + // CHECK: %6 = phi i64 [ 0, %entry ], [ %5, %resign.nonnull ] + // CHECK: store i64 %6, ptr %i +} + +// CHECK-LABEL: @testCoercions6 +void testCoercions6(SignAndStripStruct *a, SignAndAuthStruct *b) { + a->i = b->i; + // CHECK: %0 = load ptr, ptr %a.addr + // CHECK: %i = getelementptr inbounds %struct.SignAndStripStruct, ptr %0, i32 0, i32 1 + // CHECK: %1 = load ptr, ptr %b.addr + // CHECK: %i1 = getelementptr inbounds %struct.SignAndAuthStruct, ptr %1, i32 0, i32 1 + // CHECK: %2 = load i64, ptr %i1 + // CHECK: %3 = icmp ne i64 %2, 0 + // CHECK: %4 = call i64 @llvm.ptrauth.auth(i64 %2, i32 1, i64 107) + // CHECK: %5 = call i64 @llvm.ptrauth.sign(i64 %4, i32 1, i64 106) + // CHECK: %6 = phi i64 [ 0, %entry ], [ %5, %resign.nonnull ] +} + +#endif diff --git a/clang/test/CodeGenCXX/ptrauth-authenticated-null-values.cpp b/clang/test/CodeGenCXX/ptrauth-authenticated-null-values.cpp new file mode 100644 index 0000000000000..cfd0506997636 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-authenticated-null-values.cpp @@ -0,0 +1,489 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -no-enable-noundef-analysis %s -O0 -o - | FileCheck %s + +// This is largely a duplicate of CodeGen/ptrauth-authenticated-null-values.c as +// there are C++ specific branches in some struct init and copy implementations +// so we want to be sure that the behaviour is still correct in C++ mode. + +typedef void *__ptrauth(2, 0, 0, "authenticates-null-values") authenticated_null; +typedef void *__ptrauth(2, 1, 0, "authenticates-null-values") authenticated_null_addr_disc; +typedef void *__ptrauth(2, 0, 0) unauthenticated_null; + +int test_global; + +// CHECK: define void @f0(ptr [[AUTH1_ARG:%.*]], ptr [[AUTH2_ARG:%.*]]) +extern "C" void f0(authenticated_null *auth1, authenticated_null *auth2) { + *auth1 = *auth2; + // CHECK: [[AUTH1_ARG]].addr = alloca ptr + // CHECK: [[AUTH2_ARG]].addr = alloca ptr + // CHECK: store ptr [[AUTH1_ARG]], ptr [[AUTH1_ARG]].addr + // CHECK: store ptr [[AUTH2_ARG]], ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH1_ADDR:%.*]] = load ptr, ptr [[AUTH1_ARG]].addr + // CHECK: [[AUTH2_ADDR:%.*]] = load ptr, ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH2_VALUE:%.*]] = load ptr, ptr [[AUTH2_ADDR]] + // CHECK: store ptr [[AUTH2_VALUE]], ptr [[AUTH1_ADDR]] +} + +// CHECK: define void @f1(ptr [[AUTH1_ARG:%.*]], ptr [[AUTH2_ARG:%.*]]) +extern "C" void f1(unauthenticated_null *auth1, authenticated_null *auth2) { + *auth1 = *auth2; + // CHECK: [[ENTRY:.*]]: + // CHECK: [[AUTH1_ARG]].addr = alloca ptr + // CHECK: [[AUTH2_ARG]].addr = alloca ptr + // CHECK: store ptr [[AUTH1_ARG]], ptr [[AUTH1_ARG]].addr + // CHECK: store ptr [[AUTH2_ARG]], ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH1_ADDR:%.*]] = load ptr, ptr [[AUTH1_ARG]].addr + // CHECK: [[AUTH2_ADDR:%.*]] = load ptr, ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH2:%.*]] = load ptr, ptr [[AUTH2_ADDR]] + // CHECK: [[CAST_VALUE:%.*]] = ptrtoint ptr %2 to i64 + // CHECK: [[AUTHED_VALUE:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VALUE]], i32 2, i64 0) + // CHECK: [[TRUE_VALUE:%.*]] = inttoptr i64 [[AUTHED_VALUE]] to ptr + // CHECK: [[COMPARISON:%.*]] = icmp ne ptr [[TRUE_VALUE]], null + // CHECK: br i1 [[COMPARISON]], label %resign.nonnull, label %resign.cont + // CHECK: resign.nonnull: + // CHECK: %7 = ptrtoint ptr %2 to i64 + // CHECK: %8 = call i64 @llvm.ptrauth.resign(i64 %7, i32 2, i64 0, i32 2, i64 0) + // CHECK: %9 = inttoptr i64 %8 to ptr + // CHECK: br label %resign.cont + // CHECK: resign.cont: + // CHECK: [[RESULT:%.*]] = phi ptr [ null, %entry ], [ %9, %resign.nonnull ] + // CHECK: store ptr [[RESULT]], ptr [[AUTH1_ADDR]] +} + +// CHECK: define void @f2(ptr [[AUTH1_ARG:%.*]], ptr [[AUTH2_ARG:%.*]]) +extern "C" void f2(authenticated_null *auth1, unauthenticated_null *auth2) { + *auth1 = *auth2; + // CHECK: [[ENTRY:.*]]: + // CHECK: [[AUTH1_ARG]].addr = alloca ptr + // CHECK: [[AUTH2_ARG]].addr = alloca ptr + // CHECK: store ptr [[AUTH1_ARG]], ptr [[AUTH1_ARG]].addr + // CHECK: store ptr [[AUTH2_ARG]], ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH1_ADDR:%.*]] = load ptr, ptr [[AUTH1_ARG]].addr + // CHECK: [[AUTH2_ADDR:%.*]] = load ptr, ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH2:%.*]] = load ptr, ptr [[AUTH2_ADDR]] + // CHECK: [[COMPARE:%.*]] = icmp ne ptr [[AUTH2]], null + // CHECK: br i1 [[COMPARE]], label %[[NON_NULL:resign.*]], label %[[NULL:resign.*]] + // CHECK: [[NULL]]: + // CHECK: [[SIGNED_ZERO:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[SIGNED_NULL:%.*]] = inttoptr i64 [[SIGNED_ZERO]] to ptr + // CHECK: br label %[[CONT:resign.*]] + // CHECK: [[NON_NULL]]: + // CHECK: [[AUTH2_CAST:%.*]] = ptrtoint ptr [[AUTH2]] to i64 + // CHECK: [[AUTH2_AUTHED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[AUTH2_CAST]], i32 2, i64 0, i32 2, i64 0) + // CHECK: [[AUTH2:%.*]] = inttoptr i64 [[AUTH2_AUTHED]] to ptr + // CHECK: br label %[[CONT]] + + // CHECK: [[CONT]]: + // CHECK: [[RESULT:%.*]] = phi ptr [ [[SIGNED_NULL]], %[[NULL]] ], [ [[AUTH2]], %[[NON_NULL]] ] + // CHECK: store ptr [[RESULT]], ptr [[AUTH1_ADDR]] +} + +// CHECK: define void @f3(ptr [[AUTH1:%.*]], ptr [[I:%.*]]) +extern "C" void f3(authenticated_null *auth1, void *i) { + *auth1 = i; + // CHECK: [[AUTH1_ADDR:%.*]] = alloca ptr + // CHECK: [[I_ADDR:%.*]] = alloca ptr + // CHECK: store ptr [[AUTH1]], ptr [[AUTH1_ADDR]] + // CHECK: store ptr [[I]], ptr [[I_ADDR]] + // CHECK: [[AUTH1:%.*]] = load ptr, ptr [[AUTH1_ADDR]] + // CHECK: [[I:%.*]] = load ptr, ptr [[I_ADDR]] + // CHECK: [[CAST_I:%.*]] = ptrtoint ptr [[I]] to i64 + // CHECK: [[SIGNED_CAST_I:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CAST_I]], i32 2, i64 0) + // CHECK: [[SIGNED_I:%.*]] = inttoptr i64 [[SIGNED_CAST_I]] to ptr + // CHECK: store ptr [[SIGNED_I]], ptr [[AUTH1]] +} + +// CHECK: define void @f4(ptr [[AUTH1:%.*]]) +extern "C" void f4(authenticated_null *auth1) { + *auth1 = 0; + // CHECK: [[AUTH1_ADDR:%.*]] = alloca ptr + // CHECK: store ptr [[AUTH1]], ptr [[AUTH1_ADDR]] + // CHECK: [[AUTH1:%.*]] = load ptr, ptr [[AUTH1_ADDR]] + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[RESULT:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK: store ptr [[RESULT]], ptr [[AUTH1]] +} + +// CHECK: define void @f5(ptr [[AUTH1:%.*]]) +extern "C" void f5(authenticated_null *auth1) { + *auth1 = &test_global; + // CHECK: [[AUTH1_ADDR:%.*]] = alloca ptr + // CHECK: store ptr [[AUTH1]], ptr [[AUTH1_ADDR]] + // CHECK: [[AUTH1:%.*]] = load ptr, ptr [[AUTH1_ADDR]] + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @test_global to i64), i32 2, i64 0) + // CHECK: [[RESULT:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK: store ptr [[RESULT]], ptr [[AUTH1]] +} + +// CHECK: define i32 @f6(ptr [[AUTH1:%.*]]) +extern "C" int f6(authenticated_null *auth1) { + return !!*auth1; + // CHECK: [[AUTH1_ADDR:%.*]] = alloca ptr + // CHECK: store ptr [[AUTH1]], ptr [[AUTH1_ADDR]] + // CHECK: [[AUTH1:%.*]] = load ptr, ptr [[AUTH1_ADDR]] + // CHECK: [[AUTH1_V:%.*]] = load ptr, ptr [[AUTH1]] + // CHECK: [[CAST_AUTH1:%.*]] = ptrtoint ptr [[AUTH1_V]] to i64 + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_AUTH1]], i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[AUTHED]] to ptr + // CHECK: [[TOBOOL:%.*]] = icmp ne ptr [[CAST]], null + // CHECK: [[LNOT:%.*]] = xor i1 [[TOBOOL]], true + // CHECK: [[LNOT1:%.*]] = xor i1 [[LNOT]], true + // CHECK: [[LNOT_EXT:%.*]] = zext i1 [[LNOT1]] to i32 + // CHECK: ret i32 [[LNOT_EXT]] +} + +// CHECK: define void @f7(ptr [[AUTH1_ARG:%.*]], ptr [[AUTH2_ARG:%.*]]) +extern "C" void f7(authenticated_null_addr_disc *auth1, authenticated_null_addr_disc *auth2) { + *auth1 = *auth2; + // CHECK: [[AUTH1_ARG]].addr = alloca ptr + // CHECK: [[AUTH2_ARG]].addr = alloca ptr + // CHECK: store ptr [[AUTH1_ARG]], ptr [[AUTH1_ARG]].addr + // CHECK: store ptr [[AUTH2_ARG]], ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH1_ADDR:%.*]] = load ptr, ptr [[AUTH1_ARG]].addr + // CHECK: [[AUTH2_ADDR:%.*]] = load ptr, ptr [[AUTH2_ARG]].addr + // CHECK: [[AUTH2_VALUE:%.*]] = load ptr, ptr [[AUTH2_ADDR]] + // CHECK: [[CAST_AUTH2_ADDR:%.*]] = ptrtoint ptr [[AUTH2_ADDR]] to i64 + // CHECK: [[CAST_AUTH1_ADDR:%.*]] = ptrtoint ptr [[AUTH1_ADDR]] to i64 + // CHECK: [[CAST_AUTH2:%.*]] = ptrtoint ptr [[AUTH2_VALUE]] to i64 + // CHECK: [[RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[CAST_AUTH2]], i32 2, i64 [[CAST_AUTH2_ADDR]], i32 2, i64 [[CAST_AUTH1_ADDR]]) + // CHECK: [[CAST_RESIGNED_VALUE:%.*]] = inttoptr i64 [[RESIGNED]] to ptr + // CHECK: store ptr [[CAST_RESIGNED_VALUE]], ptr [[AUTH1_ADDR]] +} + +struct S0 { + int i; + authenticated_null p; +}; + +extern "C" void f8() { + struct S0 t = {.i = 1, .p = 0}; + // CHECK: define void @f8() + // CHECK: [[T:%.*]] = alloca %struct.S0 + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.S0, ptr [[T]], i32 0, i32 0 + // CHECK: store i32 1, ptr [[I]] + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S0, ptr [[T]], i32 0, i32 1 + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] +} + +extern "C" void f9() { + struct S0 t = {}; + // CHECK: define void @f9() + // CHECK: [[T:%.*]] = alloca %struct.S0 + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.S0, ptr [[T]], i32 0, i32 0 + // CHECK: store i32 0, ptr [[I]] + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S0, ptr [[T]], i32 0, i32 1 + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] +} + +extern "C" void f10() { + struct S0 t = {.i = 12}; + // CHECK: define void @f10() + // CHECK: [[T:%.*]] = alloca %struct.S0 + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.S0, ptr [[T]], i32 0, i32 0 + // CHECK: store i32 12, ptr [[I]] + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S0, ptr [[T]], i32 0, i32 1 + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] +} + +struct S1 { + authenticated_null p; + authenticated_null_addr_disc q; +}; + +extern "C" void f11() { + struct S1 t = {.p = (void *)1}; + // CHECK-LABEL: define void @f11() + // CHECK: [[T:%.*]] = alloca %struct.S1 + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S1, ptr [[T]], i32 0, i32 0 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 1, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] + // CHECK: [[Q:%.*]] = getelementptr inbounds %struct.S1, ptr [[T]], i32 0, i32 1 + // CHECK: [[ADDR_DISC:%.*]] = ptrtoint ptr [[Q]] to i64 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 [[ADDR_DISC]]) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[Q]] +} + +extern "C" void f12() { + struct S1 t = {.p = (void *)1, .q = (void *)0}; + // CHECK-LABEL: define void @f12() + // CHECK: [[T:%.*]] = alloca %struct.S1 + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S1, ptr [[T]], i32 0, i32 0 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 1, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] + // CHECK: [[Q:%.*]] = getelementptr inbounds %struct.S1, ptr [[T]], i32 0, i32 1 + // CHECK: [[ADDR_DISC:%.*]] = ptrtoint ptr [[Q]] to i64 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 [[ADDR_DISC]]) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[Q]] +} + +extern "C" void f13() { + struct S1 t = {.q = (void *)1}; + // CHECK: define void @f13() + // CHECK: [[T:%.*]] = alloca %struct.S1 + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S1, ptr [[T]], i32 0, i32 0 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] + // CHECK: [[Q:%.*]] = getelementptr inbounds %struct.S1, ptr [[T]], i32 0, i32 1 + // CHECK: [[ADDR_DISC:%.*]] = ptrtoint ptr [[Q]] to i64 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 1, i32 2, i64 [[ADDR_DISC]]) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[Q]] +} + +struct S2 { + int i; + struct S1 s1; +}; + +extern "C" void f14() { + struct S2 t = {}; + // CHECK-LABEL: define void @f14 + // CHECK: [[T:%.*]] = alloca %struct.S2 + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.S2, ptr [[T]], i32 0, i32 0 + // CHECK: store i32 0, ptr [[I]] + // CHECK: [[S1:%.*]] = getelementptr inbounds %struct.S2, ptr [[T]], i32 0, i32 1 + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S1, ptr [[S1]], i32 0, i32 0 + // CHECK: [[SIGNED_INT:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[SIGNED:%.*]] = inttoptr i64 [[SIGNED_INT]] to ptr + // CHECK: store ptr [[SIGNED]], ptr [[P]] + // CHECK: [[Q:%.*]] = getelementptr inbounds %struct.S1, ptr [[S1]], i32 0, i32 1 + // CHECK: [[ADDR_DISC:%.*]] = ptrtoint ptr [[Q]] to i64 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 [[ADDR_DISC]]) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[Q]] +} + +extern "C" void f15() { + struct S2 t = {.s1 = {}}; + // CHECK-LABEL: define void @f15 + // CHECK: [[T:%.*]] = alloca %struct.S2 + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.S2, ptr [[T]], i32 0, i32 0 + // CHECK: store i32 0, ptr [[I]] + // CHECK: [[S1:%.*]] = getelementptr inbounds %struct.S2, ptr [[T]], i32 0, i32 1 + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S1, ptr [[S1]], i32 0, i32 0 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] + // CHECK: [[Q:%.*]] = getelementptr inbounds %struct.S1, ptr [[S1]], i32 0, i32 1 + // CHECK: [[ADDR_DISC:%.*]] = ptrtoint ptr [[Q]] to i64 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 [[ADDR_DISC]]) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[Q]] +} + +extern "C" void f16() { + struct S2 t = {.i = 13}; + // CHECK-LABEL: define void @f16 + // CHECK: [[T:%.*]] = alloca %struct.S2 + // CHECK: [[I:%.*]] = getelementptr inbounds %struct.S2, ptr [[T]], i32 0, i32 0 + // CHECK: store i32 13, ptr [[I]] + // CHECK: [[S1:%.*]] = getelementptr inbounds %struct.S2, ptr [[T]], i32 0, i32 1 + // CHECK: [[P:%.*]] = getelementptr inbounds %struct.S1, ptr [[S1]], i32 0, i32 0 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[P]] + // CHECK: [[Q:%.*]] = getelementptr inbounds %struct.S1, ptr [[S1]], i32 0, i32 1 + // CHECK: [[ADDR_DISC:%.*]] = ptrtoint ptr [[Q]] to i64 + // CHECK: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 [[ADDR_DISC]]) + // CHECK: [[CAST:%.*]] = inttoptr i64 [[SIGN]] to ptr + // CHECK: store ptr [[CAST]], ptr [[Q]] +} + +extern "C" void f17(struct S2 a, struct S2 b) { + a = b; + // CHECK-LABEL: define void @f17 + // CHECK: %call = call nonnull align 8 dereferenceable(24) ptr @_ZN2S2aSERKS_(ptr +} + +// CHECK-LABEL: define linkonce_odr nonnull align 8 dereferenceable(24) ptr @_ZN2S2aSERKS_ +// CHECK: %call = call nonnull align 8 dereferenceable(16) ptr @_ZN2S1aSERKS_(ptr + +struct Subclass : S1 { + int z; +}; + +extern "C" void f18() { + Subclass t = Subclass(); + // CHECK-LABEL: define void @f18 + // CHECK: %t = alloca %struct.Subclass + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %t, ptr align 8 @0, i64 24, i1 false) + // CHECK: [[A:%.*]] = getelementptr inbounds %struct.Subclass, ptr %t, i32 0, i32 0 + // CHECK: [[B:%.*]] = getelementptr inbounds %struct.S1, ptr [[A]], i32 0, i32 0 + // CHECK: [[C:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: [[D:%.*]] = inttoptr i64 [[C]] to ptr + // CHECK: store ptr [[D]], ptr [[B]] + // CHECK: [[E:%.*]] = getelementptr inbounds %struct.S1, ptr [[A]], i32 0, i32 1 + // CHECK: [[F:%.*]] = ptrtoint ptr [[E]] to i64 + // CHECK: [[G:%.*]] = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 [[F]]) + // CHECK: [[H:%.*]] = inttoptr i64 [[G]] to ptr + // CHECK: store ptr [[H]], ptr [[E]] +} + +extern "C" void f19() { + Subclass t = {}; + // CHECK-LABEL: define void @f19 + // CHECK: %t = alloca %struct.Subclass + // CHECK: %0 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: %1 = inttoptr i64 %0 to ptr + // CHECK: store ptr %1, ptr %p + // CHECK: %q = getelementptr inbounds %struct.S1, ptr %t, i32 0, i32 1 + // CHECK: %2 = ptrtoint ptr %q to i64 + // CHECK: %3 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 %2) + // CHECK: %4 = inttoptr i64 %3 to ptr + // CHECK: store ptr %4, ptr %q, align 8 + // CHECK: %z = getelementptr inbounds %struct.Subclass, ptr %t, i32 0, i32 1 + // CHECK: store i32 0, ptr %z, align 8 +} + +extern "C" void f21(Subclass *s1, Subclass *s2) { + *s1 = *s2; + // CHECK-LABEL: define void @f21 + // CHECK: %call = call nonnull align 8 dereferenceable(20) ptr @_ZN8SubclassaSERKS_ +} + +struct S3 { + int *__ptrauth(2, 0, 0, "authenticates-null-values") f0; +}; + +extern "C" void f22() { + struct S3 s; + // CHECK-LABEL: define void @f22() + // CHECK: [[S:%.*]] = alloca %struct.S3 + // CHECK: ret void +} + +struct S4 : virtual S3 { + authenticated_null_addr_disc new_field; +}; + +extern "C" void f23() { + struct S4 s = {}; + // CHECK-LABEL: define void @f23() + // CHECK: %s = alloca %struct.S4 + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %s, ptr align 8 @1, i64 24, i1 false) + // CHECK: %0 = getelementptr inbounds %struct.S4, ptr %s, i32 0, i32 2 + // CHECK: %1 = getelementptr inbounds %struct.S3, ptr %0, i32 0, i32 0 + // CHECK: %2 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) + // CHECK: %3 = inttoptr i64 %2 to ptr + // CHECK: store ptr %3, ptr %1 + // CHECK: %4 = getelementptr inbounds %struct.S4, ptr %s, i32 0, i32 1 + // CHECK: %5 = ptrtoint ptr %4 to i64 + // CHECK: %6 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 %5) + // CHECK: %7 = inttoptr i64 %6 to ptr + // CHECK: store ptr %7, ptr %4 + // CHECK: %call = call ptr @_ZN2S4C1Ev + // CHECK: foobar: + asm("foobar:"); +} + +struct S5 : S1 { + S5() : S1() {} +}; + +extern "C" void f24() { + struct S5 s = {}; +} + +struct S6 { + int i; + authenticated_null p; + S6(){}; +}; + +extern "C" void f25() { + struct S6 s = {}; +} + +struct S7 { + void* __ptrauth(1,1,1) field1; + void* __ptrauth(1,1,1) field2; +}; + +extern "C" void f26() { + int i = 0; + struct S7 s = { .field2 = &i}; + // CHECK: %field1 = getelementptr inbounds %struct.S7, ptr %s, i32 0, i32 0 + // CHECK: store ptr null, ptr %field1 + // CHECK: %field2 = getelementptr inbounds %struct.S7, ptr %s, i32 0, i32 1 + // CHECK: %0 = ptrtoint ptr %field2 to i64 + // CHECK: %1 = call i64 @llvm.ptrauth.blend(i64 %0, i64 1) + // CHECK: %3 = call i64 @llvm.ptrauth.sign(i64 %2, i32 1, i64 %1) + // CHECK: %4 = inttoptr i64 %3 to ptr + // CHECK: store ptr %4, ptr %field2 +} + +struct AStruct; +const AStruct &foo(); +class AClass { + public: + AClass() {} + private: + virtual void f(); +}; +void AClass::f() { + const struct { + unsigned long a; + const AStruct &b; + unsigned long c; + unsigned long d; + unsigned long e; + } test []= {{ 0, foo(), 0, 0, 0 }}; + +} +AClass global; + + +// struct S1 copy constructor +// CHECK-LABEL: define linkonce_odr nonnull align 8 dereferenceable(16) ptr @_ZN2S1aSERKS_ +// CHECK: %this.addr = alloca ptr +// CHECK: [[ADDR:%.*]] = alloca ptr +// CHECK: store ptr %this, ptr %this.addr +// CHECK: store ptr %0, ptr [[ADDR]] +// CHECK: %this1 = load ptr, ptr %this.addr +// CHECK: %p = getelementptr inbounds %struct.S1, ptr %this1, i32 0, i32 0 +// CHECK: [[S1PTR:%.*]] = load ptr, ptr [[ADDR]] +// CHECK: %p2 = getelementptr inbounds %struct.S1, ptr [[S1PTR]], i32 0, i32 0 +// CHECK: [[P2:%.*]] = load ptr, ptr %p2 +// CHECK: store ptr [[P2]], ptr %p +// CHECK: %q = getelementptr inbounds %struct.S1, ptr %this1, i32 0, i32 1 +// CHECK: [[S1PTR:%.*]] = load ptr, ptr [[ADDR]] +// CHECK: %q3 = getelementptr inbounds %struct.S1, ptr [[S1PTR]], i32 0, i32 1 +// CHECK: [[Q3_ADDR:%.*]] = load ptr, ptr %q3 +// CHECK: [[Q3:%.*]] = ptrtoint ptr %q3 to i64 +// CHECK: [[Q:%.*]] = ptrtoint ptr %q to i64 +// CHECK: [[DISC:%.*]] = ptrtoint ptr [[Q3_ADDR]] to i64 +// CHECK: [[RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[DISC]], i32 2, i64 [[Q3]], i32 2, i64 [[Q]]) +// CHECK: [[CAST:%.*]] = inttoptr i64 [[RESIGNED]] to ptr +// CHECK: store ptr [[CAST]], ptr %q + +// CHECK-LABEL: define linkonce_odr ptr @_ZN2S5C2Ev +// CHECK: %this.addr = alloca ptr +// CHECK: store ptr %this, ptr %this.addr +// CHECK: %this1 = load ptr, ptr %this.addr +// CHECK: %0 = getelementptr inbounds i8, ptr %this1, i64 0 +// CHECK: call void @llvm.memset.p0.i64(ptr align 8 %0, i8 0, i64 16, i1 false) +// CHECK: %1 = getelementptr inbounds %struct.S1, ptr %this1, i32 0, i32 0 +// CHECK: %2 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 0) +// CHECK: %3 = inttoptr i64 %2 to ptr +// CHECK: store ptr %3, ptr %1 +// CHECK: %4 = getelementptr inbounds %struct.S1, ptr %this1, i32 0, i32 1 +// CHECK: %5 = ptrtoint ptr %4 to i64 +// CHECK: %6 = call i64 @llvm.ptrauth.sign(i64 0, i32 2, i64 %5) +// CHECK: %7 = inttoptr i64 %6 to ptr +// CHECK: store ptr %7, ptr %4 + +// CHECK-LABEL: define linkonce_odr ptr @_ZN2S6C2Ev +// CHECK: %this.addr = alloca ptr +// CHECK: store ptr %this, ptr %this.addr +// CHECK: %this1 = load ptr, ptr %this.addr +// CHECK: ret ptr %this1 diff --git a/clang/test/CodeGenObjC/ptrauth-class.m b/clang/test/CodeGenObjC/ptrauth-class.m new file mode 100644 index 0000000000000..db57b08a07cf1 --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-class.m @@ -0,0 +1,103 @@ +// RUN: %clang_cc1 -Wno-everything -fblocks -fptrauth-intrinsics -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -no-enable-noundef-analysis -fobjc-arc -O2 -disable-llvm-passes -o - %s | FileCheck %s + +#if __has_feature(ptrauth_objc_signable_class) +struct TestStruct { + __ptrauth(2, 1, 1234) Class isa; +}; + +@interface TestClass { +@public + __ptrauth(2, 1, 1234) Class isa; +} +@end + +struct TestConstStruct { + __ptrauth(2, 1, 1234) const Class isa; + __ptrauth(2, 1, 1234) volatile Class visa; +}; + +@interface TestConstClass { +@public + __ptrauth(2, 1, 1234) const Class isa; + __ptrauth(2, 1, 1234) volatile Class visa; +} +@end + +// CHECK-LABEL: define void @setTestStructIsa(ptr %t, ptr %c) #0 { +void setTestStructIsa(struct TestStruct *t, Class c) { + t->isa = c; + // CHECK: [[T_ADDR:%.*]] = alloca ptr, align 8 + // CHECK: [[C_ADDR:%.*]] = alloca ptr, align 8 + // CHECK: store ptr %c, ptr [[C_ADDR]], align 8 + // CHECK: [[ISA_SLOT:%.*]] = getelementptr inbounds %struct.TestStruct, ptr %0, i32 0, i32 0 + // CHECK: [[C:%.*]] = load ptr, ptr %c.addr, align 8 + // CHECK: [[CAST_ISA_SLOT:%.*]] = ptrtoint ptr [[ISA_SLOT]] to i64 + // CHECK: [[BLENDED_VALUE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ISA_SLOT]], i64 1234) + // CHECK: [[CAST_C:%.*]] = ptrtoint ptr [[C]] to i64 + // CHECK: [[AUTHENTICATED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CAST_C]], i32 2, i64 [[BLENDED_VALUE]]) +} + +// CHECK-LABEL: define void @setTestClassIsa(ptr %t, ptr %c) #0 { +void setTestClassIsa(TestClass *t, Class c) { + t->isa = c; + // CHECK: [[T_ADDR:%.*]] = alloca ptr, align 8 + // CHECK: [[C_ADDR:%.*]] = alloca ptr, align 8 + // CHECK: store ptr %c, ptr [[C_ADDR]], align 8 + // CHECK: [[T:%.*]] = load ptr, ptr [[T_ADDR]], align 8 + // CHECK: [[IVAR_OFFSET32:%.*]] = load i32, ptr @"OBJC_IVAR_$_TestClass.isa", align 8 + // CHECK: [[IVAR_OFFSET64:%.*]] = sext i32 [[IVAR_OFFSET32]] to i64 + // CHECK: [[ADDED_PTR:%.*]] = getelementptr inbounds i8, ptr %1, i64 [[IVAR_OFFSET64]] + // CHECK: [[C_VALUE:%.*]] = load ptr, ptr [[C_ADDR]], align 8 + // CHECK: [[CAST_ISA_SLOT:%.*]] = ptrtoint ptr [[ADDED_PTR]] to i64 + // CHECK: [[BLENDED_VALUE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ISA_SLOT]], i64 1234) + // CHECK: [[CAST_C_VALUE:%.*]] = ptrtoint ptr [[C_VALUE]] to i64 + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CAST_C_VALUE]], i32 2, i64 [[BLENDED_VALUE]]) +} + +// CHECK-LABEL: define ptr @getTestStructIsa(ptr %t) #0 { +Class getTestStructIsa(struct TestStruct *t) { + return t->isa; + // CHECK: [[T_ADDR:%.*]] = alloca ptr, align 8 + // CHECK: [[T_VALUE:%.*]] = load ptr, ptr [[T_ADDR]], align 8 + // CHECK: [[ISA_SLOT:%.*]] = getelementptr inbounds %struct.TestStruct, ptr [[T_VALUE]], i32 0, i32 0 + // CHECK: [[ISA_VALUE:%.*]] = load ptr, ptr [[ISA_SLOT]], align 8 + // CHECK: [[CAST_ISA_SLOT:%.*]] = ptrtoint ptr %isa to i64 + // CHECK: [[BLENDED_VALUE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ISA_SLOT]], i64 1234) + // CHECK: [[CAST_ISA_VALUE:%.*]] = ptrtoint ptr [[ISA_VALUE]] to i64 + // CHECK: [[SIGNED_VALUE:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_ISA_VALUE]], i32 2, i64 [[BLENDED_VALUE]]) +} + +// CHECK-LABEL: define ptr @getTestClassIsa(ptr %t) #0 { +Class getTestClassIsa(TestClass *t) { + return t->isa; + // CHECK: [[T_ADDR:%.*]] = alloca ptr, align 8 + // CHECK: [[T:%.*]] = load ptr, ptr [[T_ADDR]], align 8 + // CHECK: [[IVAR:%.*]] = load i32, ptr @"OBJC_IVAR_$_TestClass.isa", align 8 + // CHECK: [[IVAR_CONV:%.*]] = sext i32 [[IVAR]] to i64 + // CHECK: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[T]], i64 [[IVAR_CONV]] + // CHECK: [[LOADED_VALUE:%.*]] = load ptr, ptr [[ADD_PTR]], align 8 + // CHECK: [[INT_VALUE:%.*]] = ptrtoint ptr [[ADD_PTR]] to i64 + // CHECK: [[BLENDED_VALUE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[INT_VALUE]], i64 1234) + // CHECK: [[NULL_CHECK:%.*]] = icmp ne ptr [[LOADED_VALUE]], null + // CHECK: [[CAST_VALUE:%.*]] = ptrtoint ptr [[LOADED_VALUE]] to i64 + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VALUE]], i32 2, i64 [[BLENDED_VALUE]]) +} + +// Just enough to verify we do actually authenticate qualified Class +// CHECK: define ptr @getTestConstClassIsa(ptr %t) #0 { +Class getTestConstClassIsa(TestConstClass *t) { + return t->isa; + // CHECK: [[T_ADDR:%.*]] = alloca ptr, align 8 + // CHECK: [[T:%.*]] = load ptr, ptr [[T_ADDR]], align 8 + // CHECK: [[IVAR:%.*]] = load i32, ptr @"OBJC_IVAR_$_TestConstClass.isa", align 8 + // CHECK: [[IVAR_CONV:%.*]] = sext i32 [[IVAR]] to i64 + // CHECK: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[T]], i64 [[IVAR_CONV]] + // CHECK: [[LOADED_VALUE:%.*]] = load ptr, ptr [[ADD_PTR]], align 8 + // CHECK: [[INT_VALUE:%.*]] = ptrtoint ptr [[ADD_PTR]] to i64 + // CHECK: [[BLENDED_VALUE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[INT_VALUE]], i64 1234) + // CHECK: [[NULL_CHECK:%.*]] = icmp ne ptr [[LOADED_VALUE]], null + // CHECK: [[CAST_VALUE:%.*]] = ptrtoint ptr [[LOADED_VALUE]] to i64 + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[CAST_VALUE]], i32 2, i64 [[BLENDED_VALUE]]) +} + +#endif diff --git a/clang/test/CodeGenObjC/ptrauth-property-backing.m b/clang/test/CodeGenObjC/ptrauth-property-backing.m new file mode 100644 index 0000000000000..3878ff73d4711 --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-property-backing.m @@ -0,0 +1,78 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -fexceptions -fptrauth-intrinsics -o - %s | FileCheck %s + +typedef void (*func)(); + +__attribute__((objc_root_class)) +@interface Root { + Class isa; + void *__ptrauth(1, 1, 1, "authenticates-null-values") _field1; + void *__ptrauth(1, 1, 1, "authenticates-null-values") _field2; + func __ptrauth(1, 1, 1) _field3; + func __ptrauth(1, 1, 123) _field4; +} + +@property void *field1; +@property(nonatomic) void *field2; +@property func field3; +@property(nonatomic) func field4; +@end + +@implementation Root +@end + +// CHECK-LABEL: define internal ptr @"\01-[Root field1]" +// CHECK: [[LOAD:%.*]] = load atomic i64, ptr [[ADDR:%.*]] unordered +// CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[ADDR]] to i64 +// CHECK: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 1) +// CHECK: [[RESULT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[LOAD]], i32 1, i64 [[BLEND]]) + +// CHECK-LABEL: define internal void @"\01-[Root setField1:]" +// CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[ADDR:%.*]] to i64 +// CHECK: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 1) +// CHECK: [[RESULT:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[VALUE:%.*]], i32 1, i64 [[BLEND]]) +// CHECK: store atomic i64 [[RESULT]], ptr [[ADDR]] unordered + +// CHECK-LABEL: define internal ptr @"\01-[Root field2]" +// CHECK: %ivar = load i32, ptr @"OBJC_IVAR_$_Root._field2" +// CHECK: [[LOAD:%.*]] = load ptr, ptr [[ADDR:%.*]], +// CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[ADDR]] to i64 +// CHECK: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR:%.*]], i64 1) +// CHECK: [[VALUE:%.*]] = ptrtoint ptr [[LOAD]] to i64 +// CHECK: [[RESULT:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLEND]]) + +// CHECK-LABEL: define internal void @"\01-[Root setField2:]" +// CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[ADDR:%.*]] to i64 +// CHECK: [[BLEND:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 1) +// CHECK: [[CAST_VALUE:%.*]] = ptrtoint ptr [[VALUE:%.*]] to i64 +// CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CAST_VALUE]], i32 1, i64 [[BLEND]]) +// CHECK: [[RESULT:%.*]] = inttoptr i64 [[SIGNED]] to ptr +// CHECK: store ptr [[RESULT]], ptr [[ADDR]] + +// CHECK-LABEL: define internal ptr @"\01-[Root field3]" +// CHECK: [[VALUE:%.*]] = load atomic i64, ptr [[ADDR:%.*]] unordered, align 8 +// CHECK: [[CASTED_ADDR:%.*]] = ptrtoint ptr [[ADDR]] to i64 +// CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTED_ADDR]], i64 1) +// CHECK: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[VALUE]], i32 1, i64 [[BLENDED]], i32 0, i64 0 + +// CHECK-LABEL: define internal void @"\01-[Root setField3:]" +// CHECK: [[VALUE:%.*]] = load i64, ptr {{%.*}}, align 8 +// CHECK: [[CASTED_ADDR:%.*]] = ptrtoint ptr {{%.*}} to i64 +// CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTED_ADDR]], i64 1) +// CHECK: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[VALUE]], i32 0, i64 0, i32 1, i64 [[BLENDED]]) +// CHECK: store atomic i64 + +// CHECK-LABEL: define internal ptr @"\01-[Root field4]" +// CHECK: %ivar = load i32, ptr @"OBJC_IVAR_$_Root._field4", +// CHECK: [[VALUE:%.*]] = load ptr, ptr [[ADDR:%.*]], +// CHECK: [[CASTED_ADDR:%.*]] = ptrtoint ptr [[ADDR]] to i64 +// CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTED_ADDR]], i64 123) +// CHECK: [[CAST_VALUE:%.*]] = ptrtoint ptr [[VALUE]] to i64 +// CHECK: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[CAST_VALUE]], i32 1, i64 [[BLENDED]], i32 0, i64 0) + +// CHECK-LABEL: define internal void @"\01-[Root setField4:]" +// CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr {{%.*}} to i64 +// CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 123) +// CHECK: resign.nonnull: +// CHECK: [[VALUE:%.*]] = ptrtoint ptr %1 to i64 +// CHECK: {{%.*}} = call i64 @llvm.ptrauth.resign(i64 [[VALUE]], i32 0, i64 0, i32 1, i64 [[BLENDED]]) + diff --git a/clang/test/Preprocessor/ptrauth_feature.c b/clang/test/Preprocessor/ptrauth_feature.c index bee3454388fef..b0978b99bf260 100644 --- a/clang/test/Preprocessor/ptrauth_feature.c +++ b/clang/test/Preprocessor/ptrauth_feature.c @@ -133,10 +133,10 @@ void no_ptrauth_init_fini() {} #endif #if __has_feature(ptrauth_qualifier) -// QUAL: has_ptrauth_qualifier +// INTRIN: has_ptrauth_qualifier void has_ptrauth_qualifier() {} #else -// NOQUAL: no_ptrauth_qualifier +// NOINTRIN: no_ptrauth_qualifier void no_ptrauth_qualifier() {} #endif diff --git a/clang/test/Sema/atomic-ops-ptrauth.c b/clang/test/Sema/atomic-ops-ptrauth.c new file mode 100644 index 0000000000000..65b58379c8552 --- /dev/null +++ b/clang/test/Sema/atomic-ops-ptrauth.c @@ -0,0 +1,117 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify -fptrauth-intrinsics %s + +#include + +int i; +int *__ptrauth(2, 1, 100) authenticated_ptr = &i; +int *__ptrauth(2, 0, 200) non_addr_discriminatedauthenticated_ptr = &i; +int * wat = &i; +#define ATOMIZE(p) (__typeof__(p) volatile _Atomic *)(long)(&p) + +void f() { + static int j = 1; + __c11_atomic_init(ATOMIZE(authenticated_ptr), 5); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __c11_atomic_store(ATOMIZE(authenticated_ptr), 0, memory_order_relaxed); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __c11_atomic_load(ATOMIZE(authenticated_ptr), memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __c11_atomic_store(ATOMIZE(authenticated_ptr), 1, memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __atomic_store_n(ATOMIZE(authenticated_ptr), 4, memory_order_release); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __atomic_store(ATOMIZE(authenticated_ptr), j, memory_order_release); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __c11_atomic_exchange(ATOMIZE(authenticated_ptr), 1, memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __atomic_exchange(ATOMIZE(authenticated_ptr), &j, &j, memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __c11_atomic_fetch_add(ATOMIZE(authenticated_ptr), 1, memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __atomic_fetch_add(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __atomic_fetch_sub(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __atomic_fetch_min(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __atomic_fetch_max(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __c11_atomic_fetch_and(ATOMIZE(authenticated_ptr), 1, memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __atomic_fetch_and(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __atomic_fetch_or(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + __atomic_fetch_xor(ATOMIZE(authenticated_ptr), 3, memory_order_seq_cst); + // expected-error@-1 {{address argument to atomic operation must be a pointer to a non address discriminated type ('volatile __ptrauth(2,1,100) _Atomic(int *) *' invalid)}} + + __c11_atomic_init(ATOMIZE(non_addr_discriminatedauthenticated_ptr), &j); + __c11_atomic_store(ATOMIZE(non_addr_discriminatedauthenticated_ptr), 0, memory_order_relaxed); + __c11_atomic_load(ATOMIZE(non_addr_discriminatedauthenticated_ptr), memory_order_seq_cst); + __atomic_store(&j, ATOMIZE(non_addr_discriminatedauthenticated_ptr), memory_order_release); + // expected-warning@-1 {{incompatible pointer types passing 'volatile __ptrauth(2,0,200) _Atomic(int *) *' to parameter of type 'int *'}} + __c11_atomic_exchange(ATOMIZE(j), ATOMIZE(non_addr_discriminatedauthenticated_ptr), memory_order_seq_cst); + // expected-error@-1 {{incompatible pointer to integer conversion passing 'volatile __ptrauth(2,0,200) _Atomic(int *) *' to parameter of type 'typeof (j)' (aka 'int')}} + __c11_atomic_fetch_add(ATOMIZE(non_addr_discriminatedauthenticated_ptr), ATOMIZE(j), memory_order_seq_cst); + // expected-error@-1 {{incompatible pointer to integer conversion passing 'volatile _Atomic(typeof (j)) *' to parameter of type 'long'}} + __c11_atomic_fetch_and(ATOMIZE(j), ATOMIZE(non_addr_discriminatedauthenticated_ptr), memory_order_seq_cst); + // expected-error@-1 {{incompatible pointer to integer conversion passing 'volatile __ptrauth(2,0,200) _Atomic(int *) *' to parameter of type 'typeof (j)' (aka 'int')}} + + + __sync_fetch_and_add(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + __sync_fetch_and_sub(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + __sync_fetch_and_or(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + __sync_fetch_and_and(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + __sync_fetch_and_xor(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + __sync_fetch_and_nand(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + + __sync_add_and_fetch(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + __sync_sub_and_fetch(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + __sync_or_and_fetch(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + __sync_and_and_fetch(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + __sync_xor_and_fetch(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + __sync_nand_and_fetch(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + + __sync_bool_compare_and_swap(&authenticated_ptr, 1, 0); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + __sync_val_compare_and_swap(&authenticated_ptr, 1, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + + __sync_lock_test_and_set(&authenticated_ptr, 1); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + __sync_lock_release(&authenticated_ptr); + // expected-error@-1 {{address argument to __sync operation must be a pointer to a non address discriminated type ('int *__ptrauth(2,1,100)' invalid)}} + + +int i = 0; + + __sync_fetch_and_add(&non_addr_discriminatedauthenticated_ptr, &i); + __sync_fetch_and_sub(&non_addr_discriminatedauthenticated_ptr, &i); + __sync_fetch_and_or(&non_addr_discriminatedauthenticated_ptr, &i); + __sync_fetch_and_and(&non_addr_discriminatedauthenticated_ptr, &i); + __sync_fetch_and_xor(&non_addr_discriminatedauthenticated_ptr, &i); + + __sync_add_and_fetch(&non_addr_discriminatedauthenticated_ptr, &i); + __sync_sub_and_fetch(&non_addr_discriminatedauthenticated_ptr, &i); + __sync_or_and_fetch(&non_addr_discriminatedauthenticated_ptr, &i); + __sync_and_and_fetch(&non_addr_discriminatedauthenticated_ptr, &i); + __sync_xor_and_fetch(&non_addr_discriminatedauthenticated_ptr, &i); + + __sync_bool_compare_and_swap(&non_addr_discriminatedauthenticated_ptr, &i, &i); + __sync_val_compare_and_swap(&non_addr_discriminatedauthenticated_ptr, &i, &i); + + __sync_lock_test_and_set(&non_addr_discriminatedauthenticated_ptr, &i); + __sync_lock_release(&non_addr_discriminatedauthenticated_ptr); +} diff --git a/clang/test/Sema/ptrauth-authenticated-null.c b/clang/test/Sema/ptrauth-authenticated-null.c new file mode 100644 index 0000000000000..e8ef8af734ef5 --- /dev/null +++ b/clang/test/Sema/ptrauth-authenticated-null.c @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -verify -fptrauth-intrinsics %s + +typedef void *__ptrauth(2, 1, 0, "authenticates-null-values") authenticated_null_ptr; +typedef unsigned long long __ptrauth_restricted_intptr(2, 1, 0, "authenticates-null-values") authenticated_null_uintptr; +typedef void *__ptrauth(2, 1, 0) unauthenticated_null_ptr; +typedef unsigned long long __ptrauth_restricted_intptr(2, 1, 0) unauthenticated_null_uintptr; + +struct S { + authenticated_null_ptr a; +}; + +struct S s = {0}; +// expected-error@-1 {{globals with authenticated null values are currently unsupported}} + +authenticated_null_ptr a; +// expected-error@-1 {{globals with authenticated null values are currently unsupported}} +authenticated_null_uintptr b; +// expected-error@-1 {{globals with authenticated null values are currently unsupported}} + +unauthenticated_null_ptr c; +unauthenticated_null_uintptr d; + +int func1() { + static authenticated_null_ptr auth; + // expected-error@-1 {{static locals with authenticated null values are currently unsupported}} + return *(int *)auth++; +} + +int func2() { + static authenticated_null_ptr auth; + // expected-error@-1 {{static locals with authenticated null values are currently unsupported}} + return *(int *)auth++; +} +int func3() { + static authenticated_null_uintptr auth = 0; + // expected-error@-1 {{static locals with authenticated null values are currently unsupported}} + return auth++; +} + +static authenticated_null_uintptr auth_int; +// expected-error@-1 {{globals with authenticated null values are currently unsupported}} +static authenticated_null_ptr auth; +// expected-error@-1 {{globals with authenticated null values are currently unsupported}} +int func4() { + static authenticated_null_uintptr auth; + // expected-error@-1 {{static locals with authenticated null values are currently unsupported}} + return auth++; +} +int func5() { + static struct S auth; + // expected-error@-1 {{static locals with authenticated null values are currently unsupported}} + return (int)(unsigned long long)auth.a; +} \ No newline at end of file diff --git a/clang/test/Sema/ptrauth-authenticated-null.cpp b/clang/test/Sema/ptrauth-authenticated-null.cpp new file mode 100644 index 0000000000000..711505a148deb --- /dev/null +++ b/clang/test/Sema/ptrauth-authenticated-null.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -x c++ -std=c++11 -triple arm64-apple-ios -verify -fptrauth-intrinsics %s + +typedef void *__ptrauth(2, 1, 0, "authenticates-null-values") authenticated_null_ptr; +typedef unsigned long long __ptrauth_restricted_intptr(2, 1, 0, "authenticates-null-values") authenticated_null_uintptr; +typedef void *__ptrauth(2, 1, 0) unauthenticated_null_ptr; +typedef unsigned long long __ptrauth_restricted_intptr(2, 1, 0) unauthenticated_null_uintptr; + +struct S { + authenticated_null_ptr a; +}; + +struct S s = {0}; +// expected-error@-1 {{globals with authenticated null values are currently unsupported}} + +authenticated_null_ptr a; +// expected-error@-1 {{globals with authenticated null values are currently unsupported}} +authenticated_null_uintptr b; +// expected-error@-1 {{globals with authenticated null values are currently unsupported}} + +unauthenticated_null_ptr c; +unauthenticated_null_uintptr d; + +int func1() { + static authenticated_null_ptr auth; + // expected-error@-1 {{static locals with authenticated null values are currently unsupported}} + return *((int *)auth); +} + +int func3() { + static authenticated_null_uintptr auth = 0; + // expected-error@-1 {{static locals with authenticated null values are currently unsupported}} + return auth++; +} + +static authenticated_null_uintptr auth_int; +// expected-error@-1 {{globals with authenticated null values are currently unsupported}} +static authenticated_null_ptr auth; +// expected-error@-1 {{globals with authenticated null values are currently unsupported}} +int func4() { + static S auth; + // expected-error@-1 {{static locals with authenticated null values are currently unsupported}} + return (int)(unsigned long long)auth.a; +} diff --git a/clang/test/Sema/ptrauth-qualifier.c b/clang/test/Sema/ptrauth-qualifier.c index 5b7e54e9fe61c..1ef313a96f773 100644 --- a/clang/test/Sema/ptrauth-qualifier.c +++ b/clang/test/Sema/ptrauth-qualifier.c @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify -fptrauth-intrinsics %s +#include + #if __has_feature(ptrauth_qualifier) #warning __ptrauth qualifier enabled! // expected-warning@-1 {{__ptrauth qualifier enabled!}} @@ -25,13 +27,38 @@ __ptrauth(INVALID_KEY) int invalid2; // expected-error{{200 does not identify a __ptrauth(VALID_DATA_KEY) int invalid3; // expected-error{{__ptrauth qualifier may only be applied to pointer types}} __ptrauth(VALID_DATA_KEY) int *invalid4; // expected-error{{__ptrauth qualifier may only be applied to pointer types}} int * (__ptrauth(VALID_DATA_KEY) invalid5); // expected-error{{expected identifier or '('}} expected-error{{expected ')'}} expected-note {{to match this '('}} -int * __ptrauth(VALID_DATA_KEY) __ptrauth(VALID_DATA_KEY) invalid6; // expected-error{{type 'int *__ptrauth(2,0,0)' is already __ptrauth-qualified}} +int *__ptrauth(VALID_DATA_KEY) __ptrauth(VALID_DATA_KEY) invalid6; // expected-error{{type 'int *__ptrauth(2,0,0)' is already __ptrauth-qualified}} int * __ptrauth(VALID_DATA_KEY, 2) invalid7; // expected-error{{address discrimination flag for __ptrauth must be 0 or 1; value is 2}} int * __ptrauth(VALID_DATA_KEY, -1) invalid8; // expected-error{{address discrimination flag for __ptrauth must be 0 or 1; value is -1}} int * __ptrauth(VALID_DATA_KEY, 1, -1) invalid9; // expected-error{{extra discriminator for __ptrauth must be between 0 and 65535; value is -1}} int * __ptrauth(VALID_DATA_KEY, 1, 100000) invalid10; // expected-error{{extra discriminator for __ptrauth must be between 0 and 65535; value is 100000}} -int * __ptrauth(VALID_DATA_KEY, 1, 65535, 41) invalid11; // expected-error{{__ptrauth qualifier must take between 1 and 3 arguments}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, 41) invalid11; // expected-error{{__ptrauth options must be a string of comma separated flags, found '41'}} int * __ptrauth(VALID_DATA_KEY, 1, nonConstantGlobal) invalid12; // expected-error{{argument to __ptrauth must be an integer constant expression}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "Foo") invalid13; // expected-error{{unknown __ptrauth authentication option 'Foo'}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip", 41) invalid14; // expected-error{{__ptrauth qualifier must take between 1 and 4 arguments}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,sign-and-strip") invalid15; // expected-error{{repeated __ptrauth authentication mode}} + // expected-note@-1{{previous __ptrauth authentication mode}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "isa-pointer,isa-pointer") invalid16; // expected-error{{repeated __ptrauth authentication option}} + // expected-note@-1{{previous __ptrauth authentication option}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "isa-pointer, isa-pointer") invalid17; // expected-error{{repeated __ptrauth authentication option}} + // expected-note@-1{{previous __ptrauth authentication option}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip, , isa-pointer") invalid18; // expected-error{{empty __ptrauth authentication option}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,") invalid19; // expected-error{{empty __ptrauth authentication option}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",") invalid20; // expected-error{{empty __ptrauth authentication option}} + // expected-error@-1{{empty __ptrauth authentication option}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",,") invalid21; // expected-error{{empty __ptrauth authentication option}} + // expected-error@-1{{empty __ptrauth authentication option}} + // expected-error@-2{{empty __ptrauth authentication option}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip isa-pointer") invalid22; // expected-error{{missing comma between __ptrauth options}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip\nisa-pointer") invalid23; // expected-error{{missing comma between __ptrauth options}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip" + " isa-pointer") invalid24; // expected-error{{missing comma between __ptrauth options}} +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\n,isa-pointer") invalid25; // expected-error{{empty __ptrauth authentication option}} +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\t,isa-pointer") invalid26; // expected-error{{empty __ptrauth authentication option}} +void *__ptrauth(VALID_DATA_KEY, 1, 0, "authenticates-null-values") invalid27; // expected-error{{globals with authenticated null values are currently unsupported}} +void *__ptrauth(VALID_DATA_KEY, 1, 0, "authenticates-null-values") invalid28 = 0; // expected-error{{globals with authenticated null values are currently unsupported}} + +int (* __ptrauth(ptrauth_key_none, 0, 0) invalidFP)(int); // expected-error {{signed pointer types may not be qualified}} int * __ptrauth(VALID_DATA_KEY) valid0; int * __ptrauth(VALID_DATA_KEY) *valid1; @@ -44,6 +71,27 @@ int * __ptrauth(VALID_DATA_KEY, 1) valid7; int * __ptrauth(VALID_DATA_KEY, (_Bool) 1) valid8; int * __ptrauth(VALID_DATA_KEY, 1, 0) valid9; int * __ptrauth(VALID_DATA_KEY, 1, 65535) valid10; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip") valid12; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip") valid13; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-auth") valid14; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "isa-pointer") valid15; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-auth,isa-pointer") valid15; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,isa-pointer") valid16; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip,isa-pointer") valid17; +int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip,isa-pointer") valid18; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip ,isa-pointer") valid19; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip, isa-pointer") valid20; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip,isa-pointer ") valid21; +int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip") valid22; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip ") valid23; +int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip ") valid24; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip," + "isa-pointer") valid25; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip" + ",isa-pointer") valid26; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip\n,isa-pointer") valid27; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip\t,isa-pointer") valid28; +int *__ptrauth(VALID_DATA_KEY, 1, 0, "") valid29; extern intp redeclaration0; // expected-note {{previous declaration}} extern intp __ptrauth(VALID_DATA_KEY) redeclaration0; // expected-error{{redeclaration of 'redeclaration0' with a different type: '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)') vs 'intp' (aka 'int *')}} @@ -52,14 +100,17 @@ extern intp redeclaration1; // expected-note {{previous declaration}} extern intp __ptrauth(VALID_DATA_KEY) redeclaration1; // expected-error{{redeclaration of 'redeclaration1' with a different type: '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)') vs 'intp' (aka 'int *')}} intp __ptrauth(VALID_DATA_KEY) redeclaration2; // expected-note {{previous definition}} -intp redeclaration2 = 0; // expected-error{{redefinition of 'redeclaration2' with a different type: 'intp' (aka 'int *') vs '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)')}} +intp redeclaration2 = 0; // expected-error{{redefinition of 'redeclaration2' with a different type: 'intp' (aka 'int *') vs '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)')}} intp __ptrauth(VALID_DATA_KEY) redeclaration3; // expected-note {{previous definition}} -intp redeclaration3 = 0; // expected-error{{redefinition of 'redeclaration3' with a different type: 'intp' (aka 'int *') vs '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)')}} +intp redeclaration3 = 0; // expected-error{{redefinition of 'redeclaration3' with a different type: 'intp' (aka 'int *') vs '__ptrauth(2,0,0) intp' (aka 'int *__ptrauth(2,0,0)')}} void illegal0(intp __ptrauth(VALID_DATA_KEY)); // expected-error{{parameter types may not be qualified with __ptrauth}} intp __ptrauth(VALID_DATA_KEY) illegal1(void); // expected-error{{return types may not be qualified with __ptrauth}} +void illegal2(intp __ptrauth(ptrauth_key_none, 0, 0)); // expected-error{{parameter types may not be qualified with __ptrauth}} +intp __ptrauth(ptrauth_key_none, 0, 0) illegal3(void); // expected-error{{return types may not be qualified with __ptrauth}} + void test_code(intp p) { p = (intp __ptrauth(VALID_DATA_KEY)) 0; // expected-error{{cast types may not be qualified with __ptrauth}} @@ -70,7 +121,7 @@ void test_code(intp p) { intp __ptrauth(VALID_DATA_KEY) *ppSpecial0 = &pSpecial; intp __ptrauth(VALID_DATA_KEY) *ppSpecial1 = &pNormal; // expected-error {{initializing '__ptrauth(2,0,0) intp *' (aka 'int *__ptrauth(2,0,0) *') with an expression of type 'intp *' (aka 'int **') changes pointer-authentication of pointee type}} - intp *ppNormal0 = &pSpecial; // expected-error {{initializing 'intp *' (aka 'int **') with an expression of type '__ptrauth(2,0,0) intp *' (aka 'int *__ptrauth(2,0,0) *') changes pointer-authentication of pointee type}} + intp *ppNormal0 = &pSpecial; // expected-error {{initializing 'intp *' (aka 'int **') with an expression of type '__ptrauth(2,0,0) intp *' (aka 'int *__ptrauth(2,0,0) *') changes pointer-authentication of pointee type}} intp *ppNormal1 = &pNormal; intp *pp5 = (p ? &pSpecial : &pNormal); // expected-error {{__ptrauth qualification mismatch ('__ptrauth(2,0,0) intp *' (aka 'int *__ptrauth(2,0,0) *') and 'intp *' (aka 'int **'))}} @@ -125,3 +176,9 @@ union U2 foo2(union U2); // expected-error {{cannot use type 'union U2' for func union U3 foo3(union U3); // expected-error {{cannot use type 'union U3' for function/method return since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'union U3' for a function/method parameter since it is a union that is non-trivial to copy}} struct S4 foo4(struct S4); // expected-error {{cannot use type 'struct S4' for function/method return since it contains a union that is non-trivial to copy}} expected-error {{cannot use type 'struct S4' for a function/method parameter since it contains a union that is non-trivial to copy}} + +struct S5 { + intp __ptrauth(1, 1, 51, "authenticates-null-values") f0; +}; + +struct S5 globalS5; // expected-error {{globals with authenticated null values are currently unsupported}} diff --git a/clang/test/Sema/ptrauth-redeclared-on-null-key.c b/clang/test/Sema/ptrauth-redeclared-on-null-key.c new file mode 100644 index 0000000000000..4f330f122f08f --- /dev/null +++ b/clang/test/Sema/ptrauth-redeclared-on-null-key.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -verify -fptrauth-intrinsics %s + +#include + +typedef void* __ptrauth(1,1,1) p1; +typedef p1 __ptrauth(ptrauth_key_none,1,1) p2; +// expected-error@-1 {{type 'p1' (aka 'void *__ptrauth(1,1,1)') is already __ptrauth-qualified}} + +typedef __INTPTR_TYPE__ intptr_t; +typedef intptr_t __ptrauth_restricted_intptr(1,1,1) i1; +typedef i1 __ptrauth_restricted_intptr(ptrauth_key_none,1,1) i2; +// expected-error@-1 {{type 'i1' (aka '__ptrauth_restricted_intptr(1,1,1) long') is already __ptrauth_restricted_intptr-qualified}} + +typedef void* __ptrauth(ptrauth_key_none,1,1) p3; +typedef p1 __ptrauth(1,1,1) p4; +// expected-error@-1 {{type 'p1' (aka 'void *__ptrauth(1,1,1)') is already __ptrauth-qualified}} + +typedef __INTPTR_TYPE__ intptr_t; +typedef intptr_t __ptrauth_restricted_intptr(ptrauth_key_none,1,1) i3; +typedef i1 __ptrauth_restricted_intptr(1,1,1) i4; +// expected-error@-1 {{type 'i1' (aka '__ptrauth_restricted_intptr(1,1,1) long') is already __ptrauth_restricted_intptr-qualified}} diff --git a/clang/test/Sema/ptrauth-restricted-intptr-qualifier.c b/clang/test/Sema/ptrauth-restricted-intptr-qualifier.c new file mode 100644 index 0000000000000..6324bf3d40280 --- /dev/null +++ b/clang/test/Sema/ptrauth-restricted-intptr-qualifier.c @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify -fptrauth-intrinsics %s +#if __has_feature(ptrauth_restricted_intptr_qualifier) +int *__ptrauth_restricted_intptr(0) a; +// expected-error@-1{{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is 'int *'}} + +char __ptrauth_restricted_intptr(0) b; +// expected-error@-1{{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is 'char'}} +unsigned char __ptrauth_restricted_intptr(0) c; +// expected-error@-1{{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is 'unsigned char'}} +short __ptrauth_restricted_intptr(0) d; +// expected-error@-1{{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is 'short'}} +unsigned short __ptrauth_restricted_intptr(0) e; +// expected-error@-1{{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is 'unsigned short'}} +int __ptrauth_restricted_intptr(0) f; +// expected-error@-1{{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is 'int'}} +unsigned int __ptrauth_restricted_intptr(0) g; +// expected-error@-1{{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is 'unsigned int'}} +__int128_t __ptrauth_restricted_intptr(0) h; +// expected-error@-1{{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is '__int128_t' (aka '__int128')}} +unsigned short __ptrauth_restricted_intptr(0) i; +// expected-error@-1{{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is 'unsigned short'}} + +unsigned long long __ptrauth_restricted_intptr(0) j; +long long __ptrauth_restricted_intptr(0) k; +__SIZE_TYPE__ __ptrauth_restricted_intptr(0) l; +const unsigned long long __ptrauth_restricted_intptr(0) m; +const long long __ptrauth_restricted_intptr(0) n; +const __SIZE_TYPE__ __ptrauth_restricted_intptr(0) o; + +struct S1 { + __SIZE_TYPE__ __ptrauth_restricted_intptr(0) f0; +}; +struct S2 { + int *__ptrauth_restricted_intptr(0) f0; + // expected-error@-1{{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is 'int *'}} +}; + +void x(unsigned long long __ptrauth_restricted_intptr(0) f0); +// expected-error@-1{{parameter types may not be qualified with __ptrauth_restricted_intptr; type is '__ptrauth_restricted_intptr(0,0,0) unsigned long long'}} + +unsigned long long __ptrauth_restricted_intptr(0) y(); +// expected-error@-1{{return types may not be qualified with __ptrauth_restricted_intptr; type is '__ptrauth_restricted_intptr(0,0,0) unsigned long long'}} +#endif \ No newline at end of file diff --git a/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp b/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp new file mode 100644 index 0000000000000..d8f7be5e45d95 --- /dev/null +++ b/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp @@ -0,0 +1,67 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++2b -Wno-string-plus-int -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s + +struct S { + static constexpr auto options = "strip"; +}; + +constexpr const char* const_options(int i) { + const char* local_const = "isa-pointer"; + constexpr auto local_constexpr = ","; + static constexpr auto static_const = "strip"; + static constexpr auto static_constexpr = "sign-and-strip"; + + switch (i) { + case 0: + return ""; + case 1: + return "authenticates-null-values"; + case 2: + return local_const; + case 3: + return local_constexpr; + case 4: + return static_const; + case 5: + return static_constexpr; + case 6: + return "some characters"; + case 7: + return S::options; + case 8: + return const_options(3)+1; + default: + #ifdef __EXCEPTIONS + throw "invalid index"; + #else + __builtin_trap(); + #endif + } +} + +void test_func() { + int * __ptrauth(1,1,1,const_options(0)) zero; + int * __ptrauth(1,1,1,const_options(1)) one; + int * __ptrauth(1,1,1,const_options(2)) two; + int * __ptrauth(1,1,1,const_options(3)) three; + // expected-error@-1 2 {{empty __ptrauth authentication option}} + // expected-note@-2 {{options parameter evaluated to ','}} + int * __ptrauth(1,1,1,const_options(4)) four; + int * __ptrauth(1,1,1,const_options(5)) five; + int * __ptrauth(1,1,1,const_options(6)) six; + // expected-error@-1 {{__ptrauth authentication option 'some'}} + // expected-error@-2 {{missing comma between __ptrauth options}} + // expected-error@-3 {{__ptrauth authentication option 'characters'}} + // expected-note@-4 {{options parameter evaluated to 'some characters'}} + int * __ptrauth(1,1,1,const_options(7)) seven; + int * __ptrauth(1,1,1,const_options(8)) eight; + int * __ptrauth(1,1,1,S::options) struct_access; + int * __ptrauth(1,1,1,2 * 3) ice; + // expected-error@-1 {{__ptrauth options must be a string of comma separated flags, found '2 * 3'}} + int * __ptrauth(1,1,1,4 + "wat,strip") arithmetic_string; + int * __ptrauth(1,1,1,5 + "wat,strip") arithmetic_string2; + // expected-error@-1 {{unknown __ptrauth authentication option 'trip'}} + // expected-note@-2 {{options parameter evaluated to 'trip'}} + // Handle evaluation failing + int * __ptrauth(1,1,1,const_options(50)) fifty; + // expected-error@-1 {{__ptrauth options must be a string of comma separated flags, found 'const_options(50)'}} +} diff --git a/clang/test/SemaCXX/ptrauth-qualifier.cpp b/clang/test/SemaCXX/ptrauth-qualifier.cpp index 3b56c88d6ad1c..2cd9ebe356f61 100644 --- a/clang/test/SemaCXX/ptrauth-qualifier.cpp +++ b/clang/test/SemaCXX/ptrauth-qualifier.cpp @@ -55,10 +55,10 @@ namespace test_union { struct S1 { union { - union { // expected-note-re 4 {{'S1' is implicitly deleted because field 'test_union::S1::(anonymous union at {{.*}})' has a deleted}} - int * AQ f0; + union { + int * AQ f0; // expected-note 4 {{implicitly deleted because variant field 'f0' has an address-discriminated ptrauth qualifier}} char f1; - }; + } u; // expected-note 4 {{'S1' is implicitly deleted because field 'u' has a deleted}} int f2; }; }; @@ -127,11 +127,11 @@ bool test_composite_type0(bool c, int * AQ * a0, int * AQ * a1) { bool test_composite_type1(bool c, int * AQ * a0, int * AQ2 * a1) { auto t = c ? a0 : a1; // expected-error {{incompatible operand types ('int *__ptrauth(1,1,50) *' and 'int *__ptrauth(1,1,51) *')}} - return a0 == a1; // expected-error {{comparison of distinct pointer types ('int *__ptrauth(1,1,50) *' and 'int *__ptrauth(1,1,51) *')}} + return a0 == a1; // expected-error {{comparison of distinct pointer types ('int *__ptrauth(1,1,50) *' and 'int *__ptrauth(1,1,51) *')}} } -void test_bad_call_diag(void *AQ* ptr); // expected-note{{candidate function not viable: 1st argument ('void *__ptrauth(1,1,51) *') has __ptrauth(1,1,51) qualifier, but parameter has __ptrauth(1,1,50) qualifier}} expected-note{{candidate function not viable: 1st argument ('void **') has no ptrauth qualifier, but parameter has __ptrauth(1,1,50) qualifier}} -void test_bad_call_diag2(void ** ptr); // expected-note{{1st argument ('void *__ptrauth(1,1,50) *') has __ptrauth(1,1,50) qualifier, but parameter has no ptrauth qualifier}} +void test_bad_call_diag(void *AQ *ptr); // expected-note{{candidate function not viable: 1st argument ('void *__ptrauth(1,1,51) *') has __ptrauth(1,1,51) qualifier, but parameter has __ptrauth(1,1,50) qualifier}} expected-note{{candidate function not viable: 1st argument ('void **') has no ptrauth qualifier, but parameter has __ptrauth(1,1,50) qualifier}} +void test_bad_call_diag2(void **ptr); // expected-note{{1st argument ('void *__ptrauth(1,1,50) *') has __ptrauth(1,1,50) qualifier, but parameter has no ptrauth qualifier}} int test_call_diag() { void *AQ ptr1, *AQ2 ptr2, *ptr3; diff --git a/clang/test/SemaCXX/ptrauth-template-parameters.cpp b/clang/test/SemaCXX/ptrauth-template-parameters.cpp new file mode 100644 index 0000000000000..f848a3df53fd4 --- /dev/null +++ b/clang/test/SemaCXX/ptrauth-template-parameters.cpp @@ -0,0 +1,66 @@ +// RUN: %clang_cc1 -triple arm64e-apple-ios -fsyntax-only -verify -fptrauth-intrinsics -std=c++20 %s + +#if __has_feature(ptrauth_restricted_intptr_qualifier) + +template struct S { + T __ptrauth_restricted_intptr(0,0,1234) test; + // expected-error@-1{{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is 'void *'}} + // expected-error@-2{{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is 'int'}} + // expected-error@-3 3 {{type '__ptrauth_restricted_intptr(0,0,1234) T' is already __ptrauth_restricted_intptr-qualified}} +}; + +void f1() { + S<__INTPTR_TYPE__> basic; + S invalid_type; + // expected-note@-1{{in instantiation of template class 'S' requested here}} + S mismatched_pointer_type; + // expected-note@-1{{in instantiation of template class 'S' requested here}} + S mismatched_pointer_type_incorrect_ptrauth1; + // expected-error@-1 {{__ptrauth_restricted_intptr qualifier may only be applied to pointer sized integer types; type here is 'void *'}} + S mismatched_pointer_type_correct_ptrauth; + // expected-note@-1{{in instantiation of template class 'S' requested here}} + S<__INTPTR_TYPE__ __ptrauth_restricted_intptr(0,0,1234)> matched; + // expected-note@-1{{in instantiation of template class 'S<__ptrauth_restricted_intptr(0,0,1234) long>'}} + S<__INTPTR_TYPE__ __ptrauth_restricted_intptr(0,0,1235)> mismatching_qualifier1; + // expected-note@-1{{in instantiation of template class 'S<__ptrauth_restricted_intptr(0,0,1235) long>' requested here}} + S<__INTPTR_TYPE__ __ptrauth(0,0,1234)> mismatching_qualifier2; + // expected-error@-1{{__ptrauth qualifier may only be applied to pointer types; type here is 'long'}} + S<__INTPTR_TYPE__ __ptrauth(0,0,1235)> mismatching_qualifier3; + // expected-error@-1{{__ptrauth qualifier may only be applied to pointer types; type here is 'long'}} +}; + +void f2() { + S<__INTPTR_TYPE__> unqualified; + S<__INTPTR_TYPE__ __ptrauth_restricted_intptr(0,0,1234)> qualified; + __INTPTR_TYPE__ __ptrauth_restricted_intptr(0,0,1234)* p; + p = &unqualified.test; + p = &qualified.test; + __INTPTR_TYPE__ *mismatch; + mismatch = &unqualified.test; + // expected-error@-1{{assigning '__ptrauth_restricted_intptr(0,0,1234) long *' to 'long *' changes pointer-authentication of pointee type}} + mismatch = &qualified.test; +} + +template struct G { + T __ptrauth(0,0,1234) test; + // expected-error@-1 3 {{type '__ptrauth(0,0,1234) T' is already __ptrauth-qualified}} +}; + +template struct Indirect { + G layers; + // expected-note@-1{{in instantiation of template class 'G' requested here}} + // expected-note@-2{{in instantiation of template class 'G' requested here}} + // expected-note@-3{{in instantiation of template class 'G' requested here}} +}; + +void f3() { + Indirect one; + // expected-note@-1{{in instantiation of template class 'Indirect' requested here}} + Indirect two; + // expected-note@-1{{in instantiation of template class 'Indirect' requested here}} + Indirect three; + Indirect four; + // expected-note@-1{{in instantiation of template class 'Indirect' requested here}} +} + +#endif diff --git a/clang/test/SemaObjC/ptrauth-pointers.m b/clang/test/SemaObjC/ptrauth-pointers.m new file mode 100644 index 0000000000000..0b7a2b3e1d2cf --- /dev/null +++ b/clang/test/SemaObjC/ptrauth-pointers.m @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -fblocks -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -verify %s + +#if __has_feature(ptrauth_objc_signable_class) +@class TestClass; +typedef TestClass *ClassPtr; +typedef void(^BlockPtr)(); +@interface TestClass { +@public + __ptrauth(2, 1, 1) Class a; + __ptrauth(2, 1, 3) volatile Class vi; + __ptrauth(2, 1, 3) const Class ci; + __ptrauth(2, 1, 1) id b; + // expected-error@-1 {{__ptrauth qualifier may only be applied to pointer types; type here is 'id'}} + __ptrauth(2, 1, 2) ClassPtr c; + // expected-error@-1 {{__ptrauth qualifier may only be applied to pointer types; type here is 'ClassPtr' (aka 'TestClass *')}} + __ptrauth(2, 1, 2) BlockPtr d; + // expected-error@-1 {{__ptrauth qualifier may only be applied to pointer types; type here is 'BlockPtr' (aka 'void (^)()')}} +} + +struct TestStruct { + __ptrauth(2, 1, 3) Class e; + __ptrauth(2, 1, 3) volatile Class vi; + __ptrauth(2, 1, 3) const Class ci; + __ptrauth(2, 1, 4) id f; + // expected-error@-1 {{__ptrauth qualifier may only be applied to pointer types; type here is 'id'}} + __ptrauth(2, 1, 5) ClassPtr g; + // expected-error@-1 {{__ptrauth qualifier may only be applied to pointer types; type here is 'ClassPtr' (aka 'TestClass *')}} + __ptrauth(2, 1, 2) BlockPtr h; + // expected-error@-1 {{__ptrauth qualifier may only be applied to pointer types; type here is 'BlockPtr' (aka 'void (^)()')}} +}; + +@end + +void foo() { + __ptrauth(2, 1, 3) Class i; + __ptrauth(2, 1, 3) volatile Class vi; + __ptrauth(2, 1, 3) const Class ci; + __ptrauth(2, 1, 4) id j; + // expected-error@-1 {{__ptrauth qualifier may only be applied to pointer types; type here is 'id'}} + __ptrauth(2, 1, 5) ClassPtr k; + // expected-error@-1 {{__ptrauth qualifier may only be applied to pointer types; type here is 'ClassPtr' (aka 'TestClass *')}} + __ptrauth(2, 1, 2) BlockPtr l; + // expected-error@-1 {{__ptrauth qualifier may only be applied to pointer types; type here is 'BlockPtr' (aka 'void (^)()')}} +} + +#endif diff --git a/clang/test/SemaObjC/ptrauth-qualifier.m b/clang/test/SemaObjC/ptrauth-qualifier.m new file mode 100644 index 0000000000000..5517b2ac2b801 --- /dev/null +++ b/clang/test/SemaObjC/ptrauth-qualifier.m @@ -0,0 +1,92 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify -fptrauth-intrinsics %s + +#if __has_feature(ptrauth_qualifier) +#warning __ptrauth qualifier enabled! +// expected-warning@-1 {{__ptrauth qualifier enabled!}} +#endif + +@interface Foo +// expected-warning@-1 {{class 'Foo' defined without specifying a base class}} +// expected-note@-2 {{add a super class to fix this problem}} + +@property void *__ptrauth(1, 1, 1) invalid1; +// expected-error@-1 {{properties may not be qualified with __ptrauth; type is 'void *__ptrauth(1,1,1)'}} + +@property void *__ptrauth(1, 0, 1) invalid2; +// expected-error@-1 {{properties may not be qualified with __ptrauth; type is 'void *__ptrauth(1,0,1)'}} + +@property unsigned long long __ptrauth_restricted_intptr(1, 1, 1) invalid3; +// expected-error@-1 {{properties may not be qualified with __ptrauth_restricted_intptr; type is '__ptrauth_restricted_intptr(1,1,1) unsigned long long'}} + +@property unsigned long long __ptrauth_restricted_intptr(1, 0, 1) invalid4; +// expected-error@-1 {{properties may not be qualified with __ptrauth_restricted_intptr; type is '__ptrauth_restricted_intptr(1,0,1) unsigned long long'}} + +- (void *__ptrauth(1, 1, 1))invalid5; +// expected-error@-1 {{return types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,1,1)'}} + +- (void *__ptrauth(1, 0, 1))invalid6; +// expected-error@-1 {{return types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,0,1)'}} + +- (unsigned long long __ptrauth_restricted_intptr(1, 1, 1))invalid7; +// expected-error@-1 {{return types may not be qualified with __ptrauth_restricted_intptr; type is '__ptrauth_restricted_intptr(1,1,1) unsigned long long'}} + +- (unsigned long long __ptrauth_restricted_intptr(1, 0, 1))invalid8; +// expected-error@-1 {{return types may not be qualified with __ptrauth_restricted_intptr; type is '__ptrauth_restricted_intptr(1,0,1) unsigned long long'}} + +- (void)invalid9:(void *__ptrauth(1, 1, 1))a; +// expected-error@-1 {{parameter types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,1,1)'}} +// expected-note@-2 {{method 'invalid9:' declared here}} + +- (void)invalid10:(void *__ptrauth(1, 0, 1))a; +// expected-error@-1 {{parameter types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,0,1)'}} +// expected-note@-2 {{method 'invalid10:' declared here}} + +- (void)invalid11:(unsigned long long __ptrauth_restricted_intptr(1, 1, 1))a; +// expected-error@-1 {{parameter types may not be qualified with __ptrauth_restricted_intptr; type is '__ptrauth_restricted_intptr(1,1,1) unsigned long long'}} +// expected-note@-2 {{method 'invalid11:' declared here}} + +- (void)invalid12:(unsigned long long __ptrauth_restricted_intptr(1, 0, 1))a; +// expected-error@-1 {{parameter types may not be qualified with __ptrauth_restricted_intptr; type is '__ptrauth_restricted_intptr(1,0,1) unsigned long long'}} +// expected-note@-2 {{method 'invalid12:' declared here}} +@end + +@implementation Foo +// expected-warning@-1 4{{method definition for}} + +- (void *__ptrauth(1, 1, 1))invalid13 { +// expected-error@-1 {{return types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,1,1)'}} + return 0; +} + +- (void *__ptrauth(1, 0, 1))invalid14 { +// expected-error@-1 {{return types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,0,1)'}} + return 0; +} + +- (unsigned long long __ptrauth_restricted_intptr(1, 1, 1))invalid15 { +// expected-error@-1 {{return types may not be qualified with __ptrauth_restricted_intptr; type is '__ptrauth_restricted_intptr(1,1,1) unsigned long long'}} + return 0; +} + +- (unsigned long long __ptrauth_restricted_intptr(1, 0, 1))invalid16 { +// expected-error@-1 {{return types may not be qualified with __ptrauth_restricted_intptr; type is '__ptrauth_restricted_intptr(1,0,1) unsigned long long'}} + return 0; +} + +- (void)invalid17:(void *__ptrauth(1, 1, 1))a { +// expected-error@-1 {{parameter types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,1,1)'}} +} + +- (void)invalid18:(void *__ptrauth(1, 0, 1))a { +// expected-error@-1 {{parameter types may not be qualified with __ptrauth; type is 'void *__ptrauth(1,0,1)'}} +} + +- (void)invalid19:(unsigned long long __ptrauth_restricted_intptr(1, 1, 1))a { +// expected-error@-1 {{parameter types may not be qualified with __ptrauth_restricted_intptr; type is '__ptrauth_restricted_intptr(1,1,1) unsigned long long'}} +} + +- (void)invalid20:(unsigned long long __ptrauth_restricted_intptr(1, 0, 1))a { +// expected-error@-1 {{parameter types may not be qualified with __ptrauth_restricted_intptr; type is '__ptrauth_restricted_intptr(1,0,1) unsigned long long'}} +} + +@end \ No newline at end of file From a39ce688a6236a7101d5ad6ce4a978dca7931c35 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Tue, 2 Jul 2024 20:00:57 -0700 Subject: [PATCH 28/58] [clang] Implement pointer authentication for C++ member function pointers. --- .../include/clang/Basic/PointerAuthOptions.h | 4 - clang/lib/CodeGen/CGCall.cpp | 7 +- clang/lib/CodeGen/CGPointerAuth.cpp | 4 +- clang/lib/CodeGen/CodeGenModule.h | 11 +- clang/lib/CodeGen/ItaniumCXXABI.cpp | 169 ++++--- ...ember-function-pointer-wrapper-globals.cpp | 413 ++++++++++++++++++ .../ptrauth-member-function-pointer.cpp | 138 +++--- clang/test/Preprocessor/ptrauth_feature.c | 9 + 8 files changed, 625 insertions(+), 130 deletions(-) create mode 100644 clang/test/CodeGenCXX/ptrauth-member-function-pointer-wrapper-globals.cpp diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index 5704fdca645e6..80b3c0a2a94f8 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -203,10 +203,6 @@ class PointerAuthSchema { }; struct PointerAuthOptions { - /// Do member function pointers to virtual functions need to be built - /// as thunks? - bool ThunkCXXVirtualMemberPointers = false; - /// Should return addresses be authenticated? bool ReturnAddresses = false; diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 7714a3c5969b4..f54817c82f7f6 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -5107,10 +5107,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, llvm::Value *UnusedReturnSizePtr = nullptr; if (RetAI.isIndirect() || RetAI.isInAlloca() || RetAI.isCoerceAndExpand()) { if (IsVirtualFunctionPointerThunk && RetAI.isIndirect()) { - SRetPtr = Address(CurFn->arg_begin() + IRFunctionArgs.getSRetArgNo(), - ConvertTypeForMem(RetTy), CharUnits::fromQuantity(1)); + SRetPtr = makeNaturalAddressForPointer(CurFn->arg_begin() + + IRFunctionArgs.getSRetArgNo(), + RetTy, CharUnits::fromQuantity(1)); } else if (!ReturnValue.isNull()) { - SRetPtr = ReturnValue.getValue(); + SRetPtr = ReturnValue.getAddress(); } else { SRetPtr = CreateMemTemp(RetTy, "tmp", &SRetAlloca); if (HaveInsertPoint() && ReturnValue.isUnused()) { diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index d6ff3265ecd4e..38e565ca8f836 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -765,8 +765,7 @@ CGPointerAuthInfo CodeGenModule::getMemberFunctionPointerAuthInfo(QualType FT) { } llvm::Constant *CodeGenModule::getMemberFunctionPointer(llvm::Constant *Pointer, - QualType FT, - const FunctionDecl *) { + QualType FT) { if (CGPointerAuthInfo PointerAuth = getMemberFunctionPointerAuthInfo(FT)) return getConstantSignedPointer( Pointer, PointerAuth.getKey(), nullptr, @@ -783,7 +782,6 @@ llvm::Constant *CodeGenModule::getMemberFunctionPointer(const FunctionDecl *FD, return getMemberFunctionPointer(getRawFunctionPointer(FD, Ty), FT); } - std::optional CodeGenModule::computeVTPointerAuthentication(const CXXRecordDecl *ThisClass) { auto DefaultAuthentication = getCodeGenOpts().PointerAuth.CXXVTablePointers; diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 4ffe7cde66319..9d56c5d32f018 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -968,16 +968,15 @@ class CodeGenModule : public CodeGenTypeCache { QualType FunctionType, GlobalDecl GD = GlobalDecl()); - CGPointerAuthInfo getFunctionPointerAuthInfo(QualType T); - llvm::Constant *getMemberFunctionPointer(const FunctionDecl *FD, llvm::Type *Ty = nullptr); - llvm::Constant *getMemberFunctionPointer(llvm::Constant *pointer, - QualType functionType, - const FunctionDecl *FD = nullptr); + llvm::Constant *getMemberFunctionPointer(llvm::Constant *Pointer, + QualType FT); + + CGPointerAuthInfo getFunctionPointerAuthInfo(QualType T); - CGPointerAuthInfo getMemberFunctionPointerAuthInfo(QualType functionType); + CGPointerAuthInfo getMemberFunctionPointerAuthInfo(QualType FT); CGPointerAuthInfo getPointerAuthInfoForPointeeType(QualType type); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 578ee2043a5b8..781662f498423 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -882,19 +882,33 @@ llvm::Value *ItaniumCXXABI::EmitMemberDataPointerAddress( static llvm::Constant *pointerAuthResignConstant( llvm::Value *Ptr, const CGPointerAuthInfo &CurAuthInfo, const CGPointerAuthInfo &NewAuthInfo, CodeGenModule &CGM) { - std::optional Info = + auto *CPA = dyn_cast(Ptr); + + if (!CPA) { + // We still support llvm.ptrauth wrapper globals + std::optional Info = llvm::GlobalPtrAuthInfo::analyze(Ptr); - if (!Info || !isa(NewAuthInfo.getDiscriminator())) - return nullptr; + if (!Info || !isa(NewAuthInfo.getDiscriminator())) + return nullptr; + + assert(Info->getKey()->getZExtValue() == CurAuthInfo.getKey() && + Info->getAddrDiscriminator()->isZeroValue() && + Info->getDiscriminator() == CurAuthInfo.getDiscriminator() && + "unexpected key or discriminators"); + + return CGM.getConstantSignedPointer( + Info->getPointer(), NewAuthInfo.getKey(), nullptr, + cast(NewAuthInfo.getDiscriminator())); + } - assert(Info->getKey()->getZExtValue() == CurAuthInfo.getKey() && - Info->getAddrDiscriminator()->isZeroValue() && - Info->getDiscriminator() == CurAuthInfo.getDiscriminator() && + assert(CPA->getKey()->getZExtValue() == CurAuthInfo.getKey() && + CPA->getAddrDiscriminator()->isZeroValue() && + CPA->getDiscriminator() == CurAuthInfo.getDiscriminator() && "unexpected key or discriminators"); return CGM.getConstantSignedPointer( - Info->getPointer(), NewAuthInfo.getKey(), nullptr, + CPA->getPointer(), NewAuthInfo.getKey(), nullptr, cast(NewAuthInfo.getDiscriminator())); } @@ -934,46 +948,47 @@ ItaniumCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF, E->getCastKind() == CK_ReinterpretMemberPointer); CGBuilderTy &Builder = CGF.Builder; - QualType dstType = E->getType(); + QualType DstType = E->getType(); - if (dstType->isMemberFunctionPointerType()) - if (const auto &newAuthInfo = - CGM.getMemberFunctionPointerAuthInfo(dstType)) { - QualType srcType = E->getSubExpr()->getType(); - assert(srcType->isMemberFunctionPointerType()); - const auto &curAuthInfo = CGM.getMemberFunctionPointerAuthInfo(srcType); - llvm::Value *memFnPtr = Builder.CreateExtractValue(src, 0, "memptr.ptr"); - llvm::Type *origTy = memFnPtr->getType(); + if (DstType->isMemberFunctionPointerType()) { + if (const auto &NewAuthInfo = + CGM.getMemberFunctionPointerAuthInfo(DstType)) { + QualType SrcType = E->getSubExpr()->getType(); + assert(SrcType->isMemberFunctionPointerType()); + const auto &CurAuthInfo = CGM.getMemberFunctionPointerAuthInfo(SrcType); + llvm::Value *MemFnPtr = Builder.CreateExtractValue(src, 0, "memptr.ptr"); + llvm::Type *OrigTy = MemFnPtr->getType(); - llvm::BasicBlock *startBB = Builder.GetInsertBlock(); - llvm::BasicBlock *resignBB = CGF.createBasicBlock("resign"); - llvm::BasicBlock *mergeBB = CGF.createBasicBlock("merge"); + llvm::BasicBlock *StartBB = Builder.GetInsertBlock(); + llvm::BasicBlock *ResignBB = CGF.createBasicBlock("resign"); + llvm::BasicBlock *MergeBB = CGF.createBasicBlock("merge"); // Check whether we have a virtual offset or a pointer to a function. assert(UseARMMethodPtrABI && "ARM ABI expected"); - llvm::Value *adj = Builder.CreateExtractValue(src, 1, "memptr.adj"); - llvm::Constant *ptrdiff_1 = llvm::ConstantInt::get(CGM.PtrDiffTy, 1); - llvm::Value *andVal = Builder.CreateAnd(adj, ptrdiff_1); - llvm::Value *isVirtualOffset = - Builder.CreateIsNotNull(andVal, "is.virtual.offset"); - Builder.CreateCondBr(isVirtualOffset, mergeBB, resignBB); - - CGF.EmitBlock(resignBB); - llvm::Type *ptrTy = llvm::PointerType::getUnqual(CGM.Int8Ty); - memFnPtr = Builder.CreateIntToPtr(memFnPtr, ptrTy); - memFnPtr = CGF.EmitPointerAuthResign(memFnPtr, srcType, curAuthInfo, - newAuthInfo, - isa(src)); - memFnPtr = Builder.CreatePtrToInt(memFnPtr, origTy); - llvm::Value *resignedVal = Builder.CreateInsertValue(src, memFnPtr, 0); - resignBB = Builder.GetInsertBlock(); - - CGF.EmitBlock(mergeBB); - llvm::PHINode *newSrc = Builder.CreatePHI(src->getType(), 2); - newSrc->addIncoming(src, startBB); - newSrc->addIncoming(resignedVal, resignBB); - src = newSrc; + llvm::Value *Adj = Builder.CreateExtractValue(src, 1, "memptr.adj"); + llvm::Constant *Ptrdiff_1 = llvm::ConstantInt::get(CGM.PtrDiffTy, 1); + llvm::Value *AndVal = Builder.CreateAnd(Adj, Ptrdiff_1); + llvm::Value *IsVirtualOffset = + Builder.CreateIsNotNull(AndVal, "is.virtual.offset"); + Builder.CreateCondBr(IsVirtualOffset, MergeBB, ResignBB); + + CGF.EmitBlock(ResignBB); + llvm::Type *PtrTy = llvm::PointerType::getUnqual(CGM.Int8Ty); + MemFnPtr = Builder.CreateIntToPtr(MemFnPtr, PtrTy); + MemFnPtr = + CGF.EmitPointerAuthResign(MemFnPtr, SrcType, CurAuthInfo, NewAuthInfo, + isa(src)); + MemFnPtr = Builder.CreatePtrToInt(MemFnPtr, OrigTy); + llvm::Value *ResignedVal = Builder.CreateInsertValue(src, MemFnPtr, 0); + ResignBB = Builder.GetInsertBlock(); + + CGF.EmitBlock(MergeBB); + llvm::PHINode *NewSrc = Builder.CreatePHI(src->getType(), 2); + NewSrc->addIncoming(src, StartBB); + NewSrc->addIncoming(ResignedVal, ResignBB); + src = NewSrc; } + } // Under Itanium, reinterprets don't require any additional processing. if (E->getCastKind() == CK_ReinterpretMemberPointer) return src; @@ -1018,6 +1033,34 @@ ItaniumCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF, return Builder.CreateInsertValue(src, dstAdj, 1); } +static llvm::Constant * +pointerAuthResignMemberFunctionPointer(llvm::Constant *Src, QualType DestType, + QualType SrcType, CodeGenModule &CGM) { + assert(DestType->isMemberFunctionPointerType() && + SrcType->isMemberFunctionPointerType() && + "member function pointers expected"); + if (DestType == SrcType) + return Src; + + const auto &NewAuthInfo = CGM.getMemberFunctionPointerAuthInfo(DestType); + const auto &CurAuthInfo = CGM.getMemberFunctionPointerAuthInfo(SrcType); + + if (!NewAuthInfo && !CurAuthInfo) + return Src; + + llvm::Constant *MemFnPtr = Src->getAggregateElement(0u); + if (MemFnPtr->getNumOperands() == 0) { + // src must be a pair of null pointers. + assert(isa(MemFnPtr) && "constant int expected"); + return Src; + } + + llvm::Constant *ConstPtr = pointerAuthResignConstant( + cast(MemFnPtr)->getOperand(0), CurAuthInfo, NewAuthInfo, CGM); + ConstPtr = llvm::ConstantExpr::getPtrToInt(ConstPtr, MemFnPtr->getType()); + return ConstantFoldInsertValueInstruction(Src, ConstPtr, 0); +} + llvm::Constant * ItaniumCXXABI::EmitMemberPointerConversion(const CastExpr *E, llvm::Constant *src) { @@ -1025,26 +1068,11 @@ ItaniumCXXABI::EmitMemberPointerConversion(const CastExpr *E, E->getCastKind() == CK_BaseToDerivedMemberPointer || E->getCastKind() == CK_ReinterpretMemberPointer); - QualType dstType = E->getType(); + QualType DstType = E->getType(); - if (dstType->isMemberFunctionPointerType()) - if (const auto &newAuthInfo = - CGM.getMemberFunctionPointerAuthInfo(dstType)) { - assert(UseARMMethodPtrABI && "ARM ABI expected"); - QualType srcType = E->getSubExpr()->getType(); - const auto &curAuthInfo = CGM.getMemberFunctionPointerAuthInfo(srcType); - llvm::Constant *memFnPtr = src->getAggregateElement(0u); - if (memFnPtr->getNumOperands() == 0) { - // src must be a pair of null pointers. - assert(isa(memFnPtr) && "constant int expected"); - } else { - llvm::Constant *constPtr = pointerAuthResignConstant( - memFnPtr->getOperand(0), curAuthInfo, newAuthInfo, CGM); - constPtr = - llvm::ConstantExpr::getPtrToInt(constPtr, memFnPtr->getType()); - src = ConstantFoldInsertValueInstruction(src, constPtr, 0); - } - } + if (DstType->isMemberFunctionPointerType()) + src = pointerAuthResignMemberFunctionPointer( + src, DstType, E->getSubExpr()->getType(), CGM); // Under Itanium, reinterprets don't require any additional processing. if (E->getCastKind() == CK_ReinterpretMemberPointer) return src; @@ -1211,8 +1239,12 @@ llvm::Constant *ItaniumCXXABI::EmitMemberPointer(const APValue &MP, CharUnits ThisAdjustment = getContext().getMemberPointerPathAdjustment(MP); - if (const CXXMethodDecl *MD = dyn_cast(MPD)) - return BuildMemberPointer(MD, ThisAdjustment); + if (const CXXMethodDecl *MD = dyn_cast(MPD)) { + llvm::Constant *Src = BuildMemberPointer(MD, ThisAdjustment); + QualType SrcType = getContext().getMemberPointerType( + MD->getType(), MD->getParent()->getTypeForDecl()); + return pointerAuthResignMemberFunctionPointer(Src, MPType, SrcType, CGM); + } CharUnits FieldOffset = getContext().toCharUnitsFromBits(getContext().getFieldOffset(MPD)); @@ -3338,6 +3370,11 @@ ItaniumCXXABI::getOrCreateVirtualFunctionPointerThunk(const CXXMethodDecl *MD) { CGM.SetLLVMFunctionAttributes(MD, FnInfo, ThunkFn, /*IsThunk=*/true); CGM.SetLLVMFunctionAttributesForDefinition(MD, ThunkFn); + // Stack protection sometimes gets inserted after the musttail call. + ThunkFn->removeFnAttr(llvm::Attribute::StackProtect); + ThunkFn->removeFnAttr(llvm::Attribute::StackProtectStrong); + ThunkFn->removeFnAttr(llvm::Attribute::StackProtectReq); + // Start codegen. CodeGenFunction CGF(CGM); CGF.CurGD = GlobalDecl(MD); @@ -3365,10 +3402,12 @@ ItaniumCXXABI::getOrCreateVirtualFunctionPointerThunk(const CXXMethodDecl *MD) { llvm::CallBase *CallOrInvoke; CGF.EmitCall(CallInfo, Callee, ReturnValueSlot(), CallArgs, &CallOrInvoke, /*IsMustTail=*/true, SourceLocation(), true); - if (CallOrInvoke->getType()->isVoidTy()) + auto *Call = cast(CallOrInvoke); + Call->setTailCallKind(llvm::CallInst::TCK_MustTail); + if (Call->getType()->isVoidTy()) CGF.Builder.CreateRetVoid(); else - CGF.Builder.CreateRet(CallOrInvoke); + CGF.Builder.CreateRet(Call); // Finish the function to maintain CodeGenFunction invariants. // FIXME: Don't emit unreachable code. @@ -5076,7 +5115,7 @@ ItaniumCXXABI::getSignedVirtualMemberFunctionPointer(const CXXMethodDecl *MD) { llvm::Constant *thunk = getOrCreateVirtualFunctionPointerThunk(origMD); QualType funcType = CGM.getContext().getMemberPointerType( MD->getType(), MD->getParent()->getTypeForDecl()); - return CGM.getMemberFunctionPointer(thunk, funcType, MD); + return CGM.getMemberFunctionPointer(thunk, funcType); } void WebAssemblyCXXABI::emitBeginCatch(CodeGenFunction &CGF, diff --git a/clang/test/CodeGenCXX/ptrauth-member-function-pointer-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-member-function-pointer-wrapper-globals.cpp new file mode 100644 index 0000000000000..74c3a0c32ba24 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-member-function-pointer-wrapper-globals.cpp @@ -0,0 +1,413 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK %s + +// CHECK: %[[STRUCT_A0:.*]] = type { [4 x i32] } +// CHECK: %[[STRUCT_A1:.*]] = type { [8 x i32] } +// CHECK: %[[STRUCT_TRIVIALS:.*]] = type { [4 x i32] } + +// CHECK: @_ZN5Base011nonvirtual0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 0, i64 [[TYPEDISC0:22163]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC0]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual3Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC0]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base016virtual_variadicEiz_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base016virtual_variadicEiz_vfpthunk_, i32 0, i64 0, i64 34368 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base011nonvirtual0Ev.ptrauth.1 = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 0, i64 [[TYPEDISC1:35591]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.2 = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth.3 = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual3Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived011nonvirtual5Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived011nonvirtual5Ev, i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived08virtual6Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived08virtual6Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived010return_aggEv_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived010return_aggEv_vfpthunk_, i32 0, i64 0, i64 64418 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived04sretEv_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived04sretEv_vfpthunk_, i32 0, i64 0, i64 28187 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_, i32 0, i64 0, i64 8992 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base18virtual7Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base18virtual7Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC2:61596]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived18virtual7Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived18virtual7Ev_vfpthunk_, i32 0, i64 0, i64 25206 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.4 = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 0, i64 25206 }, section "llvm.ptrauth", align 8 + +// CHECK: @gmethod0 = global { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base011nonvirtual0Ev.ptrauth.1 to i64), i64 0 }, align 8 +// CHECK: @_ZN8Derived011nonvirtual5Ev.ptrauth.6 = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived011nonvirtual5Ev, i32 0, i64 0, i64 [[TYPEDISC0]] }, section "llvm.ptrauth", align 8 +// CHECK: @gmethod1 = global { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived011nonvirtual5Ev.ptrauth.6 to i64), i64 0 }, align 8 +// CHECK: @gmethod2 = global { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth to i64), i64 0 }, align 8 + +// CHECK: @_ZTV5Base0 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI5Base0, ptr @_ZN5Base08virtual1Ev.ptrauth, ptr @_ZN5Base08virtual3Ev.ptrauth, ptr @_ZN5Base016virtual_variadicEiz.ptrauth] }, align 8 +// CHECK: @_ZN5Base08virtual1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 2) to i64), i64 55600 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual3Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual3Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 3) to i64), i64 53007 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base016virtual_variadicEiz.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base016virtual_variadicEiz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 4) to i64), i64 7464 }, section "llvm.ptrauth", align 8 + +struct Base0 { + void nonvirtual0(); + virtual void virtual1(); + virtual void virtual3(); + virtual void virtual_variadic(int, ...); +}; + +struct A0 { + int d[4]; +}; + +struct A1 { + int d[8]; +}; + +struct __attribute__((trivial_abi)) TrivialS { + TrivialS(const TrivialS &); + ~TrivialS(); + int p[4]; +}; + +struct Derived0 : Base0 { + void virtual1() override; + void nonvirtual5(); + virtual void virtual6(); + virtual A0 return_agg(); + virtual A1 sret(); + virtual void trivial_abi(TrivialS); +}; + +struct Base1 { + virtual void virtual7(); +}; + +struct Derived1 : Base0, Base1 { + void virtual1() override; + void virtual7() override; +}; + +typedef void (Base0::*MethodTy0)(); +typedef void (Base0::*VariadicMethodTy0)(int, ...); +typedef void (Derived0::*MethodTy1)(); + +// CHECK: define void @_ZN5Base08virtual1Ev( + +// CHECK: define void @_Z5test0v() +// CHECK: %[[METHOD0:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[VARMETHOD1:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD2:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD3:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD4:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD5:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD6:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD7:.*]] = alloca { i64, i64 }, align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base011nonvirtual0Ev.ptrauth to i64), i64 0 }, ptr %[[METHOD0]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD0]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD0]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base016virtual_variadicEiz_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[VARMETHOD1]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base011nonvirtual0Ev.ptrauth.1 to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.2 to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth.3 to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived011nonvirtual5Ev.ptrauth to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived08virtual6Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived010return_aggEv_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD3]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived04sretEv_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD4]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD5]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base18virtual7Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD6]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived18virtual7Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD7]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.4 to i64), i64 0 }, ptr %[[METHOD7]], align 8 +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @_ZN5Base08virtual1Ev_vfpthunk_(ptr noundef %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK-NEXT: %[[V0:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK-NEXT: %[[VTABLE:.*]] = load ptr, ptr %[[THIS1]], align 8 +// CHECK-NEXT: %[[V2:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK-NEXT: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 2, i64 0) +// CHECK-NEXT: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK-NEXT: %[[VFN:.*]] = getelementptr inbounds ptr, ptr %[[V4]], i64 0 +// CHECK-NEXT: %[[V5:.*]] = load ptr, ptr %[[VFN]], align 8 +// CHECK-NEXT: %[[V6:.*]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK-NEXT: %[[V7:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V6]], i64 55600) +// CHECK-NEXT: musttail call void %[[V5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %[[V0]]) [ "ptrauth"(i32 0, i64 %[[V7]]) ] +// CHECK-NEXT: ret void + +// CHECK: define linkonce_odr hidden void @_ZN5Base08virtual3Ev_vfpthunk_(ptr noundef %{{.*}}) +// CHECK: load ptr, ptr %{{.*}}, align 8 +// CHECK: load ptr, ptr %{{.*}}, align 8 +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %{{.*}}, align 8 +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 2, i64 0) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: getelementptr inbounds ptr, ptr %[[V4]], i64 1 +// CHECK: call i64 @llvm.ptrauth.blend(i64 %{{.*}}, i64 53007) + +// CHECK: define linkonce_odr hidden void @_ZN5Base016virtual_variadicEiz_vfpthunk_(ptr noundef %[[THIS:.*]], i32 noundef %0, ...) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK-NEXT: %[[_ADDR:.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: store i32 %0, ptr %[[_ADDR]], align 4 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK-NEXT: %[[V1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK-NEXT: %[[V2:.*]] = load i32, ptr %[[_ADDR]], align 4 +// CHECK-NEXT: %[[VTABLE:.*]] = load ptr, ptr %[[THIS1]], align 8 +// CHECK-NEXT: %[[V4:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK-NEXT: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 2, i64 0) +// CHECK-NEXT: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK-NEXT: %[[VFN:.*]] = getelementptr inbounds ptr, ptr %[[V6]], i64 2 +// CHECK-NEXT: %[[V7:.*]] = load ptr, ptr %[[VFN]], align 8 +// CHECK-NEXT: %[[V8:.*]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK-NEXT: %[[V9:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V8]], i64 7464) +// CHECK-NEXT: musttail call void (ptr, i32, ...) %[[V7]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %[[V1]], i32 noundef %[[V2]], ...) [ "ptrauth"(i32 0, i64 %[[V9]]) ] +// CHECK-NEXT: ret void + +// CHECK: define linkonce_odr hidden void @_ZN8Derived08virtual6Ev_vfpthunk_(ptr noundef %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %[[THIS1]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 2, i64 0) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[VFN:.*]] = getelementptr inbounds ptr, ptr %[[V3]], i64 3 +// CHECK: %[[V5:.*]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: call i64 @llvm.ptrauth.blend(i64 %[[V5]], i64 55535) + +// Check that the return value of the musttail call isn't copied to a temporary. + +// CHECK: define linkonce_odr hidden [2 x i64] @_ZN8Derived010return_aggEv_vfpthunk_(ptr noundef %{{.*}}) +// CHECK: %[[CALL:.*]] = musttail call [2 x i64] %{{.*}}(ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %{{.*}}) [ "ptrauth"(i32 0, i64 %{{.*}}) ] +// CHECK-NEXT: ret [2 x i64] %[[CALL]] + +// Check that the sret pointer passed to the caller is forwarded to the musttail +// call. + +// CHECK: define linkonce_odr hidden void @_ZN8Derived04sretEv_vfpthunk_(ptr dead_on_unwind noalias writable sret(%struct.A1) align 4 %[[AGG_RESULT:.*]], ptr noundef %{{.*}}) +// CHECK: musttail call void %{{.*}}(ptr dead_on_unwind writable sret(%struct.A1) align 4 %[[AGG_RESULT]], ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %{{.*}}) [ "ptrauth"(i32 0, i64 %{{.*}}) ] +// CHECK-NEXT: ret void + +// Check that the thunk function doesn't destruct the trivial_abi argument. + +// CHECK: define linkonce_odr hidden void @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_(ptr noundef %{{.*}}, [2 x i64] %{{.*}}) +// NODEBUG-NOT: call +// CHECK: call i64 @llvm.ptrauth.auth( +// NODEBUG-NOT: call +// CHECK: call i64 @llvm.ptrauth.blend( +// NODEBUG-NOT: call +// CHECK: musttail call void +// CHECK-NEXT: ret void + +// CHECK: define linkonce_odr hidden void @_ZN5Base18virtual7Ev_vfpthunk_(ptr noundef %[[THIS:.*]]) +// CHECK: entry: +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %[[THIS1]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 2, i64 0) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: getelementptr inbounds ptr, ptr %[[V3]], i64 0 + +// CHECK: define linkonce_odr hidden void @_ZN8Derived18virtual7Ev_vfpthunk_(ptr noundef %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %[[THIS1]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 2, i64 0) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: getelementptr inbounds ptr, ptr %[[V3]], i64 3 + +void Base0::virtual1() {} + +void test0() { + MethodTy0 method0; + method0 = &Base0::nonvirtual0; + method0 = &Base0::virtual1; + method0 = &Base0::virtual3; + + VariadicMethodTy0 varmethod1; + varmethod1 = &Base0::virtual_variadic; + + MethodTy1 method2; + method2 = &Derived0::nonvirtual0; + method2 = &Derived0::virtual1; + method2 = &Derived0::virtual3; + method2 = &Derived0::nonvirtual5; + method2 = &Derived0::virtual6; + + A0 (Derived0::*method3)(); + method3 = &Derived0::return_agg; + + A1 (Derived0::*method4)(); + method4 = &Derived0::sret; + + void (Derived0::*method5)(TrivialS); + method5 = &Derived0::trivial_abi; + + void (Base1::*method6)(); + method6 = &Base1::virtual7; + + void (Derived1::*method7)(); + method7 = &Derived1::virtual7; + method7 = &Derived1::virtual1; +} + +// CHECK: define void @_Z5test1P5Base0MS_FvvE(ptr noundef %[[A0:.*]], [2 x i64] %[[A1_COERCE:.*]]) +// CHECK: %[[A1:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[A0_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[A1_ADDR:.*]] = alloca { i64, i64 }, align 8 +// CHECK: store [2 x i64] %[[A1_COERCE]], ptr %[[A1]], align 8 +// CHECK: %[[A11:.*]] = load { i64, i64 }, ptr %[[A1]], align 8 +// CHECK: store ptr %[[A0]], ptr %[[A0_ADDR]], align 8 +// CHECK: store { i64, i64 } %[[A11]], ptr %[[A1_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[A0_ADDR]], align 8 +// CHECK: %[[V2:.*]] = load { i64, i64 }, ptr %[[A1_ADDR]], align 8 +// CHECK: %[[MEMPTR_ADJ:.*]] = extractvalue { i64, i64 } %[[V2]], 1 +// CHECK: %[[MEMPTR_ADJ_SHIFTED:.*]] = ashr i64 %[[MEMPTR_ADJ]], 1 +// CHECK: %[[V4:.*]] = getelementptr inbounds i8, ptr %[[V1]], i64 %[[MEMPTR_ADJ_SHIFTED]] +// CHECK: %[[MEMPTR_PTR:.*]] = extractvalue { i64, i64 } %[[V2]], 0 +// CHECK: %[[V5:.*]] = and i64 %[[MEMPTR_ADJ]], 1 +// CHECK: %[[MEMPTR_ISVIRTUAL:.*]] = icmp ne i64 %[[V5]], 0 +// CHECK: br i1 %[[MEMPTR_ISVIRTUAL]] + +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %[[V4]], align 8 +// CHECK: %[[V7:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[V8:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V7]], i32 2, i64 0) +// CHECK: %[[V9:.*]] = inttoptr i64 %[[V8]] to ptr +// CHECK: %[[V10:.*]] = trunc i64 %[[MEMPTR_PTR]] to i32 +// CHECK: %[[V11:.*]] = zext i32 %[[V10]] to i64 +// CHECK: %[[V12:.*]] = getelementptr i8, ptr %[[V9]], i64 %[[V11]] +// CHECK: %[[MEMPTR_VIRTUALFN:.*]] = load ptr, ptr %[[V12]], align 8 +// CHECK: br + +// CHECK: %[[MEMPTR_NONVIRTUALFN:.*]] = inttoptr i64 %[[MEMPTR_PTR]] to ptr +// CHECK: br + +// CHECK: %[[V14:.*]] = phi ptr [ %[[MEMPTR_VIRTUALFN]], {{.*}} ], [ %[[MEMPTR_NONVIRTUALFN]], {{.*}} ] +// CHECK: %[[V15:.*]] = phi i64 [ 0, {{.*}} ], [ [[TYPEDISC0]], {{.*}} ] +// CHECK: call void %[[V14]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %[[V4]]) [ "ptrauth"(i32 0, i64 %[[V15]]) ] +// CHECK: ret void + +void test1(Base0 *a0, MethodTy0 a1) { + (a0->*a1)(); +} + +// CHECK: define void @_Z15testConversion0M5Base0FvvEM8Derived0FvvE([2 x i64] %[[METHOD0_COERCE:.*]], [2 x i64] %[[METHOD1_COERCE:.*]]) +// CHECK: %[[METHOD0:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[METHOD1:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[METHOD0_ADDR:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[METHOD1_ADDR:.*]] = alloca { i64, i64 }, align 8 +// CHECK: store [2 x i64] %[[METHOD0_COERCE]], ptr %[[METHOD0]], align 8 +// CHECK: %[[METHOD01:.*]] = load { i64, i64 }, ptr %[[METHOD0]], align 8 +// CHECK: store [2 x i64] %[[METHOD1_COERCE]], ptr %[[METHOD1]], align 8 +// CHECK: %[[METHOD12:.*]] = load { i64, i64 }, ptr %[[METHOD1]], align 8 +// CHECK: store { i64, i64 } %[[METHOD01]], ptr %[[METHOD0_ADDR]], align 8 +// CHECK: store { i64, i64 } %[[METHOD12]], ptr %[[METHOD1_ADDR]], align 8 +// CHECK: %[[V2:.*]] = load { i64, i64 }, ptr %[[METHOD0_ADDR]], align 8 +// CHECK: %[[MEMPTR_PTR:.*]] = extractvalue { i64, i64 } %[[V2]], 0 +// CHECK: %[[MEMPTR_ADJ:.*]] = extractvalue { i64, i64 } %[[V2]], 1 +// CHECK: %[[V3:.*]] = and i64 %[[MEMPTR_ADJ]], 1 +// CHECK: %[[IS_VIRTUAL_OFFSET:.*]] = icmp ne i64 %[[V3]], 0 +// CHECK: br i1 %[[IS_VIRTUAL_OFFSET]] + +// CHECK: %[[V4:.*]] = inttoptr i64 %[[MEMPTR_PTR]] to ptr +// CHECK: %[[V5:.*]] = icmp ne ptr %[[V4]], null +// CHECK: br i1 %[[V5]] + +// CHECK: %[[V6:.*]] = ptrtoint ptr %[[V4]] to i64 +// CHECK: %[[V7:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V6]], i32 0, i64 [[TYPEDISC0]], i32 0, i64 [[TYPEDISC1]]) +// CHECK: %[[V8:.*]] = inttoptr i64 %[[V7]] to ptr +// CHECK: br + +// CHECK: %[[V9:.*]] = phi ptr [ null, {{.*}} ], [ %[[V8]], {{.*}} ] +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V9]] to i64 +// CHECK: %[[V11:.*]] = insertvalue { i64, i64 } %[[V2]], i64 %[[V1]], 0 +// CHECK: br + +// CHECK: %[[V12:.*]] = phi { i64, i64 } [ %[[V2]], {{.*}} ], [ %[[V11]], {{.*}} ] +// CHECK: store { i64, i64 } %[[V12]], ptr %[[METHOD1_ADDR]], align 8 +// CHECK: ret void + +void testConversion0(MethodTy0 method0, MethodTy1 method1) { + method1 = method0; +} + +// CHECK: define void @_Z15testConversion1M5Base0FvvE( +// CHECK: call i64 @llvm.ptrauth.resign(i64 %{{.*}}, i32 0, i64 [[TYPEDISC0]], i32 0, i64 [[TYPEDISC1]]) + +void testConversion1(MethodTy0 method0) { + MethodTy1 method1 = reinterpret_cast(method0); +} + +// CHECK: define void @_Z15testConversion2M8Derived0FvvE( +// CHECK: call i64 @llvm.ptrauth.resign(i64 %{{.*}}, i32 0, i64 [[TYPEDISC1]], i32 0, i64 [[TYPEDISC0]]) + +void testConversion2(MethodTy1 method1) { + MethodTy0 method0 = static_cast(method1); +} + +// CHECK: define void @_Z15testConversion3M8Derived0FvvE( +// CHECK: call i64 @llvm.ptrauth.resign(i64 %{{.*}}, i32 0, i64 [[TYPEDISC1]], i32 0, i64 [[TYPEDISC0]]) + +void testConversion3(MethodTy1 method1) { + MethodTy0 method0 = reinterpret_cast(method1); +} + +// No need to call @llvm.ptrauth.resign if the source member function +// pointer is a constant. + +// CHECK: define void @_Z15testConversion4v( +// CHECK: %[[METHOD0:.*]] = alloca { i64, i64 }, align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD0]], align 8 +// CHECK: ret void + +void testConversion4() { + MethodTy0 method0 = reinterpret_cast(&Derived0::virtual1); +} + +// This code used to crash. +namespace testNonVirtualThunk { + struct R {}; + + struct B0 { + virtual void bar(); + }; + + struct B1 { + virtual R foo(); + }; + + struct D : B0, B1 { + virtual R foo(); + }; + + D d; +} + +// CHECK: define internal void @_ZN22TestAnonymousNamespace12_GLOBAL__N_11S3fooEv_vfpthunk_( + +namespace TestAnonymousNamespace { +namespace { +struct S { + virtual void foo(){}; +}; +} // namespace + +void test() { + auto t = &S::foo; +} +} // namespace TestAnonymousNamespace + +// CHECK: define void @_Z39test_builtin_ptrauth_type_discriminatorv() +// CHECK: store i32 [[TYPEDISC0]], ptr % +// CHECK: store i32 [[TYPEDISC1]], ptr % +// CHECK: store i32 [[TYPEDISC2]], ptr % + +void test_builtin_ptrauth_type_discriminator() { + unsigned d; + d = __builtin_ptrauth_type_discriminator(decltype(&Base0::virtual1)); + d = __builtin_ptrauth_type_discriminator(decltype(&Derived0::virtual6)); + d = __builtin_ptrauth_type_discriminator(decltype(&Base1::virtual7)); +} + +MethodTy1 gmethod0 = reinterpret_cast(&Base0::nonvirtual0); +MethodTy0 gmethod1 = reinterpret_cast(&Derived0::nonvirtual5); +MethodTy0 gmethod2 = reinterpret_cast(&Derived0::virtual1); + +// CHECK: define void @_Z15testConvertNullv( +// CHECK: %[[T:.*]] = alloca { i64, i64 }, +// store { i64, i64 } zeroinitializer, { i64, i64 }* %[[T]], + +void testConvertNull() { + VariadicMethodTy0 t = (VariadicMethodTy0)(MethodTy0{}); +} diff --git a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp index 74c3a0c32ba24..d62912b04e91c 100644 --- a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp +++ b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp @@ -1,35 +1,35 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG %s -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK %s - -// CHECK: %[[STRUCT_A0:.*]] = type { [4 x i32] } -// CHECK: %[[STRUCT_A1:.*]] = type { [8 x i32] } -// CHECK: %[[STRUCT_TRIVIALS:.*]] = type { [4 x i32] } - -// CHECK: @_ZN5Base011nonvirtual0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 0, i64 [[TYPEDISC0:22163]] }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC0]] }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual3Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC0]] }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN5Base016virtual_variadicEiz_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base016virtual_variadicEiz_vfpthunk_, i32 0, i64 0, i64 34368 }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN5Base011nonvirtual0Ev.ptrauth.1 = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 0, i64 [[TYPEDISC1:35591]] }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.2 = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth.3 = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual3Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN8Derived011nonvirtual5Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived011nonvirtual5Ev, i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN8Derived08virtual6Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived08virtual6Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN8Derived010return_aggEv_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived010return_aggEv_vfpthunk_, i32 0, i64 0, i64 64418 }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN8Derived04sretEv_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived04sretEv_vfpthunk_, i32 0, i64 0, i64 28187 }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_, i32 0, i64 0, i64 8992 }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN5Base18virtual7Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base18virtual7Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC2:61596]] }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN8Derived18virtual7Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived18virtual7Ev_vfpthunk_, i32 0, i64 0, i64 25206 }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.4 = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 0, i64 25206 }, section "llvm.ptrauth", align 8 - -// CHECK: @gmethod0 = global { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base011nonvirtual0Ev.ptrauth.1 to i64), i64 0 }, align 8 -// CHECK: @_ZN8Derived011nonvirtual5Ev.ptrauth.6 = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived011nonvirtual5Ev, i32 0, i64 0, i64 [[TYPEDISC0]] }, section "llvm.ptrauth", align 8 -// CHECK: @gmethod1 = global { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived011nonvirtual5Ev.ptrauth.6 to i64), i64 0 }, align 8 -// CHECK: @gmethod2 = global { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth to i64), i64 0 }, align 8 - -// CHECK: @_ZTV5Base0 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI5Base0, ptr @_ZN5Base08virtual1Ev.ptrauth, ptr @_ZN5Base08virtual3Ev.ptrauth, ptr @_ZN5Base016virtual_variadicEiz.ptrauth] }, align 8 -// CHECK: @_ZN5Base08virtual1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 2) to i64), i64 55600 }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN5Base08virtual3Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual3Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 3) to i64), i64 53007 }, section "llvm.ptrauth", align 8 -// CHECK: @_ZN5Base016virtual_variadicEiz.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base016virtual_variadicEiz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 4) to i64), i64 7464 }, section "llvm.ptrauth", align 8 +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 1 -o - %s | FileCheck %s -check-prefix=STACK-PROT +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 2 -o - %s | FileCheck %s -check-prefix=STACK-PROT +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 3 -o - %s | FileCheck %s -check-prefix=STACK-PROT + + +// CHECK: @gmethod0 = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 [[TYPEDISC1:35591]]) to i64), i64 0 }, align 8 +// CHECK: @gmethod1 = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN8Derived011nonvirtual5Ev, i32 0, i64 [[TYPEDISC0:22163]]) to i64), i64 0 }, align 8 +// CHECK: @gmethod2 = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 [[TYPEDISC0]]) to i64), i64 0 }, align 8 + +// CHECK: @__const._Z13testArrayInitv.p0 = private unnamed_addr constant [1 x { i64, i64 }] [{ i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 35591) to i64), i64 0 }], align 8 +// CHECK: @__const._Z13testArrayInitv.p1 = private unnamed_addr constant [1 x { i64, i64 }] [{ i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 35591) to i64), i64 0 }], align 8 +// CHECK: @__const._Z13testArrayInitv.c0 = private unnamed_addr constant %struct.Class0 { { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 35591) to i64), i64 0 } }, align 8 +// CHECK: @__const._Z13testArrayInitv.c1 = private unnamed_addr constant %struct.Class0 { { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 35591) to i64), i64 0 } }, align 8 + +// CHECK: @_ZTV5Base0 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI5Base0, +// CHECK-SAME: ptr ptrauth (ptr @_ZN5Base08virtual1Ev, i32 0, i64 55600, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 2)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN5Base08virtual3Ev, i32 0, i64 53007, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @_ZN5Base016virtual_variadicEiz, i32 0, i64 7464, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 4))] }, align 8 + +typedef __SIZE_TYPE__ size_t; + +namespace std { +template +class initializer_list { + const _Ep *__begin_; + size_t __size_; + + initializer_list(const _Ep *__b, size_t __s); +}; +} // namespace std struct Base0 { void nonvirtual0(); @@ -74,6 +74,10 @@ typedef void (Base0::*MethodTy0)(); typedef void (Base0::*VariadicMethodTy0)(int, ...); typedef void (Derived0::*MethodTy1)(); +struct Class0 { + MethodTy1 m0; +}; + // CHECK: define void @_ZN5Base08virtual1Ev( // CHECK: define void @_Z5test0v() @@ -85,21 +89,21 @@ typedef void (Derived0::*MethodTy1)(); // CHECK-NEXT: %[[METHOD5:.*]] = alloca { i64, i64 }, align 8 // CHECK-NEXT: %[[METHOD6:.*]] = alloca { i64, i64 }, align 8 // CHECK-NEXT: %[[METHOD7:.*]] = alloca { i64, i64 }, align 8 -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base011nonvirtual0Ev.ptrauth to i64), i64 0 }, ptr %[[METHOD0]], align 8 -// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD0]], align 8 -// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD0]], align 8 -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base016virtual_variadicEiz_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[VARMETHOD1]], align 8 -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base011nonvirtual0Ev.ptrauth.1 to i64), i64 0 }, ptr %[[METHOD2]], align 8 -// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.2 to i64), i64 0 }, ptr %[[METHOD2]], align 8 -// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth.3 to i64), i64 0 }, ptr %[[METHOD2]], align 8 -// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived011nonvirtual5Ev.ptrauth to i64), i64 0 }, ptr %[[METHOD2]], align 8 -// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived08virtual6Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD2]], align 8 -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived010return_aggEv_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD3]], align 8 -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived04sretEv_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD4]], align 8 -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD5]], align 8 -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base18virtual7Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD6]], align 8 -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived18virtual7Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD7]], align 8 -// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.4 to i64), i64 0 }, ptr %[[METHOD7]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 [[TYPEDISC0]]) to i64), i64 0 }, ptr %[[METHOD0]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 [[TYPEDISC0]]) to i64), i64 0 }, ptr %[[METHOD0]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual3Ev_vfpthunk_, i32 0, i64 [[TYPEDISC0]]) to i64), i64 0 }, ptr %[[METHOD0]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base016virtual_variadicEiz_vfpthunk_, i32 0, i64 34368) to i64), i64 0 }, ptr %[[VARMETHOD1]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 [[TYPEDISC1]]) to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 [[TYPEDISC1]]) to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual3Ev_vfpthunk_, i32 0, i64 [[TYPEDISC1]]) to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN8Derived011nonvirtual5Ev, i32 0, i64 [[TYPEDISC1]]) to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN8Derived08virtual6Ev_vfpthunk_, i32 0, i64 [[TYPEDISC1]]) to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN8Derived010return_aggEv_vfpthunk_, i32 0, i64 64418) to i64), i64 0 }, ptr %[[METHOD3]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN8Derived04sretEv_vfpthunk_, i32 0, i64 28187) to i64), i64 0 }, ptr %[[METHOD4]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_, i32 0, i64 8992) to i64), i64 0 }, ptr %[[METHOD5]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base18virtual7Ev_vfpthunk_, i32 0, i64 [[TYPEDISC2:61596]]) to i64), i64 0 }, ptr %[[METHOD6]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN8Derived18virtual7Ev_vfpthunk_, i32 0, i64 25206) to i64), i64 0 }, ptr %[[METHOD7]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 25206) to i64), i64 0 }, ptr %[[METHOD7]], align 8 // CHECK: ret void // CHECK: define linkonce_odr hidden void @_ZN5Base08virtual1Ev_vfpthunk_(ptr noundef %[[THIS:.*]]) @@ -170,7 +174,7 @@ typedef void (Derived0::*MethodTy1)(); // call. // CHECK: define linkonce_odr hidden void @_ZN8Derived04sretEv_vfpthunk_(ptr dead_on_unwind noalias writable sret(%struct.A1) align 4 %[[AGG_RESULT:.*]], ptr noundef %{{.*}}) -// CHECK: musttail call void %{{.*}}(ptr dead_on_unwind writable sret(%struct.A1) align 4 %[[AGG_RESULT]], ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %{{.*}}) [ "ptrauth"(i32 0, i64 %{{.*}}) ] +// CHECK: musttail call void %{{.*}}(ptr dead_on_unwind writable sret(%struct.A1) align 4 %[[AGG_RESULT]], ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %{{.*}}) [ "ptrauth"(i32 0, i64 %{{.*}}) ] // CHECK-NEXT: ret void // Check that the thunk function doesn't destruct the trivial_abi argument. @@ -348,7 +352,7 @@ void testConversion3(MethodTy1 method1) { // CHECK: define void @_Z15testConversion4v( // CHECK: %[[METHOD0:.*]] = alloca { i64, i64 }, align 8 -// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD0]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 [[TYPEDISC0]]) to i64), i64 0 }, ptr %[[METHOD0]], align 8 // CHECK: ret void void testConversion4() { @@ -392,18 +396,54 @@ void test() { // CHECK: store i32 [[TYPEDISC0]], ptr % // CHECK: store i32 [[TYPEDISC1]], ptr % // CHECK: store i32 [[TYPEDISC2]], ptr % +// CHECK: store i32 [[TYPEDISC0]], ptr % +// CHECK: store i32 [[TYPEDISC0]], ptr % +// CHECK: store i32 [[TYPEDISC0]], ptr % void test_builtin_ptrauth_type_discriminator() { unsigned d; d = __builtin_ptrauth_type_discriminator(decltype(&Base0::virtual1)); d = __builtin_ptrauth_type_discriminator(decltype(&Derived0::virtual6)); d = __builtin_ptrauth_type_discriminator(decltype(&Base1::virtual7)); + d = __builtin_ptrauth_type_discriminator(void (Base0::* const)()); + typedef void (Base0::* const ConstMFP)(); + typedef ConstMFP __attribute__((address_space(1))) AddrSpaceMFP; + d = __builtin_ptrauth_type_discriminator(ConstMFP); + d = __builtin_ptrauth_type_discriminator(AddrSpaceMFP); } MethodTy1 gmethod0 = reinterpret_cast(&Base0::nonvirtual0); MethodTy0 gmethod1 = reinterpret_cast(&Derived0::nonvirtual5); MethodTy0 gmethod2 = reinterpret_cast(&Derived0::virtual1); +// CHECK-LABEL: define void @_Z13testArrayInitv() +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %p0, ptr align 8 @__const._Z13testArrayInitv.p0, i64 16, i1 false) +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %p1, ptr align 8 @__const._Z13testArrayInitv.p1, i64 16, i1 false) +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %c0, ptr align 8 @__const._Z13testArrayInitv.c0, i64 16, i1 false) +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %c1, ptr align 8 @__const._Z13testArrayInitv.c1, i64 16, i1 false) +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 [[TYPEDISC1]]) to i64), i64 0 }, ptr %{{.*}} align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 [[TYPEDISC1]]) to i64), i64 0 }, ptr %{{.*}}, align 8 + +void initList(std::initializer_list); + +void testArrayInit() { + MethodTy1 p0[] = {&Base0::nonvirtual0}; + MethodTy1 p1[] = {&Base0::virtual1}; + Class0 c0{&Base0::nonvirtual0}; + Class0 c1{&Base0::virtual1}; + initList({&Base0::nonvirtual0}); + initList({&Base0::virtual1}); +} + + + +// STACK-PROT: define {{.*}}_vfpthunk{{.*}}[[ATTRS:#[0-9]+]] +// STACK-PROT: attributes [[ATTRS]] = +// STACK-PROT-NOT: ssp +// STACK-PROT-NOT: sspstrong +// STACK-PROT-NOT: sspreq +// STACK-PROT-NEXT: attributes + // CHECK: define void @_Z15testConvertNullv( // CHECK: %[[T:.*]] = alloca { i64, i64 }, // store { i64, i64 } zeroinitializer, { i64, i64 }* %[[T]], diff --git a/clang/test/Preprocessor/ptrauth_feature.c b/clang/test/Preprocessor/ptrauth_feature.c index b0978b99bf260..05b96e4600f96 100644 --- a/clang/test/Preprocessor/ptrauth_feature.c +++ b/clang/test/Preprocessor/ptrauth_feature.c @@ -140,6 +140,15 @@ void has_ptrauth_qualifier() {} void no_ptrauth_qualifier() {} #endif +// This is always enabled when ptrauth_calls is enabled, on new enough clangs. +#if __has_feature(ptrauth_member_function_pointer_type_discrimination) +// CALLS: has_ptrauth_member_function_pointer_type_discrimination +void has_ptrauth_member_function_pointer_type_discrimination() {} +#else +// NOCALLS: no_ptrauth_member_function_pointer_type_discrimination +void no_ptrauth_member_function_pointer_type_discrimination() {} +#endif + #include #if __has_feature(ptrauth_function_pointer_type_discrimination) From eb8fcc25f58b9a1234e47a0f6a2effeeb614d947 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Tue, 2 Jul 2024 20:02:00 -0700 Subject: [PATCH 29/58] [clang] Sign function pointers passed to atexit and __cxa_atexit. --- clang/lib/CodeGen/CGDeclCXX.cpp | 2 +- ...uth-static-destructors-wrapper-globals.cpp | 26 +++++++++++++++++++ .../CodeGenCXX/ptrauth-static-destructors.cpp | 10 +++---- 3 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 clang/test/CodeGenCXX/ptrauth-static-destructors-wrapper-globals.cpp diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp index 3936b3509f3d4..597e155495c23 100644 --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -339,7 +339,7 @@ void CodeGenFunction::registerGlobalDtorWithLLVM(const VarDecl &VD, llvm::Constant *Addr) { // Create a function which calls the destructor. llvm::Function *dtorStub = - cast(createAtExitStub(VD, Dtor, Addr)); + cast(createAtExitStub(VD, Dtor, Addr)); CGM.AddGlobalDtor(dtorStub); } diff --git a/clang/test/CodeGenCXX/ptrauth-static-destructors-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-static-destructors-wrapper-globals.cpp new file mode 100644 index 0000000000000..0dd929583b38c --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-static-destructors-wrapper-globals.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ +// RUN: | FileCheck %s --check-prefix=CXAATEXIT + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ +// RUN: -fno-use-cxa-atexit \ +// RUN: | FileCheck %s --check-prefix=ATEXIT + +class Foo { + public: + ~Foo() { + } +}; + +Foo global; + +// CXAATEXIT: @_ZN3FooD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN3FooD1Ev, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CXAATEXIT: define internal void @__cxx_global_var_init() +// CXAATEXIT: call i32 @__cxa_atexit(ptr @_ZN3FooD1Ev.ptrauth, ptr @global, ptr @__dso_handle) + + +// ATEXIT: @__dtor_global.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__dtor_global, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// ATEXIT: define internal void @__cxx_global_var_init() +// ATEXIT: %{{.*}} = call i32 @atexit(ptr @__dtor_global.ptrauth) + +// ATEXIT: define internal void @__dtor_global() {{.*}} section "__TEXT,__StaticInit,regular,pure_instructions" { +// ATEXIT: %{{.*}} = call ptr @_ZN3FooD1Ev(ptr @global) diff --git a/clang/test/CodeGenCXX/ptrauth-static-destructors.cpp b/clang/test/CodeGenCXX/ptrauth-static-destructors.cpp index 0dd929583b38c..a2efaf3d138a8 100644 --- a/clang/test/CodeGenCXX/ptrauth-static-destructors.cpp +++ b/clang/test/CodeGenCXX/ptrauth-static-destructors.cpp @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ // RUN: | FileCheck %s --check-prefix=CXAATEXIT -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ // RUN: -fno-use-cxa-atexit \ // RUN: | FileCheck %s --check-prefix=ATEXIT @@ -13,14 +13,12 @@ class Foo { Foo global; -// CXAATEXIT: @_ZN3FooD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN3FooD1Ev, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 // CXAATEXIT: define internal void @__cxx_global_var_init() -// CXAATEXIT: call i32 @__cxa_atexit(ptr @_ZN3FooD1Ev.ptrauth, ptr @global, ptr @__dso_handle) +// CXAATEXIT: call i32 @__cxa_atexit(ptr ptrauth (ptr @_ZN3FooD1Ev, i32 0, i64 10942), ptr @global, ptr @__dso_handle) -// ATEXIT: @__dtor_global.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__dtor_global, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 // ATEXIT: define internal void @__cxx_global_var_init() -// ATEXIT: %{{.*}} = call i32 @atexit(ptr @__dtor_global.ptrauth) +// ATEXIT: %{{.*}} = call i32 @atexit(ptr ptrauth (ptr @__dtor_global, i32 0, i64 10942)) // ATEXIT: define internal void @__dtor_global() {{.*}} section "__TEXT,__StaticInit,regular,pure_instructions" { // ATEXIT: %{{.*}} = call ptr @_ZN3FooD1Ev(ptr @global) From bcd6996ec8b091d1365510536d471e71d46a052e Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Tue, 2 Jul 2024 20:03:23 -0700 Subject: [PATCH 30/58] [clang] Sign the dtor passed to __cxa_throw as a void(*)(void*) fptr. __cxa_throw is declared to take its destructor as void (*)(void *). We must match that if function pointers can be authenticated with a discriminator based on their type. --- clang/lib/CodeGen/ItaniumCXXABI.cpp | 9 ++++++++- .../CodeGenCXX/ptrauth-throw-wrapper-globals.cpp | 16 ++++++++++++++++ clang/test/CodeGenCXX/ptrauth-throw.cpp | 14 +++++++++----- 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 clang/test/CodeGenCXX/ptrauth-throw-wrapper-globals.cpp diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 781662f498423..8852b39f46be1 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -1484,9 +1484,16 @@ void ItaniumCXXABI::emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) { if (const RecordType *RecordTy = ThrowType->getAs()) { CXXRecordDecl *Record = cast(RecordTy->getDecl()); if (!Record->hasTrivialDestructor()) { + // __cxa_throw is declared to take its destructor as void (*)(void *). We + // must match that if function pointers can be authenticated with a + // discriminator based on their type. + ASTContext &Ctx = getContext(); + QualType DtorTy = Ctx.getFunctionType(Ctx.VoidTy, {Ctx.VoidPtrTy}, + FunctionProtoType::ExtProtoInfo()); + CXXDestructorDecl *DtorD = Record->getDestructor(); Dtor = CGM.getAddrOfCXXStructor(GlobalDecl(DtorD, Dtor_Complete)); - Dtor = CGM.getFunctionPointer(Dtor, DtorD->getType()); + Dtor = CGM.getFunctionPointer(Dtor, DtorTy); } } if (!Dtor) Dtor = llvm::Constant::getNullValue(CGM.Int8PtrTy); diff --git a/clang/test/CodeGenCXX/ptrauth-throw-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-throw-wrapper-globals.cpp new file mode 100644 index 0000000000000..3c1f2cefb7002 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-throw-wrapper-globals.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s + +class Foo { + public: + ~Foo() { + } +}; + +void f() { + throw Foo(); +} + +// CHECK: @_ZN3FooD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN3FooD1Ev, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 + +// CHECK: define void @_Z1fv() +// CHECK: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTI3Foo, ptr @_ZN3FooD1Ev.ptrauth) diff --git a/clang/test/CodeGenCXX/ptrauth-throw.cpp b/clang/test/CodeGenCXX/ptrauth-throw.cpp index 3c1f2cefb7002..2c508d9d05dbd 100644 --- a/clang/test/CodeGenCXX/ptrauth-throw.cpp +++ b/clang/test/CodeGenCXX/ptrauth-throw.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s class Foo { public: @@ -6,11 +6,15 @@ class Foo { } }; +// CHECK-LABEL: define void @_Z1fv() +// CHECK: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTI3Foo, ptr ptrauth (ptr @_ZN3FooD1Ev, i32 0, i64 [[DISC:10942]])) void f() { throw Foo(); } -// CHECK: @_ZN3FooD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN3FooD1Ev, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 - -// CHECK: define void @_Z1fv() -// CHECK: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTI3Foo, ptr @_ZN3FooD1Ev.ptrauth) +// __cxa_throw is defined to take its destructor as "void (*)(void *)" in the ABI. +// CHECK-LABEL: define void @__cxa_throw({{.*}}) +// CHECK: call void {{%.*}}(ptr noundef {{%.*}}) [ "ptrauth"(i32 0, i64 [[DISC]]) ] +extern "C" void __cxa_throw(void *exception, void *, void (*dtor)(void *)) { + dtor(exception); +} From ba1a4a4b821bd9d7539062ffdfeec0786b2d1c1c Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Fri, 24 May 2024 15:17:35 -0700 Subject: [PATCH 31/58] [clang][Headers] Define ptrauth_nop_cast. --- clang/lib/Headers/ptrauth.h | 20 ++++++++++++++++++++ clang/test/CodeGen/ptrauth_nop_cast.c | 23 +++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 clang/test/CodeGen/ptrauth_nop_cast.c diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h index d02c6189a4c45..2dbeb759348bb 100644 --- a/clang/lib/Headers/ptrauth.h +++ b/clang/lib/Headers/ptrauth.h @@ -203,6 +203,24 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; ptrauth_auth_and_resign(__value, __old_key, __old_data, \ ptrauth_key_function_pointer, 0) +/* Cast a pointer to the given type without changing any signature. + + The type must be a pointer type. + The value must be an expression of function pointer type, and will be + converted to an rvalue prior to the cast. + The result has type given by the first argument. + + The result has an identical bit-pattern to the input pointer. */ +#define ptrauth_nop_cast(__type, __value) \ + ({ \ + union { \ + typeof(*(__value)) *__fptr; \ + typeof(__type) __opaque; \ + } __storage; \ + __storage.__fptr = (__value); \ + __storage.__opaque; \ + }) + /* Authenticate a data pointer. The value must be an expression of non-function pointer type. @@ -370,6 +388,8 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; __value; \ }) +#define ptrauth_nop_cast(__type, __value) (__type)(__value) + #define ptrauth_auth_data(__value, __old_key, __old_data) \ ({ \ (void)__old_key; \ diff --git a/clang/test/CodeGen/ptrauth_nop_cast.c b/clang/test/CodeGen/ptrauth_nop_cast.c new file mode 100644 index 0000000000000..872e5c78a86ae --- /dev/null +++ b/clang/test/CodeGen/ptrauth_nop_cast.c @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64e-apple-ios13.0 -fptrauth-calls -fptrauth-intrinsics -O1 %s -emit-llvm -o - | FileCheck %s + +#include + +void *test_nop_cast(void (*fptr)(int)) { + // CHECK: define noundef ptr @test_nop_cast(ptr noundef readnone returned [[FPTR:%.*]]) + // CHECK: ret ptr [[FPTR]] + return ptrauth_nop_cast(void *, fptr); +} + +typedef void (*VoidFn)(void); + +VoidFn test_nop_cast_functype(void (*fptr)(int, int)) { + // CHECK: define noundef ptr @test_nop_cast_functype(ptr noundef readnone returned [[FPTR:%.*]]) + // CHECK: ret ptr [[FPTR]] + return ptrauth_nop_cast(void (*)(void), fptr); +} + +void *test_nop_cast_direct() { + // CHECK: define nonnull ptr @test_nop_cast_direct() + // CHECK: ret ptr ptrauth (ptr @test_nop_cast_direct, i32 0) + return ptrauth_nop_cast(void *, test_nop_cast_direct); +} From 82fc01bda58ba7085985a76f1f4868e5af95a5d3 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 8 Jan 2024 09:39:13 -0800 Subject: [PATCH 32/58] [clang][docs] Document the ptrauth security model. --- clang/docs/PointerAuthentication.rst | 761 +++++++++++++++++---------- 1 file changed, 495 insertions(+), 266 deletions(-) diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst index 6e381c0dc820e..b3151ce69a163 100644 --- a/clang/docs/PointerAuthentication.rst +++ b/clang/docs/PointerAuthentication.rst @@ -182,7 +182,9 @@ Discriminators A discriminator is arbitrary extra data which alters the signature calculated for a pointer. When two pointers are signed differently --- either with different keys or with different discriminators --- an attacker cannot simply -replace one pointer with the other. +replace one pointer with the other. For more information on why discriminators +are important and how to use them effectively, see the section on `Substitution +attacks`_. To use standard cryptographic terminology, a discriminator acts as a `salt `_ in the signing of a @@ -257,10 +259,6 @@ everywhere it is needed, but at the very least, it must not be derived by inspecting information stored along with the pointer. See the section on `Attacks on pointer authentication`_ for more information. - - - - Language Features ----------------- @@ -711,43 +709,271 @@ unstable or implementation-specific. +Language ABI +------------ +This section describes the pointer-authentication ABI currently implemented in Clang for the Apple arm64e target. As other targets adopt pointer authentication, this section should be generalized to express their ABIs as well. -Theory of Operation -------------------- +Key assignments +~~~~~~~~~~~~~~~ -The threat model of pointer authentication is as follows: +ARMv8.3 provides four abstract signing keys: ``IA``, ``IB``, ``DA``, and ``DB``. The architecture designates ``IA`` and ``IB`` for signing code pointers and ``DA`` and ``DB`` for signing data pointers; this is reinforced by two properties: + +- The ISA provides instructions that perform combined auth+call and auth+load operations; these instructions can only use the ``I`` keys and ``D`` keys, respectively. + +- AArch64's TBI feature can be separately enabled for code pointers (controlling whether indirect-branch instructions ignore those bits) and data pointers (controlling whether memory-access instructions) ignore those bits. If TBI is enabled for a kind of pointer, the sign and auth operations preserve the TBI bits when signing with an associated keys (at the cost of shrinking the number of available signing bits by 8). + +arm64e then further subdivides the keys as follows: + +- The ``A`` keys are used for primarily "global" purposes like signing v-tables and function pointers. These keys are sometimes called *process-independent* or *cross-process* because on existing OSes they are not changed when changing processes, although this is not a platform guarantee. + +- The ``B`` keys are used for primarily "local" purposes like signing return addresses and frame pointers. These keys are sometimes called *process-specific* because they are typically different between processes. However, they are in fact shared across processes in one situation: systems which provide ``fork`` cannot change these keys in the child process; they can only be changed during ``exec``. + +Implementation-defined algorithms and quantities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The cryptographic hash algorithm used to compute signatures in ARMv8.3 is a private detail of the hardware implementation. + +arm64e restricts constant discriminators (used in ``__ptrauth`` and ``ptrauth_blend_discriminator``) to the range from 0 to 65535, inclusive. A 0 discriminator generally signifies that no blending is required; see the documentation for ``ptrauth_blend_discriminator``. This range is somewhat narrow but has two advantages: + +- The AArch64 ISA allows an arbitrary 16-bit immediate to be written over the top 16 bits of a register in a single instruction: + + .. code-block:: asm -- The attacker has the ability to read and write to a certain range of addresses, possibly the entire address space. However, they are constrained by the normal rules of the process: for example, they cannot write to memory that is mapped read-only, and if they access unmapped memory it will trigger a trap. + movk xN, #0x4849, LSL 48 -- The attacker has no ability to add arbitrary executable code to the program. For example, the program does not include malicious code to begin with, and the attacker cannot alter existing instructions, load a malicious shared library, or remap writable pages as executable. If the attacker wants to get the process to perform a specific sequence of actions, they must somehow subvert the normal control flow of the process. + This is ideal for the discriminator blending operation because it adds minimal code-size overhead and avoids overwriting any interesting bits from the pointer. Blending in a wider constant discriminator would either clobber interesting bits (e.g. if it was loaded with ``movk xN, #0x4c4f, LSL 32``) or require significantly more code (e.g. if the discriminator was loaded with a ``mov+bfi`` sequence). -In both of the above paragraphs, it is merely assumed that the attacker's *current* capabilities are restricted; that is, their current exploit does not directly give them the power to do these things. The attacker's immediate goal may well be to leverage their exploit to gain these capabilities, e.g. to load a malicious dynamic library into the process, even though the process does not directly contain code to do so. +- It is possible to pack a 16-bit discriminator into loader metadata with minimal compromises, whereas a wider discriminator would require extra metadata storage and therefore significantly impact load times. -Note that any bug that fits the above threat model can be immediately exploited as a denial-of-service attack by simply performing an illegal access and crashing the program. Pointer authentication cannot protect against this. While denial-of-service attacks are unfortunate, they are also unquestionably the best possible result of a bug this severe. Therefore, pointer authentication enthusiastically embraces the idea of halting the program on a pointer authentication failure rather than continuing in a possibly-compromised state. +The string hash used by ``ptrauth_string_discriminator`` is a 64-bit SipHash-2-4 using the constant seed ``b5d4c9eb79104a796fec8b1b428781d4`` (big-endian), with the result reduced by modulo to the range of non-zero discriminators (i.e. ``(rawHash % 65535) + 1``). -Pointer authentication is a form of control-flow integrity (CFI) enforcement. The basic security hypothesis behind CFI enforcement is that many bugs can only be usefully exploited (other than as a denial-of-service) by leveraging them to subvert the control flow of the program. If this is true, then by inhibiting or limiting that subversion, it may be possible to largely mitigate the security consequences of those bugs by rendering them impractical (or, ideally, impossible) to exploit. +Return addresses +~~~~~~~~~~~~~~~~ + +The kernel must ensure that attackers cannot replace LR due to an asynchronous exception; see `Register clobbering`_. If this is done by generally protecting LR, then functions which don't spill LR to the stack can avoid signing it entirely. Otherwise, the return address must be signed; on arm64e it is signed with the ``IB`` key using the stack pointer on entry as the discriminator. + +Protecting return addresses is of such particular importance that the ``IB`` key is almost entirely reserved for this purpose. -Every indirect branch in a program has a purpose. Using human intelligence, a programmer can describe where a particular branch *should* go according to this purpose: a ``return`` in ``printf`` should return to the call site, a particular call in ``qsort`` should call the comparator that was passed in as an argument, and so on. But for CFI to enforce that every branch in a program goes where it *should* in this sense would require CFI to perfectly enforce every semantic rule of the program's abstract machine; that is, it would require making the programming environment perfectly sound. That is out of scope. Instead, the goal of CFI is merely to catch attempts to make a branch go somewhere that its obviously *shouldn't* for its purpose: for example, to stop a call from branching into the middle of a function rather than its beginning. As the information available to CFI gets better about the purpose of the branch, CFI can enforce tighter and tighter restrictions on where the branch is permitted to go. Still, ultimately CFI cannot make the program sound. This may help explain why pointer authentication makes some of the choices it does: for example, to sign and authenticate mostly code pointers rather than every pointer in the program. Preventing attackers from redirecting branches is both particularly important and particularly approachable as a goal. Detecting corruption more broadly is infeasible with these techniques, and the attempt would have far higher cost. +Global offset tables +~~~~~~~~~~~~~~~~~~~~ + +The global offset table (GOT) is not ABI, but it is a common implementation technique for dynamic linking which deserves special discussion here. + +Whenever possible, signed pointers should be materialized directly in code rather than via the GOT, e.g. using an ``adrp+add+pac`` sequence on ARMv8.3. This decreases the amount of work necessary at load time to initialize the GOT, but more importantly, it defines away the potential for several attacks: + +- Attackers cannot change instructions, so there is no way to cause this code sequence to materialize a different pointer, whereas an access via the GOT always has *at minimum* a probabilistic chance to be the target of successful `substitution attacks`_. + +- The GOT is a dense pool of fixed pointers at a fixed offset relative to code; attackers can search this pool for useful pointers that can be used in `substitution attacks`_, whereas pointers that are only materialized directly are not so easily available. + +- Similarly, attackers can use `access path attacks`_ to replace a pointer to a signed pointer with a pointer to the GOT if the signing schema used within the GOT happens to be the same as the original pointer. This kind of collision becomes much less likely to be useful the fewer pointers are in the GOT in the first place. + +If this can be done for a symbol, then the compiler need only ensure that it materializes the signed pointer using registers that are safe against `register clobbering`_. + +However, many symbols can only be accessed via the GOT, e.g. because they resolve to definitions outside of the current image. In this case, care must be taken to ensure that using the GOT does not introduce weaknesses. + +- If the entire GOT can be mapped read-only after loading, then no signing is required within the GOT. In fact, not signing pointers in the GOT is preferable in this case because it makes the GOT useless for the harvesting and access-path attacks above. Storing raw pointers in this way is usually extremely unsafe, but for the special case of an immutable GOT entry it's fine because the GOT is always accessed via an address that is directly materialized in code and thus provably unattackable. (But see `Remapping`_.) + +- Otherwise, GOT entries which are used for producing a signed pointer constant must be signed. The signing schema used in the GOT need not match the target signing schema for the signed constant. To counteract the threats of substitution attacks, it's best if GOT entries can be signed with address diversity. Using a good constant discriminator as well (perhaps derived from the symbol name) can make it less useful to use a pointer to the GOT as the replacement in an :ref:`access path attack`. + +In either case, the compiler must ensure that materializing the address of a GOT entry as part of producing a signed pointer constant is not vulnerable to `register clobbering`_. If the linker also generates code for this, e.g. for call stubs, this generated code must take the same precautions. + +C function pointers +~~~~~~~~~~~~~~~~~~~ + +On arm64e, C function pointers are currently signed with the ``IA`` key without address diversity and with a constant discriminator of 0. + +The C and C++ standards do not permit C function pointers to be signed with address diversity by default: in C++ terms, function pointer types are required to be trivially copyable, which means they must be copyable with ``memcpy``. + +The use of a uniform constant discriminator is seen as a serious defect which should be remedied, and improving this is under investigation. + +C++ virtual tables +~~~~~~~~~~~~~~~~~~ + +The pointer to a C++ virtual table is currently signed with the ``DA`` key, no address diversity, and a constant discriminator of 0. The use of no address diversity, as well as the uniform constant discriminator, are seen as weaknesses. Not using address diversity allows attackers to simply copy valid v-table pointers from one object to another. However, using a uniform discriminator of 0 does have positive performance and code-size implications on ARMv8.3, and diversity for the most important v-table access pattern (virtual dispatch) is already better assured by the signing schemas used on the virtual functions. It is also known that some code in practice copies objects containing v-tables with ``memcpy``, and while this is not permitted formally, it is something that may be invasive to eliminate. + +Virtual functions in a C++ virtual table are signed with the ``IA`` key, address diversity, and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangled name of the function which originally gave rise to the v-table slot. + +C++ member function pointers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A member function pointer is signed with the ``IA`` key, no address diversity, and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the member pointer type. Address diversity is not permitted by C++ for member function pointers because they must be trivially-copyable types. -Attacks on pointer authentication +The Itanium C++ ABI specifies that member function pointers to virtual functions simply store an offset to the correct v-table slot. This ABI cannot be used securely with pointer authentication because there is no safe place to store the constant discriminator for the target v-table slot: if it's stored with the offset, an attacker can simply overwrite it with the right discriminator for the offset. Even if the programmer never uses pointers to virtual functions, the existence of this code path makes all member function pointer dereferences insecure. + +arm64e changes this ABI so that virtual function pointers are stored using dispatch thunks with vague linkage. Because arm64e supports interoperation with ``arm64`` code when pointer authentication is disabled, an arm64e member function pointer dereference still recognizes the virtual-function representation but uses an bogus discriminator on that path that should always trap if pointer authentication is enabled dynamically. + +The use of dispatch thunks means that ``==`` on member function pointers is no longer reliable for virtual functions, but this is acceptable because the standard makes no guarantees about it in the first place. + +The use of dispatch thunks also potentially enables v-tables to be signed using a declaration-specific constant discriminator in the future; otherwise this discriminator would also need to be stored in the member pointer. + +Blocks +~~~~~~ + +Block pointers are data pointers which must interoperate with the ObjC `id` type and therefore cannot be signed themselves. + +The invocation pointer in a block is signed with the ``IA`` key using address diversity and a constant dicriminator of 0. Using a uniform discriminator is seen as a weakness to be potentially improved, but this is tricky due to the subtype polymorphism directly permitted for blocks. + +Block descriptors and ``__block`` variables can contain pointers to functions that can be used to copy or destroy the object. These functions are signed with the ``IA`` key, address diversity, and a constant discriminator of 0. The structure of block descriptors is under consideration for improvement. + +Objective-C methods +~~~~~~~~~~~~~~~~~~~ + +Objective-C method lists sign methods with the ``IA`` key using address diversity and a constant discriminator of 0. Using a uniform constant discriminator is believed to be acceptable because these tables are only accessed internally to the Objective-C runtime. + +The Objective-C runtime provides additional protection to methods that have been loaded into the Objective-C method cache; this protection is private to the runtime. + +Pointer authentication cannot protect against access-path atacks against the Objective-C ``isa`` pointer, through which all dispatch occurs, because of compatibility requirements and existing and important usage of high bits in the pointer. + +Swift class methods +~~~~~~~~~~~~~~~~~~~ + +Class methods in Swift are signed in the class object with the ``IA`` key using address diversity and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangling of the original overridable method. + +Resilient class-method lookup relies on passing a method descriptor; this method descriptor should be signed but currently isn't. The lookup function returns a function pointer that is signed using ``IA`` without address diversity and with the correct constant discriminator for the looked-up method. + +Swift's equivalent of a C++ v-table pointer is the ``isa`` pointer of an object. On arm64e, this is constrained by Objective-C compatibility and cannot be a signed pointer. + +Swift heap destructors +~~~~~~~~~~~~~~~~~~~~~~ + +Objects that are retained and released with Swift's native reference-counting system, including both native classes and temporary "box" allocations, must provide a destructor function in their metadata. This destructor function is signed with the ``IA`` key using address diversity and a constant discriminator of ``0xbbbf``. + +Swift protocol requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Protocol function requirements are signed in the protocol witness table with the ``IA`` key using address diversity and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangling of the protocol requirement. + +Swift function types +~~~~~~~~~~~~~~~~~~~~ + +The invocation pointers of Swift function values are signed using the ``IA`` key without address diversity and with a constant discriminator derived loosely from the function type. + +Address diversity cannot be used by default for function values because function types are intended to be a "loadable" type which can be held and passed in registers. + +The constant discriminator currently accounts for potential abstraction in the function signature in ways that decrease the diversity of signatures; improving this is under investigation. + +Swift metadata +~~~~~~~~~~~~~~ + +Type metadata pointers in Swift are not signed. + +Type context descriptors must be signed because they frequently contain `relative addresses`_. Type context descriptors are signed with the ``DA`` key without address diversity (except when stored in type metadata) and with a constant discriminator of ``0xae86``. + +Swift value witnesses +~~~~~~~~~~~~~~~~~~~~~ + +Value witness functions in Swift are signed in the value witness table using the ``IA`` key with address diversity and an operation-specific constant discriminator which can be found in the Swift project headers. + +Swift coroutines +~~~~~~~~~~~~~~~~ + +Resumption functions for Swift coroutines are signed using the ``IA`` key without address diversity and with a constant discriminator derived from the yield type of the coroutine. Resumption functions cannot be signed with address diversity as they are returned directly in registers from the coroutine. + + + + + +Theory of Operation +------------------- + +The threat model of pointer authentication is as follows: + +- The attacker has the ability to read and write to a certain range of + addresses, possibly the entire address space. However, they are constrained + by the normal rules of the process: for example, they cannot write to memory + that is mapped read-only, and if they access unmapped memory it will trigger + a trap. + +- The attacker has no ability to add arbitrary executable code to the program. + For example, the program does not include malicious code to begin with, and + the attacker cannot alter existing instructions, load a malicious shared + library, or remap writable pages as executable. If the attacker wants to get + the process to perform a specific sequence of actions, they must somehow + subvert the normal control flow of the process. + +In both of the above paragraphs, it is merely assumed that the attacker's +*current* capabilities are restricted; that is, their current exploit does not +directly give them the power to do these things. The attacker's immediate goal +may well be to leverage their exploit to gain these capabilities, e.g. to load +a malicious dynamic library into the process, even though the process does not +directly contain code to do so. + +Note that any bug that fits the above threat model can be immediately exploited +as a denial-of-service attack by simply performing an illegal access and +crashing the program. Pointer authentication cannot protect against this. +While denial-of-service attacks are unfortunate, they are also unquestionably +the best possible result of a bug this severe. Therefore, pointer +authentication enthusiastically embraces the idea of halting the program on +a pointer authentication failure rather than continuing in a possibly +compromised state. + +Pointer authentication is a form of control-flow integrity (CFI) enforcement. +The basic security hypothesis behind CFI enforcement is that many bugs can only +be usefully exploited (other than as a denial-of-service) by leveraging them to +subvert the control flow of the program. If this is true, then by inhibiting +or limiting that subversion, it may be possible to largely mitigate the +security consequences of those bugs by rendering them impractical (or, ideally, +impossible) to exploit. + +Every indirect branch in a program has a purpose. Using human intelligence, +a programmer can describe where a particular branch *should* go according to +this purpose: a ``return`` in ``printf`` should return to the call site, +a particular call in ``qsort`` should call the comparator that was passed in as +an argument, and so on. But for CFI to enforce that every branch in a program +goes where it *should* in this sense would require CFI to perfectly enforce +every semantic rule of the program's abstract machine; that is, it would +require making the programming environment perfectly sound. That is out of +scope. Instead, the goal of CFI is merely to catch attempts to make a branch +go somewhere that it obviously *shouldn't* for its purpose: for example, to +stop a call from branching into the middle of a function rather than its +beginning. As the information available to CFI gets better about the purpose +of the branch, CFI can enforce tighter and tighter restrictions on where the +branch is permitted to go. Still, ultimately CFI cannot make the program +sound. This may help explain why pointer authentication makes some of the +choices it does: for example, to sign and authenticate mostly code pointers +rather than every pointer in the program. Preventing attackers from +redirecting branches is both particularly important and particularly +approachable as a goal. Detecting corruption more broadly is infeasible with +these techniques, and the attempt would have far higher cost. + +Attacks on Pointer Authentication ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Pointer authentication works as follows. Every indirect branch in a program has a purpose. For every purpose, the implementation chooses a :ref:`signing schema`. At some place where a pointer is known to be correct for its purpose, it is signed according to the purpose's schema. At every place where the pointer is needed for its purpose, it is authenticated according to the purpose's schema. If that authentication fails, the program is halted. +Pointer authentication works as follows. Every indirect branch in a program +has a purpose. For every purpose, the implementation chooses a :ref:`signing +schema`. At some place where a pointer is known to be correct +for its purpose, it is signed according to the purpose's schema. At every +place where the pointer is needed for its purpose, it is authenticated +according to the purpose's schema. If that authentication fails, the program +is halted. There are a variety of ways to attack this. -Attacks of interest to programmers +Attacks of Interest to Programmers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -These attacks arise from weaknesses in the default protections offered by pointer authentication. They can be addressed by using attributes or intrinsics to opt in to stronger protection. +These attacks arise from weaknesses in the default protections offered by +pointer authentication. They can be addressed by using attributes or +intrinsics to opt in to stronger protection. -Substitution attacks +Substitution Attacks ++++++++++++++++++++ -An attacker can simply overwrite a pointer intended for one purpose with a pointer intended for another purpose if both purposes use the same signing schema and that schema does not use address diversity. - -The most common source of this weakness is when code relies on using the default language rules for C function pointers. The current implementation uses the exact same signing schema for all C function pointers, even for functions of substantially different type. While efforts are ongoing to improve constant diversity for C function pointers of different type, there are necessary limits to this. The C standard requires function pointers to be copyable with ``memcpy``, which means that function pointers can never use address diversity. Furthermore, even if a function pointer can only be replaced with another function of the exact same type, that can still be useful to an attacker, as in the following example of a hand-rolled "v-table": +An attacker can simply overwrite a pointer intended for one purpose with +a pointer intended for another purpose if both purposes use the same signing +schema and that schema does not use address diversity. + +The most common source of this weakness is when code relies on using the +default language rules for C function pointers. The current implementation +uses the exact same signing schema for all C function pointers, even for +functions of substantially different type. While efforts are ongoing to +improve constant diversity for C function pointers of different type, there are +necessary limits to this. The C standard requires function pointers to be +copyable with ``memcpy``, which means that function pointers can never use +address diversity. Furthermore, even if a function pointer can only be +replaced with another function of the exact same type, that can still be useful +to an attacker, as in the following example of a hand-rolled "v-table": .. code-block:: c @@ -758,7 +984,11 @@ The most common source of this weakness is when code relies on using the default void (*logStatus)(Object *); }; -This weakness can be mitigated by using a more specific signing schema for each purpose. For example, in this example, the ``__ptrauth`` qualifier can be used with a different constant discriminator for each field. Since there's no particular reason it's important for this v-table to be copyable with ``memcpy``, the functions can also be signed with address diversity: +This weakness can be mitigated by using a more specific signing schema for each +purpose. For example, in this example, the ``__ptrauth`` qualifier can be used +with a different constant discriminator for each field. Since there's no +particular reason it's important for this v-table to be copyable with +``memcpy``, the functions can also be signed with address diversity: .. code-block:: c @@ -776,77 +1006,149 @@ This weakness can be mitigated by using a more specific signing schema for each void (*objectOperation(0xc5d4) logStatus)(Object *); }; -This weakness can also sometimes be mitigated by simply keeping the signed pointer in constant memory, but this is less effective than using better signing diversity. +This weakness can also sometimes be mitigated by simply keeping the signed +pointer in constant memory, but this is less effective than using better +signing diversity. .. _Access path attacks: -Access path attacks +Access Path Attacks +++++++++++++++++++ -If a signed pointer is often accessed indirectly (that is, by first loading the address of the object where the signed pointer is stored), an attacker can affect uses of it by overwriting the intermediate pointer in the access path. - -The most common scenario exhibiting this weakness is an object with a pointer to a "v-table" (a structure holding many function pointers). An attacker does not need to replace a signed function pointer in the v-table if they can instead simply replace the v-table pointer in the object with their own pointer --- perhaps to memory where they've constructed their own v-table, or to existing memory that coincidentally happens to contain a signed pointer at the right offset that's been signed with the right signing schema. - -This attack arises because data pointers are not signed by default. It works even if the signed pointer uses address diversity: address diversity merely means that each pointer is signed with its own storage address, which (by design) is invariant to changes in the accessing pointer. - -Using sufficiently diverse signing schemas within the v-table can provide reasonably strong mitigation against this weakness. Always use address diversity in v-tables to prevent attackers from assembling their own v-table. Avoid re-using constant discriminators to prevent attackers from replacing a v-table pointer with a pointer to totally unrelated memory that just happens to contain an similarly-signed pointer. - -Further mitigation can be attained by signing pointers to v-tables. Any signature at all should prevent attackers from forging v-table pointers; they will need to somehow harvest an existing signed pointer from elsewhere in memory. Using a meaningful constant discriminator will force this to be harvested from an object with similar structure (e.g. a different implementation of the same interface). Using address diversity will prevent such harvesting entirely. However, care must be taken when sourcing the v-table pointer originally; do not blindly sign a pointer that is not :ref:`safely derived`. +If a signed pointer is often accessed indirectly (that is, by first loading the +address of the object where the signed pointer is stored), an attacker can +affect uses of it by overwriting the intermediate pointer in the access path. + +The most common scenario exhibiting this weakness is an object with a pointer +to a "v-table" (a structure holding many function pointers). An attacker does +not need to replace a signed function pointer in the v-table if they can +instead simply replace the v-table pointer in the object with their own pointer +--- perhaps to memory where they've constructed their own v-table, or to +existing memory that coincidentally happens to contain a signed pointer at the +right offset that's been signed with the right signing schema. + +This attack arises because data pointers are not signed by default. It works +even if the signed pointer uses address diversity: address diversity merely +means that each pointer is signed with its own storage address, which (by +design) is invariant to changes in the accessing pointer. + +Using sufficiently diverse signing schemas within the v-table can provide +reasonably strong mitigation against this weakness. Always use address +diversity in v-tables to prevent attackers from assembling their own v-table. +Avoid re-using constant discriminators to prevent attackers from replacing +a v-table pointer with a pointer to totally unrelated memory that just happens +to contain an similarly-signed pointer. + +Further mitigation can be attained by signing pointers to v-tables. Any +signature at all should prevent attackers from forging v-table pointers; they +will need to somehow harvest an existing signed pointer from elsewhere in +memory. Using a meaningful constant discriminator will force this to be +harvested from an object with similar structure (e.g. a different +implementation of the same interface). Using address diversity will prevent +such harvesting entirely. However, care must be taken when sourcing the +v-table pointer originally; do not blindly sign a pointer that is not +:ref:`safely derived`. .. _Signing oracles: Signing oracles +++++++++++++++ -A signing oracle is a bit of code which can be exploited by an attacker to sign an arbitrary pointer in a way that can later be recovered. Such oracles can be used by attackers to forge signatures matching the oracle's signing schema, which is likely to cause a total compromise of pointer authentication's effectiveness. +A signing oracle is a code sequence which can be exploited by an attacker to +sign an arbitrary pointer in a way that can later be recovered. Such oracles +can be used by attackers to forge signatures matching the oracle's signing +schema, which is likely to cause a total compromise of pointer authentication's +effectiveness. -This attack only affects ordinary programmers if they are using certain treacherous patterns of code. Currently this includes: +This attack only affects ordinary programmers if they are using certain +treacherous patterns of code. Currently this includes: - all uses of the ``__ptrauth_sign_unauthenticated`` intrinsic and - assigning data pointers to ``__ptrauth``-qualified l-values. -Care must be taken in these situations to ensure that the pointer being signed has been :ref:`safely derived` or is otherwise not possible to attack. (In some cases, this may be challenging without compiler support.) +Care must be taken in these situations to ensure that the pointer being signed +has been :ref:`safely derived` or is otherwise not possible to +attack. (In some cases, this may be challenging without compiler support.) -A diagnostic will be added in the future for implicitly dangerous patterns of code, such as assigning a non-safely-derived data pointer to a ``__ptrauth``-qualified l-value. +A diagnostic will be added in the future for implicitly dangerous patterns of +code, such as assigning a non-safely-derived data pointer to +a ``__ptrauth``-qualified l-value. .. _Authentication oracles: -Authentication oracles +Authentication Oracles ++++++++++++++++++++++ -An authentication oracle is a bit of code which can be exploited by an attacker to leak whether a signed pointer is validly signed without halting the program if it isn't. Such oracles can be used to forge signatures matching the oracle's signing schema if the attacker can repeatedly invoke the oracle for different candidate signed pointers. This is likely to cause a total compromise of pointer authentication's effectiveness. +An authentication oracle is a code sequence which can be exploited by an +attacker to leak whether a signed pointer is validly signed without halting the +program if it isn't. Such oracles can be used to forge signatures matching the +oracle's signing schema if the attacker can repeatedly invoke the oracle for +different candidate signed pointers. This is likely to cause a total compromise +of pointer authentication's effectiveness. -There should be no way for an ordinary programmer to create an authentication oracle using the current set of operations. However, implementation flaws in the past have occasionally given rise to authentication oracles due to a failure to immediately trap on authentication failure. +There should be no way for an ordinary programmer to create an authentication +oracle using the current set of operations. However, implementation flaws in +the past have occasionally given rise to authentication oracles due to +a failure to immediately trap on authentication failure. -The likelihood of creating an authentication oracle is why there is currently no intrinsic which queries whether a signed pointer is validly signed. +The likelihood of creating an authentication oracle is why there is currently +no intrinsic which queries whether a signed pointer is validly signed. -Attacks of interest to implementors +Attacks of Interest to Implementors ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -These attacks are not inherent to the model; they arise from mistakes in either implementing or using the `sign` and `auth` operations. Avoiding these mistakes requires careful work throughout the system. +These attacks are not inherent to the model; they arise from mistakes in either +implementing or using the `sign` and `auth` operations. Avoiding these mistakes +requires careful work throughout the system. -Failure to trap on authentication failure +Failure to Trap on Authentication Failure +++++++++++++++++++++++++++++++++++++++++ -Any failure to halt the program on an authentication failure is likely to be exploitable by attackers to create an :ref:`authentication oracle`. +Any failure to halt the program on an authentication failure is likely to be +exploitable by attackers to create an :ref:`authentication +oracle`. There are several different ways to introduce this problem: -- The implementation might try to halt the program in some way that can be intercepted. - - For example, the ``auth`` instruction in ARMv8.3 does not directly trap; instead it corrupts its result so that it is always an invalid pointer. If the program subsequently attempts to use that pointer, that will be a bad memory access, and it will trap into the kernel. However, kernels do not usually immediately halt programs that trigger traps due to bad memory accesses; instead they notify the process to give it an opportunity to recover. If this happens with an ``auth`` failure, the attacker may be able to exploit the recovery path in a way that creates an oracle. Kernels should ensure that these sorts of traps are not recoverable. - -- A compiler might use an intermediate representation (IR) for ``sign`` and ``auth`` operations that cannot make adequate correctness guarantees. - - For example, suppose that an IR uses ARMv8.3-like semantics for ``auth``: the operation merely corrupts its result on failure instead of promising the trap. A frontend might emit patterns of IR that always follow an ``auth`` with a memory access, thinking that this ensures correctness. But if the IR can be transformed to insert code between the ``auth`` and the access, or if the ``auth`` can be speculated, then this potentially creates an oracle. It is better for ``auth`` to semantically guarantee to trap, potentially requiring an explicit check in the generated code. An ARMv8.3-like target can avoid this explicit check in the common case by recognizing the pattern of an ``auth`` followed immediately by an access. - -Attackable code sequences +- The implementation might try to halt the program in some way that can be + intercepted. + + For example, the ``auth`` instruction in Armv8.3 does not directly trap; + instead it corrupts its result so that it is always an invalid pointer. If + the program subsequently attempts to use that pointer, that will be a bad + memory access, and it will trap into the kernel. However, kernels do not + usually immediately halt programs that trigger traps due to bad memory + accesses; instead they notify the process to give it an opportunity to + recover. If this happens with an ``auth`` failure, the attacker may be able + to exploit the recovery path in a way that creates an oracle. Kernels should + ensure that these sorts of traps are not recoverable. + +- A compiler might use an intermediate representation (IR) for ``sign`` and + ``auth`` operations that cannot make adequate correctness guarantees. + + For example, suppose that an IR uses Armv8.3-like semantics for ``auth``: the + operation merely corrupts its result on failure instead of promising the + trap. A frontend might emit patterns of IR that always follow an ``auth`` + with a memory access, thinking that this ensures correctness. But if the IR + can be transformed to insert code between the ``auth`` and the access, or if + the ``auth`` can be speculated, then this potentially creates an oracle. It + is better for ``auth`` to semantically guarantee to trap, potentially + requiring an explicit check in the generated code. An Armv8.3-like target can + avoid this explicit check in the common case by recognizing the pattern of an + ``auth`` followed immediately by an access. + +Attackable Code Sequences +++++++++++++++++++++++++ -If code that is part of a pointer authentication operation is interleaved with code that may itself be vulnerable to attacks, an attacker may be able to use this to create a :ref:`signing` or :ref:`authentication` oracle. +If code that is part of a pointer authentication operation is interleaved with +code that may itself be vulnerable to attacks, an attacker may be able to use +this to create a :ref:`signing` or +:ref:`authentication` oracle. -For example, suppose that the compiler is generating a call to a function and passing two arguments: a signed constant pointer and a value derived from a call. In ARMv8.3, this code might look like so: +For example, suppose that the compiler is generating a call to a function and +passing two arguments: a signed constant pointer and a value derived from +a call. In Armv8.3, this code might look like so: .. code-block:: asm @@ -857,7 +1159,10 @@ For example, suppose that the compiler is generating a call to a function and pa mov x0, x19 ; move signed &_callback to first arg register blr _function ; call _function -This code is correct, as would be a sequencing that does *both* the ``adr`` and the ``paciza`` after the call to ``_argGenerator``. But a sequence that computes the address of ``_callback`` but leaves it as a raw pointer in a register during the call to ``_argGenerator`` would be vulnerable: +This code is correct, as would be a sequencing that does *both* the ``adr`` and +the ``paciza`` after the call to ``_argGenerator``. But a sequence that +computes the address of ``_callback`` but leaves it as a raw pointer in +a register during the call to ``_argGenerator`` would be vulnerable: .. code-block:: asm @@ -868,89 +1173,165 @@ This code is correct, as would be a sequencing that does *both* the ``adr`` and mov x0, x19 ; move signed &_callback to first arg register blr _function ; call _function -If ``_argGenerator`` spills ``x19`` (a callee-save register), and if the attacker can perform a write during this call, then the attacker can overwrite the spill slot with an arbitrary pointer that will eventually be unconditionally signed after the function returns. This would be a signing oracle. +If ``_argGenerator`` spills ``x19`` (a callee-save register), and if the +attacker can perform a write during this call, then the attacker can overwrite +the spill slot with an arbitrary pointer that will eventually be +unconditionally signed after the function returns. This would be a signing +oracle. The implementation can avoid this by obeying two basic rules: -- The compiler's intermediate representations (IR) should not provide operations that expose intermediate raw pointers. This may require providing extra operations that perform useful combinations of operations. +- The compiler's intermediate representations (IR) should not provide + operations that expose intermediate raw pointers. This may require providing + extra operations that perform useful combinations of operations. - For example, there should be an "atomic" auth-and-resign operation that should be used instead of emitting an ``auth`` operation whose result is fed into a ``sign``. + For example, there should be an "atomic" auth-and-resign operation that + should be used instead of emitting an ``auth`` operation whose result is fed + into a ``sign``. - Similarly, if a pointer should be authenticated as part of doing a memory access or a call, then the access or call should be decorated with enough information to perform the authentication; there should not be a separate ``auth`` whose result is used as the pointer operand for the access or call. (In LLVM IR, we do this for calls, but not yet for loads or stores.) + Similarly, if a pointer should be authenticated as part of doing a memory + access or a call, then the access or call should be decorated with enough + information to perform the authentication; there should not be a separate + ``auth`` whose result is used as the pointer operand for the access or call. + (In LLVM IR, we do this for calls, but not yet for loads or stores.) - "Operations" includes things like materializing a signed pointer to a known function or global variable. The compiler must be able to recognize and emit this as a unified operation, rather than potentially splitting it up as in the example above. + "Operations" includes things like materializing a signed pointer to a known + function or global variable. The compiler must be able to recognize and emit + this as a unified operation, rather than potentially splitting it up as in + the example above. -- The compiler backend should not be too aggressive about scheduling instructions that are part of a pointer authentication operation. This may require custom code-generation of these operations in some cases. +- The compiler backend should not be too aggressive about scheduling + instructions that are part of a pointer authentication operation. This may + require custom code-generation of these operations in some cases. -Register clobbering +Register Clobbering +++++++++++++++++++ -As a refinement of the section on `Attackable code sequences`_, if the attacker has the ability to modify arbitrary *register* state at arbitrary points in the program, then special care must be taken. +As a refinement of the section on `Attackable code sequences`_, if the attacker +has the ability to modify arbitrary *register* state at arbitrary points in the +program, then special care must be taken. -For example, ARMv8.3 might materialize a signed function pointer like so: +For example, Armv8.3 might materialize a signed function pointer like so: .. code-block:: asm adr x0, _callback. ; compute &_callback paciza x0 ; sign it with a constant discriminator of 0 -If an attacker has the ability to overwrite ``x0`` between these two instructions, this code sequence is vulnerable to becoming a signing oracle. - -For the most part, this sort of attack is not possible: it is a basic element of the design of modern computation that register state is private and inviolable. However, in systems that support asynchronous interrupts, this property requires the cooperation of the interrupt-handling code. If that code saves register state to memory, and that memory can be overwritten by an attacker, then essentially the attack can overwrite arbitrary register state at an arbitrary point. This could be a concern if the threat model includes attacks on the kernel or if the program uses user-space preemptive multitasking. - -(Readers might object that an attacker cannot rely on asynchronous interrupts triggering at an exact instruction boundary. In fact, researchers have had some success in doing exactly that. Even ignoring that, though, we should aim to protect against lucky attackers just as much as good ones.) - -To protect against this, saved register state must be at least partially signed (using something like `ptrauth_sign_generic_data`_). This is required for correctness anyway because saved thread states include security-critical registers such as SP, FP, PC, and LR (where applicable). Ideally, this signature would cover all the registers, but since saving and restoring registers can be very performance-sensitive, that may not be acceptable. It is sufficient to set aside a small number of scratch registers that will be guaranteed to be preserved correctly; the compiler can then be careful to only store critical values like intermediate raw pointers in those registers. - -``setjmp`` and ``longjmp`` should sign and authenticate the core registers (SP, FP, PC, and LR), but they do not need to worry about intermediate values because ``setjmp`` can only be called synchronously, and the compiler should never schedule pointer-authentication operations interleaved with arbitrary calls. +If an attacker has the ability to overwrite ``x0`` between these two +instructions, this code sequence is vulnerable to becoming a signing oracle. + +For the most part, this sort of attack is not possible: it is a basic element +of the design of modern computation that register state is private and +inviolable. However, in systems that support asynchronous interrupts, this +property requires the cooperation of the interrupt-handling code. If that code +saves register state to memory, and that memory can be overwritten by an +attacker, then essentially the attack can overwrite arbitrary register state at +an arbitrary point. This could be a concern if the threat model includes +attacks on the kernel or if the program uses user-space preemptive +multitasking. + +(Readers might object that an attacker cannot rely on asynchronous interrupts +triggering at an exact instruction boundary. In fact, researchers have had +some success in doing exactly that. Even ignoring that, though, we should aim +to protect against lucky attackers just as much as good ones.) + +To protect against this, saved register state must be at least partially signed +(using something like `ptrauth_sign_generic_data`_). This is required for +correctness anyway because saved thread states include security-critical +registers such as SP, FP, PC, and LR (where applicable). Ideally, this +signature would cover all the registers, but since saving and restoring +registers can be very performance-sensitive, that may not be acceptable. It is +sufficient to set aside a small number of scratch registers that will be +guaranteed to be preserved correctly; the compiler can then be careful to only +store critical values like intermediate raw pointers in those registers. + +``setjmp`` and ``longjmp`` should sign and authenticate the core registers (SP, +FP, PC, and LR), but they do not need to worry about intermediate values +because ``setjmp`` can only be called synchronously, and the compiler should +never schedule pointer-authentication operations interleaved with arbitrary +calls. .. _Relative addresses: -Attacks on relative addressing +Attacks on Relative Addressing ++++++++++++++++++++++++++++++ -Relative addressing is a technique used to compress and reduce the load-time cost of infrequently-used global data. The pointer authentication system is unlikely to support signing or authenticating a relative address, and in most cases it would defeat the point to do so: it would take additional storage space, and applying the signature would take extra work at load time. +Relative addressing is a technique used to compress and reduce the load-time +cost of infrequently-used global data. The pointer authentication system is +unlikely to support signing or authenticating a relative address, and in most +cases it would defeat the point to do so: it would take additional storage +space, and applying the signature would take extra work at load time. -Relative addressing is not precluded by the use of pointer authentication, but it does take extra considerations to make it secure: +Relative addressing is not precluded by the use of pointer authentication, but +it does take extra considerations to make it secure: -- Relative addresses must only be stored in read-only memory. A writable relative address can be overwritten to point nearly anywhere, making it inherently insecure; this danger can only be compensated for with techniques for protecting arbitrary data like `ptrauth_sign_generic_data`_. +- Relative addresses must only be stored in read-only memory. A writable + relative address can be overwritten to point nearly anywhere, making it + inherently insecure; this danger can only be compensated for with techniques + for protecting arbitrary data like `ptrauth_sign_generic_data`_. -- Relative addresses must only be accessed through signed pointers with adequate diversity. If an attacker can perform an `access path attack` to replace the pointer through which the relative address is accessed, they can easily cause the relative address to point wherever they want. +- Relative addresses must only be accessed through signed pointers with + adequate diversity. If an attacker can perform an `access path attack` to + replace the pointer through which the relative address is accessed, they can + easily cause the relative address to point wherever they want. -Signature forging +Signature Forging +++++++++++++++++ -If an attacker can exactly reproduce the behavior of the signing algorithm, and they know all the correct inputs to it, then they can perfectly forge a signature on an arbitrary pointer. +If an attacker can exactly reproduce the behavior of the signing algorithm, and +they know all the correct inputs to it, then they can perfectly forge +a signature on an arbitrary pointer. There are three components to avoiding this mistake: -- The abstract signing algorithm should be good: it should not have glaring flaws which would allow attackers to predict its result with better than random accuracy without knowing all the inputs (like the key values). +- The abstract signing algorithm should be good: it should not have glaring + flaws which would allow attackers to predict its result with better than + random accuracy without knowing all the inputs (like the key values). -- The key values should be kept secret. If at all possible, they should never be stored in accessible memory, or perhaps only stored encrypted. +- The key values should be kept secret. If at all possible, they should never + be stored in accessible memory, or perhaps only stored encrypted. -- Contexts that are meant to be independently protected should use different key values. For example, the kernel should not use the same keys as user processes. Different user processes should also use different keys from each other as much as possible, although this may pose its own technical challenges. +- Contexts that are meant to be independently protected should use different + key values. For example, the kernel should not use the same keys as user + processes. Different user processes should also use different keys from each + other as much as possible, although this may pose its own technical + challenges. Remapping +++++++++ -If an attacker can change the memory protections on certain pages of the program's memory, that can substantially weaken the protections afforded by pointer authentication. +If an attacker can change the memory protections on certain pages of the +program's memory, that can substantially weaken the protections afforded by +pointer authentication. -- If an attacker can inject their own executable code, they can also certainly inject code that can be used as a :ref:`signing oracle`. The same is true if they can write to the instruction stream. +- If an attacker can inject their own executable code, they can also certainly + inject code that can be used as a :ref:`signing oracle`. + The same is true if they can write to the instruction stream. -- If an attacker can remap read-only program sections to be writable, then any use of :ref:`relative addresses` in global data becomes insecure. +- If an attacker can remap read-only program sections to be writable, then any + use of :ref:`relative addresses` in global data becomes insecure. -- If an attacker can remap read-only program sections to be writable, then it is unsafe to use unsigned pointers in `global offset tables`_. +- If an attacker can remap read-only program sections to be writable, then it + is unsafe to use unsigned pointers in global offset tables. -Remapping memory in this way often requires the attacker to have already substantively subverted the control flow of the process. Nonetheless, if the operating system has a mechanism for mapping pages in a way that cannot be remapped, this should be used wherever possible. +Remapping memory in this way often requires the attacker to have already +substantively subverted the control flow of the process. Nonetheless, if the +operating system has a mechanism for mapping pages in a way that cannot be +remapped, this should be used wherever possible. .. _Safe Derivation: -Safe derivation +Safe Derivation ~~~~~~~~~~~~~~~ -Whether a data pointer is stored, even briefly, as a raw pointer can affect the security-correctness of a program. (Function pointers are never implicitly stored as raw pointers; raw pointers to functions can only be produced with the ```` intrinsics.) Repeated re-signing can also impact performance. Clang makes a modest set of guarantees in this area: +Whether a data pointer is stored, even briefly, as a raw pointer can affect the +security-correctness of a program. (Function pointers are never implicitly +stored as raw pointers; raw pointers to functions can only be produced with the +```` intrinsics.) Repeated re-signing can also impact performance. +Clang makes a modest set of guarantees in this area: - An expression of pointer type is said to be **safely derived** if: @@ -958,21 +1339,30 @@ Whether a data pointer is stored, even briefly, as a raw pointer can affect the - it is a load from a gl-value of ``__ptrauth``-qualified type. -- If a value that is safely derived is assigned to a ``__ptrauth``-qualified object, including by initialization, then the value will be directly signed as appropriate for the target qualifier and will not be stored as a raw pointer. +- If a value that is safely derived is assigned to a ``__ptrauth``-qualified + object, including by initialization, then the value will be directly signed + as appropriate for the target qualifier and will not be stored as a raw + pointer. -- If the function expression of a call is a gl-value of ``__ptrauth``-qualified type, then the call will be authenticated directly according to the source qualifier and will not be resigned to the default rule for a function pointer of its type. +- If the function expression of a call is a gl-value of ``__ptrauth``-qualified + type, then the call will be authenticated directly according to the source + qualifier and will not be resigned to the default rule for a function pointer + of its type. -These guarantees are known to be inadequate for data pointer security. In particular, Clang should be enhanced to make the following guarantees: +These guarantees are known to be inadequate for data pointer security. In +particular, Clang should be enhanced to make the following guarantees: - A pointer should additionally be considered safely derived if it is: - the address of a gl-value that is safely derived, - - the result of pointer arithmetic on a pointer that is safely derived (with some restrictions on the integer operand), + - the result of pointer arithmetic on a pointer that is safely derived (with + some restrictions on the integer operand), - the result of a comma operator where the second operand is safely derived, - - the result of a conditional operator where the selected operand is safely derived, or + - the result of a conditional operator where the selected operand is safely + derived, or - the result of loading from a safely derived gl-value. @@ -984,178 +1374,17 @@ These guarantees are known to be inadequate for data pointer security. In partic - a reference to a variable. -- An access to a safely derived gl-value should be guaranteed to not allow replacement of any of the safely-derived component values at any point in the access. "Access" should include loading a function pointer. +- An access to a safely derived gl-value should be guaranteed to not allow + replacement of any of the safely-derived component values at any point in the + access. "Access" should include loading a function pointer. - Assignments should include pointer-arithmetic operators like ``+=``. -Making these guarantees will require further work, including significant new support in LLVM IR. - -Furthermore, Clang should implement a warning when assigning a data pointer that is not safely derived to a ``__ptrauth``-qualified gl-value. - - - -Language ABI ------------- - -This section describes the pointer-authentication ABI currently implemented in Clang for the Apple arm64e target. As other targets adopt pointer authentication, this section should be generalized to express their ABIs as well. - -Key assignments -~~~~~~~~~~~~~~~ - -ARMv8.3 provides four abstract signing keys: ``IA``, ``IB``, ``DA``, and ``DB``. The architecture designates ``IA`` and ``IB`` for signing code pointers and ``DA`` and ``DB`` for signing data pointers; this is reinforced by two properties: - -- The ISA provides instructions that perform combined auth+call and auth+load operations; these instructions can only use the ``I`` keys and ``D`` keys, respectively. - -- AArch64's TBI feature can be separately enabled for code pointers (controlling whether indirect-branch instructions ignore those bits) and data pointers (controlling whether memory-access instructions) ignore those bits. If TBI is enabled for a kind of pointer, the sign and auth operations preserve the TBI bits when signing with an associated keys (at the cost of shrinking the number of available signing bits by 8). - -arm64e then further subdivides the keys as follows: - -- The ``A`` keys are used for primarily "global" purposes like signing v-tables and function pointers. These keys are sometimes called *process-independent* or *cross-process* because on existing OSes they are not changed when changing processes, although this is not a platform guarantee. - -- The ``B`` keys are used for primarily "local" purposes like signing return addresses and frame pointers. These keys are sometimes called *process-specific* because they are typically different between processes. However, they are in fact shared across processes in one situation: systems which provide ``fork`` cannot change these keys in the child process; they can only be changed during ``exec``. - -Implementation-defined algorithms and quantities -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The cryptographic hash algorithm used to compute signatures in ARMv8.3 is a private detail of the hardware implementation. - -arm64e restricts constant discriminators (used in ``__ptrauth`` and ``ptrauth_blend_discriminator``) to the range from 0 to 65535, inclusive. A 0 discriminator generally signifies that no blending is required; see the documentation for ``ptrauth_blend_discriminator``. This range is somewhat narrow but has two advantages: - -- The AArch64 ISA allows an arbitrary 16-bit immediate to be written over the top 16 bits of a register in a single instruction: - - .. code-block:: asm - - movk xN, #0x4849, LSL 48 - - This is ideal for the discriminator blending operation because it adds minimal code-size overhead and avoids overwriting any interesting bits from the pointer. Blending in a wider constant discriminator would either clobber interesting bits (e.g. if it was loaded with ``movk xN, #0x4c4f, LSL 32``) or require significantly more code (e.g. if the discriminator was loaded with a ``mov+bfi`` sequence). - -- It is possible to pack a 16-bit discriminator into loader metadata with minimal compromises, whereas a wider discriminator would require extra metadata storage and therefore significantly impact load times. - -The string hash used by ``ptrauth_string_discriminator`` is a 64-bit SipHash-2-4 using the constant seed ``b5d4c9eb79104a796fec8b1b428781d4`` (big-endian), with the result reduced by modulo to the range of non-zero discriminators (i.e. ``(rawHash % 65535) + 1``). - -Return addresses -~~~~~~~~~~~~~~~~ - -The kernel must ensure that attackers cannot replace LR due to an asynchronous exception; see `Register clobbering`_. If this is done by generally protecting LR, then functions which don't spill LR to the stack can avoid signing it entirely. Otherwise, the return address must be signed; on arm64e it is signed with the ``IB`` key using the stack pointer on entry as the discriminator. - -Protecting return addresses is of such particular importance that the ``IB`` key is almost entirely reserved for this purpose. - -Global offset tables -~~~~~~~~~~~~~~~~~~~~ - -The global offset table (GOT) is not ABI, but it is a common implementation technique for dynamic linking which deserves special discussion here. - -Whenever possible, signed pointers should be materialized directly in code rather than via the GOT, e.g. using an ``adrp+add+pac`` sequence on ARMv8.3. This decreases the amount of work necessary at load time to initialize the GOT, but more importantly, it defines away the potential for several attacks: - -- Attackers cannot change instructions, so there is no way to cause this code sequence to materialize a different pointer, whereas an access via the GOT always has *at minimum* a probabilistic chance to be the target of successful `substitution attacks`_. - -- The GOT is a dense pool of fixed pointers at a fixed offset relative to code; attackers can search this pool for useful pointers that can be used in `substitution attacks`_, whereas pointers that are only materialized directly are not so easily available. - -- Similarly, attackers can use `access path attacks`_ to replace a pointer to a signed pointer with a pointer to the GOT if the signing schema used within the GOT happens to be the same as the original pointer. This kind of collision becomes much less likely to be useful the fewer pointers are in the GOT in the first place. - -If this can be done for a symbol, then the compiler need only ensure that it materializes the signed pointer using registers that are safe against `register clobbering`_. - -However, many symbols can only be accessed via the GOT, e.g. because they resolve to definitions outside of the current image. In this case, care must be taken to ensure that using the GOT does not introduce weaknesses. - -- If the entire GOT can be mapped read-only after loading, then no signing is required within the GOT. In fact, not signing pointers in the GOT is preferable in this case because it makes the GOT useless for the harvesting and access-path attacks above. Storing raw pointers in this way is usually extremely unsafe, but for the special case of an immutable GOT entry it's fine because the GOT is always accessed via an address that is directly materialized in code and thus provably unattackable. (But see `Remapping`_.) - -- Otherwise, GOT entries which are used for producing a signed pointer constant must be signed. The signing schema used in the GOT need not match the target signing schema for the signed constant. To counteract the threats of substitution attacks, it's best if GOT entries can be signed with address diversity. Using a good constant discriminator as well (perhaps derived from the symbol name) can make it less useful to use a pointer to the GOT as the replacement in an :ref:`access path attack`. - -In either case, the compiler must ensure that materializing the address of a GOT entry as part of producing a signed pointer constant is not vulnerable to `register clobbering`_. If the linker also generates code for this, e.g. for call stubs, this generated code must take the same precautions. - -C function pointers -~~~~~~~~~~~~~~~~~~~ - -On arm64e, C function pointers are currently signed with the ``IA`` key without address diversity and with a constant discriminator of 0. - -The C and C++ standards do not permit C function pointers to be signed with address diversity by default: in C++ terms, function pointer types are required to be trivially copyable, which means they must be copyable with ``memcpy``. - -The use of a uniform constant discriminator is seen as a serious defect which should be remedied, and improving this is under investigation. - -C++ virtual tables -~~~~~~~~~~~~~~~~~~ - -The pointer to a C++ virtual table is currently signed with the ``DA`` key, no address diversity, and a constant discriminator of 0. The use of no address diversity, as well as the uniform constant discriminator, are seen as weaknesses. Not using address diversity allows attackers to simply copy valid v-table pointers from one object to another. However, using a uniform discriminator of 0 does have positive performance and code-size implications on ARMv8.3, and diversity for the most important v-table access pattern (virtual dispatch) is already better assured by the signing schemas used on the virtual functions. It is also known that some code in practice copies objects containing v-tables with ``memcpy``, and while this is not permitted formally, it is something that may be invasive to eliminate. - -Virtual functions in a C++ virtual table are signed with the ``IA`` key, address diversity, and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangled name of the function which originally gave rise to the v-table slot. - -C++ member function pointers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A member function pointer is signed with the ``IA`` key, no address diversity, and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the member pointer type. Address diversity is not permitted by C++ for member function pointers because they must be trivially-copyable types. - -The Itanium C++ ABI specifies that member function pointers to virtual functions simply store an offset to the correct v-table slot. This ABI cannot be used securely with pointer authentication because there is no safe place to store the constant discriminator for the target v-table slot: if it's stored with the offset, an attacker can simply overwrite it with the right discriminator for the offset. Even if the programmer never uses pointers to virtual functions, the existence of this code path makes all member function pointer dereferences insecure. - -arm64e changes this ABI so that virtual function pointers are stored using dispatch thunks with vague linkage. Because arm64e supports interoperation with ``arm64`` code when pointer authentication is disabled, an arm64e member function pointer dereference still recognizes the virtual-function representation but uses an bogus discriminator on that path that should always trap if pointer authentication is enabled dynamically. - -The use of dispatch thunks means that ``==`` on member function pointers is no longer reliable for virtual functions, but this is acceptable because the standard makes no guarantees about it in the first place. - -The use of dispatch thunks also potentially enables v-tables to be signed using a declaration-specific constant discriminator in the future; otherwise this discriminator would also need to be stored in the member pointer. - -Blocks -~~~~~~ - -Block pointers are data pointers which must interoperate with the ObjC `id` type and therefore cannot be signed themselves. - -The invocation pointer in a block is signed with the ``IA`` key using address diversity and a constant dicriminator of 0. Using a uniform discriminator is seen as a weakness to be potentially improved, but this is tricky due to the subtype polymorphism directly permitted for blocks. - -Block descriptors and ``__block`` variables can contain pointers to functions that can be used to copy or destroy the object. These functions are signed with the ``IA`` key, address diversity, and a constant discriminator of 0. The structure of block descriptors is under consideration for improvement. - -Objective-C methods -~~~~~~~~~~~~~~~~~~~ - -Objective-C method lists sign methods with the ``IA`` key using address diversity and a constant discriminator of 0. Using a uniform constant discriminator is believed to be acceptable because these tables are only accessed internally to the Objective-C runtime. - -The Objective-C runtime provides additional protection to methods that have been loaded into the Objective-C method cache; this protection is private to the runtime. - -Pointer authentication cannot protect against access-path atacks against the Objective-C ``isa`` pointer, through which all dispatch occurs, because of compatibility requirements and existing and important usage of high bits in the pointer. - -Swift class methods -~~~~~~~~~~~~~~~~~~~ - -Class methods in Swift are signed in the class object with the ``IA`` key using address diversity and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangling of the original overridable method. - -Resilient class-method lookup relies on passing a method descriptor; this method descriptor should be signed but currently isn't. The lookup function returns a function pointer that is signed using ``IA`` without address diversity and with the correct constant discriminator for the looked-up method. - -Swift's equivalent of a C++ v-table pointer is the ``isa`` pointer of an object. On arm64e, this is constrained by Objective-C compatibility and cannot be a signed pointer. - -Swift heap destructors -~~~~~~~~~~~~~~~~~~~~~~ - -Objects that are retained and released with Swift's native reference-counting system, including both native classes and temporary "box" allocations, must provide a destructor function in their metadata. This destructor function is signed with the ``IA`` key using address diversity and a constant discriminator of ``0xbbbf``. - -Swift protocol requirements -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Protocol function requirements are signed in the protocol witness table with the ``IA`` key using address diversity and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangling of the protocol requirement. - -Swift function types -~~~~~~~~~~~~~~~~~~~~ - -The invocation pointers of Swift function values are signed using the ``IA`` key without address diversity and with a constant discriminator derived loosely from the function type. - -Address diversity cannot be used by default for function values because function types are intended to be a "loadable" type which can be held and passed in registers. - -The constant discriminator currently accounts for potential abstraction in the function signature in ways that decrease the diversity of signatures; improving this is under investigation. - -Swift metadata -~~~~~~~~~~~~~~ - -Type metadata pointers in Swift are not signed. - -Type context descriptors must be signed because they frequently contain `relative addresses`_. Type context descriptors are signed with the ``DA`` key without address diversity (except when stored in type metadata) and with a constant discriminator of ``0xae86``. - -Swift value witnesses -~~~~~~~~~~~~~~~~~~~~~ - -Value witness functions in Swift are signed in the value witness table using the ``IA`` key with address diversity and an operation-specific constant discriminator which can be found in the Swift project headers. - -Swift coroutines -~~~~~~~~~~~~~~~~ - -Resumption functions for Swift coroutines are signed using the ``IA`` key without address diversity and with a constant discriminator derived from the yield type of the coroutine. Resumption functions cannot be signed with address diversity as they are returned directly in registers from the coroutine. - +Making these guarantees will require further work, including significant new +support in LLVM IR. +Furthermore, Clang should implement a warning when assigning a data pointer +that is not safely derived to a ``__ptrauth``-qualified gl-value. From 27b1458a28a8686f66198d4b990a3b6bfe95f42b Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 3 Jun 2024 11:05:33 -0700 Subject: [PATCH 33/58] [clang][docs] Document language ABI. --- clang/docs/PointerAuthentication.rst | 313 +++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst index b3151ce69a163..83fba80cec0e7 100644 --- a/clang/docs/PointerAuthentication.rst +++ b/clang/docs/PointerAuthentication.rst @@ -1388,6 +1388,319 @@ that is not safely derived to a ``__ptrauth``-qualified gl-value. + +Language ABI +------------ + +This section describes the pointer-authentication ABI currently implemented in +Clang for the Apple arm64e target. As other targets adopt pointer +authentication, this section should be generalized to express their ABIs as +well. + +Key Assignments +~~~~~~~~~~~~~~~ + +Armv8.3 provides four abstract signing keys: ``IA``, ``IB``, ``DA``, and +``DB``. The architecture designates ``IA`` and ``IB`` for signing code pointers +and ``DA`` and ``DB`` for signing data pointers; this is reinforced by two +properties: + +- The ISA provides instructions that perform combined auth+call and auth+load + operations; these instructions can only use the ``I`` keys and ``D`` keys, + respectively. + +- AArch64's TBI feature can be separately enabled for code pointers + (controlling whether indirect-branch instructions ignore those bits) and data + pointers (controlling whether memory-access instructions) ignore those bits. + If TBI is enabled for a kind of pointer, the sign and auth operations + preserve the TBI bits when signing with an associated keys (at the cost of + shrinking the number of available signing bits by 8). + +arm64e then further subdivides the keys as follows: + +- The ``A`` keys are used for primarily "global" purposes like signing v-tables + and function pointers. These keys are sometimes called *process-independent* + or *cross-process* because on existing OSes they are not changed when + changing processes, although this is not a platform guarantee. + +- The ``B`` keys are used for primarily "local" purposes like signing return + addresses and frame pointers. These keys are sometimes called + *process-specific* because they are typically different between processes. + However, they are in fact shared across processes in one situation: systems + which provide ``fork`` cannot change these keys in the child process; they + can only be changed during ``exec``. + +Implementation-defined Algorithms and Quantities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The cryptographic hash algorithm used to compute signatures in Armv8.3 is +a private detail of the hardware implementation. + +arm64e restricts constant discriminators (used in ``__ptrauth`` and +``ptrauth_blend_discriminator``) to the range from 0 to 65535, inclusive. +A 0 discriminator generally signifies that no blending is required; see the +documentation for ``ptrauth_blend_discriminator``. This range is somewhat +narrow but has two advantages: + +- The AArch64 ISA allows an arbitrary 16-bit immediate to be written over the + top 16 bits of a register in a single instruction: + + .. code-block:: asm + + movk xN, #0x4849, LSL 48 + + This is ideal for the discriminator blending operation because it adds + minimal code-size overhead and avoids overwriting any interesting bits from + the pointer. Blending in a wider constant discriminator would either clobber + interesting bits (e.g. if it was loaded with ``movk xN, #0x4c4f, LSL 32``) or + require significantly more code (e.g. if the discriminator was loaded with + a ``mov+bfi`` sequence). + +- It is possible to pack a 16-bit discriminator into loader metadata with + minimal compromises, whereas a wider discriminator would require extra + metadata storage and therefore significantly impact load times. + +The string hash used by ``ptrauth_string_discriminator`` is a 64-bit +SipHash-2-4 using the constant seed ``b5d4c9eb79104a796fec8b1b428781d4`` +(big-endian), with the result reduced by modulo to the range of non-zero +discriminators (i.e. ``(rawHash % 65535) + 1``). + +Return Addresses +~~~~~~~~~~~~~~~~ + +The kernel must ensure that attackers cannot replace LR due to an asynchronous +exception; see `Register clobbering`_. If this is done by generally protecting +LR, then functions which don't spill LR to the stack can avoid signing it +entirely. Otherwise, the return address must be signed; on arm64e it is signed +with the ``IB`` key using the stack pointer on entry as the discriminator. + +Protecting return addresses is of such particular importance that the ``IB`` +key is almost entirely reserved for this purpose. + +Global Offset Tables +~~~~~~~~~~~~~~~~~~~~ + +The global offset table (GOT) is not ABI, but it is a common implementation +technique for dynamic linking which deserves special discussion here. + +Whenever possible, signed pointers should be materialized directly in code +rather than via the GOT, e.g. using an ``adrp+add+pac`` sequence on Armv8.3. +This decreases the amount of work necessary at load time to initialize the GOT, +but more importantly, it defines away the potential for several attacks: + +- Attackers cannot change instructions, so there is no way to cause this code + sequence to materialize a different pointer, whereas an access via the GOT + always has *at minimum* a probabilistic chance to be the target of successful + `substitution attacks`_. + +- The GOT is a dense pool of fixed pointers at a fixed offset relative to code; + attackers can search this pool for useful pointers that can be used in + `substitution attacks`_, whereas pointers that are only materialized directly + are not so easily available. + +- Similarly, attackers can use `access path attacks`_ to replace a pointer to + a signed pointer with a pointer to the GOT if the signing schema used within + the GOT happens to be the same as the original pointer. This kind of + collision becomes much less likely to be useful the fewer pointers are in the + GOT in the first place. + +If this can be done for a symbol, then the compiler need only ensure that it +materializes the signed pointer using registers that are safe against `register +clobbering`_. + +However, many symbols can only be accessed via the GOT, e.g. because they +resolve to definitions outside of the current image. In this case, care must +be taken to ensure that using the GOT does not introduce weaknesses. + +- If the entire GOT can be mapped read-only after loading, then no signing is + required within the GOT. In fact, not signing pointers in the GOT is + preferable in this case because it makes the GOT useless for the harvesting + and access-path attacks above. Storing raw pointers in this way is usually + extremely unsafe, but for the special case of an immutable GOT entry it's + fine because the GOT is always accessed via an address that is directly + materialized in code and thus provably unattackable. (But see `Remapping`_.) + +- Otherwise, GOT entries which are used for producing a signed pointer constant + must be signed. The signing schema used in the GOT need not match the target + signing schema for the signed constant. To counteract the threats of + substitution attacks, it's best if GOT entries can be signed with address + diversity. Using a good constant discriminator as well (perhaps derived from + the symbol name) can make it less useful to use a pointer to the GOT as the + replacement in an :ref:`access path attack`. + +In either case, the compiler must ensure that materializing the address of +a GOT entry as part of producing a signed pointer constant is not vulnerable to +`register clobbering`_. If the linker also generates code for this, e.g. for +call stubs, this generated code must take the same precautions. + +C Function Pointers +~~~~~~~~~~~~~~~~~~~ + +On arm64e, C function pointers are currently signed with the ``IA`` key without +address diversity and with a constant discriminator of 0. + +The C and C++ standards do not permit C function pointers to be signed with +address diversity by default: in C++ terms, function pointer types are required +to be trivially copyable, which means they must be copyable with ``memcpy``. + +The use of a uniform constant discriminator is seen as a serious defect which +should be remedied, and improving this is under investigation. + +C++ Virtual Tables +~~~~~~~~~~~~~~~~~~ + +The pointer to a C++ virtual table is currently signed with the ``DA`` key, no +address diversity, and a constant discriminator of 0. The use of no address +diversity, as well as the uniform constant discriminator, are seen as +weaknesses. Not using address diversity allows attackers to simply copy valid +v-table pointers from one object to another. However, using a uniform +discriminator of 0 does have positive performance and code-size implications on +Armv8.3, and diversity for the most important v-table access pattern (virtual +dispatch) is already better assured by the signing schemas used on the virtual +functions. It is also known that some code in practice copies objects +containing v-tables with ``memcpy``, and while this is not permitted formally, +it is something that may be invasive to eliminate. + +Virtual functions in a C++ virtual table are signed with the ``IA`` key, +address diversity, and a constant discriminator equal to the string hash (see +`ptrauth_string_discriminator`_) of the mangled name of the function which +originally gave rise to the v-table slot. + +C++ Member Function Pointers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A member function pointer is signed with the ``IA`` key, no address diversity, +and a constant discriminator equal to the string hash (see +`ptrauth_string_discriminator`_) of the member pointer type. Address diversity +is not permitted by C++ for member function pointers because they must be +trivially-copyable types. + +The Itanium C++ ABI specifies that member function pointers to virtual +functions simply store an offset to the correct v-table slot. This ABI cannot +be used securely with pointer authentication because there is no safe place to +store the constant discriminator for the target v-table slot: if it's stored +with the offset, an attacker can simply overwrite it with the right +discriminator for the offset. Even if the programmer never uses pointers to +virtual functions, the existence of this code path makes all member function +pointer dereferences insecure. + +arm64e changes this ABI so that virtual function pointers are stored using +dispatch thunks with vague linkage. Because arm64e supports interoperation +with ``arm64`` code when pointer authentication is disabled, an arm64e member +function pointer dereference still recognizes the virtual-function +representation but uses an bogus discriminator on that path that should always +trap if pointer authentication is enabled dynamically. + +The use of dispatch thunks means that ``==`` on member function pointers is no +longer reliable for virtual functions, but this is acceptable because the +standard makes no guarantees about it in the first place. + +The use of dispatch thunks also potentially enables v-tables to be signed using +a declaration-specific constant discriminator in the future; otherwise this +discriminator would also need to be stored in the member pointer. + +Blocks +~~~~~~ + +Block pointers are data pointers which must interoperate with the ObjC `id` +type and therefore cannot be signed themselves. + +The invocation pointer in a block is signed with the ``IA`` key using address +diversity and a constant dicriminator of 0. Using a uniform discriminator is +seen as a weakness to be potentially improved, but this is tricky due to the +subtype polymorphism directly permitted for blocks. + +Block descriptors and ``__block`` variables can contain pointers to functions +that can be used to copy or destroy the object. These functions are signed +with the ``IA`` key, address diversity, and a constant discriminator of 0. The +structure of block descriptors is under consideration for improvement. + +Objective-C Methods +~~~~~~~~~~~~~~~~~~~ + +Objective-C method lists sign methods with the ``IA`` key using address +diversity and a constant discriminator of 0. Using a uniform constant +discriminator is believed to be acceptable because these tables are only +accessed internally to the Objective-C runtime. + +The Objective-C runtime provides additional protection to methods that have +been loaded into the Objective-C method cache; this protection is private to +the runtime. + +Swift Class Methods +~~~~~~~~~~~~~~~~~~~ + +Class methods in Swift are signed in the class object with the ``IA`` key using +address diversity and a constant discriminator equal to the string hash (see +`ptrauth_string_discriminator`_) of the mangling of the original overridable +method. + +Resilient class-method lookup relies on passing a method descriptor; this +method descriptor should be signed but currently isn't. The lookup function +returns a function pointer that is signed using ``IA`` without address +diversity and with the correct constant discriminator for the looked-up method. + +Swift Heap Destructors +~~~~~~~~~~~~~~~~~~~~~~ + +Objects that are retained and released with Swift's native reference-counting +system, including both native classes and temporary "box" allocations, must +provide a destructor function in their metadata. This destructor function is +signed with the ``IA`` key using address diversity and a constant discriminator +of ``0xbbbf``. + +Swift Protocol Requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Protocol function requirements are signed in the protocol witness table with +the ``IA`` key using address diversity and a constant discriminator equal to +the string hash (see `ptrauth_string_discriminator`_) of the mangling of the +protocol requirement. + +Swift Function Types +~~~~~~~~~~~~~~~~~~~~ + +The invocation pointers of Swift function values are signed using the ``IA`` +key without address diversity and with a constant discriminator derived loosely +from the function type. + +Address diversity cannot be used by default for function values because +function types are intended to be a "loadable" type which can be held and +passed in registers. + +The constant discriminator currently accounts for potential abstraction in the +function signature in ways that decrease the diversity of signatures; improving +this is under investigation. + +Swift Metadata +~~~~~~~~~~~~~~ + +Type metadata pointers in Swift are not signed. + +Type context descriptors must be signed because they frequently contain +`relative addresses`_. Type context descriptors are signed with the ``DA`` key +without address diversity (except when stored in type metadata) and with +a constant discriminator of ``0xae86``. + +Swift Value Witnesses +~~~~~~~~~~~~~~~~~~~~~ + +Value witness functions in Swift are signed in the value witness table using +the ``IA`` key with address diversity and an operation-specific constant +discriminator which can be found in the Swift project headers. + +Swift Coroutines +~~~~~~~~~~~~~~~~ + +Resumption functions for Swift coroutines are signed using the ``IA`` key +without address diversity and with a constant discriminator derived from the +yield type of the coroutine. Resumption functions cannot be signed with +address diversity as they are returned directly in registers from the +coroutine. + + + + Alternative Implementations --------------------------- From 7de1c0829fba02a99af9c30c887dabae08989e0f Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 3 Jun 2024 11:06:42 -0700 Subject: [PATCH 34/58] [clang][docs] Document language features. --- clang/docs/PointerAuthentication.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst index 83fba80cec0e7..4a475e0949174 100644 --- a/clang/docs/PointerAuthentication.rst +++ b/clang/docs/PointerAuthentication.rst @@ -281,7 +281,7 @@ There are three levels of the pointer authentication language feature: manually signing and authenticating pointers in code. These can be used in circumstances where very specific behavior is required. -Language implementation +Language Implementation ~~~~~~~~~~~~~~~~~~~~~~~ For the most part, pointer authentication is an unobserved detail of the @@ -305,7 +305,7 @@ For more information about this, see the `Language ABI`_ section. However, some aspects of the implementation are observable by the programmer or otherwise require special notice. -C data pointers +C Data Pointers ^^^^^^^^^^^^^^^ The current implementation in Clang does not sign pointers to ordinary data by @@ -316,7 +316,7 @@ A specific data pointer which is more security-sensitive than most can be signed using the `__ptrauth qualifier`_ or using the ```` intrinsics. -C function pointers +C Function Pointers ^^^^^^^^^^^^^^^^^^^ The C standard imposes restrictions on the representation and semantics of @@ -335,7 +335,7 @@ relational comparisons and hashes will vary according to the exact signature value, which is likely to change between executions of a program. In some implementations, it may also vary based on the exact function pointer type. -Null pointers +Null Pointers ^^^^^^^^^^^^^ In principle, an implementation could derive the signed null pointer value @@ -353,7 +353,7 @@ representation. On AArch64, this requires additional code when working with possibly-null pointers, such as when copying a pointer field that has been signed with address diversity. -Return addresses and frame pointers +Return Addresses and Frame Pointers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The current implementation in Clang implicitly signs both return addresses and @@ -365,7 +365,7 @@ correctly account for pointer authentication, either by stripping signatures trace during a crash) or properly authenticating them. More information about how these values are signed is available in the `Language ABI`_ section. -C++ virtual functions +C++ Virtual Functions ^^^^^^^^^^^^^^^^^^^^^ The current implementation in Clang signs virtual function pointers with @@ -401,6 +401,7 @@ a number of different tests. Clang provides several other tests only for historical purposes; for current purposes they are all equivalent to ``ptrauth_calls``. + __ptrauth qualifier ^^^^^^^^^^^^^^^^^^^ @@ -1313,7 +1314,7 @@ pointer authentication. use of :ref:`relative addresses` in global data becomes insecure. - If an attacker can remap read-only program sections to be writable, then it - is unsafe to use unsigned pointers in global offset tables. + is unsafe to use unsigned pointers in `global offset tables`_. Remapping memory in this way often requires the attacker to have already substantively subverted the control flow of the process. Nonetheless, if the From dc06f67239c4f00cff314c8f73cbb306b35597f4 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 3 Jun 2024 11:00:28 -0700 Subject: [PATCH 35/58] [clang][docs] Document __ptrauth qualifier in more depth. --- clang/docs/PointerAuthentication.rst | 57 +++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst index 4a475e0949174..ccebfa12b5ae2 100644 --- a/clang/docs/PointerAuthentication.rst +++ b/clang/docs/PointerAuthentication.rst @@ -233,6 +233,31 @@ not significantly weaken the mitigation, since collisions remain uncommon. The algorithm for blending a constant discriminator with a storage address is implementation-defined. +.. _Authentication Options: + +Authentication Options +~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to tweak the behaviour of pointer authentication using the +`options` argument to the ``__ptrauth`` attribute. These options are specified +through a string literal containing a comma-separated list of options. Current +options are + +- authentication mode: ``strip``, ``sign-and-strip``, ``sign-and-auth``. These + control whether authentication codes are ignored completely (``strip``), + whether values are signed but not authenticated (``sign-and-strip``), or the + default of full authentication (``sign-and-auth``). + +- ``authenticates-null-values``: Enables full signing and authentication of + null values. The default behaviour of pointer authentication is to not sign + or authenticate null values. This option ensures that all values, including + null values, will always be signed and authenticated. + +- ``isa-pointer``: This is used to indicate that the target value is an + Objective-C isa pointer, and needs to mask out objective-c tag bits prior to + signing or authenticating the value. + + .. _Signing schemas: Signing Schemas @@ -401,13 +426,12 @@ a number of different tests. Clang provides several other tests only for historical purposes; for current purposes they are all equivalent to ``ptrauth_calls``. - -__ptrauth qualifier +__ptrauth Qualifier ^^^^^^^^^^^^^^^^^^^ -``__ptrauth(key, address, discriminator)`` is an extended type qualifier which -causes so-qualified objects to hold pointers signed using the specified schema -rather than the default schema for such types. +``__ptrauth(key, address, discriminator [, options] )`` is an extended type +qualifier which causes so-qualified objects to hold pointers signed using the +specified schema rather than the default schema for such types. In the current implementation in Clang, the qualified type must be a C pointer type, either to a function or to an object. It currently cannot be an @@ -429,8 +453,14 @@ The qualifier's operands are as follows: - ``discriminator`` - a constant discriminator; must be a constant expression +- ``options`` - an optional list of authentication behaviour options; must be + a string literal + See `Discriminators`_ for more information about discriminators. +See :ref:`authentication options` for more information +about options. + Currently the operands must be constant-evaluable even within templates. In the future this restriction may be lifted to allow value-dependent expressions as long as they instantiate to a constant expression. @@ -453,7 +483,22 @@ a discriminator determined as follows: is ``ptrauth_blend_discriminator(&x, discriminator)``; see `ptrauth_blend_discriminator`_. -Non-triviality from address diversity +__ptrauth_restricted_intptr Qualifier +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This is a variant of the ``__ptrauth`` qualifier, that applies to pointer sized +integers. See the documentation for ``__ptrauth qualifier``. + +This feature exists to support older APIs that use [u]intptrs to hold opaque +pointer types. + +Care must be taken to avoid using the signature bit components of the signed +integers or subsequent authentication of the signed value may fail. + +Note: When applied to a global initialiser a signed uintptr can only be +initialised with the value 0 or a global address. + +Non-triviality From Address Diversity +++++++++++++++++++++++++++++++++++++ Address diversity must impose additional restrictions in order to allow the From 539b539174d3a575596ce0e6f852e5f37340b3ba Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 3 Jun 2024 11:07:17 -0700 Subject: [PATCH 36/58] [clang][docs] Document standard ABI __ptrauth qualifiers. --- clang/docs/PointerAuthentication.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst index ccebfa12b5ae2..89740bc082956 100644 --- a/clang/docs/PointerAuthentication.rst +++ b/clang/docs/PointerAuthentication.rst @@ -741,7 +741,7 @@ type. Implementations are not required to make all bits of the result equally significant; in particular, some implementations are known to not leave meaningful data in the low bits. -Standard ``__ptrauth`` qualifiers +Standard ``__ptrauth`` Qualifiers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ```` additionally provides several macros which expand to From a76a00b20ef3028c843707290dca32c16e373e39 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Tue, 12 Mar 2024 14:40:17 -0700 Subject: [PATCH 37/58] [AArch64][PAC] Sign block addresses used in indirectbr. Enabled in clang using: -fptrauth-indirect-gotos and at the IR level using function attribute: "ptrauth-indirect-gotos" Signing uses IA and a per-function integer discriminator. The discriminator isn't ABI-visible, and is currently: ptrauth_string_discriminator(" blockaddress") A sufficiently sophisticated frontend could benefit from per-indirectbr discrimination, which would need additional machinery, such as allowing "ptrauth" bundles on indirectbr. For our purposes, the simple scheme above is sufficient. --- clang/include/clang/Basic/Features.def | 1 + clang/include/clang/Basic/LangOptions.def | 2 +- clang/include/clang/Driver/Options.td | 5 +- clang/lib/Driver/ToolChains/Clang.cpp | 7 +- clang/lib/Frontend/CompilerInvocation.cpp | 3 + llvm/docs/PointerAuth.md | 15 +++ llvm/include/llvm/CodeGen/AsmPrinter.h | 7 +- llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 6 +- llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp | 35 +++++- llvm/lib/Target/AArch64/AArch64FastISel.cpp | 4 + .../Target/AArch64/AArch64ISelLowering.cpp | 53 ++++++++- llvm/lib/Target/AArch64/AArch64ISelLowering.h | 1 + llvm/lib/Target/AArch64/AArch64InstrInfo.td | 18 +++ llvm/lib/Target/AArch64/AArch64Subtarget.cpp | 11 ++ llvm/lib/Target/AArch64/AArch64Subtarget.h | 9 ++ .../GISel/AArch64InstructionSelector.cpp | 26 +++++ .../CodeGen/AArch64/ptrauth-indirectbr.ll | 106 ++++++++++++++++++ 17 files changed, 285 insertions(+), 24 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/ptrauth-indirectbr.ll diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index a9d9e881bbde6..e355fcd8a5ee5 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -119,6 +119,7 @@ FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls) FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns) FEATURE(ptrauth_vtable_pointer_address_discrimination, LangOpts.PointerAuthVTPtrAddressDiscrimination) FEATURE(ptrauth_vtable_pointer_type_discrimination, LangOpts.PointerAuthVTPtrTypeDiscrimination) +FEATURE(ptrauth_indirect_gotos, LangOpts.PointerAuthIndirectGotos) FEATURE(ptrauth_member_function_pointer_type_discrimination, LangOpts.PointerAuthCalls) FEATURE(ptrauth_init_fini, LangOpts.PointerAuthInitFini) FEATURE(ptrauth_function_pointer_type_discrimination, LangOpts.PointerAuthFunctionTypeDiscrimination) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 030ee75ddd76b..d136c461ae0fc 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -166,13 +166,13 @@ LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library fea LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics") LANGOPT(PointerAuthCalls , 1, 0, "function pointer authentication") LANGOPT(PointerAuthReturns, 1, 0, "return pointer authentication") +LANGOPT(PointerAuthIndirectGotos, 1, 0, "indirect gotos pointer authentication") LANGOPT(PointerAuthAuthTraps, 1, 0, "pointer authentication failure traps") LANGOPT(PointerAuthVTPtrAddressDiscrimination, 1, 0, "incorporate address discrimination in authenticated vtable pointers") LANGOPT(PointerAuthVTPtrTypeDiscrimination, 1, 0, "incorporate type discrimination in authenticated vtable pointers") LANGOPT(PointerAuthInitFini, 1, 0, "sign function pointers in init/fini arrays") BENIGN_LANGOPT(PointerAuthFunctionTypeDiscrimination, 1, 0, "Use type discrimination when signing function pointers") -LANGOPT(PointerAuthIndirectGotos, 1, 0, "indirect gotos pointer authentication") LANGOPT(SoftPointerAuth , 1, 0, "software emulation of pointer authentication") VALUE_LANGOPT(PointerAuthABIVersion, 32, 0, "pointer authentication ABI version") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 46f4988809865..8c7f7f57c8f1b 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4253,12 +4253,9 @@ defm strict_return : BoolFOption<"strict-return", let Group = f_Group in { let Visibility = [ClangOption, CC1Option] in { - def fptrauth_indirect_gotos : Flag<["-"], "fptrauth-indirect-gotos">, - HelpText<"Enable signing and authentication of indirect goto targets">; def fptrauth_soft : Flag<["-"], "fptrauth-soft">, HelpText<"Enable software lowering of pointer authentication">; } - def fno_ptrauth_indirect_gotos : Flag<["-"], "fno-ptrauth-indirect-gotos">; def fno_ptrauth_soft : Flag<["-"], "fno-ptrauth-soft">; } @@ -4272,6 +4269,8 @@ defm ptrauth_vtable_pointer_address_discrimination : defm ptrauth_vtable_pointer_type_discrimination : OptInCC1FFlag<"ptrauth-vtable-pointer-type-discrimination", "Enable type discrimination of vtable pointers">; defm ptrauth_init_fini : OptInCC1FFlag<"ptrauth-init-fini", "Enable signing of function pointers in init/fini arrays">; +defm ptrauth_indirect_gotos : OptInCC1FFlag<"ptrauth-indirect-gotos", + "Enable signing and authentication of indirect goto targets">; defm ptrauth_function_pointer_type_discrimination : OptInCC1FFlag<"ptrauth-function-pointer-type-discrimination", "Enable type discrimination on C function pointers">; } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 5c43c14b27a7b..fb49ea0ec0e61 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1799,6 +1799,9 @@ void Clang::AddAArch64TargetArgs(const ArgList &Args, Args.addOptInFlag( CmdArgs, options::OPT_fptrauth_function_pointer_type_discrimination, options::OPT_fno_ptrauth_function_pointer_type_discrimination); + + Args.addOptInFlag(CmdArgs, options::OPT_fptrauth_indirect_gotos, + options::OPT_fno_ptrauth_indirect_gotos); } void Clang::AddLoongArchTargetArgs(const ArgList &Args, @@ -7535,10 +7538,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &Job, // -fno-common is the default, set -fcommon only when that flag is set. Args.addOptInFlag(CmdArgs, options::OPT_fcommon, options::OPT_fno_common); - if (Args.hasFlag(options::OPT_fptrauth_indirect_gotos, - options::OPT_fno_ptrauth_indirect_gotos, false)) - CmdArgs.push_back("-fptrauth-indirect-gotos"); - if (Args.hasFlag(options::OPT_fptrauth_soft, options::OPT_fno_ptrauth_soft, false)) CmdArgs.push_back("-fptrauth-soft"); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 11f4fdd76c611..5fd38867e9dc3 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3714,6 +3714,8 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts, GenerateArg(Consumer, OPT_fptrauth_calls); if (Opts.PointerAuthReturns) GenerateArg(Consumer, OPT_fptrauth_returns); + if (Opts.PointerAuthIndirectGotos) + GenerateArg(Consumer, OPT_fptrauth_indirect_gotos); if (Opts.PointerAuthAuthTraps) GenerateArg(Consumer, OPT_fptrauth_auth_traps); if (Opts.PointerAuthVTPtrAddressDiscrimination) @@ -3743,6 +3745,7 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, Opts.PointerAuthIntrinsics = Args.hasArg(OPT_fptrauth_intrinsics); Opts.PointerAuthCalls = Args.hasArg(OPT_fptrauth_calls); Opts.PointerAuthReturns = Args.hasArg(OPT_fptrauth_returns); + Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos); Opts.PointerAuthAuthTraps = Args.hasArg(OPT_fptrauth_auth_traps); Opts.PointerAuthVTPtrAddressDiscrimination = Args.hasArg(OPT_fptrauth_vtable_pointer_address_discrimination); diff --git a/llvm/docs/PointerAuth.md b/llvm/docs/PointerAuth.md index fcf21ec179450..3e71da8f0702c 100644 --- a/llvm/docs/PointerAuth.md +++ b/llvm/docs/PointerAuth.md @@ -323,6 +323,21 @@ this attribute, as they should already be annotated with the The ``ptrauth-calls`` attribute only describes calls emitted by the backend, as part of target-specific lowering (e.g., runtime calls for TLS accesses). +#### ``ptrauth-indirect-gotos`` + +``ptrauth-indirect-gotos`` specifies that indirect gotos in this function +should authenticate their target. At the IR level, no other change is needed. +When lowering [``blockaddress`` constants](https://llvm.org/docs/LangRef.html#blockaddress), +and [``indirectbr`` instructions](https://llvm.org/docs/LangRef.html#i-indirectbr), +this tells the backend to respectively sign and authenticate the pointers. + +The specific scheme isn't ABI-visible. Currently, the AArch64 backend +signs blockaddresses using the `ASIA` key, with an integer discriminator +derived from the parent function's name, using the SipHash stable discriminator: +``` + ptrauth_string_discriminator(" blockaddress") +``` + ### Authenticated Global Relocation diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h index bac9bd839fadd..cfd04e186594e 100644 --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -579,6 +579,9 @@ class AsmPrinter : public MachineFunctionPass { report_fatal_error("ptrauth constant lowering not implemented"); } + /// Lower the specified BlockAddress to an MCExpr. + virtual const MCExpr *lowerBlockAddressConstant(const BlockAddress &BA); + /// Return true if the basic block has exactly one predecessor and the control /// transfer mechanism between the predecessor and this block is a /// fall-through. @@ -595,10 +598,6 @@ class AsmPrinter : public MachineFunctionPass { report_fatal_error("llvm.ptrauth global lowering not implemented"); } - /// Lower the specified "llvm.ptrauth" GlobalVariable to an MCExpr. - virtual const MCExpr * - lowerBlockAddressConstant(const BlockAddress *BA); - /// getSubtargetInfo() cannot be used where this is needed because we don't /// have a MachineFunction when we're lowering a GlobalIFunc, and /// getSubtargetInfo requires one. Override the implementation in targets diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 7bdcd39b7afa9..742ebeb43f60f 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -3177,7 +3177,7 @@ const MCExpr *AsmPrinter::lowerConstant(const Constant *CV) { } if (const BlockAddress *BA = dyn_cast(CV)) - return lowerBlockAddressConstant(BA); + return lowerBlockAddressConstant(*BA); if (const auto *Equiv = dyn_cast(CV)) return getObjFileLowering().lowerDSOLocalEquivalent(Equiv, TM); @@ -3859,8 +3859,8 @@ MCSymbol *AsmPrinter::GetBlockAddressSymbol(const BasicBlock *BB) const { return const_cast(this)->getAddrLabelSymbol(BB); } -const MCExpr *AsmPrinter::lowerBlockAddressConstant(const BlockAddress *BA) { - return MCSymbolRefExpr::create(GetBlockAddressSymbol(BA), OutContext); +const MCExpr *AsmPrinter::lowerBlockAddressConstant(const BlockAddress &BA) { + return MCSymbolRefExpr::create(GetBlockAddressSymbol(&BA), OutContext); } /// GetCPISymbol - Return the symbol for the specified constant pool entry. diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 8926c9ed2a577..32371751bff7a 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -112,6 +112,8 @@ class AArch64AsmPrinter : public AsmPrinter { const MCExpr *lowerConstantPtrAuth(const ConstantPtrAuth &CPA) override; + const MCExpr *lowerBlockAddressConstant(const BlockAddress &BA) override; + void authLRBeforeTailCall(const MachineFunction &MF, unsigned ScratchReg); void emitStartOfAsmFile(Module &M) override; @@ -151,7 +153,7 @@ class AArch64AsmPrinter : public AsmPrinter { void emitSled(const MachineInstr &MI, SledKind Kind); - // Emit the sequence for BLRA (authenticate + branch). + // Emit the sequence for BRA/BLRA (authenticate + branch/call). void emitPtrauthBranch(const MachineInstr *MI); // Emit the sequence for AUT or AUTPAC. @@ -1875,6 +1877,7 @@ unsigned AArch64AsmPrinter::emitPtrauthDiscriminator(uint16_t Disc, void AArch64AsmPrinter::emitPtrauthBranch(const MachineInstr *MI) { unsigned InstsEmitted = 0; + bool IsCall = MI->getOpcode() == AArch64::BLRA; unsigned BrTarget = MI->getOperand(0).getReg(); auto Key = (AArch64PACKey::ID)MI->getOperand(1).getImm(); @@ -1891,10 +1894,17 @@ void AArch64AsmPrinter::emitPtrauthBranch(const MachineInstr *MI) { bool IsZeroDisc = DiscReg == AArch64::XZR; unsigned Opc; - if (Key == AArch64PACKey::IA) - Opc = IsZeroDisc ? AArch64::BLRAAZ : AArch64::BLRAA; - else - Opc = IsZeroDisc ? AArch64::BLRABZ : AArch64::BLRAB; + if (IsCall) { + if (Key == AArch64PACKey::IA) + Opc = IsZeroDisc ? AArch64::BLRAAZ : AArch64::BLRAA; + else + Opc = IsZeroDisc ? AArch64::BLRABZ : AArch64::BLRAB; + } else { + if (Key == AArch64PACKey::IA) + Opc = IsZeroDisc ? AArch64::BRAAZ : AArch64::BRAA; + else + Opc = IsZeroDisc ? AArch64::BRABZ : AArch64::BRAB; + } MCInst BRInst; BRInst.setOpcode(Opc); @@ -2571,6 +2581,20 @@ void AArch64AsmPrinter::LowerMOVaddrPAC(const MachineInstr &MI) { assert(STI->getInstrInfo()->getInstSizeInBytes(MI) >= InstsEmitted * 4); } +const MCExpr * +AArch64AsmPrinter::lowerBlockAddressConstant(const BlockAddress &BA) { + const MCExpr *BAE = AsmPrinter::lowerBlockAddressConstant(BA); + const Function &Fn = *BA.getFunction(); + + if (std::optional BADisc = + STI->getPtrAuthBlockAddressDiscriminator(Fn)) + return AArch64AuthMCExpr::create(BAE, *BADisc, AArch64PACKey::IA, + /* HasAddressDiversity= */ false, + OutContext); + + return BAE; +} + // Simple pseudo-instructions have their lowering (with expansion to real // instructions) auto-generated. #include "AArch64GenMCPseudoLowering.inc" @@ -2725,6 +2749,7 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { LowerPtrauthAuthLoad(*MI); return; + case AArch64::BRA: case AArch64::BLRA: emitPtrauthBranch(MI); return; diff --git a/llvm/lib/Target/AArch64/AArch64FastISel.cpp b/llvm/lib/Target/AArch64/AArch64FastISel.cpp index 94759c922f459..aa35838ad8531 100644 --- a/llvm/lib/Target/AArch64/AArch64FastISel.cpp +++ b/llvm/lib/Target/AArch64/AArch64FastISel.cpp @@ -2522,6 +2522,10 @@ bool AArch64FastISel::selectIndirectBr(const Instruction *I) { if (AddrReg == 0) return false; + // Authenticated indirectbr is not implemented yet. + if (FuncInfo.MF->getFunction().hasFnAttribute("ptrauth-indirect-gotos")) + return false; + // Emit the indirect branch. const MCInstrDesc &II = TII.get(AArch64::BR); AddrReg = constrainOperandRegClass(II, AddrReg, II.getNumDefs()); diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index 79e85d7a9ba92..f3dd0f6aea3ec 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -85,6 +85,7 @@ #include "llvm/Support/InstructionCost.h" #include "llvm/Support/KnownBits.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/SipHash.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" @@ -509,6 +510,7 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM, setOperationAction(ISD::SELECT_CC, MVT::f64, Custom); setOperationAction(ISD::BR_JT, MVT::Other, Custom); setOperationAction(ISD::JumpTable, MVT::i64, Custom); + setOperationAction(ISD::BRIND, MVT::Other, Custom); setOperationAction(ISD::SETCCCARRY, MVT::i64, Custom); setOperationAction(ISD::PtrAuthGlobalAddress, MVT::i64, Custom); @@ -6694,6 +6696,8 @@ SDValue AArch64TargetLowering::LowerOperation(SDValue Op, return LowerJumpTable(Op, DAG); case ISD::BR_JT: return LowerBR_JT(Op, DAG); + case ISD::BRIND: + return LowerBRIND(Op, DAG); case ISD::ConstantPool: return LowerConstantPool(Op, DAG); case ISD::BlockAddress: @@ -10697,6 +10701,26 @@ SDValue AArch64TargetLowering::LowerBR_JT(SDValue Op, return DAG.getNode(ISD::BRIND, DL, MVT::Other, JTInfo, SDValue(Dest, 0)); } +SDValue AArch64TargetLowering::LowerBRIND(SDValue Op, SelectionDAG &DAG) const { + MachineFunction &MF = DAG.getMachineFunction(); + std::optional BADisc = + Subtarget->getPtrAuthBlockAddressDiscriminator(MF.getFunction()); + if (!BADisc) + return SDValue(); + + SDLoc DL(Op); + SDValue Chain = Op.getOperand(0); + SDValue Dest = Op.getOperand(1); + + SDValue Disc = DAG.getTargetConstant(*BADisc, DL, MVT::i64); + SDValue Key = DAG.getTargetConstant(AArch64PACKey::IA, DL, MVT::i32); + SDValue AddrDisc = DAG.getRegister(AArch64::XZR, MVT::i64); + + SDNode *BrA = DAG.getMachineNode(AArch64::BRA, DL, MVT::Other, + {Dest, Key, Disc, AddrDisc, Chain}); + return SDValue(BrA, 0); +} + SDValue AArch64TargetLowering::LowerConstantPool(SDValue Op, SelectionDAG &DAG) const { ConstantPoolSDNode *CP = cast(Op); @@ -10716,15 +10740,36 @@ SDValue AArch64TargetLowering::LowerConstantPool(SDValue Op, SDValue AArch64TargetLowering::LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const { - BlockAddressSDNode *BA = cast(Op); + BlockAddressSDNode *BAN = cast(Op); + const BlockAddress *BA = BAN->getBlockAddress(); + + if (std::optional BADisc = + Subtarget->getPtrAuthBlockAddressDiscriminator(*BA->getFunction())) { + SDLoc DL(Op); + + // This isn't cheap, but BRIND is rare. + SDValue TargetBA = DAG.getTargetBlockAddress(BA, BAN->getValueType(0)); + + SDValue Disc = DAG.getTargetConstant(*BADisc, DL, MVT::i64); + + SDValue Key = DAG.getTargetConstant(AArch64PACKey::IA, DL, MVT::i32); + SDValue AddrDisc = DAG.getRegister(AArch64::XZR, MVT::i64); + + SDNode *MOV = + DAG.getMachineNode(AArch64::MOVaddrPAC, DL, {MVT::Other, MVT::Glue}, + {TargetBA, Key, AddrDisc, Disc}); + return DAG.getCopyFromReg(SDValue(MOV, 0), DL, AArch64::X16, MVT::i64, + SDValue(MOV, 1)); + } + CodeModel::Model CM = getTargetMachine().getCodeModel(); if (CM == CodeModel::Large && !Subtarget->isTargetMachO()) { if (!getTargetMachine().isPositionIndependent()) - return getAddrLarge(BA, DAG); + return getAddrLarge(BAN, DAG); } else if (CM == CodeModel::Tiny) { - return getAddrTiny(BA, DAG); + return getAddrTiny(BAN, DAG); } - return getAddr(BA, DAG); + return getAddr(BAN, DAG); } SDValue AArch64TargetLowering::LowerDarwin_VASTART(SDValue Op, diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h index 047c852bb01d2..69386dac318ca 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h @@ -1143,6 +1143,7 @@ class AArch64TargetLowering : public TargetLowering { SelectionDAG &DAG) const; SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerBRIND(SDValue Op, SelectionDAG &DAG) const; SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBlockAddress(SDValue Op, SelectionDAG &DAG) const; SDValue LowerAAPCS_VASTART(SDValue Op, SelectionDAG &DAG) const; diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 3a339886ce7de..94b9a2ebf8c94 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -1786,6 +1786,24 @@ let Predicates = [HasPAuth] in { let Uses = [SP]; } + // BRA pseudo, generalized version of BRAA/BRAB/Z. + // This directly manipulates x16/x17, which are the only registers the OS + // guarantees are safe to use for sensitive operations. + def BRA : Pseudo<(outs), (ins GPR64noip:$Rn, i32imm:$Key, i64imm:$Disc, + GPR64noip:$AddrDisc), []>, Sched<[]> { + let isCodeGenOnly = 1; + let hasNoSchedulingInfo = 1; + let hasSideEffects = 1; + let mayStore = 0; + let mayLoad = 0; + let isBranch = 1; + let isTerminator = 1; + let isBarrier = 1; + let isIndirectBranch = 1; + let Size = 12; // 4 fixed + 8 variable, to compute discriminator. + let Defs = [X17]; + } + let isReturn = 1, isTerminator = 1, isBarrier = 1 in { def RETAA : AuthReturn<0b010, 0, "retaa">; def RETAB : AuthReturn<0b010, 1, "retab">; diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp index 1fad1d5ca6d7d..7275116e38f2e 100644 --- a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp @@ -24,6 +24,7 @@ #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineScheduler.h" #include "llvm/IR/GlobalValue.h" +#include "llvm/Support/SipHash.h" #include "llvm/TargetParser/AArch64TargetParser.h" using namespace llvm; @@ -574,6 +575,16 @@ AArch64Subtarget::getAuthenticatedLRCheckMethod() const { return AArch64PAuth::AuthCheckMethod::None; } +std::optional AArch64Subtarget::getPtrAuthBlockAddressDiscriminator( + const Function &ParentFn) const { + if (!ParentFn.hasFnAttribute("ptrauth-indirect-gotos")) + return std::nullopt; + // We currently have one simple mechanism for all targets. + // This isn't ABI, so we can always do better in the future. + return getPointerAuthStableSipHash( + (Twine(ParentFn.getName()) + " blockaddress").str()); +} + bool AArch64Subtarget::enableMachinePipeliner() const { return getSchedModel().hasInstrSchedModel(); } diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.h b/llvm/lib/Target/AArch64/AArch64Subtarget.h index 5faba09aa67bd..172beaaaabc01 100644 --- a/llvm/lib/Target/AArch64/AArch64Subtarget.h +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.h @@ -412,6 +412,15 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo { /// Choose a method of checking LR before performing a tail call. AArch64PAuth::AuthCheckMethod getAuthenticatedLRCheckMethod() const; + /// Compute the integer discriminator for a given BlockAddress constant, if + /// blockaddress signing is enabled (using function attribute + /// "ptrauth-indirect-gotos"). + /// Note that this assumes the discriminator is independent of the indirect + /// goto branch site itself, i.e., it's the same for all BlockAddresses in + /// a function. + std::optional + getPtrAuthBlockAddressDiscriminator(const Function &ParentFn) const; + const PseudoSourceValue *getAddressCheckPSV() const { return AddressCheckPSV.get(); } diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp index 1faf18c101b80..0decf123fcff6 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp @@ -2548,6 +2548,15 @@ bool AArch64InstructionSelector::select(MachineInstr &I) { return selectCompareBranch(I, MF, MRI); case TargetOpcode::G_BRINDIRECT: { + if (std::optional BADisc = + STI.getPtrAuthBlockAddressDiscriminator(MF.getFunction())) { + auto MI = MIB.buildInstr(AArch64::BRA, {}, {I.getOperand(0).getReg()}); + MI.addImm(AArch64PACKey::IA); + MI.addImm(*BADisc); + MI.addReg(/*AddrDisc=*/AArch64::XZR); + I.eraseFromParent(); + return constrainSelectedInstRegOperands(*MI, TII, TRI, RBI); + } I.setDesc(TII.get(AArch64::BR)); return constrainSelectedInstRegOperands(I, TII, TRI, RBI); } @@ -3466,6 +3475,23 @@ bool AArch64InstructionSelector::select(MachineInstr &I) { return true; } case TargetOpcode::G_BLOCK_ADDR: { + Function *BAFn = I.getOperand(1).getBlockAddress()->getFunction(); + if (std::optional BADisc = + STI.getPtrAuthBlockAddressDiscriminator(*BAFn)) { + MIB.buildInstr(TargetOpcode::IMPLICIT_DEF, {AArch64::X16}, {}); + MIB.buildInstr(TargetOpcode::IMPLICIT_DEF, {AArch64::X17}, {}); + MIB.buildInstr(AArch64::MOVaddrPAC) + .addBlockAddress(I.getOperand(1).getBlockAddress()) + .addImm(AArch64PACKey::IA) + .addReg(/*AddrDisc=*/AArch64::XZR) + .addImm(*BADisc) + .constrainAllUses(TII, TRI, RBI); + MIB.buildCopy(I.getOperand(0).getReg(), Register(AArch64::X16)); + RBI.constrainGenericRegister(I.getOperand(0).getReg(), + AArch64::GPR64RegClass, MRI); + I.eraseFromParent(); + return true; + } if (TM.getCodeModel() == CodeModel::Large && !TM.isPositionIndependent()) { materializeLargeCMVal(I, I.getOperand(1).getBlockAddress(), 0); I.eraseFromParent(); diff --git a/llvm/test/CodeGen/AArch64/ptrauth-indirectbr.ll b/llvm/test/CodeGen/AArch64/ptrauth-indirectbr.ll new file mode 100644 index 0000000000000..db49422cf1abb --- /dev/null +++ b/llvm/test/CodeGen/AArch64/ptrauth-indirectbr.ll @@ -0,0 +1,106 @@ +; RUN: llc -mtriple arm64e-apple-darwin \ +; RUN: -asm-verbose=false -aarch64-enable-collect-loh=false \ +; RUN: -o - %s | FileCheck %s + +; RUN: llc -mtriple arm64e-apple-darwin \ +; RUN: -global-isel -global-isel-abort=1 -verify-machineinstrs \ +; RUN: -asm-verbose=false -aarch64-enable-collect-loh=false \ +; RUN: -o - %s | FileCheck %s + +; The discriminator is the same for all blockaddresses in the function. +; ptrauth_string_discriminator("test_blockaddress blockaddress") == 52152 + +; CHECK-LABEL: _test_blockaddress: +; CHECK: adrp x16, [[F1BB1ADDR:Ltmp[0-9]+]]@PAGE +; CHECK-NEXT: add x16, x16, [[F1BB1ADDR]]@PAGEOFF +; CHECK-NEXT: mov x17, #[[F1DISCVAL:52152]] +; CHECK-NEXT: pacia x16, x17 +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: adrp x16, [[F1BB2ADDR:Ltmp[0-9]+]]@PAGE +; CHECK-NEXT: add x16, x16, [[F1BB2ADDR]]@PAGEOFF +; CHECK-NEXT: mov x17, #[[F1DISCVAL]] +; CHECK-NEXT: pacia x16, x17 +; CHECK-NEXT: mov x1, x16 +; CHECK-NEXT: bl _dummy_choose +; CHECK-NEXT: mov x17, #[[F1DISCVAL]] +; CHECK-NEXT: braa x0, x17 +; CHECK: [[F1BB1ADDR]]: +; CHECK-NEXT: [[F1BB1:LBB[0-9_]+]]: +; CHECK-NEXT: mov w0, #1 +; CHECK: [[F1BB2ADDR]]: +; CHECK-NEXT: [[F1BB2:LBB[0-9_]+]]: +; CHECK-NEXT: mov w0, #2 +define i32 @test_blockaddress() #0 { +entry: + %tmp0 = call ptr @dummy_choose(ptr blockaddress(@test_blockaddress, %bb1), ptr blockaddress(@test_blockaddress, %bb2)) + indirectbr ptr %tmp0, [label %bb1, label %bb2] + +bb1: + ret i32 1 + +bb2: + ret i32 2 +} + +; Test another function to compare the discriminator. +; ptrauth_string_discriminator("test_blockaddress_2 blockaddress") == 22012 + +; CHECK-LABEL: _test_blockaddress_2: +; CHECK: adrp x16, [[F2BB1ADDR:Ltmp[0-9]+]]@PAGE +; CHECK-NEXT: add x16, x16, [[F2BB1ADDR]]@PAGEOFF +; CHECK-NEXT: mov x17, #[[F2DISCVAL:22012]] +; CHECK-NEXT: pacia x16, x17 +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: adrp x16, [[F2BB2ADDR:Ltmp[0-9]+]]@PAGE +; CHECK-NEXT: add x16, x16, [[F2BB2ADDR]]@PAGEOFF +; CHECK-NEXT: mov x17, #[[F2DISCVAL]] +; CHECK-NEXT: pacia x16, x17 +; CHECK-NEXT: mov x1, x16 +; CHECK-NEXT: bl _dummy_choose +; CHECK-NEXT: mov x17, #[[F2DISCVAL]] +; CHECK-NEXT: braa x0, x17 +; CHECK: [[F2BB1ADDR]]: +; CHECK-NEXT: [[F2BB1:LBB[0-9_]+]]: +; CHECK-NEXT: mov w0, #1 +; CHECK: [[F2BB2ADDR]]: +; CHECK-NEXT: [[F2BB2:LBB[0-9_]+]]: +; CHECK-NEXT: mov w0, #2 +define i32 @test_blockaddress_2() #0 { +entry: + %tmp0 = call ptr @dummy_choose(ptr blockaddress(@test_blockaddress_2, %bb1), ptr blockaddress(@test_blockaddress_2, %bb2)) + indirectbr ptr %tmp0, [label %bb1, label %bb2] + +bb1: + ret i32 1 + +bb2: + ret i32 2 +} + +; CHECK-LABEL: _test_blockaddress_other_function: +; CHECK: adrp x16, [[F1BB1ADDR]]@PAGE +; CHECK-NEXT: add x16, x16, [[F1BB1ADDR]]@PAGEOFF +; CHECK-NEXT: mov x17, #[[F1DISCVAL]] +; CHECK-NEXT: pacia x16, x17 +; CHECK-NEXT: mov x0, x16 +; CHECK-NEXT: ret +define ptr @test_blockaddress_other_function() #0 { + ret ptr blockaddress(@test_blockaddress, %bb1) +} + +; CHECK-LABEL: .section __DATA,__const +; CHECK-NEXT: .globl _test_blockaddress_array +; CHECK-NEXT: .p2align 4 +; CHECK-NEXT: _test_blockaddress_array: +; CHECK-NEXT: .quad [[F1BB1ADDR]]@AUTH(ia,[[F1DISCVAL]] +; CHECK-NEXT: .quad [[F1BB2ADDR]]@AUTH(ia,[[F1DISCVAL]] +; CHECK-NEXT: .quad [[F2BB1ADDR]]@AUTH(ia,[[F2DISCVAL]] +; CHECK-NEXT: .quad [[F2BB2ADDR]]@AUTH(ia,[[F2DISCVAL]] +@test_blockaddress_array = constant [4 x ptr] [ + ptr blockaddress(@test_blockaddress, %bb1), ptr blockaddress(@test_blockaddress, %bb2), + ptr blockaddress(@test_blockaddress_2, %bb1), ptr blockaddress(@test_blockaddress_2, %bb2) +] + +declare ptr @dummy_choose(ptr, ptr) + +attributes #0 = { "ptrauth-indirect-gotos" nounwind } From ee58803b830f84dd9a6f34a943e450265d5b0d58 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 7 Aug 2023 09:15:16 -0700 Subject: [PATCH 38/58] [compiler-rt] Support the arm64e architecture in sanitizer runtimes. --- compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake | 2 +- compiler-rt/lib/asan/scripts/asan_symbolize.py | 1 + compiler-rt/lib/builtins/assembly.h | 2 +- compiler-rt/lib/sanitizer_common/sanitizer_common.h | 3 +++ .../lib/sanitizer_common/sanitizer_procmaps_mac.cpp | 5 +++++ compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cpp | 7 +++++++ .../lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp | 2 ++ compiler-rt/lib/ubsan/ubsan_type_hash_itanium.cpp | 2 +- 8 files changed, 21 insertions(+), 3 deletions(-) diff --git a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake index ac4a71202384d..06500b90ec10c 100644 --- a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake +++ b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake @@ -18,7 +18,7 @@ set(WASM64 wasm64) set(VE ve) if(APPLE) - set(ARM64 arm64) + set(ARM64 arm64 arm64e) set(ARM32 armv7 armv7s armv7k) set(ARM64_32 arm64_32) set(X86_64 x86_64 x86_64h) diff --git a/compiler-rt/lib/asan/scripts/asan_symbolize.py b/compiler-rt/lib/asan/scripts/asan_symbolize.py index b08769614aeb1..4bae3cd90909f 100755 --- a/compiler-rt/lib/asan/scripts/asan_symbolize.py +++ b/compiler-rt/lib/asan/scripts/asan_symbolize.py @@ -59,6 +59,7 @@ def is_valid_arch(s): "armv7s", "armv7k", "arm64", + "arm64e", "powerpc64", "powerpc64le", "s390x", diff --git a/compiler-rt/lib/builtins/assembly.h b/compiler-rt/lib/builtins/assembly.h index 8c42fc773483b..82e620ea0bdff 100644 --- a/compiler-rt/lib/builtins/assembly.h +++ b/compiler-rt/lib/builtins/assembly.h @@ -118,7 +118,7 @@ #define BTI_J #endif -#if (BTI_FLAG | PAC_FLAG) != 0 +#if !defined(__APPLE__) && (BTI_FLAG | PAC_FLAG) != 0 #define GNU_PROPERTY_BTI_PAC \ GNU_PROPERTY(GNU_PROPERTY_AARCH64_FEATURE_1_AND, BTI_FLAG | PAC_FLAG) #else diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h index fbe9b3cac8d1c..b03fda07605b2 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h @@ -724,6 +724,7 @@ enum ModuleArch { kModuleArchARMV7S, kModuleArchARMV7K, kModuleArchARM64, + kModuleArchARM64E, kModuleArchLoongArch64, kModuleArchRISCV64, kModuleArchHexagon @@ -797,6 +798,8 @@ inline const char *ModuleArchToString(ModuleArch arch) { return "armv7k"; case kModuleArchARM64: return "arm64"; + case kModuleArchARM64E: + return "arm64e"; case kModuleArchLoongArch64: return "loongarch64"; case kModuleArchRISCV64: diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cpp index b44e016a0e5bc..ef2090f74ccd5 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cpp @@ -32,6 +32,9 @@ #ifndef CPU_TYPE_ARM64 #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) #endif +#ifndef CPU_SUBTYPE_ARM64_E +#define CPU_SUBTYPE_ARM64_E ((cpu_subtype_t)2) +#endif namespace __sanitizer { @@ -323,6 +326,8 @@ ModuleArch ModuleArchFromCpuType(cpu_type_t cputype, cpu_subtype_t cpusubtype) { CHECK(0 && "Invalid subtype of ARM"); return kModuleArchUnknown; case CPU_TYPE_ARM64: + if (cpusubtype == CPU_SUBTYPE_ARM64_E) + return kModuleArchARM64E; return kModuleArchARM64; default: CHECK(0 && "Invalid CPU type"); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cpp index d24fae98213aa..9464b1c79ff8f 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cpp @@ -17,6 +17,10 @@ #include "sanitizer_platform.h" #include "sanitizer_ptrauth.h" +#if __has_feature(ptrauth_returns) +#include +#endif + namespace __sanitizer { uptr StackTrace::GetNextInstructionPc(uptr pc) { @@ -132,6 +136,9 @@ void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top, // a platform where this isn't true, we need to reconsider this check. if (pc1 < kPageSize) break; +#if __has_feature(ptrauth_returns) + pc1 = (uhwptr)ptrauth_strip((void *)pc1, ptrauth_key_return_address); +#endif if (pc1 != pc) { trace_buffer[size++] = (uptr) pc1; } diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp index 74458028ae8f5..5601a19650f56 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cpp @@ -270,6 +270,8 @@ class LLVMSymbolizerProcess final : public SymbolizerProcess { const char *const kSymbolizerArch = "--default-arch=loongarch64"; #elif SANITIZER_RISCV64 const char *const kSymbolizerArch = "--default-arch=riscv64"; +#elif defined(__arm64e__) + const char* const kSymbolizerArch = "--default-arch=arm64e"; #elif defined(__aarch64__) const char* const kSymbolizerArch = "--default-arch=arm64"; #elif defined(__arm__) diff --git a/compiler-rt/lib/ubsan/ubsan_type_hash_itanium.cpp b/compiler-rt/lib/ubsan/ubsan_type_hash_itanium.cpp index 468a8fcd603f0..15788574dd995 100644 --- a/compiler-rt/lib/ubsan/ubsan_type_hash_itanium.cpp +++ b/compiler-rt/lib/ubsan/ubsan_type_hash_itanium.cpp @@ -207,7 +207,7 @@ struct VtablePrefix { std::type_info *TypeInfo; }; VtablePrefix *getVtablePrefix(void *Vtable) { - Vtable = ptrauth_auth_data(Vtable, ptrauth_key_cxx_vtable_pointer, 0); + Vtable = ptrauth_strip(Vtable, ptrauth_key_cxx_vtable_pointer); VtablePrefix *Vptr = reinterpret_cast(Vtable); VtablePrefix *Prefix = Vptr - 1; if (!IsAccessibleMemoryRange((uptr)Prefix, sizeof(VtablePrefix))) From 1a9cafe4bb2fa573d4796ca66c63270bb3bd3ed2 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 27 Sep 2021 08:00:00 -0700 Subject: [PATCH 39/58] libunwind/libcxxabi/compiler-rt: arm64e exception handling support. --- compiler-rt/lib/builtins/gcc_personality_v0.c | 31 +++- libcxxabi/include/__cxxabi_config.h | 11 ++ libcxxabi/src/cxa_exception.h | 30 ++-- libcxxabi/src/cxa_personality.cpp | 36 ++++- libunwind/include/libunwind.h | 25 +++- libunwind/src/AddressSpace.hpp | 33 +++-- libunwind/src/CompactUnwinder.hpp | 28 ++-- libunwind/src/DwarfInstructions.hpp | 12 +- libunwind/src/DwarfParser.hpp | 43 +++++- libunwind/src/Registers.hpp | 139 +++++++++++++++++- libunwind/src/UnwindCursor.hpp | 61 +++++++- libunwind/src/UnwindLevel1.c | 23 ++- libunwind/src/UnwindRegistersRestore.S | 21 ++- libunwind/src/UnwindRegistersSave.S | 9 +- libunwind/src/libunwind.cpp | 132 ++++++++++++++++- libunwind/src/libunwind_ext.h | 12 ++ 16 files changed, 558 insertions(+), 88 deletions(-) diff --git a/compiler-rt/lib/builtins/gcc_personality_v0.c b/compiler-rt/lib/builtins/gcc_personality_v0.c index ef63a5fb83472..f3794f983b414 100644 --- a/compiler-rt/lib/builtins/gcc_personality_v0.c +++ b/compiler-rt/lib/builtins/gcc_personality_v0.c @@ -30,6 +30,13 @@ EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, void *, PCONTEXT, _Unwind_Personality_Fn); #endif +#if defined(__APPLE__) && defined(__arm64e__) && __has_feature(ptrauth_qualifier) +#include +#define PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(key, addressDiscriminated, discriminator_string) __ptrauth_restricted_intptr(key, addressDiscriminated, __builtin_ptrauth_string_discriminator(discriminator_string)) +#else +#define PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(...) +#endif + // Pointer encodings documented at: // http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html @@ -205,7 +212,7 @@ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0( return continueUnwind(exceptionObject, context); uintptr_t pc = (uintptr_t)_Unwind_GetIP(context) - 1; - uintptr_t funcStart = (uintptr_t)_Unwind_GetRegionStart(context); + uintptr_t PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, 1, "__gcc_personality_v0'funcStart") funcStart = (uintptr_t)_Unwind_GetRegionStart(context); uintptr_t pcOffset = pc - funcStart; // Parse LSDA header. @@ -224,11 +231,11 @@ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0( const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength; const uint8_t *p = callSiteTableStart; while (p < callSiteTableEnd) { - uintptr_t start = readEncodedPointer(&p, callSiteEncoding); - size_t length = readEncodedPointer(&p, callSiteEncoding); - size_t landingPad = readEncodedPointer(&p, callSiteEncoding); + uintptr_t PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, 1, "__gcc_personality_v0'start") start = readEncodedPointer(&p, callSiteEncoding); + size_t PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, 1, "__gcc_personality_v0'length") length = readEncodedPointer(&p, callSiteEncoding); + size_t PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, 1, "__gcc_personality_v0'landingPadOffset") landingPadOffset = readEncodedPointer(&p, callSiteEncoding); readULEB128(&p); // action value not used for C code - if (landingPad == 0) + if (landingPadOffset == 0) continue; // no landing pad for this entry if ((start <= pcOffset) && (pcOffset < (start + length))) { // Found landing pad for the PC. @@ -238,7 +245,19 @@ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0( _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)exceptionObject); _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0); - _Unwind_SetIP(context, (funcStart + landingPad)); + size_t PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, 1, "__gcc_personality_v0'landingPad") landingPad = funcStart + landingPadOffset; +#if defined(__APPLE__) && defined(__arm64e__) && __has_feature(ptrauth_qualifier) + uintptr_t stack_pointer = _Unwind_GetGR(context, -2); + const uintptr_t existingDiscriminator = ptrauth_blend_discriminator(&landingPad, __builtin_ptrauth_schema_extra_discriminator(typeof(landingPad))); + uintptr_t newIP = (uintptr_t)ptrauth_auth_and_resign(*(void**)&landingPad, + __builtin_ptrauth_schema_key(typeof(landingPad)), + existingDiscriminator, + ptrauth_key_return_address, + stack_pointer); + _Unwind_SetIP(context, newIP); +#else + _Unwind_SetIP(context, landingPad); +#endif return _URC_INSTALL_CONTEXT; } } diff --git a/libcxxabi/include/__cxxabi_config.h b/libcxxabi/include/__cxxabi_config.h index e8aa37e6d5ec9..97a7ceb55abf4 100644 --- a/libcxxabi/include/__cxxabi_config.h +++ b/libcxxabi/include/__cxxabi_config.h @@ -103,4 +103,15 @@ #define _LIBCXXABI_DTOR_FUNC #endif +#if defined(__APPLE__) && defined(__arm64e__) && __has_feature(ptrauth_qualifier) +# include +# define _LIBCXXABI_PTRAUTH(__key, __address_discriminated, __discriminator) \ + __ptrauth(__key, __address_discriminated, __builtin_ptrauth_string_discriminator(__discriminator)) +# define _LIBCXXABI_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, __discriminator) \ + __ptrauth_restricted_intptr(__key, __address_discriminated, __builtin_ptrauth_string_discriminator(__discriminator)) +#else +# define _LIBCXXABI_PTRAUTH(__key, __address_discriminated, __discriminator) +# define _LIBCXXABI_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, __discriminator) +#endif + #endif // ____CXXABI_CONFIG_H diff --git a/libcxxabi/src/cxa_exception.h b/libcxxabi/src/cxa_exception.h index aba08f2992103..4ef33ee75b200 100644 --- a/libcxxabi/src/cxa_exception.h +++ b/libcxxabi/src/cxa_exception.h @@ -47,10 +47,10 @@ struct _LIBCXXABI_HIDDEN __cxa_exception { // In Wasm, a destructor returns its argument void *(_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *); #else - void (_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *); + void (_LIBCXXABI_DTOR_FUNC* _LIBCXXABI_PTRAUTH(ptrauth_key_function_pointer, 1, "__cxa_exception::exceptionDestructor") exceptionDestructor)(void*); #endif - std::unexpected_handler unexpectedHandler; - std::terminate_handler terminateHandler; + std::unexpected_handler _LIBCXXABI_PTRAUTH(ptrauth_key_function_pointer, 1, "__cxa_exception::unexpectedHandler") unexpectedHandler; + std::terminate_handler _LIBCXXABI_PTRAUTH(ptrauth_key_function_pointer, 1, "__cxa_exception::terminateHandler") terminateHandler; __cxa_exception *nextException; @@ -61,10 +61,10 @@ struct _LIBCXXABI_HIDDEN __cxa_exception { int propagationCount; #else int handlerSwitchValue; - const unsigned char *actionRecord; - const unsigned char *languageSpecificData; - void *catchTemp; - void *adjustedPtr; + const unsigned char* _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_data, 1, "__cxa_exception::actionRecord") actionRecord; + const unsigned char* _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_data, 1, "__cxa_exception::languageSpecificData") languageSpecificData; + void* _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_data, 1, "__cxa_exception::catchTemp") catchTemp; + void* _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_data, 1, "__cxa_exception::adjustedPtr") adjustedPtr; #endif #if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI) @@ -79,6 +79,8 @@ struct _LIBCXXABI_HIDDEN __cxa_exception { // http://sourcery.mentor.com/archives/cxx-abi-dev/msg01924.html // The layout of this structure MUST match the layout of __cxa_exception, with // primaryException instead of referenceCount. +// The tags used in the pointer authentication qualifiers also need to match +// those of the corresponding members in __cxa_exception. struct _LIBCXXABI_HIDDEN __cxa_dependent_exception { #if defined(__LP64__) || defined(_WIN64) || defined(_LIBCXXABI_ARM_EHABI) void* reserve; // padding. @@ -86,9 +88,9 @@ struct _LIBCXXABI_HIDDEN __cxa_dependent_exception { #endif std::type_info *exceptionType; - void (_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *); - std::unexpected_handler unexpectedHandler; - std::terminate_handler terminateHandler; + void (_LIBCXXABI_DTOR_FUNC* _LIBCXXABI_PTRAUTH(ptrauth_key_function_pointer, 1, "__cxa_exception::exceptionDestructor") exceptionDestructor)(void*); + std::unexpected_handler _LIBCXXABI_PTRAUTH(ptrauth_key_function_pointer, 1, "__cxa_exception::unexpectedHandler") unexpectedHandler; + std::terminate_handler _LIBCXXABI_PTRAUTH(ptrauth_key_function_pointer, 1, "__cxa_exception::terminateHandler") terminateHandler; __cxa_exception *nextException; @@ -99,10 +101,10 @@ struct _LIBCXXABI_HIDDEN __cxa_dependent_exception { int propagationCount; #else int handlerSwitchValue; - const unsigned char *actionRecord; - const unsigned char *languageSpecificData; - void * catchTemp; - void *adjustedPtr; + const unsigned char* _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_data, 1, "__cxa_exception::actionRecord") actionRecord; + const unsigned char* _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_data, 1, "__cxa_exception::languageSpecificData") languageSpecificData; + void* _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_data, 1, "__cxa_exception::catchTemp") catchTemp; + void* _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_data, 1, "__cxa_exception::adjustedPtr") adjustedPtr; #endif #if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI) diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp index 843a18a4cbd8a..04c4552ec2331 100644 --- a/libcxxabi/src/cxa_personality.cpp +++ b/libcxxabi/src/cxa_personality.cpp @@ -22,6 +22,11 @@ #include "private_typeinfo.h" #include "unwind.h" +#if defined(__APPLE__) && defined(__arm64e__) && __has_feature(ptrauth_qualifier) +#include +#include "libunwind.h" +#endif + // TODO: This is a temporary workaround for libc++abi to recognize that it's being // built against LLVM's libunwind. LLVM's libunwind started reporting _LIBUNWIND_VERSION // in LLVM 15 -- we can remove this workaround after shipping LLVM 17. Once we remove @@ -527,12 +532,17 @@ get_thrown_object_ptr(_Unwind_Exception* unwind_exception) namespace { +typedef const uint8_t* _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_code, 1, "scan_results::languageSpecificData") lsd_ptr_t; +typedef const uint8_t* _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_code, 1, "scan_results::actionRecord") action_ptr_t; +typedef uintptr_t _LIBCXXABI_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_code, 1, "scan_results::landingPad") landing_pad_t; +typedef void* _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_code, 1, "scan_results::landingPad") landing_pad_ptr_t; + struct scan_results { int64_t ttypeIndex; // > 0 catch handler, < 0 exception spec handler, == 0 a cleanup - const uint8_t* actionRecord; // Currently unused. Retained to ease future maintenance. - const uint8_t* languageSpecificData; // Needed only for __cxa_call_unexpected - uintptr_t landingPad; // null -> nothing found, else something found + action_ptr_t actionRecord; // Currently unused. Retained to ease future maintenance. + lsd_ptr_t languageSpecificData; // Needed only for __cxa_call_unexpected + landing_pad_t landingPad; // null -> nothing found, else something found void* adjustedPtr; // Used in cxa_exception.cpp _Unwind_Reason_Code reason; // One of _URC_FATAL_PHASE1_ERROR, // _URC_FATAL_PHASE2_ERROR, @@ -557,7 +567,18 @@ set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context, reinterpret_cast(unwind_exception)); _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), static_cast(results.ttypeIndex)); +#if defined(__APPLE__) && defined(__arm64e__) && __has_feature(ptrauth_qualifier) + auto stack_pointer = _Unwind_GetGR(context, UNW_REG_SP); + const auto existingDiscriminator = ptrauth_blend_discriminator(&results.landingPad, __builtin_ptrauth_schema_extra_discriminator(decltype(results.landingPad))); + unw_word_t newIP = (unw_word_t)ptrauth_auth_and_resign(*(void**)&results.landingPad, + __builtin_ptrauth_schema_key(decltype(results.landingPad)), + existingDiscriminator, + ptrauth_key_return_address, + stack_pointer); + _Unwind_SetIP(context, newIP); +#else _Unwind_SetIP(context, results.landingPad); +#endif } /* @@ -691,12 +712,12 @@ static void scan_eh_tab(scan_results &results, _Unwind_Action actions, // The call sites are ordered in increasing value of start uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding); uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding); - uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding); + landing_pad_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding); uintptr_t actionEntry = readULEB128(&callSitePtr); if ((start <= ipOffset) && (ipOffset < (start + length))) #else // __USING_SJLJ_EXCEPTIONS__ || __WASM_EXCEPTIONS__ // ip is 1-based index into this table - uintptr_t landingPad = readULEB128(&callSitePtr); + landing_pad_t landingPad = readULEB128(&callSitePtr); uintptr_t actionEntry = readULEB128(&callSitePtr); if (--ip == 0) #endif // __USING_SJLJ_EXCEPTIONS__ || __WASM_EXCEPTIONS__ @@ -935,8 +956,7 @@ __gxx_personality_v0 results.ttypeIndex = exception_header->handlerSwitchValue; results.actionRecord = exception_header->actionRecord; results.languageSpecificData = exception_header->languageSpecificData; - results.landingPad = - reinterpret_cast(exception_header->catchTemp); + set_landing_pad_as_ptr(results, exception_header->catchTemp); results.adjustedPtr = exception_header->adjustedPtr; // Jump to the handler. @@ -970,7 +990,7 @@ __gxx_personality_v0 exc->handlerSwitchValue = static_cast(results.ttypeIndex); exc->actionRecord = results.actionRecord; exc->languageSpecificData = results.languageSpecificData; - exc->catchTemp = reinterpret_cast(results.landingPad); + exc->catchTemp = get_landing_pad_as_ptr(results); exc->adjustedPtr = results.adjustedPtr; #ifdef __WASM_EXCEPTIONS__ // Wasm only uses a single phase (_UA_SEARCH_PHASE), so save the diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h index b2dae8feed9a3..a197e50f5d159 100644 --- a/libunwind/include/libunwind.h +++ b/libunwind/include/libunwind.h @@ -43,6 +43,17 @@ #define LIBUNWIND_AVAIL #endif +#if defined(__APPLE__) && defined(__arm64e__) && __has_feature(ptrauth_qualifier) +#include +#define _LIBUNWIND_PTRAUTH(__key, __address_discriminated, __discriminator) \ + __ptrauth(__key, __address_discriminated, __builtin_ptrauth_string_discriminator(__discriminator)) +#define _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, __discriminator) \ + __ptrauth_restricted_intptr(__key, __address_discriminated, __builtin_ptrauth_string_discriminator(__discriminator)) +#else +#define _LIBUNWIND_PTRAUTH(__key, __address_discriminated, __discriminator) +#define _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, __discriminator) +#endif + #if defined(_WIN32) && defined(__SEH__) #define LIBUNWIND_CURSOR_ALIGNMENT_ATTR __attribute__((__aligned__(16))) #else @@ -88,17 +99,17 @@ typedef double unw_fpreg_t; #endif struct unw_proc_info_t { - unw_word_t start_ip; /* start address of function */ - unw_word_t end_ip; /* address after end of function */ - unw_word_t lsda; /* address of language specific data area, */ + unw_word_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_independent_code, 1, "unw_proc_info_t::start_ip") start_ip; /* start address of function */ + unw_word_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_independent_code, 1, "unw_proc_info_t::end_ip") end_ip; /* address after end of function */ + unw_word_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "unw_proc_info_t::lsda") lsda; /* address of language specific data area, */ /* or zero if not used */ - unw_word_t handler; /* personality routine, or zero if not used */ + unw_word_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, 1, "unw_proc_info_t::handler") handler; /* personality routine, or zero if not used */ unw_word_t gp; /* not used */ - unw_word_t flags; /* not used */ + unw_word_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "unw_proc_info_t::flags") flags; /* not used */ uint32_t format; /* compact unwind encoding, or zero if none */ uint32_t unwind_info_size; /* size of DWARF unwind info, or zero if none */ - unw_word_t unwind_info; /* address of DWARF unwind info, or zero */ - unw_word_t extra; /* mach_header of mach-o image containing func */ + unw_word_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "unw_proc_info_t::unwind_info") unwind_info; /* address of DWARF unwind info, or zero */ + unw_word_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "unw_proc_info_t::extra") extra; /* mach_header of mach-o image containing func */ }; typedef struct unw_proc_info_t unw_proc_info_t; diff --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp index 5551c7d4bef1c..9a5c8fb4db3fb 100644 --- a/libunwind/src/AddressSpace.hpp +++ b/libunwind/src/AddressSpace.hpp @@ -129,26 +129,26 @@ struct UnwindInfoSections { defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) || \ defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) // No dso_base for SEH. - uintptr_t dso_base; + uintptr_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "UnwindInfoSections::dso_base") dso_base; #endif #if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) - size_t text_segment_length; + size_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "UnwindInfoSections::text_segment_length") text_segment_length; #endif #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) - uintptr_t dwarf_section; - size_t dwarf_section_length; + uintptr_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "UnwindInfoSections::dwarf_section") dwarf_section; + size_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "UnwindInfoSections::dwarf_section_length") dwarf_section_length; #endif #if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) - uintptr_t dwarf_index_section; - size_t dwarf_index_section_length; + uintptr_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "UnwindInfoSections::dwarf_index_section") dwarf_index_section; + size_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "UnwindInfoSections::dwarf_index_section_length") dwarf_index_section_length; #endif #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) - uintptr_t compact_unwind_section; - size_t compact_unwind_section_length; + uintptr_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "UnwindInfoSections::compact_unwind_section") compact_unwind_section; + size_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "UnwindInfoSections::compact_unwind_section_length") compact_unwind_section_length; #endif #if defined(_LIBUNWIND_ARM_EHABI) - uintptr_t arm_section; - size_t arm_section_length; + uintptr_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "UnwindInfoSections::arm_section") arm_section; + size_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, 1, "UnwindInfoSections::arm_section_length") arm_section_length; #endif }; @@ -196,7 +196,8 @@ class _LIBUNWIND_HIDDEN LocalAddressSpace { static int64_t getSLEB128(pint_t &addr, pint_t end); pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, - pint_t datarelBase = 0); + pint_t datarelBase = 0, + pint_t *resultAddr = nullptr); bool findFunctionName(pint_t addr, char *buf, size_t bufLen, unw_word_t *offset); bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info); @@ -269,7 +270,7 @@ inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) { inline LocalAddressSpace::pint_t LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, - pint_t datarelBase) { + pint_t datarelBase, pint_t *resultAddr) { pint_t startAddr = addr; const uint8_t *p = (uint8_t *)addr; pint_t result; @@ -353,8 +354,14 @@ LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, break; } - if (encoding & DW_EH_PE_indirect) + if (encoding & DW_EH_PE_indirect) { + if (resultAddr) + *resultAddr = result; result = getP(result); + } else { + if (resultAddr) + *resultAddr = startAddr; + } return result; } diff --git a/libunwind/src/CompactUnwinder.hpp b/libunwind/src/CompactUnwinder.hpp index a7a8a153d86a4..11863eb23df8c 100644 --- a/libunwind/src/CompactUnwinder.hpp +++ b/libunwind/src/CompactUnwinder.hpp @@ -499,7 +499,7 @@ class CompactUnwinder_arm64 { static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A &addressSpace, - Registers_arm64 ®isters); + unw_word_t procInfoFlags, Registers_arm64 ®isters); private: typename A::pint_t pint_t; @@ -507,23 +507,23 @@ class CompactUnwinder_arm64 { static int stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A &addressSpace, - Registers_arm64 ®isters); + unw_word_t procInfoFlags, Registers_arm64 ®isters); static int stepWithCompactEncodingFrameless( compact_unwind_encoding_t compactEncoding, uint64_t functionStart, - A &addressSpace, Registers_arm64 ®isters); + A &addressSpace, unw_word_t procInfoFlags, Registers_arm64 ®isters); }; template int CompactUnwinder_arm64::stepWithCompactEncoding( compact_unwind_encoding_t compactEncoding, uint64_t functionStart, - A &addressSpace, Registers_arm64 ®isters) { + A &addressSpace, unw_word_t procInfoFlags, Registers_arm64 ®isters) { switch (compactEncoding & UNWIND_ARM64_MODE_MASK) { case UNWIND_ARM64_MODE_FRAME: return stepWithCompactEncodingFrame(compactEncoding, functionStart, - addressSpace, registers); + addressSpace, procInfoFlags, registers); case UNWIND_ARM64_MODE_FRAMELESS: return stepWithCompactEncodingFrameless(compactEncoding, functionStart, - addressSpace, registers); + addressSpace, procInfoFlags, registers); } _LIBUNWIND_ABORT("invalid compact unwind encoding"); } @@ -531,6 +531,7 @@ int CompactUnwinder_arm64::stepWithCompactEncoding( template int CompactUnwinder_arm64::stepWithCompactEncodingFrameless( compact_unwind_encoding_t encoding, uint64_t, A &addressSpace, + unw_word_t procInfoFlags, Registers_arm64 ®isters) { uint32_t stackSize = 16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK); @@ -601,11 +602,15 @@ int CompactUnwinder_arm64::stepWithCompactEncodingFrameless( savedRegisterLoc -= 8; } + Registers_arm64::reg_t linkRegister = registers.getRegister(UNW_AARCH64_LR); + // subtract stack size off of sp registers.setSP(savedRegisterLoc); + registers.normalizeNewLinkRegister(linkRegister, procInfoFlags); + // set pc to be value in lr - registers.setIP(registers.getRegister(UNW_AARCH64_LR)); + registers.setIP(linkRegister); return UNW_STEP_SUCCESS; } @@ -613,7 +618,7 @@ int CompactUnwinder_arm64::stepWithCompactEncodingFrameless( template int CompactUnwinder_arm64::stepWithCompactEncodingFrame( compact_unwind_encoding_t encoding, uint64_t, A &addressSpace, - Registers_arm64 ®isters) { + unw_word_t procInfoFlags, Registers_arm64 ®isters) { uint64_t savedRegisterLoc = registers.getFP() - 8; if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) { @@ -685,8 +690,13 @@ int CompactUnwinder_arm64::stepWithCompactEncodingFrame( registers.setFP(addressSpace.get64(fp)); // old sp is fp less saved fp and lr registers.setSP(fp + 16); + // pop return address into pc - registers.setIP(addressSpace.get64(fp + 8)); + Registers_arm64::reg_t linkRegister = addressSpace.get64(fp + 8); + + registers.normalizeNewLinkRegister(linkRegister, procInfoFlags); + + registers.setIP(linkRegister); return UNW_STEP_SUCCESS; } diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp index bd9ece60ee588..4e91168535d8f 100644 --- a/libunwind/src/DwarfInstructions.hpp +++ b/libunwind/src/DwarfInstructions.hpp @@ -22,6 +22,9 @@ #include "dwarf2.h" #include "libunwind_ext.h" +#if __has_feature(ptrauth_calls) +#include +#endif namespace libunwind { @@ -35,6 +38,7 @@ class DwarfInstructions { typedef typename A::sint_t sint_t; static int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart, + unw_word_t procInfoFlags, R ®isters, bool &isSignalFrame, bool stage2); private: @@ -189,7 +193,9 @@ bool DwarfInstructions::getRA_SIGN_STATE(A &addressSpace, R registers, template int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, - pint_t fdeStart, R ®isters, + pint_t fdeStart, + unw_word_t procInfoFlags, + R ®isters, bool &isSignalFrame, bool stage2) { FDE_Info fdeInfo; CIE_Info cieInfo; @@ -245,7 +251,7 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, // by a CFI directive later on. newRegisters.setSP(cfa); - pint_t returnAddress = 0; + typename R::reg_t returnAddress = 0; constexpr int lastReg = R::lastDwarfRegNum(); static_assert(static_cast(CFI_Parser::kMaxRegisterNumber) >= lastReg, @@ -363,6 +369,8 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, } #endif + newRegisters.normalizeNewLinkRegister(returnAddress, procInfoFlags); + // Return address is address after call site instruction, so setting IP to // that does simulates a return. newRegisters.setIP(returnAddress); diff --git a/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp index 0682942ce1379..163df5e0a8de6 100644 --- a/libunwind/src/DwarfParser.hpp +++ b/libunwind/src/DwarfParser.hpp @@ -23,6 +23,10 @@ #include "config.h" +#if __has_feature(ptrauth_calls) +#include +#endif + namespace libunwind { /// CFI_Parser does basic parsing of a CFI (Call Frame Information) records. @@ -43,7 +47,8 @@ class CFI_Parser { uint8_t lsdaEncoding; uint8_t personalityEncoding; uint8_t personalityOffsetInCIE; - pint_t personality; + personality_t personality; + pint_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, 1, "CIE_Info::personality") personality_t; uint32_t codeAlignFactor; int dataAlignFactor; bool isSignalFrame; @@ -310,6 +315,17 @@ bool CFI_Parser::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, } return false; } +namespace { +// This helper function handles setting the manually signed personality on +// CIE_Info without attempt to authenticate and/or re-sign +template +void set_cie_info_personality(CIE_Info *info, T signed_personality) { + static_assert(sizeof(info->personality) == sizeof(signed_personality), + "Signed personality is the wrong size"); + memmove((void *)&info->personality, (void *)&signed_personality, + sizeof(signed_personality)); +} +} /// Extract info from a CIE template @@ -366,6 +382,7 @@ const char *CFI_Parser::parseCIE(A &addressSpace, pint_t cie, cieInfo->returnAddressRegister = (uint8_t)raReg; // parse augmentation data based on augmentation string const char *result = NULL; + pint_t resultAddr = 0; if (addressSpace.get8(strStart) == 'z') { // parse augmentation data length addressSpace.getULEB128(p, cieContentEnd); @@ -374,13 +391,31 @@ const char *CFI_Parser::parseCIE(A &addressSpace, pint_t cie, case 'z': cieInfo->fdesHaveAugmentationData = true; break; - case 'P': + case 'P': { cieInfo->personalityEncoding = addressSpace.get8(p); ++p; cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie); - cieInfo->personality = addressSpace - .getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding); + pint_t personality = addressSpace + .getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding, + /*datarelBase=*/0, &resultAddr); +#if __has_feature(ptrauth_calls) + if (personality) { + // The GOT for the personality function was signed address authenticated. + // Manually re-sign with the CIE_Info::personality schema. If we could guarantee the + // encoding of the personality we could avoid this by simply giving resultAddr the + // correct ptrauth schema and performing an assignment. + const auto discriminator = ptrauth_blend_discriminator(&cieInfo->personality, __builtin_ptrauth_string_discriminator("CIE_Info::personality")); + void* signedPtr = ptrauth_auth_and_resign((void*)personality, + ptrauth_key_function_pointer, + resultAddr, + ptrauth_key_function_pointer, + discriminator); + personality = (pint_t)signedPtr; + } +#endif + set_cie_info_personality(cieInfo, personality); break; + } case 'L': cieInfo->lsdaEncoding = addressSpace.get8(p); ++p; diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp index d11ddb3426d52..fdc8e88ca2250 100644 --- a/libunwind/src/Registers.hpp +++ b/libunwind/src/Registers.hpp @@ -18,6 +18,11 @@ #include "cet_unwind.h" #include "config.h" #include "libunwind.h" +#include "libunwind_ext.h" + +#if __has_feature(ptrauth_calls) +#include +#endif namespace libunwind { @@ -93,6 +98,10 @@ class _LIBUNWIND_HIDDEN Registers_x86 { uint32_t getEDI() const { return _registers.__edi; } void setEDI(uint32_t value) { _registers.__edi = value; } + typedef uint32_t reg_t; + void normalizeNewLinkRegister(reg_t&, unw_word_t) { } + void normalizeExistingLinkRegister(reg_t&) { } + private: struct GPRs { unsigned int __eax; @@ -311,6 +320,10 @@ class _LIBUNWIND_HIDDEN Registers_x86_64 { uint64_t getR15() const { return _registers.__r15; } void setR15(uint64_t value) { _registers.__r15 = value; } + typedef uint64_t reg_t; + void normalizeNewLinkRegister(reg_t&, unw_word_t) { } + void normalizeExistingLinkRegister(reg_t&) { } + private: struct GPRs { uint64_t __rax; @@ -622,6 +635,10 @@ class _LIBUNWIND_HIDDEN Registers_ppc { uint64_t getLR() const { return _registers.__lr; } void setLR(uint32_t value) { _registers.__lr = value; } + typedef uint32_t reg_t; + void normalizeNewLinkRegister(reg_t&, unw_word_t) { } + void normalizeExistingLinkRegister(reg_t&) { } + private: struct ppc_thread_state_t { unsigned int __srr0; /* Instruction address register (PC) */ @@ -1819,6 +1836,8 @@ class _LIBUNWIND_HIDDEN Registers_arm64 { public: Registers_arm64(); Registers_arm64(const void *registers); + Registers_arm64(const Registers_arm64&); + Registers_arm64& operator=(const Registers_arm64&); bool validRegister(int num) const; uint64_t getRegister(int num) const; @@ -1838,11 +1857,100 @@ class _LIBUNWIND_HIDDEN Registers_arm64 { uint64_t getSP() const { return _registers.__sp; } void setSP(uint64_t value) { _registers.__sp = value; } - uint64_t getIP() const { return _registers.__pc; } - void setIP(uint64_t value) { _registers.__pc = value; } + uint64_t getIP() const { + uint64_t value = _registers.__pc; +#if __has_feature(ptrauth_calls) + // Note the value of the PC was signed to its address in the register state + // but everyone else expects it to be sign by the SP, so convert on return. + value = (uint64_t)ptrauth_auth_and_resign((void*)_registers.__pc, + ptrauth_key_return_address, + &_registers.__pc, + ptrauth_key_return_address, + getSP()); +#endif + return value; + } + void setIP(uint64_t value) { +#if __has_feature(ptrauth_calls) + // Note the value which was set should have been signed with the SP. + // We then resign with the slot we are being stored in to so that both SP and LR + // can't be spoofed at the same time. + value = (uint64_t)ptrauth_auth_and_resign((void*)value, + ptrauth_key_return_address, + getSP(), + ptrauth_key_return_address, + &_registers.__pc); +#endif + _registers.__pc = value; + } uint64_t getFP() const { return _registers.__fp; } void setFP(uint64_t value) { _registers.__fp = value; } + typedef uint64_t reg_t; + void normalizeNewLinkRegister(reg_t& linkRegister, unw_word_t procInfoFlags) { + (void)linkRegister; + (void)procInfoFlags; +#if __has_feature(ptrauth_calls) + if (procInfoFlags == ProcInfoFlags_IsARM64Image && !__unw_is_pointer_auth_enabled()) { + // If the current frame is arm64e then the LR should have been signed by + // the SP. In this case, we'll just leave it as is. For other frames, + // we'll now sign the LR so that setIP below can assume all inputs are signed. + linkRegister = (uint64_t)ptrauth_sign_unauthenticated((void*)linkRegister, + ptrauth_key_return_address, + _registers.__sp); + __asm__ volatile ("":::"memory"); + if (__unw_is_pointer_auth_enabled()) { + linkRegister = (uint64_t)-1; + // Force the optimizer to commit the update to value + __asm__ volatile ("":"+rm"(linkRegister)::"memory"); + _LIBUNWIND_ABORT("Inconsistent invalid authentication state"); + } + } +#endif + } + + void normalizeExistingLinkRegister(reg_t& linkRegister) { + (void)linkRegister; +#if __has_feature(ptrauth_calls) + // If we are in an arm64/arm64e frame, then the PC should have been signed with the SP + linkRegister = (uint64_t)ptrauth_auth_data((void*)linkRegister, ptrauth_key_return_address, _registers.__sp); +#endif + } + + void normalizeNewFramePointer(reg_t& framePointer, unw_word_t procInfoFlags) { + (void)framePointer; + (void)procInfoFlags; +#if __has_feature(ptrauth_frames) + if (procInfoFlags == ProcInfoFlags_IsARM64Image && !__unw_is_pointer_auth_enabled()) { + // If the current frame is arm64e then the FP should have been signed by + // the SP. In this case, we'll just leave it as is. For other frames, + // we'll now sign the FP so that setFP below can assume all inputs are signed. + framePointer = (uint64_t)ptrauth_sign_unauthenticated((void*)framePointer, + ptrauth_key_frame_pointer, + _registers.__sp); + __asm__ volatile ("":::"memory"); + if (__unw_is_pointer_auth_enabled()) { + framePointer = (uint64_t)-1; + // Force the optimizer to commit the update to value + __asm__ volatile ("":"+rm"(framePointer)::"memory"); + _LIBUNWIND_ABORT("Inconsistent invalid authentication state"); + } + } +#endif + } + + void normalizeExistingFramePointer(reg_t& framePointer) { + (void)framePointer; +#if __has_feature(ptrauth_frames) + // If we are in an arm64/arm64e frame, then the FP should have been signed with the SP + framePointer = (uint64_t)ptrauth_auth_data((void*)framePointer, ptrauth_key_frame_pointer, _registers.__sp); +#endif + } + + // arm64_32 and i386 simulator hack + void normalizeNewLinkRegister(uint32_t&, unw_word_t) { } + void normalizeExistingLinkRegister(uint32_t&) { } + private: struct GPRs { uint64_t __x[29]; // x0-x28 @@ -1870,6 +1978,25 @@ inline Registers_arm64::Registers_arm64(const void *registers) { memcpy(_vectorHalfRegisters, static_cast(registers) + sizeof(GPRs), sizeof(_vectorHalfRegisters)); +#if __has_feature(ptrauth_calls) + uint64_t pcRegister = 0; + memcpy(&pcRegister, ((uint8_t*)&_registers) + offsetof(GPRs, __pc), sizeof(pcRegister)); + setIP(pcRegister); +#endif +} + +inline Registers_arm64::Registers_arm64(const Registers_arm64& other) { + *this = other; +} + +inline Registers_arm64& Registers_arm64::operator=(const Registers_arm64& other) { + memcpy(&_registers, &other._registers, sizeof(_registers)); + memcpy(_vectorHalfRegisters, &other._vectorHalfRegisters, + sizeof(_vectorHalfRegisters)); +#if __has_feature(ptrauth_calls) + setIP(other.getIP()); +#endif + return *this; } inline Registers_arm64::Registers_arm64() { @@ -1895,7 +2022,7 @@ inline bool Registers_arm64::validRegister(int regNum) const { inline uint64_t Registers_arm64::getRegister(int regNum) const { if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC) - return _registers.__pc; + return getIP(); if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP) return _registers.__sp; if (regNum == UNW_AARCH64_RA_SIGN_STATE) @@ -1911,7 +2038,7 @@ inline uint64_t Registers_arm64::getRegister(int regNum) const { inline void Registers_arm64::setRegister(int regNum, uint64_t value) { if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC) - _registers.__pc = value; + setIP(value); else if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP) _registers.__sp = value; else if (regNum == UNW_AARCH64_RA_SIGN_STATE) @@ -2133,6 +2260,10 @@ class _LIBUNWIND_HIDDEN Registers_arm { uint32_t getIP() const { return _registers.__pc; } void setIP(uint32_t value) { _registers.__pc = value; } + typedef uint32_t reg_t; + void normalizeNewLinkRegister(reg_t&, unw_word_t) { } + void normalizeExistingLinkRegister(reg_t&) { } + void saveVFPAsX() { assert(_use_X_for_vfp_save || !_saved_vfp_d0_d15); _use_X_for_vfp_save = true; diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 66fe8e2a32cca..610f7004516b0 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -41,6 +41,10 @@ #define _LIBUNWIND_CHECK_LINUX_SIGRETURN 1 #endif +#if __has_feature(ptrauth_calls) +#include +#endif + #include "AddressSpace.hpp" #include "CompactUnwinder.hpp" #include "config.h" @@ -1020,12 +1024,26 @@ class UnwindCursor : public AbstractUnwindCursor{ bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections §s, uint32_t fdeSectionOffsetHint=0); int stepWithDwarfFDE(bool stage2) { + typename R::reg_t pc = this->getReg(UNW_REG_IP); + _registers.normalizeExistingLinkRegister(pc); return DwarfInstructions::stepWithDwarf( - _addressSpace, (pint_t)this->getReg(UNW_REG_IP), - (pint_t)_info.unwind_info, _registers, _isSignalFrame, stage2); + _addressSpace, pc, (pint_t)_info.unwind_info, + _info.flags, _registers, _isSignalFrame, stage2); } #endif + void setProcInfoFlags() { +#if __has_feature(ptrauth_calls) + // If the extra field is set, then it is set to the mach header and we can use + // that to determine which flags to set + if (_info.extra) { + const mach_header_64 *mh = (const mach_header_64 *)_info.extra; + if ((mh->cpusubtype & ~CPU_SUBTYPE_MASK) != CPU_SUBTYPE_ARM64_E) + _info.flags = ProcInfoFlags_IsARM64Image; + } +#endif + } + #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) bool getInfoFromCompactEncodingSection(pint_t pc, const UnwindInfoSections §s); @@ -1646,6 +1664,7 @@ bool UnwindCursor::getInfoFromFdeCie( _info.unwind_info = fdeInfo.fdeStart; _info.unwind_info_size = static_cast(fdeInfo.fdeLength); _info.extra = static_cast(dso_base); + setProcInfoFlags(); return true; } return false; @@ -1711,6 +1730,18 @@ bool UnwindCursor::getInfoFromDwarfSection(pint_t pc, #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) +// This helper function handles setting the manually signed handler on +// unw_proc_info without attempt to authenticate and/or re-sign +namespace { +template +void set_proc_info_handler(unw_proc_info_t &info, T signed_handler) { + static_assert(sizeof(info.handler) == sizeof(signed_handler), + "Signed handler is the wrong size"); + memmove((void *)&info.handler, (void *)&signed_handler, + sizeof(signed_handler)); +} +} // unnamed namespace + template bool UnwindCursor::getInfoFromCompactEncodingSection(pint_t pc, const UnwindInfoSections §s) { @@ -1944,6 +1975,17 @@ bool UnwindCursor::getInfoFromCompactEncodingSection(pint_t pc, personalityIndex * sizeof(uint32_t)); pint_t personalityPointer = sects.dso_base + (pint_t)personalityDelta; personality = _addressSpace.getP(personalityPointer); +#if __has_feature(ptrauth_calls) + // The GOT for the personality function was signed address authenticated. + // Resign is as a regular function pointer. + const auto discriminator = ptrauth_blend_discriminator(&_info.handler, __builtin_ptrauth_string_discriminator("unw_proc_info_t::handler")); + void* signedPtr = ptrauth_auth_and_resign((void*)personality, + ptrauth_key_function_pointer, + personalityPointer, + ptrauth_key_function_pointer, + discriminator); + personality = (__typeof(personality))signedPtr; +#endif if (log) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), " "personalityDelta=0x%08X, personality=0x%08llX\n", @@ -1957,13 +1999,14 @@ bool UnwindCursor::getInfoFromCompactEncodingSection(pint_t pc, _info.start_ip = funcStart; _info.end_ip = funcEnd; _info.lsda = lsda; - _info.handler = personality; + set_proc_info_handler(_info, personality); _info.gp = 0; _info.flags = 0; _info.format = encoding; _info.unwind_info = 0; _info.unwind_info_size = 0; _info.extra = sects.dso_base; + setProcInfoFlags(); return true; } #endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) @@ -2556,19 +2599,22 @@ int UnwindCursor::stepWithTBTable(pint_t pc, tbtable *TBTable, } #endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + template void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) _isSigReturn = false; #endif - pint_t pc = static_cast(this->getReg(UNW_REG_IP)); + typename R::reg_t pc = this->getReg(UNW_REG_IP); #if defined(_LIBUNWIND_ARM_EHABI) // Remove the thumb bit so the IP represents the actual instruction address. // This matches the behaviour of _Unwind_GetIP on arm. pc &= (pint_t)~0x1; #endif + _registers.normalizeExistingLinkRegister(pc); + // Exit early if at the top of the stack. if (pc == 0) { _unwindInfoMissing = true; @@ -2962,8 +3008,11 @@ void UnwindCursor::getInfo(unw_proc_info_t *info) { template bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, unw_word_t *offset) { - return _addressSpace.findFunctionName((pint_t)this->getReg(UNW_REG_IP), - buf, bufLen, offset); + typename R::reg_t pc = this->getReg(UNW_REG_IP); + + _registers.normalizeExistingLinkRegister(pc); + + return _addressSpace.findFunctionName(pc, buf, bufLen, offset); } #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) diff --git a/libunwind/src/UnwindLevel1.c b/libunwind/src/UnwindLevel1.c index 48e7bc3b9e00e..197b66f1ec301 100644 --- a/libunwind/src/UnwindLevel1.c +++ b/libunwind/src/UnwindLevel1.c @@ -31,6 +31,10 @@ #include "libunwind_ext.h" #include "unwind.h" +#if __has_feature(ptrauth_calls) +#include +#endif + #if !defined(_LIBUNWIND_ARM_EHABI) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ !defined(__wasm__) @@ -131,8 +135,7 @@ unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except // If there is a personality routine, ask it if it will want to stop at // this frame. if (frameInfo.handler != 0) { - _Unwind_Personality_Fn p = - (_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler); + _Unwind_Personality_Fn p = frameInfo->handler; _LIBUNWIND_TRACE_UNWINDING( "unwind_phase1(ex_obj=%p): calling personality function %p", (void *)exception_object, (void *)(uintptr_t)p); @@ -251,8 +254,7 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except ++framesWalked; // If there is a personality routine, tell it we are unwinding. if (frameInfo.handler != 0) { - _Unwind_Personality_Fn p = - (_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler); + _Unwind_Personality_Fn p = frameInfo->handler; _Unwind_Action action = _UA_CLEANUP_PHASE; if (sp == exception_object->private_2) { // Tell personality this was the frame it marked in phase 1. @@ -365,8 +367,7 @@ unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor, ++framesWalked; // If there is a personality routine, tell it we are unwinding. if (frameInfo.handler != 0) { - _Unwind_Personality_Fn p = - (_Unwind_Personality_Fn)(intptr_t)(frameInfo.handler); + _Unwind_Personality_Fn p = frameInfo->handler; _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2_forced(ex_obj=%p): calling personality function %p", (void *)exception_object, (void *)(uintptr_t)p); @@ -569,6 +570,16 @@ _LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { unw_cursor_t *cursor = (unw_cursor_t *)context; unw_word_t result; __unw_get_reg(cursor, UNW_REG_IP, &result); + +#if __has_feature(ptrauth_calls) + // If we are in an arm64e frame, then the PC should have been signed with the sp + { + unw_word_t sp; + __unw_get_reg(cursor, UNW_REG_SP, &sp); + result = (unw_word_t)ptrauth_auth_data((void*)result, ptrauth_key_return_address, sp); + } +#endif + _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIxPTR, (void *)context, result); return (uintptr_t)result; diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S index 67d9e05711898..0e27b848c083d 100644 --- a/libunwind/src/UnwindRegistersRestore.S +++ b/libunwind/src/UnwindRegistersRestore.S @@ -653,7 +653,6 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) ldp x24,x25, [x0, #0x0C0] ldp x26,x27, [x0, #0x0D0] ldp x28,x29, [x0, #0x0E0] - ldr x30, [x0, #0x100] // restore pc into lr ldp d0, d1, [x0, #0x110] ldp d2, d3, [x0, #0x120] @@ -677,12 +676,26 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) // context struct, because it is allocated on the stack, and an exception // could clobber the de-allocated portion of the stack after sp has been // restored. - ldr x16, [x0, #0x0F8] + + ldr x16, [x0, #0x0F8] // load sp into scratch + ldr lr, [x0, #0x100] // restore pc into lr +#if __has_feature(ptrauth_calls) + // The LR is signed with its address inside the register state. Time + // to resign to be a regular ROP signed pointer + add x1, x0, #0x100 + autib lr, x1 + pacib lr, x16 // signed the scratch register for sp +#endif + ldp x0, x1, [x0, #0x000] // restore x0,x1 mov sp,x16 // restore sp - ret x30 // jump to pc +#if __has_feature(ptrauth_calls) + retab +#else + ret +#endif -#elif defined(__arm__) && !defined(__APPLE__) +#elif defined(__arm__) #if !defined(__ARM_ARCH_ISA_ARM) #if (__ARM_ARCH_ISA_THUMB == 2) diff --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S index 5bf6055fe4147..cc5a6e5af4035 100644 --- a/libunwind/src/UnwindRegistersSave.S +++ b/libunwind/src/UnwindRegistersSave.S @@ -726,6 +726,9 @@ LnoR2Fix: // .p2align 2 DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) +#if __has_feature(ptrauth_calls) + pacibsp +#endif stp x0, x1, [x0, #0x000] stp x2, x3, [x0, #0x010] stp x4, x5, [x0, #0x020] @@ -764,9 +767,13 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) str d30, [x0, #0x200] str d31, [x0, #0x208] mov x0, #0 // return UNW_ESUCCESS +#if __has_feature(ptrauth_calls) + retab +#else ret +#endif -#elif defined(__arm__) && !defined(__APPLE__) +#elif defined(__arm__) #if !defined(__ARM_ARCH_ISA_ARM) #if (__ARM_ARCH_ISA_THUMB == 2) diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp index cf39ec5f7dbdf..fb9d73ee6a4fa 100644 --- a/libunwind/src/libunwind.cpp +++ b/libunwind/src/libunwind.cpp @@ -14,6 +14,10 @@ #include "config.h" #include "libunwind_ext.h" +#if __has_feature(ptrauth_calls) +#include +#endif + #include // Define the __has_feature extension for compilers that do not support it so @@ -94,6 +98,62 @@ _LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor, } _LIBUNWIND_WEAK_ALIAS(__unw_init_local, unw_init_local) +#if __has_feature(ptrauth_calls) +extern "C" { +__attribute__((noinline)) unw_word_t __unw_is_pointer_auth_enabled(void) { + static const size_t authenticated = __builtin_ptrauth_string_discriminator("__unw_is_pointer_auth_enabled authenticated"); + static const size_t unauthenticated = __builtin_ptrauth_string_discriminator("__unw_is_pointer_auth_enabled unauthenticated"); + static_assert(authenticated != unauthenticated, "authenticated and unauthenticated constants must differ"); + static_assert(authenticated != 0, "authenticated constant must be non-zero"); + static_assert(unauthenticated != 0, "unauthenticated constant must be non-zero"); + static size_t __ptrauth_restricted_intptr(ptrauth_key_return_address, 1, __builtin_ptrauth_string_discriminator("libunwind __unw_is_pointer_auth_enabled")) mode; + if (!mode) { + // This isn't atomic, but the worst case is we end up doing this twice + int ptrauth_enabled; + size_t ptrauth_enabled_size = sizeof(ptrauth_enabled); + struct AuthenticatedValues { + // These are more or less arbitrary schemas, we're just trying to be reasonably confident + // that we'll get a non-zero signature in at least one case, which this should be more + // than sufficient for. + size_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, 1, "value1") value1; + size_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, 1, "value2") value2; + size_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, 1, "value3") value3; + size_t _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, 0, "value4") value4; + }; + struct UnauthenticatedValues { + size_t value1; + size_t value2; + size_t value3; + size_t value4; + }; + union { + AuthenticatedValues authenticated; + UnauthenticatedValues unauthenticated; + } u; + memset(&u, 0, sizeof(u)); + u.authenticated.value1 = 1; + u.authenticated.value2 = 1; + u.authenticated.value3 = 1; + u.authenticated.value4 = 1; + size_t result = u.unauthenticated.value1; + result |= u.unauthenticated.value2; + result |= u.unauthenticated.value3; + result |= u.unauthenticated.value4; + ptrauth_enabled = result > 1; + if (ptrauth_enabled == 0) + mode = unauthenticated; + else + mode = authenticated; + } + if (mode == authenticated) + return 1; + if (mode == unauthenticated) + return 0; + _LIBUNWIND_ABORT("Invalid authentication state"); +} +} +#endif + /// Get value of specified register at cursor position in stack frame. _LIBUNWIND_HIDDEN int __unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum, unw_word_t *value) { @@ -118,22 +178,86 @@ _LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum, typedef LocalAddressSpace::pint_t pint_t; AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; if (co->validReg(regNum)) { - co->setReg(regNum, (pint_t)value); // special case altering IP to re-find info (being called by personality // function) if (regNum == UNW_REG_IP) { unw_proc_info_t info; // First, get the FDE for the old location and then update it. co->getInfo(&info); - co->setInfoBasedOnIPRegister(false); + +#if __has_feature(ptrauth_calls) + // It is only valid to set the IP within the current function. + // This is important for ptrauth, otherwise the IP cannot be correctly + // signed. + unw_word_t stripped_value = (unw_word_t)ptrauth_strip((void*)value, ptrauth_key_return_address); + assert(stripped_value >= info.start_ip && stripped_value <= info.end_ip); +#endif + + pint_t sp = (pint_t)co->getReg(UNW_REG_SP); + +#if __has_feature(ptrauth_calls) + // If we are in an arm64e frame, then the PC should have been signed with the sp + { + const mach_header_64 *mh = (const mach_header_64 *)info.extra; + // Note, if we don't have mh, we assume this was created by unw_init_local inside + // libunwind.dylib itself, and we know we are arm64e. + pint_t pc = (pint_t)co->getReg(UNW_REG_IP); + if ((mh->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64_E) { + if (ptrauth_auth_and_resign((void*)pc, ptrauth_key_return_address, sp, + ptrauth_key_return_address, sp) != (void*)pc) { + abort_report_np("Bad unwind through arm64e (0x%llX, 0x%llX)->0x%llX\n", + pc, sp, (pint_t)ptrauth_auth_data((void*)pc, ptrauth_key_return_address, sp)); + } + } + } +#endif + + pint_t orgArgSize = (pint_t)info.gp; + uint64_t orgFuncStart = info.start_ip; + // and adjust REG_SP if there was a DW_CFA_GNU_args_size // If the original call expects stack adjustment, perform this now. // Normal frame unwinding would have included the offset already in the // CFA computation. // Note: for PA-RISC and other platforms where the stack grows up, // this should actually be - info.gp. LLVM doesn't currently support // any such platforms and Clang doesn't export a macro for them. - if (info.gp) - co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + info.gp); + if ((orgFuncStart == info.start_ip) && (orgArgSize != 0)) { + co->setReg(UNW_REG_SP, sp + orgArgSize); +#if __has_feature(ptrauth_calls) + if (!__unw_is_pointer_auth_enabled()) { + value = (pint_t)ptrauth_sign_unauthenticated((void*)stripped_value, ptrauth_key_return_address, sp + orgArgSize); + // We re-test the authentication state after authentication so an attacker that + // does manage to get to this oracle they have to have already completely bypassed + // ptrauth guards rather than having transitory control + __asm__ ("":::"memory"); + if (__unw_is_pointer_auth_enabled()) { + value = (unw_word_t)-1; + // Force the optimizer to commit the update to value + __asm__ volatile ("":"+rm"(value),"+rm"(value)::"memory"); + _LIBUNWIND_ABORT("Inconsistent invalid authentication state"); + } + } +#endif + co->setReg(UNW_REG_IP, value); + } else { +#if __has_feature(ptrauth_calls) + if (!__unw_is_pointer_auth_enabled()) { + // Adding !__unw_is_pointer_auth_enabled() to the extra discriminator as above + value = (pint_t)ptrauth_sign_unauthenticated((void*)stripped_value, ptrauth_key_return_address, sp); + // As above + __asm__ ("":::"memory"); + if (__unw_is_pointer_auth_enabled()) { + value = (unw_word_t)-1; + __asm__ volatile ("":"+rm"(value),"+rm"(value)::"memory"); + _LIBUNWIND_ABORT("Inconsistent invalid authentication state"); + } + } +#endif + co->setReg(UNW_REG_IP, value); + } + co->setInfoBasedOnIPRegister(false); + } else { + co->setReg(regNum, (pint_t)value); } return UNW_ESUCCESS; } diff --git a/libunwind/src/libunwind_ext.h b/libunwind/src/libunwind_ext.h index 28db43a4f6eef..486d7c5e96e9d 100644 --- a/libunwind/src/libunwind_ext.h +++ b/libunwind/src/libunwind_ext.h @@ -121,6 +121,11 @@ extern int __unw_add_find_dynamic_unwind_sections( extern int __unw_remove_find_dynamic_unwind_sections( unw_find_dynamic_unwind_sections find_dynamic_unwind_sections); + +#if __has_feature(ptrauth_qualifier) +extern unw_word_t __unw_is_pointer_auth_enabled(void); +#endif + #endif #if defined(_LIBUNWIND_ARM_EHABI) @@ -134,4 +139,11 @@ extern _Unwind_Reason_Code _Unwind_VRS_Interpret(_Unwind_Context *context, } #endif +enum ProcInfoFlags { + ProcInfoFlags_NoFlags = 0, +#if __has_feature(ptrauth_calls) + ProcInfoFlags_IsARM64Image = 1 +#endif +}; + #endif // __LIBUNWIND_EXT__ From f5c9969af167a9dba58c27443586162a6cbd8ccf Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 27 Sep 2021 08:00:00 -0700 Subject: [PATCH 40/58] [Coroutines] Support ptrauth. --- llvm/include/llvm/IR/Constants.h | 10 ++ llvm/lib/IR/Constants.cpp | 12 +++ llvm/lib/Transforms/Coroutines/CoroInstr.h | 12 ++- llvm/lib/Transforms/Coroutines/CoroInternal.h | 17 +++- llvm/lib/Transforms/Coroutines/CoroSplit.cpp | 74 +++++++++++++- llvm/lib/Transforms/Coroutines/Coroutines.cpp | 15 +++ .../Coroutines/coro-retcon-ptrauth.ll | 99 +++++++++++++++++++ 7 files changed, 233 insertions(+), 6 deletions(-) create mode 100644 llvm/test/Transforms/Coroutines/coro-retcon-ptrauth.ll diff --git a/llvm/include/llvm/IR/Constants.h b/llvm/include/llvm/IR/Constants.h index 4d13c3880c692..fe05a97e73be6 100644 --- a/llvm/include/llvm/IR/Constants.h +++ b/llvm/include/llvm/IR/Constants.h @@ -1056,6 +1056,16 @@ class ConstantPtrAuth final : public Constant { return !getAddrDiscriminator()->isNullValue(); } + /// A constant value for the address discriminator which has special + /// significance to coroutine lowering. + enum { AddrDiscriminator_UseCoroStorage = 1 }; + + /// Whether the address uses a special address discriminator. + /// These discriminators can't be used in real pointer-auth values; they + /// can only be used in "prototype" values that indicate how some real + /// schema is supposed to be produced. + bool hasSpecialAddressDiscriminator(uint64_t value) const; + /// Check whether an authentication operation with key \p Key and (possibly /// blended) discriminator \p Discriminator is known to be compatible with /// this ptrauth signed pointer. diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp index bc91f904d7e87..41349168e450e 100644 --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -2060,6 +2060,18 @@ Value *ConstantPtrAuth::handleOperandChangeImpl(Value *From, Value *ToV) { Values, this, From, To, NumUpdated, OperandNo); } +bool ConstantPtrAuth::hasSpecialAddressDiscriminator(uint64_t value) const { + auto *CastV = dyn_cast(getAddrDiscriminator()); + if (!CastV || CastV->getOpcode() != Instruction::IntToPtr) + return false; + + auto IntVal = dyn_cast(CastV->getOperand(0)); + if (!IntVal) + return false; + + return IntVal->getValue() == value; +} + bool ConstantPtrAuth::isKnownCompatibleWith(const Value *Key, const Value *Discriminator, const DataLayout &DL) const { diff --git a/llvm/lib/Transforms/Coroutines/CoroInstr.h b/llvm/lib/Transforms/Coroutines/CoroInstr.h index a31703fe01304..c80621443cfdc 100644 --- a/llvm/lib/Transforms/Coroutines/CoroInstr.h +++ b/llvm/lib/Transforms/Coroutines/CoroInstr.h @@ -25,6 +25,7 @@ #ifndef LLVM_LIB_TRANSFORMS_COROUTINES_COROINSTR_H #define LLVM_LIB_TRANSFORMS_COROUTINES_COROINSTR_H +#include "llvm/IR/Constants.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/Support/raw_ostream.h" @@ -254,7 +255,16 @@ class LLVM_LIBRARY_VISIBILITY AnyCoroIdRetconInst : public AnyCoroIdInst { /// attributes, and calling convention of the continuation function(s) /// are taken from this declaration. Function *getPrototype() const { - return cast(getArgOperand(PrototypeArg)->stripPointerCasts()); + Value *rawPrototype = getArgOperand(PrototypeArg)->stripPointerCasts(); + if (auto CPA = dyn_cast(rawPrototype)) { + rawPrototype = + const_cast(CPA->getPointer()->stripPointerCasts()); + } + return cast(rawPrototype); + } + + ConstantPtrAuth *getPtrAuthInfo() const { + return dyn_cast(getArgOperand(PrototypeArg)); } /// Return the function to use for allocating memory. diff --git a/llvm/lib/Transforms/Coroutines/CoroInternal.h b/llvm/lib/Transforms/Coroutines/CoroInternal.h index 5716fd0ea4ab9..eabaf78748cc1 100644 --- a/llvm/lib/Transforms/Coroutines/CoroInternal.h +++ b/llvm/lib/Transforms/Coroutines/CoroInternal.h @@ -125,6 +125,7 @@ struct LLVM_LIBRARY_VISIBILITY Shape { }; struct RetconLoweringStorage { + Constant *ResumePtrAuthInfo; Function *ResumePrototype; Function *Alloc; Function *Dealloc; @@ -181,10 +182,22 @@ struct LLVM_LIBRARY_VISIBILITY Shape { return ConstantInt::get(getIndexType(), Value); } + ConstantPtrAuth *getResumePtrAuthInfo() const { + switch (ABI) { + case coro::ABI::Switch: + case coro::ABI::Async: + return nullptr; + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: + if (!RetconLowering.ResumePtrAuthInfo) return nullptr; + return dyn_cast(RetconLowering.ResumePtrAuthInfo); + } + } + PointerType *getSwitchResumePointerType() const { assert(ABI == coro::ABI::Switch); - assert(FrameTy && "frame type not assigned"); - return cast(FrameTy->getElementType(SwitchFieldIndex::Resume)); + assert(FrameTy && "frame type not assigned"); + return cast(FrameTy->getElementType(SwitchFieldIndex::Resume)); } FunctionType *getResumeFunctionType() const { diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp index 299b514d34f1c..73fbf52902149 100644 --- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp @@ -937,6 +937,19 @@ void CoroCloner::create() { VMap[&A] = DummyArgs.back(); } + switch (Shape.ABI) { + case coro::ABI::Retcon: + case coro::ABI::RetconOnce: + // In retcon coroutines, replace any remaining uses of the original + // storage value with the storage parameter. + VMap[Shape.getRetconCoroId()->getStorage()] = &*NewF->arg_begin(); + break; + + case coro::ABI::Switch: + case coro::ABI::Async: + break; + } + SmallVector Returns; // Ignore attempts to change certain attributes of the function. @@ -1109,8 +1122,10 @@ void CoroCloner::create() { // Remap frame pointer. Value *OldFramePtr = VMap[Shape.FramePtr]; - NewFramePtr->takeName(OldFramePtr); - OldFramePtr->replaceAllUsesWith(NewFramePtr); + if (OldFramePtr != NewFramePtr) { + NewFramePtr->takeName(OldFramePtr); + OldFramePtr->replaceAllUsesWith(NewFramePtr); + } // Remap vFrame pointer. auto *NewVFrame = Builder.CreateBitCast( @@ -1769,6 +1784,47 @@ static void splitAsyncCoroutine(Function &F, coro::Shape &Shape, } } +/// Produced a signed pointer value with this same schema. If the address +/// is provided, and this schema uses address discrimination, use that +/// address instead of the stored address discriminator. +static Value *emitSignedPointer(IRBuilder<> &Builder, + ConstantPtrAuth *Schema, + Constant *ConstPointer, + Value *Address) { + auto Module = Builder.GetInsertBlock()->getModule(); + if (!Schema->hasAddressDiscriminator()) { + return Schema->getWithSameSchema(ConstPointer); + } + + auto IntPtrTy = Schema->getDiscriminator()->getType(); + + // Blend the provided address with the extra discriminator if the extra + // discriminator is non-zero. + Value *Discriminator = Builder.CreatePtrToInt(Address, IntPtrTy); + auto ExtraDiscriminator = + const_cast(Schema->getDiscriminator()); + if (!ExtraDiscriminator->isNullValue()) { + auto Blend = Intrinsic::getDeclaration(Module, Intrinsic::ptrauth_blend); + Discriminator = + Builder.CreateCall(Blend, {Discriminator, ExtraDiscriminator}); + } + + // Sign the pointer with the discriminator. + + // The intrinsic wants an integer. + Value *Pointer = Builder.CreatePtrToInt(ConstPointer, IntPtrTy); + + // call i64 @llvm.ptrauth.sign.i64(i64 %pointer, i32 %key, i64 %discriminator) + auto Sign = Intrinsic::getDeclaration(Module, Intrinsic::ptrauth_sign); + auto Key = const_cast(Schema->getKey()); + Pointer = Builder.CreateCall(Sign, { Pointer, Key, Discriminator }); + + // Convert back to the original pointer type. + Pointer = Builder.CreateIntToPtr(Pointer, ConstPointer->getType()); + + return Pointer; +} + static void splitRetconCoroutine(Function &F, coro::Shape &Shape, SmallVectorImpl &Clones, TargetTransformInfo &TTI) { @@ -1880,9 +1936,21 @@ static void splitRetconCoroutine(Function &F, coro::Shape &Shape, Builder.CreateRet(RetV); } + // Sign the continuation value if requested. + Value *ContinuationValue = Continuation; + if (auto PtrAuthInfo = Shape.getResumePtrAuthInfo()) { + assert(!PtrAuthInfo->hasAddressDiscriminator() || + PtrAuthInfo->hasSpecialAddressDiscriminator( + ConstantPtrAuth::AddrDiscriminator_UseCoroStorage)); + IRBuilder<> Builder(Branch); + ContinuationValue = + emitSignedPointer(Builder, PtrAuthInfo, Continuation, + Id->getStorage()); + } + // Branch to the return block. Branch->setSuccessor(0, ReturnBB); - ReturnPHIs[0]->addIncoming(Continuation, SuspendBB); + ReturnPHIs[0]->addIncoming(ContinuationValue, SuspendBB); size_t NextPHIIndex = 1; for (auto &VUse : Suspend->value_operands()) ReturnPHIs[NextPHIIndex++]->addIncoming(&*VUse, SuspendBB); diff --git a/llvm/lib/Transforms/Coroutines/Coroutines.cpp b/llvm/lib/Transforms/Coroutines/Coroutines.cpp index 1a92bc1636257..02954065fa66d 100644 --- a/llvm/lib/Transforms/Coroutines/Coroutines.cpp +++ b/llvm/lib/Transforms/Coroutines/Coroutines.cpp @@ -339,11 +339,23 @@ void coro::Shape::buildFrom(Function &F) { : coro::ABI::RetconOnce); auto Prototype = ContinuationId->getPrototype(); this->RetconLowering.ResumePrototype = Prototype; + auto AuthInfo = ContinuationId->getPtrAuthInfo(); + this->RetconLowering.ResumePtrAuthInfo = AuthInfo; this->RetconLowering.Alloc = ContinuationId->getAllocFunction(); this->RetconLowering.Dealloc = ContinuationId->getDeallocFunction(); this->RetconLowering.ReturnBlock = nullptr; this->RetconLowering.IsFrameInlineInStorage = false; + if (AuthInfo && AuthInfo->hasAddressDiscriminator() && + !AuthInfo->hasSpecialAddressDiscriminator( + ConstantPtrAuth::AddrDiscriminator_UseCoroStorage)) { +#ifndef NDEBUG + AuthInfo->dump(); +#endif + report_fatal_error("ptrauth-signed prototype must not have address " + "diversity"); + } + // Determine the result value types, and make sure they match up with // the values passed to the suspends. auto ResultTys = getRetconResultTypes(); @@ -514,6 +526,9 @@ void coro::Shape::emitDealloc(IRBuilder<> &Builder, Value *Ptr, /// Check that the given value is a well-formed prototype for the /// llvm.coro.id.retcon.* intrinsics. static void checkWFRetconPrototype(const AnyCoroIdRetconInst *I, Value *V) { + if (auto PtrAuth = dyn_cast(V)) { + V = const_cast(PtrAuth->getPointer()); + } auto F = dyn_cast(V->stripPointerCasts()); if (!F) fail(I, "llvm.coro.id.retcon.* prototype not a Function", V); diff --git a/llvm/test/Transforms/Coroutines/coro-retcon-ptrauth.ll b/llvm/test/Transforms/Coroutines/coro-retcon-ptrauth.ll new file mode 100644 index 0000000000000..1ba83f4ae38b0 --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-retcon-ptrauth.ll @@ -0,0 +1,99 @@ +; RUN: opt -passes='module(coro-early),cgscc(coro-split),coro-cleanup' -S %s | FileCheck %s +target datalayout = "E-p:64:64" + +%swift.type = type { i64 } +%swift.opaque = type opaque +%T4red215EmptyCollectionV = type opaque +%TSi = type <{ i64 }> + +define ptr @f(ptr %buffer, i32 %n) { +entry: + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, ptr %buffer, ptr ptrauth (ptr @prototype, i32 2, i64 12867), ptr @allocate, ptr @deallocate) + %hdl = call ptr @llvm.coro.begin(token %id, ptr null) + br label %loop + +loop: + %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] + call void @print(i32 %n.val) + %unwind0 = call i1 (...) @llvm.coro.suspend.retcon.i1() + br i1 %unwind0, label %cleanup, label %resume + +resume: + %inc = add i32 %n.val, 1 + br label %loop + +cleanup: + call i1 @llvm.coro.end(ptr %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define ptr @f(ptr %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[FRAME_T:%.*]], ptr %buffer, i32 0, i32 0 +; CHECK-NEXT: store i32 %n, ptr [[T1]] +; CHECK-NEXT: call void @print(i32 %n) +; CHECK-NEXT: ret ptr ptrauth (ptr [[RESUME:@.*]], i32 2, i64 12867) + +; CHECK: define internal ptr [[RESUME]](ptr noalias noundef nonnull align 4 dereferenceable(8) %0, i1 zeroext %1) { +; CHECK: ptrauth (ptr [[RESUME]], i32 2, i64 12867) + +define ptr @g(ptr %buffer, i32 %n) { +entry: + + %id = call token @llvm.coro.id.retcon(i32 8, i32 4, ptr %buffer, ptr ptrauth (ptr @prototype, i32 2, i64 8723, ptr inttoptr(i64 1 to ptr)), ptr @allocate, ptr @deallocate) + %hdl = call ptr @llvm.coro.begin(token %id, ptr null) + br label %loop + +loop: + %n.val = phi i32 [ %n, %entry ], [ %inc, %resume ] + call void @print(i32 %n.val) + %unwind0 = call i1 (...) @llvm.coro.suspend.retcon.i1() + br i1 %unwind0, label %cleanup, label %resume + +resume: + %inc = add i32 %n.val, 1 + br label %loop + +cleanup: + call i1 @llvm.coro.end(ptr %hdl, i1 0) + unreachable +} + +; CHECK-LABEL: define ptr @g(ptr %buffer, i32 %n) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[FRAME_T:%.*]], ptr %buffer, i32 0, i32 0 +; CHECK-NEXT: store i32 %n, ptr [[T1]] +; CHECK-NEXT: call void @print(i32 %n) +; CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr %buffer to i64 +; CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 8723) +; CHECK-NEXT: [[T2:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr [[RESUME:@.*]] to i64), i32 2, i64 [[T1]]) +; CHECK-NEXT: [[T3:%.*]] = inttoptr i64 [[T2]] to ptr +; CHECK-NEXT: ret ptr [[T3]] + +; CHECK: define internal ptr [[RESUME]](ptr noalias noundef nonnull align 4 dereferenceable(8) %0, i1 zeroext %1) { + +; CHECK: common.ret: +; CHECK: [[RETVAL:%.*]] = phi ptr [ [[T4:%.*]], %resume ], [ null, +; CHECK: ret ptr [[RETVAL]] + +; CHECK: call void @print(i32 %inc) +; CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr %0 to i64 +; CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 8723) +; CHECK-NEXT: [[T2:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr [[RESUME]] to i64), i32 2, i64 [[T1]]) +; CHECK-NEXT: [[T3:%.*]] = inttoptr i64 [[T2]] to ptr + +declare noalias ptr @malloc(i64) #5 +declare void @free(ptr nocapture) #5 + +declare token @llvm.coro.id.retcon(i32, i32, ptr, ptr, ptr, ptr) +declare ptr @llvm.coro.begin(token, ptr) +declare i1 @llvm.coro.suspend.retcon.i1(...) +declare i1 @llvm.coro.end(ptr, i1) +declare ptr @llvm.coro.prepare.retcon(ptr) + +declare ptr @prototype(ptr, i1 zeroext) + +declare noalias ptr @allocate(i32 %size) +declare void @deallocate(ptr %ptr) + +declare void @print(i32) From c8f83cba47f19c24cfd8065b6a9a333492ae53bc Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 3 Jun 2024 16:21:32 -0700 Subject: [PATCH 41/58] [clang] Peephole ptrauth sign/auth/resign builtins of function pointers. --- clang/lib/CodeGen/CGExpr.cpp | 125 ++++++++++-------- clang/lib/CodeGen/CodeGenModule.h | 4 + .../ptrauth-function-builtin-peepholes.c | 46 +++++++ clang/test/CodeGen/ptrauth-wrapper-globals.c | 31 ----- 4 files changed, 117 insertions(+), 89 deletions(-) create mode 100644 clang/test/CodeGen/ptrauth-function-builtin-peepholes.c diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index b915632a1ded2..ade7ab376fefd 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5588,40 +5588,48 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) { } static unsigned getPointerAuthKeyValue(const ASTContext &Context, - const Expr *key) { - Expr::EvalResult result; - bool success = key->EvaluateAsInt(result, Context); - assert(success && "pointer auth key wasn't a constant?"); (void) success; - return result.Val.getInt().getZExtValue(); -} - -static bool isFunctionPointerAuth(CodeGenModule &CGM, const Expr *key, - const Expr *discriminator) { + const Expr *Key) { + Expr::EvalResult Result; + bool Success = Key->EvaluateAsInt(Result, Context); + assert(Success && "pointer auth key wasn't a constant?"); + (void)Success; + return Result.Val.getInt().getZExtValue(); +} + +/// Whether a function pointer of type FPT is authenticated using key and +/// discriminator in the normal ABI rules. +bool CodeGenModule::isFunctionPointerAuthenticated(QualType FPT, + const Expr *Key, + const Expr *Discriminator) { // Verify that the ABI uses function-pointer signing at all. - auto &authSchema = CGM.getCodeGenOpts().PointerAuth.FunctionPointers; - if (!authSchema.isEnabled()) + auto &Schema = getCodeGenOpts().PointerAuth.FunctionPointers; + if (!Schema.isEnabled()) return false; // Verify that the key matches the ABI's key. - if (authSchema.getKey() != getPointerAuthKeyValue(CGM.getContext(), key)) + if (Schema.getKey() != getPointerAuthKeyValue(getContext(), Key)) return false; - // If the ABI uses weird discrimination for function pointers, just give up. - assert(!authSchema.isAddressDiscriminated()); - if (authSchema.getOtherDiscrimination() - != PointerAuthSchema::Discrimination::None) { - return false; - } + assert(!Schema.isAddressDiscriminated()); - if (discriminator->getType()->isPointerType()) { - return discriminator->isNullPointerConstant(CGM.getContext(), - Expr::NPC_NeverValueDependent); - } else { - assert(discriminator->getType()->isIntegerType()); - Expr::EvalResult result; - return (discriminator->EvaluateAsInt(result, CGM.getContext()) && - result.Val.getInt() == 0); + uint16_t DiscVal = 0; + if (Schema.hasOtherDiscrimination()) { + if (FPT->isFunctionPointerType() || FPT->isFunctionReferenceType()) + FPT = FPT->getPointeeType(); + if (FPT->isFunctionType()) + DiscVal = getContext().getPointerAuthTypeDiscriminator(FPT); } + + if (Discriminator->getType()->isPointerType()) + return DiscVal == 0 && Discriminator->isNullPointerConstant( + getContext(), Expr::NPC_NeverValueDependent); + + if (!Discriminator->getType()->isIntegerType()) + return false; + + Expr::EvalResult Result; + return Discriminator->EvaluateAsInt(Result, getContext()) && + Result.Val.getInt() == DiscVal; } /// Given an expression for a function pointer that's been signed with @@ -5629,25 +5637,25 @@ static bool isFunctionPointerAuth(CodeGenModule &CGM, const Expr *key, /// and an expression for the discriminator, produce a callee for the /// function pointer using that scheme. static CGCallee EmitSignedFunctionPointerCallee(CodeGenFunction &CGF, - const Expr *functionPointerExpr, - const Expr *keyExpr, - const Expr *discriminatorExpr) { - llvm::Value *calleePtr = CGF.EmitScalarExpr(functionPointerExpr); - auto key = getPointerAuthKeyValue(CGF.getContext(), keyExpr); - auto discriminator = CGF.EmitScalarExpr(discriminatorExpr); - - if (discriminator->getType()->isPointerTy()) - discriminator = CGF.Builder.CreatePtrToInt(discriminator, CGF.IntPtrTy); - - auto functionType = - functionPointerExpr->getType()->castAs()->getPointeeType(); - CGCalleeInfo calleeInfo(functionType->getAs()); - CGPointerAuthInfo pointerAuth(key, PointerAuthenticationMode::SignAndAuth, - /* isaIsaPointer */ false, - /* authenticatesNullValues */ false, - discriminator); - CGCallee callee(calleeInfo, calleePtr, pointerAuth); - return callee; + const Expr *FunctionPointerExpr, + const Expr *KeyExpr, + const Expr *DiscriminatorExpr) { + llvm::Value *CalleePtr = CGF.EmitScalarExpr(FunctionPointerExpr); + unsigned Key = getPointerAuthKeyValue(CGF.getContext(), KeyExpr); + llvm::Value *Discriminator = CGF.EmitScalarExpr(DiscriminatorExpr); + + if (Discriminator->getType()->isPointerTy()) + Discriminator = CGF.Builder.CreatePtrToInt(Discriminator, CGF.IntPtrTy); + + QualType FTy = + FunctionPointerExpr->getType()->castAs()->getPointeeType(); + CGCalleeInfo CalleeInfo(FTy->getAs()); + CGPointerAuthInfo PointerAuth(Key, PointerAuthenticationMode::SignAndAuth, + /* IsIsaPointer */ false, + /* AuthenticatesNullValues */ false, + Discriminator); + CGCallee Callee(CalleeInfo, CalleePtr, PointerAuth); + return Callee; } CGCallee CodeGenFunction::EmitCallee(const Expr *E) { @@ -5700,34 +5708,35 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) { } else if (auto PDE = dyn_cast(E)) { return CGCallee::forPseudoDestructor(PDE); - // Peephole specific builtin calls. + // Peephole specific builtin calls. } else if (auto CE = dyn_cast(E)) { - if (unsigned builtin = CE->getBuiltinCallee()) { + if (unsigned Builtin = CE->getBuiltinCallee()) { // If the callee is a __builtin_ptrauth_sign_unauthenticated to the // ABI function-pointer signing schema, perform an unauthenticated call. - if (builtin == Builtin::BI__builtin_ptrauth_sign_unauthenticated && - isFunctionPointerAuth(CGM, CE->getArg(1), CE->getArg(2))) { - CGCallee callee = EmitCallee(CE->getArg(0)); - if (callee.isOrdinary()) - callee.setPointerAuthInfo(CGPointerAuthInfo()); - return callee; + if (Builtin == Builtin::BI__builtin_ptrauth_sign_unauthenticated && + CGM.isFunctionPointerAuthenticated(CE->getArg(0)->getType(), + CE->getArg(1), CE->getArg(2))) { + CGCallee Callee = EmitCallee(CE->getArg(0)); + if (Callee.isOrdinary()) + Callee.setPointerAuthInfo(CGPointerAuthInfo()); + return Callee; } // If the callee is a __builtin_ptrauth_auth_and_resign to the // ABI function-pointer signing schema, avoid the intermediate resign. - if (builtin == Builtin::BI__builtin_ptrauth_auth_and_resign && - isFunctionPointerAuth(CGM, CE->getArg(3), CE->getArg(4))) { + if (Builtin == Builtin::BI__builtin_ptrauth_auth_and_resign && + CGM.isFunctionPointerAuthenticated(CE->getArg(0)->getType(), + CE->getArg(3), CE->getArg(4))) return EmitSignedFunctionPointerCallee(*this, CE->getArg(0), CE->getArg(1), CE->getArg(2)); // If the callee is a __builtin_ptrauth_auth when ABI function pointer // signing is disabled, we need to promise to use the unattackable // OperandBundle code pattern. - } else if (builtin == Builtin::BI__builtin_ptrauth_auth && - !CGM.getCodeGenOpts().PointerAuth.FunctionPointers.isEnabled()) { + if (Builtin == Builtin::BI__builtin_ptrauth_auth && + !CGM.getCodeGenOpts().PointerAuth.FunctionPointers.isEnabled()) return EmitSignedFunctionPointerCallee(*this, CE->getArg(0), CE->getArg(1), CE->getArg(2)); - } } } diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 9d56c5d32f018..51979b8cb69fa 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1010,6 +1010,10 @@ class CodeGenModule : public CodeGenTypeCache { std::optional getVTablePointerAuthentication(const CXXRecordDecl *thisClass); + bool isFunctionPointerAuthenticated(QualType FunctionPointerTy, + const Expr *Key, + const Expr *Discriminator); + CGPointerAuthInfo EmitPointerAuthInfo(const RecordDecl *RD); // Return whether RTTI information should be emitted for this target. diff --git a/clang/test/CodeGen/ptrauth-function-builtin-peepholes.c b/clang/test/CodeGen/ptrauth-function-builtin-peepholes.c new file mode 100644 index 0000000000000..372f9230691a3 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-function-builtin-peepholes.c @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,NOFPDIV +// RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,FPDIV + +#include + +#define FNPTRKEY 0 +#define FNPTRDISC ptrauth_function_pointer_type_discriminator(void(*)(void)) + +void (*fnptr)(void); +long discriminator; + +// NOFPDIV: @fnptr_discriminator = global i64 [[FPDISC:0]], align 8 +// FPDIV: @fnptr_discriminator = global i64 [[FPDISC:18983]], align 8 +long fnptr_discriminator = FNPTRDISC; + + +// CHECK-LABEL: define void @test_sign_unauthenticated_peephole() +void test_sign_unauthenticated_peephole() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: call void [[T0]](){{$}} + // CHECK-NEXT: ret void + __builtin_ptrauth_sign_unauthenticated(fnptr, FNPTRKEY, FNPTRDISC)(); +} + +// This peephole doesn't kick in because it's incorrect when ABI pointer +// authentication is enabled. +// CHECK-LABEL: define void @test_auth_peephole() +void test_auth_peephole() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: [[T1:%.*]] = load i64, ptr @discriminator, + // CHECK-NEXT: [[T2:%.*]] = ptrtoint ptr [[T0]] to i64 + // CHECK-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T2]], i32 0, i64 [[T1]]) + // CHECK-NEXT: [[T4:%.*]] = inttoptr i64 [[T3]] to ptr + // CHECK-NEXT: call void [[T4]]() [ "ptrauth"(i32 0, i64 [[FPDISC]]) ] + // CHECK-NEXT: ret void + __builtin_ptrauth_auth(fnptr, 0, discriminator)(); +} + +// CHECK-LABEL: define void @test_auth_and_resign_peephole() +void test_auth_and_resign_peephole() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: [[T1:%.*]] = load i64, ptr @discriminator, + // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 2, i64 [[T1]]) ] + // CHECK-NEXT: ret void + __builtin_ptrauth_auth_and_resign(fnptr, 2, discriminator, FNPTRKEY, FNPTRDISC)(); +} diff --git a/clang/test/CodeGen/ptrauth-wrapper-globals.c b/clang/test/CodeGen/ptrauth-wrapper-globals.c index a68617a19a323..5370c867e1fdc 100644 --- a/clang/test/CodeGen/ptrauth-wrapper-globals.c +++ b/clang/test/CodeGen/ptrauth-wrapper-globals.c @@ -42,37 +42,6 @@ void test_direct_builtin_call() { abort(); } -// CHECK-LABEL: define void @test_sign_unauthenticated_peephole() -void test_sign_unauthenticated_peephole() { - // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, - // CHECK-NEXT: call void [[T0]](){{$}} - // CHECK-NEXT: ret void - __builtin_ptrauth_sign_unauthenticated(fnptr, FNPTRKEY, 0)(); -} - -// This peephole doesn't kick in because it's incorrect when ABI pointer -// authentication is enabled. -// CHECK-LABEL: define void @test_auth_peephole() -void test_auth_peephole() { - // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, - // CHECK-NEXT: [[T1:%.*]] = load i64, ptr @discriminator, - // CHECK-NEXT: [[T2:%.*]] = ptrtoint ptr [[T0]] to i64 - // CHECK-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T2]], i32 0, i64 [[T1]]) - // CHECK-NEXT: [[T4:%.*]] = inttoptr i64 [[T3]] to ptr - // CHECK-NEXT: call void [[T4]]() [ "ptrauth"(i32 0, i64 0) ] - // CHECK-NEXT: ret void - __builtin_ptrauth_auth(fnptr, 0, discriminator)(); -} - -// CHECK-LABEL: define void @test_auth_and_resign_peephole() -void test_auth_and_resign_peephole() { - // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, - // CHECK-NEXT: [[T1:%.*]] = load i64, ptr @discriminator, - // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 2, i64 [[T1]]) ] - // CHECK-NEXT: ret void - __builtin_ptrauth_auth_and_resign(fnptr, 2, discriminator, FNPTRKEY, 0)(); -} - // CHECK-LABEL: define ptr @test_function_pointer() // CHECK: [[EXTERNAL_FUNCTION]] void (*test_function_pointer())(void) { From f2776c808055d25798149f9299c2066a542f6188 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Fri, 24 May 2024 13:18:16 -0700 Subject: [PATCH 42/58] [clang] Strip function pointers in UBSan type checks. --- clang/lib/CodeGen/CGExpr.cpp | 9 +++++++++ clang/test/CodeGen/ubsan-function.cpp | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index ade7ab376fefd..7cf8bdd94bab6 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -6018,6 +6018,15 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee CGM.getLLVMContext(), {PrefixSigType, Int32Ty}, /*isPacked=*/true); llvm::Value *CalleePtr = Callee.getFunctionPointer(); + if (CGM.getCodeGenOpts().PointerAuth.FunctionPointers) { + // Use raw pointer since we are using the callee pointer as data here. + Address Addr = + Address(CalleePtr, CalleePtr->getType(), + CharUnits::fromQuantity( + CalleePtr->getPointerAlignment(CGM.getDataLayout())), + Callee.getPointerAuthInfo(), nullptr); + CalleePtr = Addr.emitRawPointer(*this); + } // On 32-bit Arm, the low bit of a function pointer indicates whether // it's using the Arm or Thumb instruction set. The actual first diff --git a/clang/test/CodeGen/ubsan-function.cpp b/clang/test/CodeGen/ubsan-function.cpp index ccaefc7b4d201..ee00390a5386b 100644 --- a/clang/test/CodeGen/ubsan-function.cpp +++ b/clang/test/CodeGen/ubsan-function.cpp @@ -6,6 +6,8 @@ // RUN: %clang_cc1 -triple apple-macosx-x86_64 -emit-llvm -o - %s -fsanitize=function -fno-sanitize-recover=all | FileCheck %s --check-prefixes=CHECK,64 +// RUN: %clang_cc1 -triple arm64e-apple-ios -emit-llvm -o - %s -fsanitize=function -fno-sanitize-recover=all -fptrauth-calls | FileCheck %s --check-prefixes=CHECK,GNU,64,64e + // GNU: define{{.*}} void @_Z3funv() #0 !func_sanitize ![[FUNCSAN:.*]] { // MSVC: define{{.*}} void @"?fun@@YAXXZ"() #0 !func_sanitize ![[FUNCSAN:.*]] { void fun() {} @@ -15,6 +17,8 @@ void fun() {} // ARM: ptrtoint ptr {{.*}} to i32, !nosanitize !5 // ARM: and i32 {{.*}}, -2, !nosanitize !5 // ARM: inttoptr i32 {{.*}} to ptr, !nosanitize !5 +// 64e: %[[STRIPPED:.*]] = ptrtoint ptr {{.*}} to i64, !nosanitize +// 64e: call i64 @llvm.ptrauth.auth(i64 %[[STRIPPED]], i32 0, i64 0), !nosanitize // CHECK: getelementptr <{ i32, i32 }>, ptr {{.*}}, i32 -1, i32 0, !nosanitize // CHECK: load i32, ptr {{.*}}, align {{.*}}, !nosanitize // CHECK: icmp eq i32 {{.*}}, -1056584962, !nosanitize From d99449639cc5d27ffd1bbfa5e59edfd2f9bad021 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Fri, 24 May 2024 12:15:57 -0700 Subject: [PATCH 43/58] [clang][CodeGen] Sign objc atomic property copy helper fn ptrs. --- ...operty-object-reference-wrapper-globals.mm | 68 +++++++++++++++++++ .../ptrauth-property-object-reference.mm | 10 +-- 2 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 clang/test/CodeGenObjCXX/ptrauth-property-object-reference-wrapper-globals.mm diff --git a/clang/test/CodeGenObjCXX/ptrauth-property-object-reference-wrapper-globals.mm b/clang/test/CodeGenObjCXX/ptrauth-property-object-reference-wrapper-globals.mm new file mode 100644 index 0000000000000..b9ff4975ca1a6 --- /dev/null +++ b/clang/test/CodeGenObjCXX/ptrauth-property-object-reference-wrapper-globals.mm @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 %s -triple arm64-apple-ios11.0 -fobjc-runtime=ios-11.0 -fptrauth-calls -emit-llvm -o - | FileCheck %s + +extern int DEFAULT(); + +struct TCPPObject +{ + TCPPObject(); + ~TCPPObject(); + TCPPObject(const TCPPObject& inObj, int i = DEFAULT()); + TCPPObject& operator=(const TCPPObject& inObj); + int filler[64]; +}; + + +@interface MyDocument +{ +@private + TCPPObject _cppObject; + TCPPObject _cppObject1; +} +@property (assign, readwrite, atomic) const TCPPObject MyProperty; +@property (assign, readwrite, atomic) const TCPPObject MyProperty1; +@end + +@implementation MyDocument + @synthesize MyProperty = _cppObject; + @synthesize MyProperty1 = _cppObject1; +@end + +// CHECK-LABEL: @__copy_helper_atomic_property_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__copy_helper_atomic_property_, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 + +// CHECK-LABEL: @__assign_helper_atomic_property_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__assign_helper_atomic_property_, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 + +// CHECK-LABEL: define internal void @__copy_helper_atomic_property_(ptr noundef %0, ptr noundef %1) # +// CHECK: [[TWO:%.*]] = load ptr, ptr [[ADDR:%.*]], align 8 +// CHECK: [[THREE:%.*]] = load ptr, ptr [[ADDR1:%.*]], align 8 +// CHECK: [[CALL:%.*]] = call noundef i32 @_Z7DEFAULTv() +// CHECK: call noundef ptr @_ZN10TCPPObjectC1ERKS_i(ptr noundef nonnull align {{[0-9]+}} dereferenceable(256) [[TWO]], ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[THREE]], i32 noundef [[CALL]]) +// CHECK: ret void + +// CHECK: define internal void @"\01-[MyDocument MyProperty]"(ptr dead_on_unwind noalias writable sret(%{{.*}} align 4 %[[AGG_RESULT:.*]], ptr noundef %[[SELF:.*]], +// CHECK: %[[RESULT_PTR:.*]] = alloca ptr, align 8 +// CHECK: %[[SELF_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[AGG_RESULT]], ptr %[[RESULT_PTR]], align 8 +// CHECK: store ptr %[[SELF]], ptr %[[SELF_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[SELF_ADDR]], align 8 +// CHECK: %[[IVAR:.*]] = load i32, ptr @"OBJC_IVAR_$_MyDocument._cppObject", align 8, +// CHECK: %[[IVAR_CONV:.*]] = sext i32 %[[IVAR]] to i64 +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 %[[IVAR_CONV]] +// CHECK: call void @objc_copyCppObjectAtomic(ptr noundef %[[AGG_RESULT]], ptr noundef %[[ADD_PTR]], ptr noundef @__copy_helper_atomic_property_.ptrauth) + +// CHECK-LABEL: define internal void @__assign_helper_atomic_property_(ptr noundef %0, ptr noundef %1) # +// CHECK: [[THREE:%.*]] = load ptr, ptr [[ADDR1:%.*]], align 8 +// CHECK: [[TWO:%.*]] = load ptr, ptr [[ADDR:%.*]], align 8 +// CHECK: [[CALL:%.*]] = call noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) ptr @_ZN10TCPPObjectaSERKS_(ptr noundef nonnull align {{[0-9]+}} dereferenceable(256) [[TWO]], ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[THREE]]) +// CHECK: ret void + +// CHECK: define internal void @"\01-[MyDocument setMyProperty:]"(ptr noundef %[[SELF:.*]], ptr noundef %{{.*}}, ptr noundef %[[MYPROPERTY:.*]]) +// CHECK: %[[SELF_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[MYPROPERTY_INDIRECT:.*]]_addr = alloca ptr, align 8 +// CHECK: store ptr %[[SELF]], ptr %[[SELF_ADDR]], align 8 +// CHECK: store ptr %[[MYPROPERTY]], ptr %[[MYPROPERTY_INDIRECT]]_addr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[SELF_ADDR]], align 8 +// CHECK: %[[IVAR:.*]] = load i32, ptr @"OBJC_IVAR_$_MyDocument._cppObject", align 8, +// CHECK: %[[IVAR_CONV:.*]] = sext i32 %[[IVAR]] to i64 +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 %[[IVAR_CONV]] +// CHECK: call void @objc_copyCppObjectAtomic(ptr noundef %[[ADD_PTR]], ptr noundef %[[MYPROPERTY]], ptr noundef @__assign_helper_atomic_property_.ptrauth) +// CHECK: ret void diff --git a/clang/test/CodeGenObjCXX/ptrauth-property-object-reference.mm b/clang/test/CodeGenObjCXX/ptrauth-property-object-reference.mm index b9ff4975ca1a6..f82f41b9dc4bb 100644 --- a/clang/test/CodeGenObjCXX/ptrauth-property-object-reference.mm +++ b/clang/test/CodeGenObjCXX/ptrauth-property-object-reference.mm @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -triple arm64-apple-ios11.0 -fobjc-runtime=ios-11.0 -fptrauth-calls -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination %s -triple arm64-apple-ios11.0 -fobjc-runtime=ios-11.0 -fptrauth-calls -emit-llvm -o - | FileCheck %s extern int DEFAULT(); @@ -27,10 +27,6 @@ @implementation MyDocument @synthesize MyProperty1 = _cppObject1; @end -// CHECK-LABEL: @__copy_helper_atomic_property_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__copy_helper_atomic_property_, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 - -// CHECK-LABEL: @__assign_helper_atomic_property_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__assign_helper_atomic_property_, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 - // CHECK-LABEL: define internal void @__copy_helper_atomic_property_(ptr noundef %0, ptr noundef %1) # // CHECK: [[TWO:%.*]] = load ptr, ptr [[ADDR:%.*]], align 8 // CHECK: [[THREE:%.*]] = load ptr, ptr [[ADDR1:%.*]], align 8 @@ -47,7 +43,7 @@ @implementation MyDocument // CHECK: %[[IVAR:.*]] = load i32, ptr @"OBJC_IVAR_$_MyDocument._cppObject", align 8, // CHECK: %[[IVAR_CONV:.*]] = sext i32 %[[IVAR]] to i64 // CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 %[[IVAR_CONV]] -// CHECK: call void @objc_copyCppObjectAtomic(ptr noundef %[[AGG_RESULT]], ptr noundef %[[ADD_PTR]], ptr noundef @__copy_helper_atomic_property_.ptrauth) +// CHECK: call void @objc_copyCppObjectAtomic(ptr noundef %[[AGG_RESULT]], ptr noundef %[[ADD_PTR]], ptr noundef ptrauth (ptr @__copy_helper_atomic_property_, i32 0, i64 29656)) // CHECK-LABEL: define internal void @__assign_helper_atomic_property_(ptr noundef %0, ptr noundef %1) # // CHECK: [[THREE:%.*]] = load ptr, ptr [[ADDR1:%.*]], align 8 @@ -64,5 +60,5 @@ @implementation MyDocument // CHECK: %[[IVAR:.*]] = load i32, ptr @"OBJC_IVAR_$_MyDocument._cppObject", align 8, // CHECK: %[[IVAR_CONV:.*]] = sext i32 %[[IVAR]] to i64 // CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 %[[IVAR_CONV]] -// CHECK: call void @objc_copyCppObjectAtomic(ptr noundef %[[ADD_PTR]], ptr noundef %[[MYPROPERTY]], ptr noundef @__assign_helper_atomic_property_.ptrauth) +// CHECK: call void @objc_copyCppObjectAtomic(ptr noundef %[[ADD_PTR]], ptr noundef %[[MYPROPERTY]], ptr noundef ptrauth (ptr @__assign_helper_atomic_property_, i32 0, i64 29656)) // CHECK: ret void From e32a2df8c9292cbe9f53ac03b52413c5c5322ec3 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 27 Sep 2021 08:00:00 -0700 Subject: [PATCH 44/58] [clang] Sign the v-table pointer in ObjC exception RTTI. --- .../clang/CodeGen/ConstantInitBuilder.h | 9 +++------ clang/lib/CodeGen/CGObjCMac.cpp | 13 ++++++++++-- clang/lib/CodeGen/ConstantInitBuilder.cpp | 20 ++++++++----------- .../ptrauth-attr-exception-wrapper-globals.m | 17 ++++++++++++++++ .../test/CodeGenObjC/ptrauth-attr-exception.m | 5 ++--- 5 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 clang/test/CodeGenObjC/ptrauth-attr-exception-wrapper-globals.m diff --git a/clang/include/clang/CodeGen/ConstantInitBuilder.h b/clang/include/clang/CodeGen/ConstantInitBuilder.h index 2560aa27dfa73..a7424abbc07eb 100644 --- a/clang/include/clang/CodeGen/ConstantInitBuilder.h +++ b/clang/include/clang/CodeGen/ConstantInitBuilder.h @@ -206,12 +206,9 @@ class ConstantAggregateBuilderBase { void addSignedPointer(llvm::Constant *Pointer, const PointerAuthSchema &Schema, GlobalDecl CalleeDecl, QualType CalleeType); - - /// Add a signed pointer using the given pointer authentication schema. - void addSignedPointer(llvm::Constant *pointer, - unsigned key, - bool useAddressDiscrimination, - llvm::ConstantInt *otherDiscriminator); + void addSignedPointer(llvm::Constant *Pointer, unsigned Key, + bool UseAddressDiscrimination, + llvm::ConstantInt *OtherDiscriminator); /// Add a null pointer of a specific type. void addNullPointer(llvm::PointerType *ptrTy) { diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 2cab598f7bf20..65a0a585958e1 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -7847,8 +7847,17 @@ CGObjCNonFragileABIMac::GetInterfaceEHType(const ObjCInterfaceDecl *ID, ConstantInitBuilder builder(CGM); auto values = builder.beginStruct(ObjCTypes.EHTypeTy); - if (auto &Schema = CGM.getCodeGenOpts().PointerAuth.CXXVTablePointers) { - values.addSignedPointer(VTablePtr, Schema, GlobalDecl(), QualType()); + if (auto &Schema = + CGM.getCodeGenOpts().PointerAuth.CXXTypeInfoVTablePointer) { + uint32_t discrimination = 0; + if (Schema.hasOtherDiscrimination()) { + assert(Schema.getOtherDiscrimination() == + PointerAuthSchema::Discrimination::Constant); + discrimination = Schema.getConstantDiscrimination(); + } + values.addSignedPointer( + VTablePtr, Schema.getKey(), Schema.isAddressDiscriminated(), + llvm::ConstantInt::get(CGM.IntPtrTy, discrimination)); } else { values.add(VTablePtr); } diff --git a/clang/lib/CodeGen/ConstantInitBuilder.cpp b/clang/lib/CodeGen/ConstantInitBuilder.cpp index 48fe8d9cad4c6..d52de786c162c 100644 --- a/clang/lib/CodeGen/ConstantInitBuilder.cpp +++ b/clang/lib/CodeGen/ConstantInitBuilder.cpp @@ -315,18 +315,14 @@ void ConstantAggregateBuilderBase::addSignedPointer( add(SignedPointer); } -/// Sign the given pointer and add it to the constant initializer -/// currently being built. void ConstantAggregateBuilderBase::addSignedPointer( - llvm::Constant *pointer, unsigned key, - bool useAddressDiscrimination, llvm::ConstantInt *otherDiscriminator) { - llvm::Constant *storageAddress = nullptr; - if (useAddressDiscrimination) { - storageAddress = getAddrOfCurrentPosition(pointer->getType()); - } + llvm::Constant *Pointer, unsigned Key, bool UseAddressDiscrimination, + llvm::ConstantInt *OtherDiscriminator) { + llvm::Constant *StorageAddress = nullptr; + if (UseAddressDiscrimination) + StorageAddress = getAddrOfCurrentPosition(Pointer->getType()); - llvm::Constant *signedPointer = - Builder.CGM.getConstantSignedPointer(pointer, key, storageAddress, - otherDiscriminator); - add(signedPointer); + llvm::Constant *SignedPointer = Builder.CGM.getConstantSignedPointer( + Pointer, Key, StorageAddress, OtherDiscriminator); + add(SignedPointer); } diff --git a/clang/test/CodeGenObjC/ptrauth-attr-exception-wrapper-globals.m b/clang/test/CodeGenObjC/ptrauth-attr-exception-wrapper-globals.m new file mode 100644 index 0000000000000..6932600a4d3fc --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-attr-exception-wrapper-globals.m @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -fexceptions -fobjc-exceptions -o - %s | FileCheck %s + +__attribute__((objc_root_class)) +@interface Root { + Class isa; +} +@end + +__attribute__((objc_exception)) +@interface A : Root +@end + +@implementation A +@end + +// CHECK: @objc_ehtype_vtable.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds (ptr, ptr @objc_ehtype_vtable, i32 2), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @"OBJC_EHTYPE_$_A" = global {{%.*}} { ptr @objc_ehtype_vtable.ptrauth, diff --git a/clang/test/CodeGenObjC/ptrauth-attr-exception.m b/clang/test/CodeGenObjC/ptrauth-attr-exception.m index 6932600a4d3fc..5062ce85a9e40 100644 --- a/clang/test/CodeGenObjC/ptrauth-attr-exception.m +++ b/clang/test/CodeGenObjC/ptrauth-attr-exception.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -fexceptions -fobjc-exceptions -o - %s | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -fexceptions -fobjc-exceptions -o - %s | FileCheck %s __attribute__((objc_root_class)) @interface Root { @@ -13,5 +13,4 @@ @interface A : Root @implementation A @end -// CHECK: @objc_ehtype_vtable.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds (ptr, ptr @objc_ehtype_vtable, i32 2), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 -// CHECK: @"OBJC_EHTYPE_$_A" = global {{%.*}} { ptr @objc_ehtype_vtable.ptrauth, +// CHECK: @"OBJC_EHTYPE_$_A" = global {{%.*}} { ptr ptrauth (ptr getelementptr inbounds (ptr, ptr @objc_ehtype_vtable, i32 2), i32 2), From 37d802a3858f91c1b0fdac9722d85a40a0683f73 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Fri, 21 Jun 2024 14:39:44 -0700 Subject: [PATCH 45/58] [clang] Implement pointer authentication for blocks. --- clang/include/clang/Basic/Features.def | 1 + clang/include/clang/Basic/LangOptions.def | 1 + .../include/clang/Basic/PointerAuthOptions.h | 7 + clang/include/clang/Driver/Options.td | 2 + clang/lib/CodeGen/CGBlocks.cpp | 145 ++++++++++-------- clang/lib/Driver/ToolChains/Clang.cpp | 3 + clang/lib/Frontend/CompilerInvocation.cpp | 8 + clang/lib/Headers/ptrauth.h | 11 +- .../CodeGen/ptrauth-blocks-wrapper-globals.c | 36 +++++ clang/test/CodeGen/ptrauth-blocks.c | 10 +- ...capture-function-byref-wrapper-globals.cpp | 46 ++++++ .../ptrauth-block-capture-function-byref.cpp | 45 ++++++ ...block-descriptor-pointer-wrapper-globals.m | 36 +++++ .../ptrauth-block-descriptor-pointer.m | 30 ++++ 14 files changed, 307 insertions(+), 74 deletions(-) create mode 100644 clang/test/CodeGen/ptrauth-blocks-wrapper-globals.c create mode 100644 clang/test/CodeGenCXX/ptrauth-block-capture-function-byref-wrapper-globals.cpp create mode 100644 clang/test/CodeGenCXX/ptrauth-block-capture-function-byref.cpp create mode 100644 clang/test/CodeGenObjC/ptrauth-block-descriptor-pointer-wrapper-globals.m create mode 100644 clang/test/CodeGenObjC/ptrauth-block-descriptor-pointer.m diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index e355fcd8a5ee5..213d36dd6274c 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -123,6 +123,7 @@ FEATURE(ptrauth_indirect_gotos, LangOpts.PointerAuthIndirectGotos) FEATURE(ptrauth_member_function_pointer_type_discrimination, LangOpts.PointerAuthCalls) FEATURE(ptrauth_init_fini, LangOpts.PointerAuthInitFini) FEATURE(ptrauth_function_pointer_type_discrimination, LangOpts.PointerAuthFunctionTypeDiscrimination) +FEATURE(ptrauth_signed_block_descriptors, LangOpts.PointerAuthBlockDescriptorPointers) EXTENSION(swiftcc, PP.getTargetInfo().checkCallingConvention(CC_Swift) == clang::TargetInfo::CCCR_OK) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index d136c461ae0fc..20e014b34e1c4 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -173,6 +173,7 @@ LANGOPT(PointerAuthVTPtrTypeDiscrimination, 1, 0, "incorporate type discriminati LANGOPT(PointerAuthInitFini, 1, 0, "sign function pointers in init/fini arrays") BENIGN_LANGOPT(PointerAuthFunctionTypeDiscrimination, 1, 0, "Use type discrimination when signing function pointers") +LANGOPT(PointerAuthBlockDescriptorPointers, 1, 0, "enable signed block descriptors") LANGOPT(SoftPointerAuth , 1, 0, "software emulation of pointer authentication") VALUE_LANGOPT(PointerAuthABIVersion, 32, 0, "pointer authentication ABI version") diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index 80b3c0a2a94f8..d93b5fd0c5752 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -25,6 +25,10 @@ namespace clang { constexpr unsigned PointerAuthKeyNone = -1; +/// Constant discriminator to be used with block descriptor pointers. The value +/// is ptrauth_string_discriminator("block_descriptor") +constexpr uint16_t BlockDescriptorConstantDiscriminator = 0xC0BB; + class PointerAuthSchema { public: enum class Kind : unsigned { @@ -246,6 +250,9 @@ struct PointerAuthOptions { /// The ABI for __block variable copy/destroy function pointers. PointerAuthSchema BlockByrefHelperFunctionPointers; + /// The ABI for pointers to block descriptors. + PointerAuthSchema BlockDescriptorPointers; + /// The ABI for Objective-C method lists. PointerAuthSchema ObjCMethodListFunctionPointers; }; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 8c7f7f57c8f1b..8b93db57b1252 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4273,6 +4273,8 @@ defm ptrauth_indirect_gotos : OptInCC1FFlag<"ptrauth-indirect-gotos", "Enable signing and authentication of indirect goto targets">; defm ptrauth_function_pointer_type_discrimination : OptInCC1FFlag<"ptrauth-function-pointer-type-discrimination", "Enable type discrimination on C function pointers">; +defm ptrauth_block_descriptor_pointers : OptInCC1FFlag<"ptrauth-block-descriptor-pointers", + "Enable signing block descriptor pointers">; } let Group = f_Group in { diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index 35756b495d653..341cf400f9926 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -146,7 +146,8 @@ static std::string getBlockDescriptorName(const CGBlockInfo &BlockInfo, /// }; /// \endcode static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM, - const CGBlockInfo &blockInfo) { + const CGBlockInfo &blockInfo, + BlockFlags &flags) { ASTContext &C = CGM.getContext(); llvm::IntegerType *ulong = @@ -168,33 +169,18 @@ static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM, return desc; } - // If there isn't an equivalent block descriptor global variable, create a new - // one. - ConstantInitBuilder builder(CGM); - auto elements = builder.beginStruct(); - - // reserved - elements.addInt(ulong, 0); - - // Size - // FIXME: What is the right way to say this doesn't fit? We should give - // a user diagnostic in that case. Better fix would be to change the - // API to size_t. - elements.addInt(ulong, blockInfo.BlockSize.getQuantity()); + size_t blockSize = blockInfo.BlockSize.getQuantity(); // Optional copy/dispose helpers. + llvm::Constant *copyHelper; + llvm::Constant *disposeHelper; + bool needsCopyDispose = blockInfo.NeedsCopyDispose; bool hasInternalHelper = false; - if (blockInfo.NeedsCopyDispose) { - auto &schema = - CGM.getCodeGenOpts().PointerAuth.BlockHelperFunctionPointers; - + if (needsCopyDispose) { // copy_func_helper_decl - llvm::Constant *copyHelper = buildCopyHelper(CGM, blockInfo); - elements.addSignedPointer(copyHelper, schema, GlobalDecl(), QualType()); - + copyHelper = buildCopyHelper(CGM, blockInfo); // destroy_func_decl - llvm::Constant *disposeHelper = buildDisposeHelper(CGM, blockInfo); - elements.addSignedPointer(disposeHelper, schema, GlobalDecl(), QualType()); + disposeHelper = buildDisposeHelper(CGM, blockInfo); if (cast(copyHelper->stripPointerCasts()) ->hasInternalLinkage() || @@ -206,17 +192,34 @@ static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM, // Signature. Mandatory ObjC-style method descriptor @encode sequence. std::string typeAtEncoding = CGM.getContext().getObjCEncodingForBlock(blockInfo.getBlockExpr()); - elements.add(CGM.GetAddrOfConstantCString(typeAtEncoding).getPointer()); + llvm::Constant *encodingString = + CGM.GetAddrOfConstantCString(typeAtEncoding).getPointer(); // GC layout. + llvm::Constant *layoutString; if (C.getLangOpts().ObjC) { if (CGM.getLangOpts().getGC() != LangOptions::NonGC) - elements.add(CGM.getObjCRuntime().BuildGCBlockLayout(CGM, blockInfo)); + layoutString = CGM.getObjCRuntime().BuildGCBlockLayout(CGM, blockInfo); else - elements.add(CGM.getObjCRuntime().BuildRCBlockLayout(CGM, blockInfo)); + layoutString = CGM.getObjCRuntime().BuildRCBlockLayout(CGM, blockInfo); + } else + layoutString = llvm::ConstantPointerNull::get(i8p); + + + ConstantInitBuilder builder(CGM); + auto elements = builder.beginStruct(); + elements.addInt(ulong, 0); + elements.addInt(ulong, blockSize); + + if (needsCopyDispose) { + auto &schema = + CGM.getCodeGenOpts().PointerAuth.BlockHelperFunctionPointers; + elements.addSignedPointer(copyHelper, schema, GlobalDecl(), QualType()); + elements.addSignedPointer(disposeHelper, schema, GlobalDecl(), QualType()); } - else - elements.addNullPointer(i8p); + + elements.add(encodingString); + elements.add(layoutString); unsigned AddrSpace = 0; if (C.getLangOpts().OpenCL) @@ -802,7 +805,7 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { assert(blockAddr.isValid() && "block has no address!"); llvm::Constant *isa; - llvm::Constant *descriptor; + llvm::Value *descriptor; BlockFlags flags; if (!IsOpenCL) { // If the block is non-escaping, set field 'isa 'to NSConcreteGlobalBlock @@ -813,9 +816,6 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { : CGM.getNSConcreteStackBlock(); isa = blockISA; - // Build the block descriptor. - descriptor = buildBlockDescriptor(CGM, blockInfo); - // Compute the initial on-stack block flags. flags = BLOCK_HAS_SIGNATURE; if (blockInfo.HasCapturedVariableLayout) @@ -828,6 +828,9 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { flags |= BLOCK_USE_STRET; if (blockInfo.NoEscape) flags |= BLOCK_IS_NOESCAPE | BLOCK_IS_GLOBAL; + + // Build the block descriptor. + descriptor = buildBlockDescriptor(CGM, blockInfo, flags); } auto projectField = [&](unsigned index, const Twine &name) -> Address { @@ -848,6 +851,20 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { offset += size; index++; }; + auto addSignedHeaderField = [&](llvm::Value *value, + const PointerAuthSchema &schema, + GlobalDecl decl, QualType type, + CharUnits size, const Twine &name) { + auto storageAddress = projectField(index, name); + if (schema) { + auto authInfo = EmitPointerAuthInfo( + schema, storageAddress.emitRawPointer(*this), decl, type); + value = EmitPointerAuthSign(authInfo, value); + } + Builder.CreateStore(value, storageAddress); + offset += size; + index++; + }; if (!IsOpenCL) { addHeaderField(isa, getPointerSize(), "block.isa"); @@ -866,20 +883,18 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { if (!IsOpenCL) { llvm::Value *blockFnPtr = llvm::ConstantExpr::getBitCast(InvokeFn, VoidPtrTy); - auto blockFnPtrAddr = projectField(index, "block.invoke"); - if (auto &schema = - CGM.getCodeGenOpts().PointerAuth.BlockInvocationFunctionPointers) { - QualType type = blockInfo.getBlockExpr()->getType() - ->castAs()->getPointeeType(); - auto authInfo = EmitPointerAuthInfo( - schema, blockFnPtrAddr.emitRawPointer(*this), GlobalDecl(), type); - blockFnPtr = EmitPointerAuthSign(authInfo, blockFnPtr); - } - Builder.CreateStore(blockFnPtr, blockFnPtrAddr); - offset += getPointerSize(); - index++; - - addHeaderField(descriptor, getPointerSize(), "block.descriptor"); + QualType type = blockInfo.getBlockExpr() + ->getType() + ->castAs() + ->getPointeeType(); + addSignedHeaderField( + blockFnPtr, + CGM.getCodeGenOpts().PointerAuth.BlockInvocationFunctionPointers, + GlobalDecl(), type, getPointerSize(), "block.invoke"); + + addSignedHeaderField( + descriptor, CGM.getCodeGenOpts().PointerAuth.BlockDescriptorPointers, + GlobalDecl(), type, getPointerSize(), "block.descriptor"); } else if (auto *Helper = CGM.getTargetCodeGenInfo().getTargetOpenCLBlockHelper()) { addHeaderField(blockFn, GenVoidPtrSize, "block.invoke"); @@ -981,8 +996,10 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { } // If it's a reference variable, copy the reference into the block field. - } else if (type->getAs()) { - Builder.CreateStore(src.emitRawPointer(*this), blockField); + } else if (auto refType = type->getAs()) { + llvm::Value *value = + getAsNaturalPointerTo(src, refType->getPointeeType()); + Builder.CreateStore(value, blockField); // If type is const-qualified, copy the value into the block field. } else if (type.isConstQualified() && @@ -1321,17 +1338,19 @@ static llvm::Constant *buildGlobalBlock(CodeGenModule &CGM, bool IsOpenCL = CGM.getLangOpts().OpenCL; bool IsWindows = CGM.getTarget().getTriple().isOSWindows(); if (!IsOpenCL) { + // __flags + BlockFlags flags = BLOCK_IS_GLOBAL | BLOCK_HAS_SIGNATURE; + if (blockInfo.UsesStret) + flags |= BLOCK_USE_STRET; + + llvm::Constant *descriptor = buildBlockDescriptor(CGM, blockInfo, flags); + // isa if (IsWindows) fields.addNullPointer(CGM.Int8PtrPtrTy); else fields.add(CGM.getNSConcreteGlobalBlock()); - // __flags - BlockFlags flags = BLOCK_IS_GLOBAL | BLOCK_HAS_SIGNATURE; - if (blockInfo.UsesStret) - flags |= BLOCK_USE_STRET; - fields.addInt(CGM.IntTy, flags.getBitMask()); // Reserved @@ -1348,20 +1367,22 @@ static llvm::Constant *buildGlobalBlock(CodeGenModule &CGM, } else { fields.add(blockFn); } - } else { + + // Descriptor + fields.addSignedPointer( + descriptor, CGM.getCodeGenOpts().PointerAuth.BlockDescriptorPointers, + GlobalDecl(), QualType()); + } else { // IsOpenCL fields.addInt(CGM.IntTy, blockInfo.BlockSize.getQuantity()); fields.addInt(CGM.IntTy, blockInfo.BlockAlign.getQuantity()); // Function fields.add(blockFn); - } - if (!IsOpenCL) { - // Descriptor - fields.add(buildBlockDescriptor(CGM, blockInfo)); - } else if (auto *Helper = - CGM.getTargetCodeGenInfo().getTargetOpenCLBlockHelper()) { - for (auto *I : Helper->getCustomFieldValues(CGM, blockInfo)) { - fields.add(I); + if (auto *Helper = + CGM.getTargetCodeGenInfo().getTargetOpenCLBlockHelper()) { + for (auto *I : Helper->getCustomFieldValues(CGM, blockInfo)) { + fields.add(I); + } } } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index fb49ea0ec0e61..8df4f3bee7c10 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1800,6 +1800,9 @@ void Clang::AddAArch64TargetArgs(const ArgList &Args, CmdArgs, options::OPT_fptrauth_function_pointer_type_discrimination, options::OPT_fno_ptrauth_function_pointer_type_discrimination); + Args.addOptInFlag(CmdArgs, options::OPT_fptrauth_block_descriptor_pointers, + options::OPT_fno_ptrauth_block_descriptor_pointers); + Args.addOptInFlag(CmdArgs, options::OPT_fptrauth_indirect_gotos, options::OPT_fno_ptrauth_indirect_gotos); } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 5fd38867e9dc3..aaf6da5fa13a9 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1666,6 +1666,10 @@ void CompilerInvocation::setDefaultPointerAuthOptions( PointerAuthSchema(Key::ASIA, true, Discrimination::None); Opts.BlockByrefHelperFunctionPointers = PointerAuthSchema(Key::ASIA, true, Discrimination::None); + if (LangOpts.PointerAuthBlockDescriptorPointers) + Opts.BlockDescriptorPointers = + PointerAuthSchema(Key::ASDA, true, Discrimination::Constant, + BlockDescriptorConstantDiscriminator); Opts.ObjCMethodListFunctionPointers = PointerAuthSchema(Key::ASIA, true, Discrimination::None); @@ -3726,6 +3730,8 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts, GenerateArg(Consumer, OPT_fptrauth_init_fini); if (Opts.PointerAuthFunctionTypeDiscrimination) GenerateArg(Consumer, OPT_fptrauth_function_pointer_type_discrimination); + if (Opts.PointerAuthBlockDescriptorPointers) + GenerateArg(Consumer, OPT_fptrauth_block_descriptor_pointers); if (Opts.PointerAuthIndirectGotos) GenerateArg(Consumer, OPT_fptrauth_indirect_gotos); @@ -3754,6 +3760,8 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, Opts.PointerAuthInitFini = Args.hasArg(OPT_fptrauth_init_fini); Opts.PointerAuthFunctionTypeDiscrimination = Args.hasArg(OPT_fptrauth_function_pointer_type_discrimination); + Opts.PointerAuthBlockDescriptorPointers = + Args.hasArg(OPT_fptrauth_block_descriptor_pointers); Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos); Opts.SoftPointerAuth = Args.hasArg(OPT_fptrauth_soft); diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h index 2dbeb759348bb..f25eacf49ee4c 100644 --- a/clang/lib/Headers/ptrauth.h +++ b/clang/lib/Headers/ptrauth.h @@ -45,6 +45,10 @@ typedef enum { (or, in other words, the value of the stack pointer on function entry) */ ptrauth_key_frame_pointer = ptrauth_key_process_dependent_data, + /* The key used to sign C++ v-table pointers. + The extra data is always 0. */ + ptrauth_key_cxx_vtable_pointer = ptrauth_key_process_independent_data, + /* The key used to sign block function pointers, including: invocation functions, block object copy functions, @@ -56,11 +60,10 @@ typedef enum { Note that block object pointers themselves (i.e. the direct representations of values of block-pointer type) are not signed. */ - ptrauth_key_block_function = ptrauth_key_asia, + ptrauth_key_block_function = ptrauth_key_process_independent_code, - /* The key used to sign C++ v-table pointers. - The extra data is always 0. */ - ptrauth_key_cxx_vtable_pointer = ptrauth_key_process_independent_data, + /* The key used to sign block descriptor pointers. */ + ptrauth_key_block_descriptor_pointer = ptrauth_key_process_independent_data, /* Other pointers signed under the ABI use private ABI rules. */ diff --git a/clang/test/CodeGen/ptrauth-blocks-wrapper-globals.c b/clang/test/CodeGen/ptrauth-blocks-wrapper-globals.c new file mode 100644 index 0000000000000..3556c75623149 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-blocks-wrapper-globals.c @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -fblocks -emit-llvm %s -o - | FileCheck %s + +void (^blockptr)(void); + +// CHECK: [[INVOCATION_1:@.*]] = private constant { ptr, i32, i64, i64 } { ptr {{@.*}}, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ ptr, i32, i32, ptr, ptr }, ptr [[GLOBAL_BLOCK_1:@.*]], i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: [[GLOBAL_BLOCK_1]] = internal constant { ptr, i32, i32, ptr, ptr } { ptr @_NSConcreteGlobalBlock, i32 1342177280, i32 0, ptr [[INVOCATION_1]], +void (^globalblock)(void) = ^{}; + +// CHECK-LABEL: define void @test_block_call() +void test_block_call() { + // CHECK: [[T0:%.*]] = load ptr, ptr @blockptr, + // CHECK-NEXT: [[FNADDR:%.*]] = getelementptr inbounds {{.*}}, ptr [[T0]], i32 0, i32 3 + // CHECK-NEXT: [[T1:%.*]] = load ptr, ptr [[FNADDR]], + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint ptr [[FNADDR]] to i64 + // CHECK-NEXT: call void [[T1]](ptr noundef [[T0]]) [ "ptrauth"(i32 0, i64 [[DISC]]) ] + blockptr(); +} + +void use_block(int (^)(void)); + +// CHECK-LABEL: define void @test_block_literal( +void test_block_literal(int i) { + // CHECK: [[I:%.*]] = alloca i32, + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:.*]], align + // CHECK: [[FNPTRADDR:%.*]] = getelementptr inbounds [[BLOCK_T]], ptr [[BLOCK]], i32 0, i32 3 + // CHECK-NEXT: [[DISCRIMINATOR:%.*]] = ptrtoint ptr [[FNPTRADDR]] to i64 + // CHECK-NEXT: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr {{@.*}} to i64), i32 0, i64 [[DISCRIMINATOR]]) + // CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK-NEXT: store ptr [[T0]], ptr [[FNPTRADDR]] + use_block(^{return i;}); +} + +struct A { + int value; +}; +struct A *createA(void); diff --git a/clang/test/CodeGen/ptrauth-blocks.c b/clang/test/CodeGen/ptrauth-blocks.c index 3556c75623149..04b359c5c2533 100644 --- a/clang/test/CodeGen/ptrauth-blocks.c +++ b/clang/test/CodeGen/ptrauth-blocks.c @@ -1,9 +1,8 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -fblocks -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -fblocks -emit-llvm %s -o - | FileCheck %s void (^blockptr)(void); -// CHECK: [[INVOCATION_1:@.*]] = private constant { ptr, i32, i64, i64 } { ptr {{@.*}}, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ ptr, i32, i32, ptr, ptr }, ptr [[GLOBAL_BLOCK_1:@.*]], i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth" -// CHECK: [[GLOBAL_BLOCK_1]] = internal constant { ptr, i32, i32, ptr, ptr } { ptr @_NSConcreteGlobalBlock, i32 1342177280, i32 0, ptr [[INVOCATION_1]], +// CHECK: [[GLOBAL_BLOCK_1:@.*]] = internal constant { ptr, i32, i32, ptr, ptr } { ptr @_NSConcreteGlobalBlock, i32 1342177280, i32 0, ptr ptrauth (ptr {{@.*}}, i32 0, i64 0, ptr getelementptr inbounds ({ ptr, i32, i32, ptr, ptr }, ptr [[GLOBAL_BLOCK_1]], i32 0, i32 3)), ptr {{@.*}} void (^globalblock)(void) = ^{}; // CHECK-LABEL: define void @test_block_call() @@ -29,8 +28,3 @@ void test_block_literal(int i) { // CHECK-NEXT: store ptr [[T0]], ptr [[FNPTRADDR]] use_block(^{return i;}); } - -struct A { - int value; -}; -struct A *createA(void); diff --git a/clang/test/CodeGenCXX/ptrauth-block-capture-function-byref-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-block-capture-function-byref-wrapper-globals.cpp new file mode 100644 index 0000000000000..14c372dbcaf2e --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-block-capture-function-byref-wrapper-globals.cpp @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -fblocks -triple arm64-apple-ios -fptrauth-calls -emit-llvm -no-enable-noundef-analysis -std=c++11 %s -o - | FileCheck %s + +// CHECK: @_Z8handler2v.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_Z8handler2v, i32 0, i64 0, i64 0 }, section "llvm.ptrauth" +// CHECK: @global_handler = global ptr @_Z8handler2v.ptrauth + +// CHECK: define void @_Z7handlerv() +__attribute__((noinline)) void handler() { + asm volatile(""); +} + +// CHECK: define void @_Z8handler2v() +__attribute__((noinline)) void handler2() { + asm volatile(""); +} +void (*global_handler)() = &handler2; + +// CHECK: define void @_Z11callHandlerRFvvE(ptr nonnull %handler) +void callHandler(void (&handler)()) { + asm volatile(""); + // Check basic usage of function reference + ^{ + handler(); + }(); +// CHECK: [[HANDLER:%.*]] = load ptr, ptr %handler.addr +// CHECK: store ptr [[HANDLER]], ptr %block.captured, +// CHECK: foobar: + asm volatile("foobar:"); + + // Check escape of function reference + ^{ + global_handler = handler; + }(); +// CHECK: [[CAPTURE_SLOT:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr %block1, i32 0, i32 5 +// CHECK: [[HANDLER:%.*]] = load ptr, ptr %handler.addr +// CHECK: store ptr [[HANDLER]], ptr [[CAPTURE_SLOT]] + asm volatile(""); + + // Check return of function reference + ^{ + return handler; + }()(); +// CHECK: [[CAPTURE_SLOT:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr %block8, i32 0, i32 5 +// CHECK: [[HANDLER:%.*]] = load ptr, ptr %handler.addr +// CHECK: store ptr [[HANDLER]], ptr [[CAPTURE_SLOT]] + asm volatile(""); +} diff --git a/clang/test/CodeGenCXX/ptrauth-block-capture-function-byref.cpp b/clang/test/CodeGenCXX/ptrauth-block-capture-function-byref.cpp new file mode 100644 index 0000000000000..f1d97932f0c48 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-block-capture-function-byref.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fblocks -triple arm64-apple-ios -fptrauth-calls -emit-llvm -no-enable-noundef-analysis -std=c++11 %s -o - | FileCheck %s + +// CHECK: @global_handler = global ptr ptrauth (ptr @_Z8handler2v, i32 0) + +// CHECK: define void @_Z7handlerv() +__attribute__((noinline)) void handler() { + asm volatile(""); +} + +// CHECK: define void @_Z8handler2v() +__attribute__((noinline)) void handler2() { + asm volatile(""); +} +void (*global_handler)() = &handler2; + +// CHECK: define void @_Z11callHandlerRFvvE(ptr nonnull %handler) +void callHandler(void (&handler)()) { + asm volatile(""); + // Check basic usage of function reference + ^{ + handler(); + }(); +// CHECK: [[HANDLER:%.*]] = load ptr, ptr %handler.addr +// CHECK: store ptr [[HANDLER]], ptr %block.captured, +// CHECK: foobar: + asm volatile("foobar:"); + + // Check escape of function reference + ^{ + global_handler = handler; + }(); +// CHECK: [[CAPTURE_SLOT:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr %block1, i32 0, i32 5 +// CHECK: [[HANDLER:%.*]] = load ptr, ptr %handler.addr +// CHECK: store ptr [[HANDLER]], ptr [[CAPTURE_SLOT]] + asm volatile(""); + + // Check return of function reference + ^{ + return handler; + }()(); +// CHECK: [[CAPTURE_SLOT:%.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr }>, ptr %block8, i32 0, i32 5 +// CHECK: [[HANDLER:%.*]] = load ptr, ptr %handler.addr +// CHECK: store ptr [[HANDLER]], ptr [[CAPTURE_SLOT]] + asm volatile(""); +} diff --git a/clang/test/CodeGenObjC/ptrauth-block-descriptor-pointer-wrapper-globals.m b/clang/test/CodeGenObjC/ptrauth-block-descriptor-pointer-wrapper-globals.m new file mode 100644 index 0000000000000..5566ebdc928f2 --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-block-descriptor-pointer-wrapper-globals.m @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -fobjc-arc -fblocks -fptrauth-calls -fptrauth-block-descriptor-pointers -triple arm64e-apple-ios -emit-llvm -o - %s | FileCheck %s + +void a() { + // Test out a global block. + void (^blk)(void) = ^{}; +} + +// CHECK: @"__block_descriptor_32_e5_v8\01?0l" = linkonce_odr hidden unnamed_addr constant + +// CHECK: @"__block_descriptor_32_e5_v8\01?0l.ptrauth" = private constant { ptr, i32, i64, i64 } { +// CHECK-SAME: ptr @"__block_descriptor_32_e5_v8\01?0l", +// CHECK-SAME: i32 2, +// CHECK-SAME: i64 ptrtoint (ptr getelementptr inbounds ({ {{.*}} }, ptr @__block_literal_global, i32 0, i32 4) to i64), +// CHECK-SAME: i64 49339 } + +// CHECK: @__block_literal_global = internal constant { ptr, i32, i32, ptr, ptr } { +// CHECK-SAME: ptr @_NSConcreteGlobalBlock, +// CHECK-SAME: i32 1342177280 +// CHECK-SAME: i32 0, +// CHECK-SAME: ptr @__a_block_invoke.ptrauth, +// CHECK-SAME: ptr @"__block_descriptor_32_e5_v8\01?0l.ptrauth" } + +void b(int p) { + // CHECK-LABEL: define void @b + + // Test out a stack block. + void (^blk)(void) = ^{(void)p;}; + + // CHECK: [[BLOCK:%.*]] = alloca <{ ptr, i32, i32, ptr, ptr, i32 }> + // CHECK: [[BLOCK_DESCRIPTOR_REF:%.*]] = getelementptr inbounds <{ {{.*}} }>, ptr [[BLOCK]], i32 0, i32 4 + // CHECK: [[BLOCK_DESCRIPTOR_REF_INT:%.*]] = ptrtoint ptr [[BLOCK_DESCRIPTOR_REF]] to i64 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[BLOCK_DESCRIPTOR_REF_INT]], i64 49339) + // CHECK: [[SIGNED_REF:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @"__block_descriptor_36_e5_v8\01?0l" to i64), i32 2, i64 [[BLENDED]]) + // CHECK: [[SIGNED_REF_PTR:%.*]] = inttoptr i64 [[SIGNED_REF]] to ptr + // CHECK: store ptr [[SIGNED_REF_PTR]], ptr [[BLOCK_DESCRIPTOR_REF]] +} diff --git a/clang/test/CodeGenObjC/ptrauth-block-descriptor-pointer.m b/clang/test/CodeGenObjC/ptrauth-block-descriptor-pointer.m new file mode 100644 index 0000000000000..d96c309afa872 --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-block-descriptor-pointer.m @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fobjc-arc -fblocks -fptrauth-calls -fptrauth-block-descriptor-pointers -triple arm64e-apple-ios -emit-llvm -o - %s | FileCheck %s + +void a() { + // Test out a global block. + void (^blk)(void) = ^{}; +} + +// CHECK: @"__block_descriptor_32_e5_v8\01?0l" = linkonce_odr hidden unnamed_addr constant + +// CHECK: @__block_literal_global = internal constant { ptr, i32, i32, ptr, ptr } { +// CHECK-SAME: ptr @_NSConcreteGlobalBlock, +// CHECK-SAME: i32 1342177280 +// CHECK-SAME: i32 0, +// CHECK-SAME: ptr ptrauth (ptr @__a_block_invoke, i32 0, i64 0, ptr getelementptr inbounds ({ ptr, i32, i32, ptr, ptr }, ptr @__block_literal_global, i32 0, i32 3)), +// CHECK-SAME: ptr ptrauth (ptr @"__block_descriptor_32_e5_v8\01?0l", i32 2, i64 49339, ptr getelementptr inbounds ({ ptr, i32, i32, ptr, ptr }, ptr @__block_literal_global, i32 0, i32 4)) + +void b(int p) { + // CHECK-LABEL: define void @b + + // Test out a stack block. + void (^blk)(void) = ^{(void)p;}; + + // CHECK: [[BLOCK:%.*]] = alloca <{ ptr, i32, i32, ptr, ptr, i32 }> + // CHECK: [[BLOCK_DESCRIPTOR_REF:%.*]] = getelementptr inbounds <{ {{.*}} }>, ptr [[BLOCK]], i32 0, i32 4 + // CHECK: [[BLOCK_DESCRIPTOR_REF_INT:%.*]] = ptrtoint ptr [[BLOCK_DESCRIPTOR_REF]] to i64 + // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[BLOCK_DESCRIPTOR_REF_INT]], i64 49339) + // CHECK: [[SIGNED_REF:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @"__block_descriptor_36_e5_v8\01?0l" to i64), i32 2, i64 [[BLENDED]]) + // CHECK: [[SIGNED_REF_PTR:%.*]] = inttoptr i64 [[SIGNED_REF]] to ptr + // CHECK: store ptr [[SIGNED_REF_PTR]], ptr [[BLOCK_DESCRIPTOR_REF]] +} From 5cc1cc3dd10f3637ee4e9884324f635d93bb2d6c Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Tue, 2 Jul 2024 21:32:40 -0700 Subject: [PATCH 46/58] [clang] Implement pointer authentication for ObjC method lists. --- clang/include/clang/Basic/Features.def | 1 + .../include/clang/Basic/PointerAuthOptions.h | 7 +++ clang/lib/CodeGen/CGObjCMac.cpp | 59 ++++++++++++++----- clang/lib/Frontend/CompilerInvocation.cpp | 3 + clang/lib/Headers/ptrauth.h | 3 + ...auth-method-list-pointer-wrapper-globals.m | 14 +++++ .../CodeGenObjC/ptrauth-method-list-pointer.m | 12 ++++ .../ptrauth-method-list-wrapper-globals.m | 30 ++++++++++ clang/test/CodeGenObjC/ptrauth-method-list.m | 16 ++--- 9 files changed, 122 insertions(+), 23 deletions(-) create mode 100644 clang/test/CodeGenObjC/ptrauth-method-list-pointer-wrapper-globals.m create mode 100644 clang/test/CodeGenObjC/ptrauth-method-list-pointer.m create mode 100644 clang/test/CodeGenObjC/ptrauth-method-list-wrapper-globals.m diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 213d36dd6274c..696ed068959df 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -116,6 +116,7 @@ FEATURE(ptrauth_qualifier_authentication_mode, LangOpts.PointerAuthIntrinsics) FEATURE(ptrauth_restricted_intptr_qualifier, LangOpts.PointerAuthIntrinsics) FEATURE(ptrauth_objc_signable_class, true) FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls) +FEATURE(ptrauth_objc_method_list_pointer, LangOpts.PointerAuthCalls) FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns) FEATURE(ptrauth_vtable_pointer_address_discrimination, LangOpts.PointerAuthVTPtrAddressDiscrimination) FEATURE(ptrauth_vtable_pointer_type_discrimination, LangOpts.PointerAuthVTPtrTypeDiscrimination) diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index d93b5fd0c5752..f5e29ddef71f4 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -29,6 +29,10 @@ constexpr unsigned PointerAuthKeyNone = -1; /// is ptrauth_string_discriminator("block_descriptor") constexpr uint16_t BlockDescriptorConstantDiscriminator = 0xC0BB; +/// Constant discriminator to be used with method list pointers. The value is +/// ptrauth_string_discriminator("method_list_t") +constexpr uint16_t MethodListPointerConstantDiscriminator = 0xC310; + class PointerAuthSchema { public: enum class Kind : unsigned { @@ -255,6 +259,9 @@ struct PointerAuthOptions { /// The ABI for Objective-C method lists. PointerAuthSchema ObjCMethodListFunctionPointers; + + /// The ABI for a reference to an Objective-C method list in _class_ro_t. + PointerAuthSchema ObjCMethodListPointer; }; } // end namespace clang diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 65a0a585958e1..aab124b6b5bba 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -6334,11 +6334,20 @@ llvm::GlobalVariable * CGObjCNonFragileABIMac::BuildClassRoTInitializer( methods.push_back(MD); } - values.add(emitMethodList(ID->getObjCRuntimeNameAsString(), - (flags & NonFragileABI_Class_Meta) - ? MethodListType::ClassMethods - : MethodListType::InstanceMethods, - methods)); + llvm::Constant *MethListPtr = emitMethodList( + ID->getObjCRuntimeNameAsString(), + (flags & NonFragileABI_Class_Meta) ? MethodListType::ClassMethods + : MethodListType::InstanceMethods, + methods); + + const auto &MethListSchema = + CGM.getCodeGenOpts().PointerAuth.ObjCMethodListPointer; + if (MethListSchema && !MethListPtr->isNullValue()) { + values.addSignedPointer(MethListPtr, MethListSchema, GlobalDecl(), + QualType()); + } else { + values.add(MethListPtr); + } const ObjCInterfaceDecl *OID = ID->getClassInterface(); assert(OID && "CGObjCNonFragileABIMac::BuildClassRoTInitializer"); @@ -6659,12 +6668,27 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { } } - auto instanceMethodList = emitMethodList( + llvm::Constant *instanceMethodList = emitMethodList( listName, MethodListType::CategoryInstanceMethods, instanceMethods); - auto classMethodList = emitMethodList( + llvm::Constant *classMethodList = emitMethodList( listName, MethodListType::CategoryClassMethods, classMethods); - values.add(instanceMethodList); - values.add(classMethodList); + + const auto &MethListSchema = + CGM.getCodeGenOpts().PointerAuth.ObjCMethodListPointer; + if (MethListSchema && !instanceMethodList->isNullValue()) { + values.addSignedPointer(instanceMethodList, MethListSchema, GlobalDecl(), + QualType()); + } else { + values.add(instanceMethodList); + } + + if (MethListSchema && !classMethodList->isNullValue()) { + values.addSignedPointer(classMethodList, MethListSchema, + GlobalDecl(), QualType()); + } else { + values.add(classMethodList); + } + // Keep track of whether we have actual metadata to emit. bool isEmptyCategory = instanceMethodList->isNullValue() && classMethodList->isNullValue(); @@ -6736,21 +6760,23 @@ void CGObjCNonFragileABIMac::emitMethodConstant(ConstantArrayBuilder &builder, const ObjCMethodDecl *MD, bool forProtocol) { auto method = builder.beginStruct(ObjCTypes.MethodTy); - method.add(GetMethodVarName(MD->getSelector())); - method.add(GetMethodVarType(MD)); + + llvm::Constant *MVN = GetMethodVarName(MD->getSelector()); + llvm::Constant *MVT = GetMethodVarType(MD); + llvm::Function *fn = GetMethodDefinition(MD); + + method.add(MVN); + method.add(MVT); if (forProtocol) { // Protocol methods have no implementation. So, this entry is always NULL. method.addNullPointer(ObjCTypes.Int8PtrProgramASTy); } else { - llvm::Function *fn = GetMethodDefinition(MD); assert(fn && "no definition for method?"); if (const auto &schema = CGM.getCodeGenOpts().PointerAuth.ObjCMethodListFunctionPointers) { - auto *bitcast = - llvm::ConstantExpr::getBitCast(fn, ObjCTypes.Int8PtrProgramASTy); - method.addSignedPointer(bitcast, schema, GlobalDecl(), QualType()); + method.addSignedPointer(fn, schema, GlobalDecl(), QualType()); } else { method.add(fn); } @@ -6816,7 +6842,8 @@ CGObjCNonFragileABIMac::emitMethodList(Twine name, MethodListType kind, auto values = builder.beginStruct(); // sizeof(struct _objc_method) - unsigned Size = CGM.getDataLayout().getTypeAllocSize(ObjCTypes.MethodTy); + uint32_t Size = CGM.getDataLayout().getTypeAllocSize(ObjCTypes.MethodTy); + values.addInt(ObjCTypes.IntTy, Size); // method_count values.addInt(ObjCTypes.IntTy, methods.size()); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index aaf6da5fa13a9..6b0ff6cfaf558 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1673,6 +1673,9 @@ void CompilerInvocation::setDefaultPointerAuthOptions( Opts.ObjCMethodListFunctionPointers = PointerAuthSchema(Key::ASIA, true, Discrimination::None); + Opts.ObjCMethodListPointer = + PointerAuthSchema(Key::ASDA, true, Discrimination::Constant, + MethodListPointerConstantDiscriminator); } Opts.IndirectGotos = LangOpts.PointerAuthIndirectGotos; Opts.ReturnAddresses = LangOpts.PointerAuthReturns; diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h index f25eacf49ee4c..627d2bcd67c71 100644 --- a/clang/lib/Headers/ptrauth.h +++ b/clang/lib/Headers/ptrauth.h @@ -65,6 +65,9 @@ typedef enum { /* The key used to sign block descriptor pointers. */ ptrauth_key_block_descriptor_pointer = ptrauth_key_process_independent_data, + /* The key used to sign metadata pointers to Objective-C method-lists. */ + ptrauth_key_method_list_pointer = ptrauth_key_process_independent_data, + /* Other pointers signed under the ABI use private ABI rules. */ } ptrauth_key; diff --git a/clang/test/CodeGenObjC/ptrauth-method-list-pointer-wrapper-globals.m b/clang/test/CodeGenObjC/ptrauth-method-list-pointer-wrapper-globals.m new file mode 100644 index 0000000000000..14d5f46a865b4 --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-method-list-pointer-wrapper-globals.m @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -fptrauth-calls -triple arm64e-apple-ios %s -emit-llvm -o - | FileCheck %s + +@implementation X +-(void)meth {} +@end + +// By default, we should be signing the method list pointer and not emitting +// relative method lists. + +// CHECK: @"_OBJC_$_INSTANCE_METHODS_X" = internal global { i32, i32, [1 x %struct._objc_method] } { i32 24, i32 1, [1 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01-[X meth].ptrauth" }] }, section "__DATA, __objc_const", align 8 + +// CHECK: @"_OBJC_$_INSTANCE_METHODS_X.ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"_OBJC_$_INSTANCE_METHODS_X", i32 2, i64 ptrtoint (ptr getelementptr inbounds (%struct._class_ro_t, ptr @"_OBJC_CLASS_RO_$_X", i32 0, i32 5) to i64), i64 49936 }, section "llvm.ptrauth", align 8 + +// CHECK: @"_OBJC_CLASS_RO_$_X" = internal global %struct._class_ro_t { i32 2, i32 0, i32 0, ptr null, ptr @OBJC_CLASS_NAME_, ptr @"_OBJC_$_INSTANCE_METHODS_X.ptrauth", ptr null, ptr null, ptr null, ptr null }, section "__DATA, __objc_const", align 8 diff --git a/clang/test/CodeGenObjC/ptrauth-method-list-pointer.m b/clang/test/CodeGenObjC/ptrauth-method-list-pointer.m new file mode 100644 index 0000000000000..32b588d07a567 --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-method-list-pointer.m @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-calls -triple arm64e-apple-ios %s -emit-llvm -o - | FileCheck %s + +@implementation X +-(void)meth {} +@end + +// By default, we should be signing the method list pointer and not emitting +// relative method lists. + +// CHECK: @"_OBJC_$_INSTANCE_METHODS_X" = internal global { i32, i32, [1 x %struct._objc_method] } { i32 24, i32 1, [1 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_, ptr @OBJC_METH_VAR_TYPE_, ptr ptrauth (ptr @"\01-[X meth]", i32 0, i64 0, ptr getelementptr inbounds ({ i32, i32, [1 x %struct._objc_method] }, ptr @"_OBJC_$_INSTANCE_METHODS_X", i32 0, i32 2, i32 0, i32 2)) }] }, section "__DATA, __objc_const", align 8 + +// CHECK: @"_OBJC_CLASS_RO_$_X" = internal global %struct._class_ro_t { i32 2, i32 0, i32 0, ptr null, ptr @OBJC_CLASS_NAME_, ptr ptrauth (ptr @"_OBJC_$_INSTANCE_METHODS_X", i32 2, i64 49936, ptr getelementptr inbounds (%struct._class_ro_t, ptr @"_OBJC_CLASS_RO_$_X", i32 0, i32 5)), ptr null, ptr null, ptr null, ptr null }, section "__DATA, __objc_const", align 8 diff --git a/clang/test/CodeGenObjC/ptrauth-method-list-wrapper-globals.m b/clang/test/CodeGenObjC/ptrauth-method-list-wrapper-globals.m new file mode 100644 index 0000000000000..8cde7f26145fe --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-method-list-wrapper-globals.m @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fptrauth-calls -fobjc-arc -fblocks -fobjc-runtime=ios-7 -triple arm64-apple-ios -emit-llvm -o - %s | FileCheck %s + +// CHECK: @"\01+[C pm1].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01+[C pm1]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth", +// CHECK: @"\01+[C m1].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01+[C m1]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 1, i32 2) to i64), i64 0 }, section "llvm.ptrauth", +// CHECK: @"_OBJC_$_CLASS_METHODS_C" = internal global { i32, i32, [2 x %struct._objc_method] } { i32 24, i32 2, [2 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01+[C pm1].ptrauth" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME{{.*}}, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01+[C m1].ptrauth" }] }, section "__DATA, __objc_const", +// CHECK: @"\01-[C pm0].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01-[C pm0]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_INSTANCE_METHODS_C", i32 0, i32 2, i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth", +// CHECK: @"\01-[C m0].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01-[C m0]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_INSTANCE_METHODS_C", i32 0, i32 2, i32 1, i32 2) to i64), i64 0 }, section "llvm.ptrauth", +// CHECK: @"_OBJC_$_INSTANCE_METHODS_C" = internal global { i32, i32, [2 x %struct._objc_method] } { i32 24, i32 2, [2 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_.3, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01-[C pm0].ptrauth" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME{{.*}} ptr @OBJC_METH_VAR_TYPE_, ptr @"\01-[C m0].ptrauth" }] }, section "__DATA, __objc_const", + +@protocol P +- (void) pm0; ++ (void) pm1; +@end + +@interface C

+- (void) m0; ++ (void) m1; +@end + +@implementation C +- (void) pm0 {} ++ (void) pm1 {} +- (void) m0 {} ++ (void) m1 {} +@end + +void test_method_list(C *c) { + [c m0]; + [C m1]; +} diff --git a/clang/test/CodeGenObjC/ptrauth-method-list.m b/clang/test/CodeGenObjC/ptrauth-method-list.m index 8cde7f26145fe..263d467c432af 100644 --- a/clang/test/CodeGenObjC/ptrauth-method-list.m +++ b/clang/test/CodeGenObjC/ptrauth-method-list.m @@ -1,11 +1,13 @@ -// RUN: %clang_cc1 -fptrauth-calls -fobjc-arc -fblocks -fobjc-runtime=ios-7 -triple arm64-apple-ios -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-calls -fobjc-arc -fblocks -fobjc-runtime=ios-7 -triple arm64-apple-ios -emit-llvm -o - %s | FileCheck %s -// CHECK: @"\01+[C pm1].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01+[C pm1]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth", -// CHECK: @"\01+[C m1].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01+[C m1]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 1, i32 2) to i64), i64 0 }, section "llvm.ptrauth", -// CHECK: @"_OBJC_$_CLASS_METHODS_C" = internal global { i32, i32, [2 x %struct._objc_method] } { i32 24, i32 2, [2 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01+[C pm1].ptrauth" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME{{.*}}, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01+[C m1].ptrauth" }] }, section "__DATA, __objc_const", -// CHECK: @"\01-[C pm0].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01-[C pm0]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_INSTANCE_METHODS_C", i32 0, i32 2, i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth", -// CHECK: @"\01-[C m0].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01-[C m0]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_INSTANCE_METHODS_C", i32 0, i32 2, i32 1, i32 2) to i64), i64 0 }, section "llvm.ptrauth", -// CHECK: @"_OBJC_$_INSTANCE_METHODS_C" = internal global { i32, i32, [2 x %struct._objc_method] } { i32 24, i32 2, [2 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_.3, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01-[C pm0].ptrauth" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME{{.*}} ptr @OBJC_METH_VAR_TYPE_, ptr @"\01-[C m0].ptrauth" }] }, section "__DATA, __objc_const", +// CHECK: @"_OBJC_$_CLASS_METHODS_C" = internal global { i32, i32, [2 x %struct._objc_method] } { i32 24, i32 2, [2 x %struct._objc_method] [ +// CHECK-SAME: %struct._objc_method { ptr @OBJC_METH_VAR_NAME_, ptr @OBJC_METH_VAR_TYPE_, ptr ptrauth (ptr @"\01+[C pm1]", i32 0, i64 0, ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 0, i32 2)) }, +// CHECK-SAME: %struct._objc_method { ptr @OBJC_METH_VAR_NAME_.1, ptr @OBJC_METH_VAR_TYPE_, ptr ptrauth (ptr @"\01+[C m1]", i32 0, i64 0, ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 1, i32 2)) } +// CHECK-SAME: ] }, section "__DATA, __objc_const", +// CHECK: @"_OBJC_$_INSTANCE_METHODS_C" = internal global { i32, i32, [2 x %struct._objc_method] } { i32 24, i32 2, [2 x %struct._objc_method] [ +// CHECK-SAME: %struct._objc_method { ptr @OBJC_METH_VAR_NAME_.3, ptr @OBJC_METH_VAR_TYPE_, ptr ptrauth (ptr @"\01-[C pm0]", i32 0, i64 0, ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_INSTANCE_METHODS_C", i32 0, i32 2, i32 0, i32 2)) }, +// CHECK-SAME: %struct._objc_method { ptr @OBJC_METH_VAR_NAME_.4, ptr @OBJC_METH_VAR_TYPE_, ptr ptrauth (ptr @"\01-[C m0]", i32 0, i64 0, ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_INSTANCE_METHODS_C", i32 0, i32 2, i32 1, i32 2)) } +// CHECK-SAME: ] }, section "__DATA, __objc_const", @protocol P - (void) pm0; From 539fced93d7558c6b6e4d34f576c4ceeb35ceb72 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Wed, 1 May 2024 14:16:17 -0700 Subject: [PATCH 47/58] [clang] Add -fptrauth-objc-isa-masking to mask isa ptrs pre/post auth. --- clang/include/clang/Basic/Features.def | 1 + clang/include/clang/Basic/LangOptions.def | 1 + clang/include/clang/Driver/Options.td | 2 + clang/lib/CodeGen/CodeGenFunction.cpp | 30 +++- clang/lib/CodeGen/CodeGenFunction.h | 2 + clang/lib/CodeGen/CodeGenModule.cpp | 8 ++ clang/lib/CodeGen/CodeGenModule.h | 4 + clang/lib/Frontend/CompilerInvocation.cpp | 5 + clang/test/CodeGen/ptrauth-isa-masking.c | 161 ++++++++++++++++++++++ 9 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 clang/test/CodeGen/ptrauth-isa-masking.c diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 696ed068959df..7d3eff89c9393 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -125,6 +125,7 @@ FEATURE(ptrauth_member_function_pointer_type_discrimination, LangOpts.PointerAut FEATURE(ptrauth_init_fini, LangOpts.PointerAuthInitFini) FEATURE(ptrauth_function_pointer_type_discrimination, LangOpts.PointerAuthFunctionTypeDiscrimination) FEATURE(ptrauth_signed_block_descriptors, LangOpts.PointerAuthBlockDescriptorPointers) +FEATURE(ptrauth_objc_isa_masking, LangOpts.PointerAuthObjcIsaMasking) EXTENSION(swiftcc, PP.getTargetInfo().checkCallingConvention(CC_Swift) == clang::TargetInfo::CCCR_OK) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 20e014b34e1c4..b4cdda866cfeb 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -174,6 +174,7 @@ LANGOPT(PointerAuthInitFini, 1, 0, "sign function pointers in init/fini arrays") BENIGN_LANGOPT(PointerAuthFunctionTypeDiscrimination, 1, 0, "Use type discrimination when signing function pointers") LANGOPT(PointerAuthBlockDescriptorPointers, 1, 0, "enable signed block descriptors") +LANGOPT(PointerAuthObjcIsaMasking, 1, 0, "pre- and post-authentication masking for Objective-C isa pointer") LANGOPT(SoftPointerAuth , 1, 0, "software emulation of pointer authentication") VALUE_LANGOPT(PointerAuthABIVersion, 32, 0, "pointer authentication ABI version") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 8b93db57b1252..61b6c474b3066 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4275,6 +4275,8 @@ defm ptrauth_function_pointer_type_discrimination : OptInCC1FFlag<"ptrauth-funct "Enable type discrimination on C function pointers">; defm ptrauth_block_descriptor_pointers : OptInCC1FFlag<"ptrauth-block-descriptor-pointers", "Enable signing block descriptor pointers">; +defm ptrauth_objc_isa_masking : OptInCC1FFlag<"ptrauth-objc-isa-masking", + "Enable pre- and post-authentication masking mode of Objective-c isa pointers">; } let Group = f_Group in { diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 5a33b258373b4..ff9918ed40d09 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -3171,6 +3171,11 @@ void CodeGenFunction::EmitPointerAuthOperandBundle( Bundles.emplace_back("ptrauth", Args); } +llvm::Value *CodeGenFunction::getObjCIsaMask() { + llvm::Constant *maskAddress = CGM.getObjCIsaMaskAddress(); + return Builder.CreatePtrToInt(maskAddress, IntPtrTy); +} + static llvm::Value *EmitPointerAuthCommon(CodeGenFunction &CGF, const CGPointerAuthInfo &PointerAuth, llvm::Value *Pointer, @@ -3188,11 +3193,22 @@ static llvm::Value *EmitPointerAuthCommon(CodeGenFunction &CGF, // Convert the pointer to intptr_t before signing it. auto OrigType = Pointer->getType(); Pointer = CGF.Builder.CreatePtrToInt(Pointer, CGF.IntPtrTy); + llvm::Value *MaskedBits = nullptr; + if (PointerAuth.isIsaPointer() && + CGF.getLangOpts().PointerAuthObjcIsaMasking) { + llvm::Value *Mask = CGF.getObjCIsaMask(); + llvm::Value *MaskedPointer = CGF.Builder.CreateAnd(Pointer, Mask); + MaskedBits = CGF.Builder.CreateXor(Pointer, MaskedPointer); + Pointer = MaskedPointer; + } // call i64 @llvm.ptrauth.sign.i64(i64 %pointer, i32 %key, i64 %discriminator) auto Intrinsic = CGF.CGM.getIntrinsic(IntrinsicID); Pointer = CGF.EmitRuntimeCall(Intrinsic, {Pointer, Key, Discriminator}); + if (MaskedBits) + Pointer = CGF.Builder.CreateOr(Pointer, MaskedBits); + // Convert back to the original type. Pointer = CGF.Builder.CreateIntToPtr(Pointer, OrigType); return Pointer; @@ -3257,7 +3273,8 @@ CodeGenFunction::EmitPointerAuthResignCall(llvm::Value *value, if (curAuth.getAuthenticationMode() != PointerAuthenticationMode::SignAndAuth || newAuth.getAuthenticationMode() != - PointerAuthenticationMode::SignAndAuth) { + PointerAuthenticationMode::SignAndAuth || + curAuth.isIsaPointer() != newAuth.isIsaPointer()) { auto authedValue = EmitPointerAuthAuth(curAuth, value); return EmitPointerAuthSign(newAuth, authedValue); } @@ -3265,6 +3282,14 @@ CodeGenFunction::EmitPointerAuthResignCall(llvm::Value *value, auto origType = value->getType(); value = Builder.CreatePtrToInt(value, IntPtrTy); + llvm::Value *masked_bits = nullptr; + if (curAuth.isIsaPointer() && getLangOpts().PointerAuthObjcIsaMasking) { + llvm::Value *mask = getObjCIsaMask(); + auto masked_value = Builder.CreateAnd(value, mask); + masked_bits = Builder.CreateXor(value, masked_value); + value = masked_value; + } + auto curKey = Builder.getInt32(curAuth.getKey()); auto newKey = Builder.getInt32(newAuth.getKey()); @@ -3283,6 +3308,9 @@ CodeGenFunction::EmitPointerAuthResignCall(llvm::Value *value, value = EmitRuntimeCall( intrinsic, {value, curKey, curDiscriminator, newKey, newDiscriminator}); + if (masked_bits) + value = Builder.CreateOr(value, masked_bits); + // Convert back to the original type. value = Builder.CreateIntToPtr(value, origType); return value; diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 7d2dbf75d9692..c26c2d9e56e03 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2303,6 +2303,8 @@ class CodeGenFunction : public CodeGenTypeCache { const ObjCPropertyImplDecl *propImpl, llvm::Constant *AtomicHelperFn); + llvm::Value *getObjCIsaMask(); + //===--------------------------------------------------------------------===// // Block Bits //===--------------------------------------------------------------------===// diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index aac32d1471963..1d779bc5049ac 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -7743,3 +7743,11 @@ void CodeGenModule::moveLazyEmissionStates(CodeGenModule *NewBuilder) { NewBuilder->ABI->MangleCtx = std::move(ABI->MangleCtx); } + +llvm::Constant *CodeGenModule::getObjCIsaMaskAddress() { + if (!ObjCIsaMaskAddress) { + ObjCIsaMaskAddress = GetOrCreateLLVMGlobal( + "objc_absolute_packed_isa_class_mask", Int8Ty, LangAS::Default, nullptr); + } + return ObjCIsaMaskAddress; +} diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 51979b8cb69fa..88095984b0a41 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -621,6 +621,8 @@ class CodeGenModule : public CodeGenTypeCache { std::optional computeVTPointerAuthentication(const CXXRecordDecl *ThisClass); + llvm::Constant *ObjCIsaMaskAddress = nullptr; + public: CodeGenModule(ASTContext &C, IntrusiveRefCntPtr FS, const HeaderSearchOptions &headersearchopts, @@ -1016,6 +1018,8 @@ class CodeGenModule : public CodeGenTypeCache { CGPointerAuthInfo EmitPointerAuthInfo(const RecordDecl *RD); + llvm::Constant *getObjCIsaMaskAddress(); + // 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/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 6b0ff6cfaf558..17c5dedcdfd80 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3747,6 +3747,9 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts, if (Opts.PointerAuthKernelABIVersion) GenerateArg(Consumer, OPT_fptrauth_kernel_abi_version); } + + if (Opts.PointerAuthObjcIsaMasking) + GenerateArg(Consumer, OPT_fptrauth_objc_isa_masking); } static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, @@ -3766,6 +3769,8 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, Opts.PointerAuthBlockDescriptorPointers = Args.hasArg(OPT_fptrauth_block_descriptor_pointers); + Opts.PointerAuthObjcIsaMasking = Args.hasArg(OPT_fptrauth_objc_isa_masking); + Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos); Opts.SoftPointerAuth = Args.hasArg(OPT_fptrauth_soft); diff --git a/clang/test/CodeGen/ptrauth-isa-masking.c b/clang/test/CodeGen/ptrauth-isa-masking.c new file mode 100644 index 0000000000000..607ff50b381ad --- /dev/null +++ b/clang/test/CodeGen/ptrauth-isa-masking.c @@ -0,0 +1,161 @@ +// RUN: %clang_cc1 -cc1 -nostdsysteminc -fptrauth-intrinsics -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-auth -fptrauth-objc-isa-masking -triple arm64-apple-ios -emit-llvm -O0 -disable-llvm-passes -o - %s | FileCheck %s +#if __has_feature(ptrauth_qualifier_authentication_mode) +#define test_ptrauth(a...) __ptrauth(a) +#else +#define test_ptrauth(a...) +#endif +typedef struct { + void *test_ptrauth(2, 0, 0, "isa-pointer") isa_0_0; + void *test_ptrauth(2, 1, 0, "isa-pointer") isa_1_0; + // Just using distinct discriminators + void *test_ptrauth(2, 0, 13, "isa-pointer") isa_0_13; + void *test_ptrauth(2, 1, 17, "isa-pointer") isa_1_17; + void *test_ptrauth(2, 1, 17, "") non_isa; + +} TestStruct1; + +// CHECK: @objc_absolute_packed_isa_class_mask = external global i8 + +// CHECK: define void @testWrite( +void testWrite(TestStruct1 *t, void *p) { + t->isa_0_0 = p; + // CHECK: [[VALUE:%.*]] = ptrtoint ptr + // CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) + // CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[POINTER]], i32 2, i64 0) + // CHECK: [[RETAGGED:%.*]] = or i64 [[SIGNED]], [[TAG_BITS]] + + t->isa_1_0 = p; + // CHECK: resign.nonnull1: + // CHECK: [[VALUE:%.*]] = ptrtoint ptr [[TMP:%.*]] to i64 + // CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) + // CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[POINTER]], i32 2 + // CHECK: [[RETAGGED:%.*]] = or i64 [[SIGNED]], [[TAG_BITS]] + + t->isa_0_13 = p; + // CHECK: resign.nonnull3: + // CHECK: [[VALUE:%.*]] = ptrtoint ptr [[TMP:%.*]] to i64 + // CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) + // CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[POINTER]], i32 2, i64 13) + // CHECK: [[RETAGGED:%.*]] = or i64 [[SIGNED]], [[TAG_BITS]] + + t->isa_1_17 = p; + // CHECK: resign.nonnull5: + // CHECK: [[VALUE:%.*]] = ptrtoint ptr [[TMP:%.*]] to i64 + // CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) + // CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[POINTER]], i32 2 + // CHECK: [[RETAGGED:%.*]] = or i64 [[SIGNED]], [[TAG_BITS]] + + t->non_isa = p; + // CHECK: resign.nonnull7: + // CHECK: [[POINTER:%.*]] = ptrtoint ptr [[TMP:%.*]] to i64 + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[POINTER]], i32 2 +} + +// CHECK: define void @testRead +void testRead(TestStruct1 *t, void *p[5]) { + p[0] = t->isa_0_0; + // CHECK: resign.nonnull: + // CHECK: [[VALUE:%.*]] = ptrtoint ptr [[TMP:%.*]] to i64 + // CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) + // CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[POINTER]], i32 2, i64 0) + // CHECK: [[RETAGGED:%.*]] = or i64 [[AUTHED]], [[TAG_BITS]] + + p[1] = t->isa_1_0; + // CHECK: resign.nonnull1: + // CHECK: [[VALUE:%.*]] = ptrtoint ptr [[TMP:%.*]] to i64 + // CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) + // CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[POINTER]], i32 2, + // CHECK: [[RETAGGED:%.*]] = or i64 [[AUTHED]], [[TAG_BITS]] + + p[2] = t->isa_0_13; + // CHECK: resign.nonnull4: + // CHECK: [[VALUE:%.*]] = ptrtoint ptr [[TMP:%.*]] to i64 + // CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) + // CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[POINTER]], i32 2, i64 13) + // CHECK: [[RETAGGED:%.*]] = or i64 [[AUTHED]], [[TAG_BITS]] + + p[3] = t->isa_1_17; + // CHECK: resign.nonnull7: + // CHECK: [[VALUE:%.*]] = ptrtoint ptr [[TMP:%.*]] to i64 + // CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) + // CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[POINTER]] + // CHECK: [[RETAGGED:%.*]] = or i64 [[AUTHED]], [[TAG_BITS]] + + p[4] = t->non_isa; + // CHECK: resign.nonnull10: + // CHECK: [[VALUE:%.*]] = ptrtoint ptr [[TMP:%.*]] to i64 + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]] +} + +// CHECK: define void @conditional_mask_write +void conditional_mask_write(TestStruct1 *t, void *p[2]) { + if (p[0]) { + t->isa_0_0 = p[0]; + // CHECK: [[VALUE:%.*]] = ptrtoint ptr + // CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) + // CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[POINTER]] + // CHECK: [[RETAGGED:%.*]] = or i64 [[SIGNED]], [[TAG_BITS]] + } + t->isa_1_0 = p[1]; + // CHECK: resign.nonnull3: + // CHECK: [[VALUE:%.*]] = ptrtoint ptr + // CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) + // CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] + // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[POINTER]] + // CHECK: [[RETAGGED:%.*]] = or i64 [[SIGNED]], [[TAG_BITS]] +} + +void conditional_mask_read(TestStruct1 *t, void *p[2]) { + if (p[0]) { + p[0] = t->isa_0_0; + // CHECK: [[VALUE:%.*]] = ptrtoint ptr + // CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) + // CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[POINTER]] + // CHECK: [[RETAGGED:%.*]] = or i64 [[AUTHED]], [[TAG_BITS]] + } + p[1] = t->isa_1_0; + // CHECK: resign.nonnull2: + // CHECK: [[VALUE:%.*]] = ptrtoint ptr + // CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) + // CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] + // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[POINTER]] + // CHECK: [[RETAGGED:%.*]] = or i64 [[AUTHED]], [[TAG_BITS]] +} + +// CHECK-LABEL: define void @copy +void copy(TestStruct1 *x, TestStruct1 *y) { + *x = *y; +} + +// CHECK: define linkonce_odr hidden void @__copy_assignment +// CHECK: resign.nonnull: +// CHECK: [[VALUE:%.*]] = ptrtoint ptr [[TMP:%.*]] to i64 +// CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) +// CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] +// CHECK: [[RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[POINTER]], i32 2, +// CHECK: [[RETAGGED:%.*]] = or i64 [[RESIGNED]], [[TAG_BITS]] +// CHECK: [[ADDR1:%.*]] = ptrtoint ptr +// CHECK: [[BLEND1:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[ADDR1]], i64 17) +// CHECK: [[ADDR2:%.*]] = ptrtoint ptr +// CHECK: [[BLEND2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[ADDR2]], i64 17) +// CHECK: [[VALUE:%.*]] = ptrtoint ptr +// CHECK: [[POINTER:%.*]] = and i64 [[VALUE]], ptrtoint (ptr @objc_absolute_packed_isa_class_mask to i64) +// CHECK: [[TAG_BITS:%.*]] = xor i64 [[VALUE]], [[POINTER]] +// CHECK: [[RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[POINTER]], i32 2, i64 [[BLEND1]], i32 2, i64 [[BLEND2]]) +// CHECK: [[RETAGGED:%.*]] = or i64 [[RESIGNED]], [[TAG_BITS]] +// CHECK: [[ADDR1:%.*]] = ptrtoint ptr +// CHECK: [[BLEND1:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[ADDR1]], i64 17) +// CHECK: [[ADDR2:%.*]] = ptrtoint ptr +// CHECK: [[BLEND2:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[ADDR2]], i64 17) +// CHECK: [[POINTER:%.*]] = ptrtoint ptr +// CHECK: [[RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[POINTER]], i32 2, i64 [[BLEND1]], i32 2, i64 [[BLEND2]]) From 921369ffac77ce74d4f3ec3ac07dc8c340369a63 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Wed, 3 Jul 2024 09:56:08 -0700 Subject: [PATCH 48/58] [clang] Sign objc isa and super pointers. --- clang/docs/PointerAuthentication.rst | 6 ++ clang/include/clang/Basic/Features.def | 4 + clang/include/clang/Basic/LangOptions.def | 2 + clang/include/clang/Basic/LangOptions.h | 1 + .../include/clang/Basic/PointerAuthOptions.h | 14 +++ clang/include/clang/Driver/Options.td | 6 ++ clang/lib/CodeGen/CGBlocks.cpp | 14 ++- clang/lib/CodeGen/CGObjCMac.cpp | 13 ++- clang/lib/CodeGen/CodeGenModule.cpp | 4 +- clang/lib/Driver/ToolChains/Clang.cpp | 4 + clang/lib/Frontend/CompilerInvocation.cpp | 46 ++++++++++ clang/lib/Headers/ptrauth.h | 4 + .../ptrauth-cfstr-isa-wrapper-globals.c | 27 ++++++ clang/test/CodeGen/ptrauth-cfstr-isa.c | 24 +++++ .../ptrauth-blocks-wrapper-globals.m | 61 +++++++++++++ clang/test/CodeGenObjC/ptrauth-blocks.m | 25 ++++-- .../ptrauth-isa-auth-mode-wrapper-globals.m | 89 +++++++++++++++++++ .../test/CodeGenObjC/ptrauth-isa-auth-mode.m | 87 ++++++++++++++++++ .../ptrauth-isa-super-wrapper-globals.m | 58 ++++++++++++ clang/test/CodeGenObjC/ptrauth-isa-super.m | 54 +++++++++++ .../ptrauth-h-isa-authentication-config.c | 19 ++++ .../Preprocessor/ptrauth_objc_isa_feature.c | 11 +++ 22 files changed, 560 insertions(+), 13 deletions(-) create mode 100644 clang/test/CodeGen/ptrauth-cfstr-isa-wrapper-globals.c create mode 100644 clang/test/CodeGen/ptrauth-cfstr-isa.c create mode 100644 clang/test/CodeGenObjC/ptrauth-blocks-wrapper-globals.m create mode 100644 clang/test/CodeGenObjC/ptrauth-isa-auth-mode-wrapper-globals.m create mode 100644 clang/test/CodeGenObjC/ptrauth-isa-auth-mode.m create mode 100644 clang/test/CodeGenObjC/ptrauth-isa-super-wrapper-globals.m create mode 100644 clang/test/CodeGenObjC/ptrauth-isa-super.m create mode 100644 clang/test/Preprocessor/ptrauth-h-isa-authentication-config.c create mode 100644 clang/test/Preprocessor/ptrauth_objc_isa_feature.c diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst index 89740bc082956..1e856f2698da1 100644 --- a/clang/docs/PointerAuthentication.rst +++ b/clang/docs/PointerAuthentication.rst @@ -1673,6 +1673,12 @@ The Objective-C runtime provides additional protection to methods that have been loaded into the Objective-C method cache; this protection is private to the runtime. +Objective-C ``isa`` and ``super`` Pointers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Objective-C ``isa`` and ``super`` pointers are both signed with the ``DA`` key +with constant discriminators of 0x6AE1 and 0x25DA respectively. + Swift Class Methods ~~~~~~~~~~~~~~~~~~~ diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 7d3eff89c9393..ec406d6c5457e 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -125,6 +125,10 @@ FEATURE(ptrauth_member_function_pointer_type_discrimination, LangOpts.PointerAut FEATURE(ptrauth_init_fini, LangOpts.PointerAuthInitFini) FEATURE(ptrauth_function_pointer_type_discrimination, LangOpts.PointerAuthFunctionTypeDiscrimination) FEATURE(ptrauth_signed_block_descriptors, LangOpts.PointerAuthBlockDescriptorPointers) +FEATURE(ptrauth_objc_isa, LangOpts.getPointerAuthObjcIsaAuthentication() == PointerAuthenticationMode::SignAndStrip || LangOpts.getPointerAuthObjcIsaAuthentication() == PointerAuthenticationMode::SignAndAuth) +FEATURE(ptrauth_objc_isa_strips, LangOpts.getPointerAuthObjcIsaAuthentication() == PointerAuthenticationMode::Strip || LangOpts.getPointerAuthObjcIsaAuthentication() == PointerAuthenticationMode::SignAndStrip) +FEATURE(ptrauth_objc_isa_signs, LangOpts.getPointerAuthObjcIsaAuthentication() == PointerAuthenticationMode::SignAndStrip || LangOpts.getPointerAuthObjcIsaAuthentication() == PointerAuthenticationMode::SignAndAuth) +FEATURE(ptrauth_objc_isa_authenticates, LangOpts.getPointerAuthObjcIsaAuthentication() == PointerAuthenticationMode::SignAndAuth) FEATURE(ptrauth_objc_isa_masking, LangOpts.PointerAuthObjcIsaMasking) EXTENSION(swiftcc, PP.getTargetInfo().checkCallingConvention(CC_Swift) == diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index b4cdda866cfeb..954b1bfe32355 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -174,6 +174,8 @@ LANGOPT(PointerAuthInitFini, 1, 0, "sign function pointers in init/fini arrays") BENIGN_LANGOPT(PointerAuthFunctionTypeDiscrimination, 1, 0, "Use type discrimination when signing function pointers") LANGOPT(PointerAuthBlockDescriptorPointers, 1, 0, "enable signed block descriptors") +ENUM_LANGOPT(PointerAuthObjcIsaAuthentication, PointerAuthenticationMode, 2, + PointerAuthenticationMode::None, "authentication mode for objc isa") LANGOPT(PointerAuthObjcIsaMasking, 1, 0, "pre- and post-authentication masking for Objective-C isa pointer") LANGOPT(SoftPointerAuth , 1, 0, "software emulation of pointer authentication") diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 72eddb243ea32..0062118fec690 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -84,6 +84,7 @@ class LangOptionsBase { public: using Visibility = clang::Visibility; using RoundingMode = llvm::RoundingMode; + using PointerAuthenticationMode = clang::PointerAuthenticationMode; enum GCMode { NonGC, GCOnly, HybridGC }; enum StackProtectorMode { SSPOff, SSPOn, SSPStrong, SSPReq }; diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index f5e29ddef71f4..68412e41c19eb 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -25,6 +25,14 @@ namespace clang { constexpr unsigned PointerAuthKeyNone = -1; +/// Constant discriminator to be used with objective-c isa pointers. The value +/// is ptrauth_string_discriminator("isa") +constexpr uint16_t IsaPointerConstantDiscriminator = 0x6AE1; + +/// Constant discriminator to be used with objective-c superclass pointers. +/// The value is ptrauth_string_discriminator("objc_class:superclass") +constexpr uint16_t SuperPointerConstantDiscriminator = 0xB5AB; + /// Constant discriminator to be used with block descriptor pointers. The value /// is ptrauth_string_discriminator("block_descriptor") constexpr uint16_t BlockDescriptorConstantDiscriminator = 0xC0BB; @@ -262,6 +270,12 @@ struct PointerAuthOptions { /// The ABI for a reference to an Objective-C method list in _class_ro_t. PointerAuthSchema ObjCMethodListPointer; + + /// The ABI for Objective-C isa pointers. + PointerAuthSchema ObjCIsaPointers; + + /// The ABI for Objective-C superclass pointers. + PointerAuthSchema ObjCSuperPointers; }; } // end namespace clang diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 61b6c474b3066..982d7b9e50fc2 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4275,11 +4275,17 @@ defm ptrauth_function_pointer_type_discrimination : OptInCC1FFlag<"ptrauth-funct "Enable type discrimination on C function pointers">; defm ptrauth_block_descriptor_pointers : OptInCC1FFlag<"ptrauth-block-descriptor-pointers", "Enable signing block descriptor pointers">; +defm ptrauth_objc_isa : OptInCC1FFlag<"ptrauth-objc-isa", + "Enable signing and authentication of Objective-C object's 'isa' field">; defm ptrauth_objc_isa_masking : OptInCC1FFlag<"ptrauth-objc-isa-masking", "Enable pre- and post-authentication masking mode of Objective-c isa pointers">; } let Group = f_Group in { + def fptrauth_objc_isa_mode : Joined<["-"], "fptrauth-objc-isa-mode=">, + Visibility<[ClangOption, CC1Option, CC1AsOption]>, + HelpText<"Authentication mode for ObjC isa field. Full auth if unspecified">, Values<"strip,sign-and-strip,sign-and-auth">; + def fptrauth_abi_version_EQ : Joined<["-"], "fptrauth-abi-version=">, Visibility<[ClangOption, CC1Option, CC1AsOption]>, HelpText<"Pointer Authentication ABI version">; diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index 341cf400f9926..39cf630dfdf8e 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -867,7 +867,9 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { }; if (!IsOpenCL) { - addHeaderField(isa, getPointerSize(), "block.isa"); + addSignedHeaderField( + isa, CGM.getCodeGenOpts().PointerAuth.ObjCIsaPointers, GlobalDecl(), + QualType(), getPointerSize(), "block.isa"); addHeaderField(llvm::ConstantInt::get(IntTy, flags.getBitMask()), getIntSize(), "block.flags"); addHeaderField(llvm::ConstantInt::get(IntTy, 0), getIntSize(), @@ -1348,8 +1350,14 @@ static llvm::Constant *buildGlobalBlock(CodeGenModule &CGM, // isa if (IsWindows) fields.addNullPointer(CGM.Int8PtrPtrTy); - else - fields.add(CGM.getNSConcreteGlobalBlock()); + else { + if (auto authentication = + CGM.getCodeGenOpts().PointerAuth.ObjCIsaPointers) { + fields.addSignedPointer(CGM.getNSConcreteGlobalBlock(), authentication, + GlobalDecl(), QualType()); + } else + fields.add(CGM.getNSConcreteGlobalBlock()); + } fields.addInt(CGM.IntTy, flags.getBitMask()); diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index aab124b6b5bba..3ba9b5df9127f 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -2007,7 +2007,9 @@ CGObjCCommonMac::GenerateConstantNSString(const StringLiteral *Literal) { auto Fields = Builder.beginStruct(NSConstantStringType); // Class pointer. - Fields.add(Class); + Fields.addSignedPointer( + Class, CGM.getCodeGenOpts().PointerAuth.ObjCIsaPointers, GlobalDecl(), + QualType()); // String pointer. llvm::Constant *C = @@ -6398,9 +6400,14 @@ CGObjCNonFragileABIMac::BuildClassObject(const ObjCInterfaceDecl *CI, bool HiddenVisibility) { ConstantInitBuilder builder(CGM); auto values = builder.beginStruct(ObjCTypes.ClassnfABITy); - values.add(IsAGV); + values.addSignedPointer( + IsAGV, CGM.getCodeGenOpts().PointerAuth.ObjCIsaPointers, GlobalDecl(), + QualType()); if (SuperClassGV) { - values.add(SuperClassGV); + values.addSignedPointer( + SuperClassGV, + CGM.getCodeGenOpts().PointerAuth.ObjCSuperPointers, GlobalDecl(), + QualType()); } else { values.addNullPointer(ObjCTypes.ClassnfABIPtrTy); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 1d779bc5049ac..fe8111d0e3907 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -6244,7 +6244,9 @@ CodeGenModule::GetAddrOfConstantCFString(const StringLiteral *Literal) { auto Fields = Builder.beginStruct(STy); // Class pointer. - Fields.add(cast(CFConstantStringClassRef)); + Fields.addSignedPointer( + cast(CFConstantStringClassRef), + getCodeGenOpts().PointerAuth.ObjCIsaPointers, GlobalDecl(), QualType()); // Flags. if (IsSwiftABI) { diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 8df4f3bee7c10..4d79b6d794fc7 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1800,6 +1800,10 @@ void Clang::AddAArch64TargetArgs(const ArgList &Args, CmdArgs, options::OPT_fptrauth_function_pointer_type_discrimination, options::OPT_fno_ptrauth_function_pointer_type_discrimination); + if (Args.hasFlag(options::OPT_fptrauth_objc_isa, + options::OPT_fno_ptrauth_objc_isa, false)) + CmdArgs.push_back("-fptrauth-objc-isa-mode=sign-and-auth"); + Args.addOptInFlag(CmdArgs, options::OPT_fptrauth_block_descriptor_pointers, options::OPT_fno_ptrauth_block_descriptor_pointers); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 17c5dedcdfd80..5d32a8eb8f0da 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1676,6 +1676,16 @@ void CompilerInvocation::setDefaultPointerAuthOptions( Opts.ObjCMethodListPointer = PointerAuthSchema(Key::ASDA, true, Discrimination::Constant, MethodListPointerConstantDiscriminator); + + auto IsaAuthenticationMode = LangOpts.getPointerAuthObjcIsaAuthentication(); + if (IsaAuthenticationMode != PointerAuthenticationMode::None) { + Opts.ObjCIsaPointers = PointerAuthSchema( + Key::ASDA, true, IsaAuthenticationMode, Discrimination::Constant, + IsaPointerConstantDiscriminator, true); + Opts.ObjCSuperPointers = PointerAuthSchema( + Key::ASDA, true, IsaAuthenticationMode, Discrimination::Constant, + SuperPointerConstantDiscriminator); + } } Opts.IndirectGotos = LangOpts.PointerAuthIndirectGotos; Opts.ReturnAddresses = LangOpts.PointerAuthReturns; @@ -3748,6 +3758,24 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts, GenerateArg(Consumer, OPT_fptrauth_kernel_abi_version); } + { + StringRef Value; + switch (Opts.getPointerAuthObjcIsaAuthentication()) { + case PointerAuthenticationMode::None: + break; + case PointerAuthenticationMode::Strip: + Value = PointerAuthenticationOptionStrip; + break; + case PointerAuthenticationMode::SignAndStrip: + Value = PointerAuthenticationOptionSignAndStrip; + break; + case PointerAuthenticationMode::SignAndAuth: + Value = PointerAuthenticationOptionSignAndAuth; + break; + } + if (!Value.empty()) + GenerateArg(Consumer, OPT_fptrauth_objc_isa_mode, Value); + } if (Opts.PointerAuthObjcIsaMasking) GenerateArg(Consumer, OPT_fptrauth_objc_isa_masking); } @@ -3769,6 +3797,24 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, Opts.PointerAuthBlockDescriptorPointers = Args.hasArg(OPT_fptrauth_block_descriptor_pointers); + if (auto modeArg = Args.getLastArg(OPT_fptrauth_objc_isa_mode)) { + StringRef Value = modeArg->getValue(); + std::optional isaAuthenticationMode = + llvm::StringSwitch>(Value) + .Case(PointerAuthenticationOptionStrip, + PointerAuthenticationMode::Strip) + .Case(PointerAuthenticationOptionSignAndStrip, + PointerAuthenticationMode::SignAndStrip) + .Case(PointerAuthenticationOptionSignAndAuth, + PointerAuthenticationMode::SignAndAuth) + .Default(std::nullopt); + if (!isaAuthenticationMode) { + Diags.Report(diag::err_drv_unsupported_option_argument) + << modeArg->getOption().getName() << modeArg->getValue(); + return; + } + Opts.setPointerAuthObjcIsaAuthentication(*isaAuthenticationMode); + } Opts.PointerAuthObjcIsaMasking = Args.hasArg(OPT_fptrauth_objc_isa_masking); Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos); diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h index 627d2bcd67c71..a741aeed20f86 100644 --- a/clang/lib/Headers/ptrauth.h +++ b/clang/lib/Headers/ptrauth.h @@ -62,6 +62,10 @@ typedef enum { representations of values of block-pointer type) are not signed. */ ptrauth_key_block_function = ptrauth_key_process_independent_code, + /* The key used to sign Objective-C isa and super pointers. */ + ptrauth_key_objc_isa_pointer = ptrauth_key_process_independent_data, + ptrauth_key_objc_super_pointer = ptrauth_key_process_independent_data, + /* The key used to sign block descriptor pointers. */ ptrauth_key_block_descriptor_pointer = ptrauth_key_process_independent_data, diff --git a/clang/test/CodeGen/ptrauth-cfstr-isa-wrapper-globals.c b/clang/test/CodeGen/ptrauth-cfstr-isa-wrapper-globals.c new file mode 100644 index 0000000000000..af34211899724 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-cfstr-isa-wrapper-globals.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -cc1 -internal-isystem /Users/oliver/llvm-internal/debug/lib/clang/11.0.0/include -nostdsysteminc -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-strip -triple arm64-apple-ios -emit-llvm -O2 -disable-llvm-passes -o - %s | FileCheck %s +// RUN: %clang_cc1 -cc1 -internal-isystem /Users/oliver/llvm-internal/debug/lib/clang/11.0.0/include -nostdsysteminc -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-auth -triple arm64-apple-ios -emit-llvm -O2 -disable-llvm-passes -o - %s | FileCheck %s + +#define CFSTR __builtin___CFStringMakeConstantString + +void f() { + CFSTR("Hello, World!"); +} + +const void *G = CFSTR("yo joe"); + +void h() { + static const void *h = CFSTR("Goodbye, World!"); +} + +// CHECK: @__CFConstantStringClassReference = external global [0 x i32] +// CHECK: @__CFConstantStringClassReference.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__CFConstantStringClassReference, i32 2, i64 ptrtoint (ptr @_unnamed_cfstring_ to i64), i64 27361 }, section "llvm.ptrauth", align 8 +// CHECK: @.str = private unnamed_addr constant [14 x i8] c"Hello, World!\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @_unnamed_cfstring_ = private global %struct.__NSConstantString_tag { ptr @__CFConstantStringClassReference.ptrauth, i32 1992, ptr @.str, i64 13 }, section "__DATA,__cfstring" +// CHECK: @__CFConstantStringClassReference.ptrauth.1 = private constant { ptr, i32, i64, i64 } { ptr @__CFConstantStringClassReference, i32 2, i64 ptrtoint (ptr @_unnamed_cfstring_.3 to i64), i64 27361 }, section "llvm.ptrauth" +// CHECK: @.str.2 = private unnamed_addr constant [7 x i8] c"yo joe\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @_unnamed_cfstring_.3 = private global %struct.__NSConstantString_tag { ptr @__CFConstantStringClassReference.ptrauth.1, i32 1992, ptr @.str.2, i64 6 }, section "__DATA,__cfstring" +// CHECK: @G = global ptr @_unnamed_cfstring_.3 +// CHECK: @h.h = internal global ptr @_unnamed_cfstring_.6 +// CHECK: @__CFConstantStringClassReference.ptrauth.4 = private constant { ptr, i32, i64, i64 } { ptr @__CFConstantStringClassReference, i32 2, i64 ptrtoint (ptr @_unnamed_cfstring_.6 to i64), i64 27361 }, section "llvm.ptrauth" +// CHECK: @.str.5 = private unnamed_addr constant [16 x i8] c"Goodbye, World!\00", section "__TEXT,__cstring,cstring_literals" +// CHECK: @_unnamed_cfstring_.6 = private global %struct.__NSConstantString_tag { ptr @__CFConstantStringClassReference.ptrauth.4, i32 1992, ptr @.str.5, i64 15 }, section "__DATA,__cfstring" diff --git a/clang/test/CodeGen/ptrauth-cfstr-isa.c b/clang/test/CodeGen/ptrauth-cfstr-isa.c new file mode 100644 index 0000000000000..17fbdfee5cf5b --- /dev/null +++ b/clang/test/CodeGen/ptrauth-cfstr-isa.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -cc1 -internal-isystem /Users/oliver/llvm-internal/debug/lib/clang/11.0.0/include -nostdsysteminc -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-strip -triple arm64-apple-ios -emit-llvm -O2 -disable-llvm-passes -o - %s | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -cc1 -internal-isystem /Users/oliver/llvm-internal/debug/lib/clang/11.0.0/include -nostdsysteminc -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-auth -triple arm64-apple-ios -emit-llvm -O2 -disable-llvm-passes -o - %s | FileCheck %s + +#define CFSTR __builtin___CFStringMakeConstantString + +void f() { + CFSTR("Hello, World!"); +} + +const void *G = CFSTR("yo joe"); + +void h() { + static const void *h = CFSTR("Goodbye, World!"); +} + +// CHECK: @__CFConstantStringClassReference = external global [0 x i32] +// CHECK: @.str = private unnamed_addr constant [14 x i8] c"Hello, World!\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @_unnamed_cfstring_ = private global %struct.__NSConstantString_tag { ptr ptrauth (ptr @__CFConstantStringClassReference, i32 2, i64 27361, ptr @_unnamed_cfstring_), i32 1992, ptr @.str, i64 13 }, section "__DATA,__cfstring" +// CHECK: @.str.1 = private unnamed_addr constant [7 x i8] c"yo joe\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @_unnamed_cfstring_.2 = private global %struct.__NSConstantString_tag { ptr ptrauth (ptr @__CFConstantStringClassReference, i32 2, i64 27361, ptr @_unnamed_cfstring_.2), i32 1992, ptr @.str.1, i64 6 }, section "__DATA,__cfstring" +// CHECK: @G = global ptr @_unnamed_cfstring_.2 +// CHECK: @h.h = internal global ptr @_unnamed_cfstring_.4 +// CHECK: @.str.3 = private unnamed_addr constant [16 x i8] c"Goodbye, World!\00", section "__TEXT,__cstring,cstring_literals" +// CHECK: @_unnamed_cfstring_.4 = private global %struct.__NSConstantString_tag { ptr ptrauth (ptr @__CFConstantStringClassReference, i32 2, i64 27361, ptr @_unnamed_cfstring_.4), i32 1992, ptr @.str.3, i64 15 }, section "__DATA,__cfstring" diff --git a/clang/test/CodeGenObjC/ptrauth-blocks-wrapper-globals.m b/clang/test/CodeGenObjC/ptrauth-blocks-wrapper-globals.m new file mode 100644 index 0000000000000..f626d0491fd66 --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-blocks-wrapper-globals.m @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -fptrauth-calls -fobjc-arc -fblocks -fobjc-runtime=ios-7 -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s + +void (^blockptr)(void); + +// CHECK: [[INVOCATION_1:@.*]] = private constant { ptr, i32, i64, i64 } { ptr {{@.*}}, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ ptr, i32, i32, ptr, ptr }, ptr [[GLOBAL_BLOCK_1:@.*]], i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: [[GLOBAL_BLOCK_1]] = internal constant { ptr, i32, i32, ptr, ptr } { ptr @_NSConcreteGlobalBlock, i32 1342177280, i32 0, ptr [[INVOCATION_1]], +void (^globalblock)(void) = ^{}; + +// CHECK: [[COPYDISPOSE_COPY:@.*]] = private constant { ptr, i32, i64, i64 } { ptr {{@.*}}, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i64, i64, ptr, ptr, ptr, i64 }, ptr [[COPYDISPOSE_DESCRIPTOR:@.*]], i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: [[COPYDISPOSE_DISPOSE:@.*]] = private constant { ptr, i32, i64, i64 } { ptr {{@.*}}, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i64, i64, ptr, ptr, ptr, i64 }, ptr [[COPYDISPOSE_DESCRIPTOR]], i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: [[COPYDISPOSE_DESCRIPTOR:@.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, ptr, ptr, ptr, i64 } { i64 0, i64 40, ptr [[COPYDISPOSE_COPY]], ptr [[COPYDISPOSE_DISPOSE]], + +@interface A +- (int) count; +@end + +// CHECK-LABEL: define void @test_block_call() +void test_block_call() { + // CHECK: [[BLOCK:%.*]] = load ptr, ptr @blockptr, + // CHECK-NEXT: [[FNADDR:%.*]] = getelementptr inbounds {{.*}}, ptr [[BLOCK]], i32 0, i32 3 + // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[FNADDR]], + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint ptr [[FNADDR]] to i64 + // CHECK-NEXT: call void [[T0]](ptr noundef [[BLOCK]]) [ "ptrauth"(i32 0, i64 [[DISC]]) ] + blockptr(); +} + +void use_block(int (^)(void)); + +// CHECK-LABEL: define void @test_block_literal( +void test_block_literal(int i) { + // CHECK: [[I:%.*]] = alloca i32, + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:.*]], align + // CHECK: [[FNPTRADDR:%.*]] = getelementptr inbounds [[BLOCK_T]], ptr [[BLOCK]], i32 0, i32 3 + // CHECK-NEXT: [[DISCRIMINATOR:%.*]] = ptrtoint ptr [[FNPTRADDR]] to i64 + // CHECK-NEXT: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr {{@.*}} to i64), i32 0, i64 [[DISCRIMINATOR]]) + // CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK-NEXT: store ptr [[T0]], ptr [[FNPTRADDR]] + use_block(^{return i;}); +} + +// CHECK-LABEL: define void @test_copy_destroy +void test_copy_destroy(A *a) { + // CHECK: [[COPYDISPOSE_DESCRIPTOR]] + use_block(^{return [a count];}); +} + +// CHECK-LABEL: define void @test_byref_copy_destroy +void test_byref_copy_destroy(A *a) { + // CHECK: [[COPY_FIELD:%.*]] = getelementptr inbounds [[BYREF_T:%.*]], ptr [[BYREF:%.*]], i32 0, i32 4 + // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[COPY_FIELD]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr {{@.*}} to i64), i32 0, i64 [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr + // CHECK-NEXT: store ptr [[T2]], ptr [[COPY_FIELD]], align 8 + // CHECK: [[DISPOSE_FIELD:%.*]] = getelementptr inbounds [[BYREF_T]], ptr [[BYREF]], i32 0, i32 5 + // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[DISPOSE_FIELD]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr {{@.*}} to i64), i32 0, i64 [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr + // CHECK-NEXT: store ptr [[T2]], ptr [[DISPOSE_FIELD]], align 8 + __block A *aweak = a; + use_block(^{return [aweak count];}); +} diff --git a/clang/test/CodeGenObjC/ptrauth-blocks.m b/clang/test/CodeGenObjC/ptrauth-blocks.m index f626d0491fd66..9422cc9345108 100644 --- a/clang/test/CodeGenObjC/ptrauth-blocks.m +++ b/clang/test/CodeGenObjC/ptrauth-blocks.m @@ -1,14 +1,13 @@ -// RUN: %clang_cc1 -fptrauth-calls -fobjc-arc -fblocks -fobjc-runtime=ios-7 -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-strip -fobjc-arc -fblocks -fobjc-runtime=ios-7 -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-auth -fobjc-arc -fblocks -fobjc-runtime=ios-7 -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s void (^blockptr)(void); -// CHECK: [[INVOCATION_1:@.*]] = private constant { ptr, i32, i64, i64 } { ptr {{@.*}}, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ ptr, i32, i32, ptr, ptr }, ptr [[GLOBAL_BLOCK_1:@.*]], i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth" -// CHECK: [[GLOBAL_BLOCK_1]] = internal constant { ptr, i32, i32, ptr, ptr } { ptr @_NSConcreteGlobalBlock, i32 1342177280, i32 0, ptr [[INVOCATION_1]], + +// CHECK: [[GLOBAL_BLOCK_1:@.*]] = internal constant { ptr, i32, i32, ptr, ptr } { ptr ptrauth (ptr @_NSConcreteGlobalBlock, i32 2, i64 27361, ptr [[GLOBAL_BLOCK_1]]), i32 1342177280, i32 0, ptr ptrauth (ptr {{@.*}}, i32 0, i64 0, ptr getelementptr inbounds ({ ptr, i32, i32, ptr, ptr }, ptr [[GLOBAL_BLOCK_1]], i32 0, i32 3)), void (^globalblock)(void) = ^{}; -// CHECK: [[COPYDISPOSE_COPY:@.*]] = private constant { ptr, i32, i64, i64 } { ptr {{@.*}}, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i64, i64, ptr, ptr, ptr, i64 }, ptr [[COPYDISPOSE_DESCRIPTOR:@.*]], i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth" -// CHECK: [[COPYDISPOSE_DISPOSE:@.*]] = private constant { ptr, i32, i64, i64 } { ptr {{@.*}}, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i64, i64, ptr, ptr, ptr, i64 }, ptr [[COPYDISPOSE_DESCRIPTOR]], i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth" -// CHECK: [[COPYDISPOSE_DESCRIPTOR:@.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, ptr, ptr, ptr, i64 } { i64 0, i64 40, ptr [[COPYDISPOSE_COPY]], ptr [[COPYDISPOSE_DISPOSE]], +// CHECK: [[COPYDISPOSE_DESCRIPTOR:@.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, ptr, ptr, ptr, i64 } { i64 0, i64 40, ptr ptrauth (ptr {{@.*}}, i32 0, i64 0, ptr getelementptr inbounds ({ i64, i64, ptr, ptr, ptr, i64 }, ptr [[COPYDISPOSE_DESCRIPTOR]], i32 0, i32 2)), ptr ptrauth (ptr {{@.*}}, i32 0, i64 0, ptr getelementptr inbounds ({ i64, i64, ptr, ptr, ptr, i64 }, ptr [[COPYDISPOSE_DESCRIPTOR]], i32 0, i32 3)), @interface A - (int) count; @@ -30,6 +29,12 @@ void test_block_call() { void test_block_literal(int i) { // CHECK: [[I:%.*]] = alloca i32, // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:.*]], align + // CHECK: [[ISAPTRADDR:%.*]] = getelementptr inbounds [[BLOCK_T]], ptr [[BLOCK]], i32 0, i32 0 + // CHECK-NEXT: [[ISAPTRADDR_I:%.*]] = ptrtoint ptr [[ISAPTRADDR]] to i64 + // CHECK-NEXT: [[ISADISCRIMINATOR:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[ISAPTRADDR_I]], i64 27361) + // CHECK-NEXT: [[SIGNEDISA:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @_NSConcreteStackBlock to i64), i32 2, i64 [[ISADISCRIMINATOR]]) + // CHECK-NEXT: [[SIGNEDISAPTR:%.*]] = inttoptr i64 [[SIGNEDISA]] to ptr + // CHECK-NEXT: store ptr [[SIGNEDISAPTR]], ptr [[ISAPTRADDR]], align 8 // CHECK: [[FNPTRADDR:%.*]] = getelementptr inbounds [[BLOCK_T]], ptr [[BLOCK]], i32 0, i32 3 // CHECK-NEXT: [[DISCRIMINATOR:%.*]] = ptrtoint ptr [[FNPTRADDR]] to i64 // CHECK-NEXT: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr {{@.*}} to i64), i32 0, i64 [[DISCRIMINATOR]]) @@ -59,3 +64,11 @@ void test_byref_copy_destroy(A *a) { __block A *aweak = a; use_block(^{return [aweak count];}); } + +void test_conversion_helper(id); + +void test_conversion(id a) { + test_conversion_helper(^{ + (void)a; + }); +} diff --git a/clang/test/CodeGenObjC/ptrauth-isa-auth-mode-wrapper-globals.m b/clang/test/CodeGenObjC/ptrauth-isa-auth-mode-wrapper-globals.m new file mode 100644 index 0000000000000..60a2bbfdb91c3 --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-isa-auth-mode-wrapper-globals.m @@ -0,0 +1,89 @@ +// RUN: %clang_cc1 -I %S/Inputs -Wno-objc-root-class -fptrauth-intrinsics -fptrauth-calls -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s -fptrauth-objc-isa-mode=strip | FileCheck --check-prefix=CHECK-STRIP %s +// RUN: %clang_cc1 -I %S/Inputs -Wno-objc-root-class -fptrauth-intrinsics -fptrauth-calls -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s -fptrauth-objc-isa-mode=sign-and-strip | FileCheck --check-prefix=CHECK-SIGN-AND-STRIP %s +// RUN: %clang_cc1 -I %S/Inputs -Wno-objc-root-class -fptrauth-intrinsics -fptrauth-calls -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s -fptrauth-objc-isa-mode=sign-and-auth | FileCheck --check-prefix=CHECK-SIGN-AND-AUTH %s +// RUN: %clang_cc1 -I %S/Inputs -Wno-objc-root-class -fptrauth-intrinsics -fptrauth-calls -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck --check-prefix=CHECK-DISABLED %s + +#include +_Static_assert(!__has_feature(ptrauth_objc_isa_masking), "wat"); +#if __has_feature(ptrauth_qualifier_authentication_mode) + +@class NSString; +NSString *aString = @"foo"; + +// CHECK-SIGN-AND-AUTH: @__CFConstantStringClassReference.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__CFConstantStringClassReference, i32 2, i64 ptrtoint (ptr @_unnamed_cfstring_ to i64), i64 27361 }, section "llvm.ptrauth" +// CHECK-SIGN-AND-AUTH: @.str = private unnamed_addr constant [4 x i8] c"foo\00", section "__TEXT,__cstring,cstring_literals" +// CHECK-SIGN-AND-AUTH: @_unnamed_cfstring_ = private global %struct.__NSConstantString_tag { ptr @__CFConstantStringClassReference.ptrauth, i32 1992, ptr @.str, i64 3 }, section "__DATA,__cfstring" +// CHECK-SIGN-AND-AUTH: @aString = global ptr @_unnamed_cfstring_ + +// CHECK-SIGN-AND-STRIP: @__CFConstantStringClassReference.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__CFConstantStringClassReference, i32 2, i64 ptrtoint (ptr @_unnamed_cfstring_ to i64), i64 27361 }, section "llvm.ptrauth" +// CHECK-SIGN-AND-STRIP: @.str = private unnamed_addr constant [4 x i8] c"foo\00", section "__TEXT,__cstring,cstring_literals" +// CHECK-SIGN-AND-STRIP: @_unnamed_cfstring_ = private global %struct.__NSConstantString_tag { ptr @__CFConstantStringClassReference.ptrauth, i32 1992, ptr @.str, i64 3 }, section "__DATA,__cfstring" +// CHECK-SIGN-AND-STRIP: @aString = global ptr @_unnamed_cfstring_ + +// CHECK-STRIP: @.str = private unnamed_addr constant [4 x i8] c"foo\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK-STRIP: @_unnamed_cfstring_ = private global %struct.__NSConstantString_tag { ptr @__CFConstantStringClassReference, i32 1992, ptr @.str, i64 3 }, section "__DATA,__cfstring" +// CHECK-STRIP: @aString = global ptr @_unnamed_cfstring_ + +// CHECK-DISABLED: @.str = private unnamed_addr constant [4 x i8] c"foo\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK-DISABLED: @_unnamed_cfstring_ = private global %struct.__NSConstantString_tag { ptr @__CFConstantStringClassReference, i32 1992, ptr @.str, i64 3 }, section "__DATA,__cfstring" +// CHECK-DISABLED: @aString = global ptr @_unnamed_cfstring_ + +#if __has_feature(ptrauth_objc_isa_signs) +int ptrauth_objc_isa_signs_global = 0; // Verifying compilation path +// CHECK-SIGN-AND-AUTH: @ptrauth_objc_isa_signs_global = global i32 0, align 4 +// CHECK-SIGN-AND-STRIP: @ptrauth_objc_isa_signs_global = global i32 0, align 4 +#if __has_feature(ptrauth_objc_isa_authenticates) +int ptrauth_objc_isa_signs_and_auths_global = 0; // Verifying compilation path +// CHECK-SIGN-AND-AUTH: @ptrauth_objc_isa_signs_and_auths_global = global i32 0, align 4 +#elif __has_feature(ptrauth_objc_isa_strips) +int ptrauth_objc_isa_signs_and_strips_global = 0; // Verifying compilation path + // CHECK-SIGN-AND-STRIP: @ptrauth_objc_isa_signs_and_strips_global = global i32 0, align 4 +#else +_Static_assert(false, "none of the tests should hit this path"); +#endif +#else +_Static_assert(!__has_feature(ptrauth_objc_isa_authenticates), "Strip and auth is an invalid mode"); +#if __has_feature(ptrauth_objc_isa_strips) +int ptrauth_objc_isa_strips_global = 0; // Verifying compilation path + // CHECK-STRIP: @ptrauth_objc_isa_strips_global = global i32 0, align 4 +#else +// Make sure that the isa features don't lie when objc isa signing is completely disabled +int ptrauth_objc_isa_disabled_global = 0; // Verifying compilation path + // CHECK-DISABLED: @ptrauth_objc_isa_disabled_global = global i32 0, align 4 +#endif +#endif + +typedef struct { + void *__ptrauth_objc_isa_pointer ptr_isa; + __UINT64_TYPE__ __ptrauth_objc_isa_uintptr int_isa; +} TestStruct; + +void testModes(TestStruct *instruct, TestStruct *outstruct); +void testModes(TestStruct *instruct, TestStruct *outstruct) { + instruct->ptr_isa = *(void **)outstruct->ptr_isa; + instruct->int_isa = *(__UINT64_TYPE__ *)outstruct->int_isa; + // CHECK-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.strip + // CHECK-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.strip + // CHECK-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.strip + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.sign + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.strip + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.sign + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.auth + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.sign + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.auth + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.sign +} + +#endif diff --git a/clang/test/CodeGenObjC/ptrauth-isa-auth-mode.m b/clang/test/CodeGenObjC/ptrauth-isa-auth-mode.m new file mode 100644 index 0000000000000..b30eb66782398 --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-isa-auth-mode.m @@ -0,0 +1,87 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -I %S/Inputs -Wno-objc-root-class -fptrauth-intrinsics -fptrauth-calls -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s -fptrauth-objc-isa-mode=strip | FileCheck --check-prefix=CHECK-STRIP %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -I %S/Inputs -Wno-objc-root-class -fptrauth-intrinsics -fptrauth-calls -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s -fptrauth-objc-isa-mode=sign-and-strip | FileCheck --check-prefix=CHECK-SIGN-AND-STRIP %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -I %S/Inputs -Wno-objc-root-class -fptrauth-intrinsics -fptrauth-calls -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s -fptrauth-objc-isa-mode=sign-and-auth | FileCheck --check-prefix=CHECK-SIGN-AND-AUTH %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -I %S/Inputs -Wno-objc-root-class -fptrauth-intrinsics -fptrauth-calls -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck --check-prefix=CHECK-DISABLED %s + +#include +_Static_assert(!__has_feature(ptrauth_objc_isa_masking), "wat"); +#if __has_feature(ptrauth_qualifier_authentication_mode) + +@class NSString; +NSString *aString = @"foo"; + +// CHECK-SIGN-AND-AUTH: @.str = private unnamed_addr constant [4 x i8] c"foo\00", section "__TEXT,__cstring,cstring_literals" +// CHECK-SIGN-AND-AUTH: @_unnamed_cfstring_ = private global %struct.__NSConstantString_tag { ptr ptrauth (ptr @__CFConstantStringClassReference, i32 2, i64 27361, ptr @_unnamed_cfstring_), i32 1992, ptr @.str, i64 3 }, section "__DATA,__cfstring" +// CHECK-SIGN-AND-AUTH: @aString = global ptr @_unnamed_cfstring_ + +// CHECK-SIGN-AND-STRIP: @.str = private unnamed_addr constant [4 x i8] c"foo\00", section "__TEXT,__cstring,cstring_literals" +// CHECK-SIGN-AND-STRIP: @_unnamed_cfstring_ = private global %struct.__NSConstantString_tag { ptr ptrauth (ptr @__CFConstantStringClassReference, i32 2, i64 27361, ptr @_unnamed_cfstring_), i32 1992, ptr @.str, i64 3 }, section "__DATA,__cfstring" +// CHECK-SIGN-AND-STRIP: @aString = global ptr @_unnamed_cfstring_ + +// CHECK-STRIP: @.str = private unnamed_addr constant [4 x i8] c"foo\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK-STRIP: @_unnamed_cfstring_ = private global %struct.__NSConstantString_tag { ptr @__CFConstantStringClassReference, i32 1992, ptr @.str, i64 3 }, section "__DATA,__cfstring" +// CHECK-STRIP: @aString = global ptr @_unnamed_cfstring_ + +// CHECK-DISABLED: @.str = private unnamed_addr constant [4 x i8] c"foo\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK-DISABLED: @_unnamed_cfstring_ = private global %struct.__NSConstantString_tag { ptr @__CFConstantStringClassReference, i32 1992, ptr @.str, i64 3 }, section "__DATA,__cfstring" +// CHECK-DISABLED: @aString = global ptr @_unnamed_cfstring_ + +#if __has_feature(ptrauth_objc_isa_signs) +int ptrauth_objc_isa_signs_global = 0; // Verifying compilation path +// CHECK-SIGN-AND-AUTH: @ptrauth_objc_isa_signs_global = global i32 0, align 4 +// CHECK-SIGN-AND-STRIP: @ptrauth_objc_isa_signs_global = global i32 0, align 4 +#if __has_feature(ptrauth_objc_isa_authenticates) +int ptrauth_objc_isa_signs_and_auths_global = 0; // Verifying compilation path +// CHECK-SIGN-AND-AUTH: @ptrauth_objc_isa_signs_and_auths_global = global i32 0, align 4 +#elif __has_feature(ptrauth_objc_isa_strips) +int ptrauth_objc_isa_signs_and_strips_global = 0; // Verifying compilation path + // CHECK-SIGN-AND-STRIP: @ptrauth_objc_isa_signs_and_strips_global = global i32 0, align 4 +#else +_Static_assert(false, "none of the tests should hit this path"); +#endif +#else +_Static_assert(!__has_feature(ptrauth_objc_isa_authenticates), "Strip and auth is an invalid mode"); +#if __has_feature(ptrauth_objc_isa_strips) +int ptrauth_objc_isa_strips_global = 0; // Verifying compilation path + // CHECK-STRIP: @ptrauth_objc_isa_strips_global = global i32 0, align 4 +#else +// Make sure that the isa features don't lie when objc isa signing is completely disabled +int ptrauth_objc_isa_disabled_global = 0; // Verifying compilation path + // CHECK-DISABLED: @ptrauth_objc_isa_disabled_global = global i32 0, align 4 +#endif +#endif + +typedef struct { + void *__ptrauth_objc_isa_pointer ptr_isa; + __UINT64_TYPE__ __ptrauth_objc_isa_uintptr int_isa; +} TestStruct; + +void testModes(TestStruct *instruct, TestStruct *outstruct); +void testModes(TestStruct *instruct, TestStruct *outstruct) { + instruct->ptr_isa = *(void **)outstruct->ptr_isa; + instruct->int_isa = *(__UINT64_TYPE__ *)outstruct->int_isa; + // CHECK-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.strip + // CHECK-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.strip + // CHECK-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.strip + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.sign + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.strip + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-STRIP: [[TMP:%.*]] = call i64 @llvm.ptrauth.sign + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.auth + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.sign + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.auth + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.blend + // CHECK-SIGN-AND-AUTH: [[TMP:%.*]] = call i64 @llvm.ptrauth.sign +} + +#endif diff --git a/clang/test/CodeGenObjC/ptrauth-isa-super-wrapper-globals.m b/clang/test/CodeGenObjC/ptrauth-isa-super-wrapper-globals.m new file mode 100644 index 0000000000000..274a18c9abb68 --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-isa-super-wrapper-globals.m @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -I %S/Inputs -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-strip -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -no-enable-noundef-analysis -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck %s +// RUN: %clang_cc1 -I %S/Inputs -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-auth -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -no-enable-noundef-analysis -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck %s + +#include "literal-support.h" + +#if __has_feature(objc_bool) +#define YES __objc_yes +#define NO __objc_no +#else +#define YES ((BOOL)1) +#define NO ((BOOL)0) +#endif + +@class NSString; + +// CHECK: @"OBJC_METACLASS_$_Base" = external global %struct._class_t +// CHECK: @OBJC_CLASS_NAME_ = private unnamed_addr constant [2 x i8] c"C\00", section "__TEXT,__objc_classname,cstring_literals", align 1 +// CHECK: @OBJC_METH_VAR_NAME_.1 = private unnamed_addr constant [11 x i8] c"super_test\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK: @OBJC_METH_VAR_TYPE_ = private unnamed_addr constant [8 x i8] c"v16@0:8\00", section "__TEXT,__objc_methtype,cstring_literals", align 1 +// CHECK: @"\01+[C super_test].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01+[C super_test]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [1 x %struct._objc_method] }, ptr @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @"_OBJC_$_CLASS_METHODS_C" = internal global { i32, i32, [1 x %struct._objc_method] } { i32 24, i32 1, [1 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_.1, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01+[C super_test].ptrauth" }] }, section "__DATA, __objc_const", align 8 +// CHECK: @"_OBJC_$_CLASS_METHODS_C.ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"_OBJC_$_CLASS_METHODS_C", i32 2, i64 ptrtoint (ptr getelementptr inbounds (%struct._class_ro_t, ptr @"_OBJC_METACLASS_RO_$_C", i32 0, i32 5) to i64), i64 49936 }, section "llvm.ptrauth", align 8 +// CHECK: @"_OBJC_METACLASS_RO_$_C" = internal global %struct._class_ro_t { i32 129, i32 40, i32 40, ptr null, ptr @OBJC_CLASS_NAME_, ptr @"_OBJC_$_CLASS_METHODS_C.ptrauth", ptr null, ptr null, ptr null, ptr null }, section "__DATA, __objc_const", align 8 +// CHECK: @"OBJC_METACLASS_$_Base.ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"OBJC_METACLASS_$_Base", i32 2, i64 ptrtoint (ptr @"OBJC_METACLASS_$_C" to i64), i64 27361 }, section "llvm.ptrauth", align 8 +// CHECK: @"OBJC_METACLASS_$_Base.ptrauth.2" = private constant { ptr, i32, i64, i64 } { ptr @"OBJC_METACLASS_$_Base", i32 2, i64 ptrtoint (ptr getelementptr inbounds (%struct._class_t, ptr @"OBJC_METACLASS_$_C", i32 0, i32 1) to i64), i64 46507 }, section "llvm.ptrauth", align 8 +// CHECK: @"OBJC_CLASS_$_Base" = external global %struct._class_t +// CHECK: @"_OBJC_CLASS_RO_$_C" = internal global %struct._class_ro_t { i32 128, i32 0, i32 0, ptr null, ptr @OBJC_CLASS_NAME_, ptr null, ptr null, ptr null, ptr null, ptr null }, section "__DATA, __objc_const", align 8 +// CHECK: @"OBJC_METACLASS_$_C.ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"OBJC_METACLASS_$_C", i32 2, i64 ptrtoint (ptr @"OBJC_CLASS_$_C" to i64), i64 27361 }, section "llvm.ptrauth", align 8 +// CHECK: @"OBJC_CLASS_$_Base.ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"OBJC_CLASS_$_Base", i32 2, i64 ptrtoint (ptr getelementptr inbounds (%struct._class_t, ptr @"OBJC_CLASS_$_C", i32 0, i32 1) to i64), i64 46507 }, section "llvm.ptrauth", align 8 +// CHECK: @"OBJC_CLASS_$_C" = global %struct._class_t { ptr @"OBJC_METACLASS_$_C.ptrauth", ptr @"OBJC_CLASS_$_Base.ptrauth", ptr @_objc_empty_cache, ptr null, ptr @"_OBJC_CLASS_RO_$_C" }, section "__DATA, __objc_data", align 8 + +@interface Base ++ (void)test; +@end + +@interface C : Base +@end + +@implementation C +// CHECK-LABEL: define internal void @"\01+[C super_test]"(ptr %self, ptr %_cmd) #1 { ++ (void)super_test { + return [super test]; + // CHECK: [[SELF_ADDR:%.*]] = alloca ptr, align 8 + // CHECK: [[CMD_ADDR:%.*]] = alloca ptr, align 8 + // CHECK: [[SUPER_STRUCT:%.*]] = alloca %struct._objc_super, align 8 + // CHECK: store ptr %self, ptr [[SELF_ADDR]], align 8, !tbaa !{{[0-9]+}} + // CHECK: store ptr %_cmd, ptr [[CMD_ADDR]], align 8, !tbaa !{{[0-9]+}} + // CHECK: [[TARGET:%.*]] = load ptr, ptr [[SELF_ADDR]], align 8, !tbaa !{{[0-9]+}} + // CHECK: [[OBJC_SUPER_TARGET:%.*]] = getelementptr inbounds %struct._objc_super, ptr [[SUPER_STRUCT]], i32 0, i32 0 + // CHECK: store ptr [[TARGET]], ptr [[OBJC_SUPER_TARGET]], align 8 + // CHECK: [[SUPER_REFERENCES:%.*]] = load ptr, ptr @"OBJC_CLASSLIST_SUP_REFS_$_" + // CHECK: [[OBJC_SUPER_SUPER:%.*]] = getelementptr inbounds %struct._objc_super, ptr [[SUPER_STRUCT]], i32 0, i32 1 + // CHECK: store ptr [[SUPER_REFERENCES]], ptr [[OBJC_SUPER_SUPER:%.*]], align 8 + // CHECK: call void @objc_msgSendSuper2(ptr %objc_super, ptr %4) +} +@end + +id str = @""; diff --git a/clang/test/CodeGenObjC/ptrauth-isa-super.m b/clang/test/CodeGenObjC/ptrauth-isa-super.m new file mode 100644 index 0000000000000..0616bd76a7f37 --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-isa-super.m @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -I %S/Inputs -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-strip -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -no-enable-noundef-analysis -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -I %S/Inputs -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-auth -triple arm64-apple-ios -fobjc-runtime=ios-12.2 -emit-llvm -no-enable-noundef-analysis -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck %s + +#include "literal-support.h" + +#if __has_feature(objc_bool) +#define YES __objc_yes +#define NO __objc_no +#else +#define YES ((BOOL)1) +#define NO ((BOOL)0) +#endif + +@class NSString; + +// CHECK: @"OBJC_METACLASS_$_Base" = external global %struct._class_t +// CHECK: @OBJC_CLASS_NAME_ = private unnamed_addr constant [2 x i8] c"C\00", section "__TEXT,__objc_classname,cstring_literals", align 1 +// CHECK: @OBJC_METH_VAR_NAME_.1 = private unnamed_addr constant [11 x i8] c"super_test\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK: @OBJC_METH_VAR_TYPE_ = private unnamed_addr constant [8 x i8] c"v16@0:8\00", section "__TEXT,__objc_methtype,cstring_literals", align 1 + +// CHECK: @"_OBJC_$_CLASS_METHODS_C" = internal global { i32, i32, [1 x %struct._objc_method] } { i32 24, i32 1, [1 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_.1, ptr @OBJC_METH_VAR_TYPE_, ptr ptrauth (ptr @"\01+[C super_test]", i32 0, i64 0, ptr getelementptr inbounds ({ i32, i32, [1 x %struct._objc_method] }, ptr @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 0, i32 2)) }] }, section "__DATA, __objc_const", align 8 + +// CHECK: @"_OBJC_METACLASS_RO_$_C" = internal global %struct._class_ro_t { i32 129, i32 40, i32 40, ptr null, ptr @OBJC_CLASS_NAME_, ptr ptrauth (ptr @"_OBJC_$_CLASS_METHODS_C", i32 2, i64 49936, ptr getelementptr inbounds (%struct._class_ro_t, ptr @"_OBJC_METACLASS_RO_$_C", i32 0, i32 5)), ptr null, ptr null, ptr null, ptr null }, section "__DATA, __objc_const", align 8 +// CHECK: @"OBJC_CLASS_$_Base" = external global %struct._class_t +// CHECK: @"_OBJC_CLASS_RO_$_C" = internal global %struct._class_ro_t { i32 128, i32 0, i32 0, ptr null, ptr @OBJC_CLASS_NAME_, ptr null, ptr null, ptr null, ptr null, ptr null }, section "__DATA, __objc_const", align 8 +// CHECK: @"OBJC_CLASS_$_C" = global %struct._class_t { ptr ptrauth (ptr @"OBJC_METACLASS_$_C", i32 2, i64 27361, ptr @"OBJC_CLASS_$_C"), ptr ptrauth (ptr @"OBJC_CLASS_$_Base", i32 2, i64 46507, ptr getelementptr inbounds (%struct._class_t, ptr @"OBJC_CLASS_$_C", i32 0, i32 1)), ptr @_objc_empty_cache, ptr null, ptr @"_OBJC_CLASS_RO_$_C" }, section "__DATA, __objc_data", align 8 + +@interface Base ++ (void)test; +@end + +@interface C : Base +@end + +@implementation C +// CHECK-LABEL: define internal void @"\01+[C super_test]"(ptr %self, ptr %_cmd) #1 { ++ (void)super_test { + return [super test]; + // CHECK: [[SELF_ADDR:%.*]] = alloca ptr, align 8 + // CHECK: [[CMD_ADDR:%.*]] = alloca ptr, align 8 + // CHECK: [[SUPER_STRUCT:%.*]] = alloca %struct._objc_super, align 8 + // CHECK: store ptr %self, ptr [[SELF_ADDR]], align 8, !tbaa !{{[0-9]+}} + // CHECK: store ptr %_cmd, ptr [[CMD_ADDR]], align 8, !tbaa !{{[0-9]+}} + // CHECK: [[TARGET:%.*]] = load ptr, ptr [[SELF_ADDR]], align 8, !tbaa !{{[0-9]+}} + // CHECK: [[OBJC_SUPER_TARGET:%.*]] = getelementptr inbounds %struct._objc_super, ptr [[SUPER_STRUCT]], i32 0, i32 0 + // CHECK: store ptr [[TARGET]], ptr [[OBJC_SUPER_TARGET]], align 8 + // CHECK: [[SUPER_REFERENCES:%.*]] = load ptr, ptr @"OBJC_CLASSLIST_SUP_REFS_$_" + // CHECK: [[OBJC_SUPER_SUPER:%.*]] = getelementptr inbounds %struct._objc_super, ptr [[SUPER_STRUCT]], i32 0, i32 1 + // CHECK: store ptr [[SUPER_REFERENCES]], ptr [[OBJC_SUPER_SUPER:%.*]], align 8 + // CHECK: call void @objc_msgSendSuper2(ptr %objc_super, ptr %4) +} +@end + +id str = @""; diff --git a/clang/test/Preprocessor/ptrauth-h-isa-authentication-config.c b/clang/test/Preprocessor/ptrauth-h-isa-authentication-config.c new file mode 100644 index 0000000000000..5c63a38620d96 --- /dev/null +++ b/clang/test/Preprocessor/ptrauth-h-isa-authentication-config.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -nostdsysteminc -fptrauth-intrinsics -fptrauth-calls -fptrauth-objc-isa-mode=strip -fptrauth-objc-isa-masking -triple arm64-apple-ios -E -O0 -disable-llvm-passes -o - %s | FileCheck --check-prefix=CHECK-STRIP %s +// RUN: %clang_cc1 -nostdsysteminc -fptrauth-intrinsics -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-strip -fptrauth-objc-isa-masking -triple arm64-apple-ios -E -O0 -disable-llvm-passes -o - %s | FileCheck --check-prefix=CHECK-SIGN-AND-STRIP %s +// RUN: %clang_cc1 -nostdsysteminc -fptrauth-intrinsics -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-auth -fptrauth-objc-isa-masking -triple arm64-apple-ios -E -O0 -disable-llvm-passes -o - %s | FileCheck --check-prefix=CHECK-SIGN-AND-AUTH %s +// RUN: %clang_cc1 -nostdsysteminc -fptrauth-intrinsics -fptrauth-calls -fptrauth-objc-isa-mode=strip -triple arm64-apple-ios -E -O0 -disable-llvm-passes -o - %s | FileCheck --check-prefix=CHECK-STRIP-NO-MASK %s +// RUN: %clang_cc1 -nostdsysteminc -fptrauth-intrinsics -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-strip -triple arm64-apple-ios -E -O0 -disable-llvm-passes -o - %s | FileCheck --check-prefix=CHECK-SIGN-AND-STRIP-NO-MASK %s +// RUN: %clang_cc1 -nostdsysteminc -fptrauth-intrinsics -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-auth -triple arm64-apple-ios -E -O0 -disable-llvm-passes -o - %s | FileCheck --check-prefix=CHECK-SIGN-AND-AUTH-NO-MASK %s + +#include +#define _TO_STRING(x) #x +#define TO_STRING(x) _TO_STRING(x) + +const char *test = TO_STRING(__ptrauth_objc_isa_pointer); + +// CHECK-STRIP: const char *test = "__ptrauth(ptrauth_key_objc_isa_pointer, 1, 0x6AE1 , \"strip\" \",isa-pointer\")"; +// CHECK-SIGN-AND-STRIP: const char *test = "__ptrauth(ptrauth_key_objc_isa_pointer, 1, 0x6AE1 , \"sign-and-strip\" \",isa-pointer\")"; +// CHECK-SIGN-AND-AUTH: const char *test = "__ptrauth(ptrauth_key_objc_isa_pointer, 1, 0x6AE1 , \"sign-and-auth\" \",isa-pointer\")"; +// CHECK-STRIP-NO-MASK: const char *test = "__ptrauth(ptrauth_key_objc_isa_pointer, 1, 0x6AE1 , \"strip\" )"; +// CHECK-SIGN-AND-STRIP-NO-MASK: const char *test = "__ptrauth(ptrauth_key_objc_isa_pointer, 1, 0x6AE1 , \"sign-and-strip\" )"; +// CHECK-SIGN-AND-AUTH-NO-MASK: const char *test = "__ptrauth(ptrauth_key_objc_isa_pointer, 1, 0x6AE1 , \"sign-and-auth\" )"; diff --git a/clang/test/Preprocessor/ptrauth_objc_isa_feature.c b/clang/test/Preprocessor/ptrauth_objc_isa_feature.c new file mode 100644 index 0000000000000..8e5de43013bc0 --- /dev/null +++ b/clang/test/Preprocessor/ptrauth_objc_isa_feature.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 %s -E -triple=arm64-- -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-strip | FileCheck %s --check-prefixes=ENABLED +// RUN: %clang_cc1 %s -E -triple=arm64-- -fptrauth-calls -fptrauth-objc-isa-mode=sign-and-auth | FileCheck %s --check-prefixes=ENABLED +// RUN: %clang_cc1 %s -E -triple=arm64-- -fptrauth-calls | FileCheck %s --check-prefixes=DISABLED + +#if __has_feature(ptrauth_objc_isa) +// ENABLED: has_ptrauth_objc_isa +void has_ptrauth_objc_isa() {} +#else +// DISABLED: no_ptrauth_objc_isa +void no_ptrauth_objc_isa() {} +#endif From a44a31263f4ef8381e77d4e6ec7307a1addc63cd Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Wed, 1 May 2024 15:12:35 -0700 Subject: [PATCH 49/58] [clang] Implement the ptrauth_struct type attribute. --- clang/include/clang/AST/ASTContext.h | 21 + .../clang/AST/CXXRecordDeclDefinitionBits.def | 4 + clang/include/clang/AST/DeclCXX.h | 8 + clang/include/clang/AST/Stmt.h | 2 +- clang/include/clang/Basic/Attr.td | 7 + clang/include/clang/Basic/AttrDocs.td | 41 + .../clang/Basic/DiagnosticSemaKinds.td | 15 +- clang/include/clang/Basic/TokenKinds.def | 2 + clang/include/clang/Parse/Parser.h | 1 + clang/include/clang/Sema/Sema.h | 6 + clang/lib/AST/ASTContext.cpp | 56 ++ clang/lib/AST/DeclCXX.cpp | 9 +- clang/lib/AST/ExprConstant.cpp | 24 +- clang/lib/AST/ItaniumMangle.cpp | 16 +- clang/lib/CodeGen/CGPointerAuth.cpp | 33 + clang/lib/CodeGen/ItaniumCXXABI.cpp | 10 +- clang/lib/Parse/ParseExpr.cpp | 29 + clang/lib/Sema/SemaChecking.cpp | 7 +- clang/lib/Sema/SemaDecl.cpp | 15 + clang/lib/Sema/SemaDeclAttr.cpp | 25 + clang/lib/Sema/SemaDeclCXX.cpp | 28 + clang/lib/Sema/SemaExpr.cpp | 23 +- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 26 + .../ptrauth-function-lvalue-cast-disc.c | 13 + .../CodeGen/ptrauth-function-lvalue-cast.c | 13 + .../ptrauth-struct-attr-wrapper-globals.cpp | 820 ++++++++++++++++++ clang/test/CodeGenCXX/ptrauth-struct-attr.cpp | 813 +++++++++++++++++ .../ptrauth-struct-attr-wrapper-globals.m | 720 +++++++++++++++ clang/test/CodeGenObjC/ptrauth-struct-attr.m | 718 +++++++++++++++ ...a-attribute-supported-attributes-list.test | 1 + clang/test/Sema/ptrauth-struct-attr.c | 31 + clang/test/SemaCXX/ptrauth-struct-attr.cpp | 88 ++ .../SemaCXX/ptrauth-template-parameters.cpp | 14 + 33 files changed, 3624 insertions(+), 15 deletions(-) create mode 100644 clang/test/CodeGenCXX/ptrauth-struct-attr-wrapper-globals.cpp create mode 100644 clang/test/CodeGenCXX/ptrauth-struct-attr.cpp create mode 100644 clang/test/CodeGenObjC/ptrauth-struct-attr-wrapper-globals.m create mode 100644 clang/test/CodeGenObjC/ptrauth-struct-attr.m create mode 100644 clang/test/Sema/ptrauth-struct-attr.c create mode 100644 clang/test/SemaCXX/ptrauth-struct-attr.cpp diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index b3913341ec92e..cd04705241d40 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1310,6 +1310,16 @@ class ASTContext : public RefCountedBase { /// Return the "other" type-specific discriminator for the given type. uint16_t getPointerAuthTypeDiscriminator(QualType T); + /// Return the key of attribute ptrauth_struct on the record. If the attribute + /// isn't on the record, return the none key. If the key argument is value + /// dependent, set the boolean flag to false. + std::pair getPointerAuthStructKey(const RecordDecl *RD) const; + + /// Return the discriminator of attribute ptrauth_struct on the record. If the + /// key argument is value dependent, set the boolean flag to false. + std::pair + getPointerAuthStructDisc(const RecordDecl *RD) const; + // Determine whether the type can have qualifier __ptrauth with key // ptrauth_key_none. bool canQualifyWithPtrAuthKeyNone(QualType T) const { @@ -1322,9 +1332,20 @@ class ASTContext : public RefCountedBase { if (PointeeType->isFunctionType()) return false; + // Disallow the qualifer on classes annotated with attribute ptrauth_struct + // with a key that isn't the none key. + if (auto *RD = PointeeType->getAsRecordDecl()) { + std::pair P = getPointerAuthStructKey(RD); + if (P.first && P.second != PointerAuthKeyNone) + return false; + } + return true; } + bool hasPointerAuthStructMismatch(const RecordDecl *RD0, + const RecordDecl *RD1) const; + bool typeContainsAuthenticatedNull(QualType) const; bool typeContainsAuthenticatedNull(const Type *) const; std::optional tryTypeContainsAuthenticatedNull(QualType) const; diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def index cdf0804680ad0..2848cc0405205 100644 --- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def +++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def @@ -249,4 +249,8 @@ FIELD(HasDeclaredCopyAssignmentWithConstParam, 1, MERGE_OR) /// base classes or fields have a no-return destructor FIELD(IsAnyDestructorNoReturn, 1, NO_MERGE) +/// Whether this class or any of its direct or indirect base classes are +/// annotated with `ptrauth_struct` that uses a key that isn't the none key. +FIELD(HasSignedClassInHierarchy, 1, NO_MERGE) + #undef FIELD diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index fb52ac804849d..97e47ae534cc1 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1018,6 +1018,14 @@ class CXXRecordDecl : public RecordDecl { return data().NeedOverloadResolutionForDestructor; } + bool hasSignedClassInHierarchy() const { + return data().HasSignedClassInHierarchy; + } + + void setHasSignedClassInHierarchy() { + data().HasSignedClassInHierarchy = true; + } + /// Determine whether this class describes a lambda function object. bool isLambda() const { // An update record can't turn a non-lambda into a lambda. diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 9cd7a364cd3f1..8ed9e748ffedd 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -530,7 +530,7 @@ class alignas(void *) Stmt { unsigned : NumExprBits; LLVM_PREFERRED_TYPE(UnaryExprOrTypeTrait) - unsigned Kind : 3; + unsigned Kind : 4; LLVM_PREFERRED_TYPE(bool) unsigned IsType : 1; // true if operand is a type, false if an expression. }; diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 41defca695c61..5a8e1f33b7ad3 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3335,6 +3335,13 @@ def PointerAuth : TypeAttr { let Documentation = [PtrAuthDocs]; } +def PointerAuthStruct : InheritableAttr { + let Spellings = [Clang<"ptrauth_struct">]; + let Args = [ExprArgument<"Key">, ExprArgument<"Discriminator">]; + let Subjects = SubjectList<[Record]>; + let Documentation = [PointerAuthStructDocs]; +} + def Unused : InheritableAttr { let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">, C23<"", "maybe_unused", 202106>]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 622f5b8206320..10fb09d1399b9 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1918,6 +1918,47 @@ features that implicitly use pointer authentication. }]; } +def PointerAuthStructDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +The ``ptrauth_struct`` attribute allows programmers to opt in to using signed +pointers for all pointers to a particular struct, union, or C++ class type. In +C++, this also includes references to the type. + +The attribute takes two arguments: + +- The first argument is the abstract signing key, which should be a constant + from ````. +- The second argument is a constant discriminator. + +These arguments are identical to the arguments to the ``__ptrauth`` qualifier +except that ``ptrauth_struct`` does not take an argument for enabling address +diversity. + +.. code-block:: c++ + + // key=ptrauth_key_process_dependent_data,discriminator=100 + struct __attribute__((ptrauth_struct(ptrauth_key_process_dependent_data,100))) S0 { + int f0[4]; + }; + +If a particular pointer to the type is qualified with a ``__ptrauth`` qualifier, +that qualifier takes precedence over ptrauth_struct: + +.. code-block:: c++ + + struct __attribute__((ptrauth_struct(ptrauth_key_process_dependent_data,100))) S0 { + int f0[4]; + }; + + void test(S0 *s0) { + // `p` is signed using `__ptrauth(ptrauth_key_process_dependent_data, 1, 200)`. + S0 * __ptrauth(ptrauth_key_process_dependent_data, 1, 200) p = s0; + } + + }]; +} + def ExternalSourceSymbolDocs : Documentation { let Category = DocCatDecl; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 033c2b66d5260..c56e0528281ad 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -970,11 +970,12 @@ def err_ptrauth_qualifier_signed_pointer_type : Error< def err_ptrauth_qualifier_bad_arg_count : Error< "%0 qualifier must take between 1 and 4 arguments">; def err_ptrauth_arg_not_ice : Error< - "argument to __ptrauth must be an integer constant expression">; + "argument to %select{__ptrauth|ptrauth_struct}0 must be an integer constant " + "expression">; def err_ptrauth_address_discrimination_invalid : Error< "address discrimination flag for __ptrauth must be 0 or 1; value is %0">; def err_ptrauth_extra_discriminator_invalid : Error< - "extra discriminator for __ptrauth must be between " + "extra discriminator for %select{__ptrauth|ptrauth_struct}2 must be between " "0 and %1; value is %0">; def note_ptrauth_evaluating_options : Note< "options parameter evaluated to '%0'">; @@ -998,6 +999,16 @@ def err_ptrauth_invalid_authenticated_null_global : Error< "%select{static locals|globals}0 with authenticated null values are currently unsupported" >; +// ptrauth_struct attribute. +def err_ptrauth_struct_key_disc_not_record : Error< + "attribute ptrauth_struct can only be used on a record type">; +def err_ptrauth_struct_signing_mismatch : Error< + "%0 is signed differently from the previous declaration">; +def note_previous_ptrauth_struct_declaration : Note< + "previous declaration of %0 is here">; +def err_ptrauth_invalid_struct_attr_dynamic_class : Error< + "attribute ptrauth_struct cannot be used on class %0 or its subclasses " + "because it is a dynamic class">; /// main() // static main() is not an error in C, just in C++. diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 128971047fcb3..2bd0d422fd4fc 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -591,6 +591,8 @@ KEYWORD(__private_extern__ , KEYALL) KEYWORD(__module_private__ , KEYALL) UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_type_discriminator, PtrAuthTypeDiscriminator, KEYALL) +UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_struct_key, PtrAuthStructKey, KEYALL) +UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_struct_disc, PtrAuthStructDiscriminator, KEYALL) // Extension that will be enabled for Microsoft, Borland and PS4, but can be // disabled via '-fno-declspec'. diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 820e0e4519305..257bd3bd026e1 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3889,6 +3889,7 @@ class Parser : public CodeCompletionHandler { ExprResult ParseExpressionTrait(); ExprResult ParseBuiltinPtrauthTypeDiscriminator(); + ExprResult ParseBuiltinPtrauthStructKeyOrDisc(bool ParseKey); //===--------------------------------------------------------------------===// // Preprocessor code-completion pass-through diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f5afa45e239c2..ebddad8337bbb 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3464,6 +3464,9 @@ class Sema final : public SemaBase { // Extra discriminator argument of __ptrauth. PADAK_ExtraDiscPtrAuth, + + // Type discriminator argument of ptrauth_struct. + PADAK_TypeDiscPtrAuthStruct, }; bool checkPointerAuthDiscriminatorArg(Expr *arg, PointerAuthDiscArgKind kind, @@ -4492,6 +4495,9 @@ class Sema final : public SemaBase { StringRef &Str, SourceLocation *ArgLocation = nullptr); + void addPointerAuthStructAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *Key, Expr *Disc); + /// Determine if type T is a valid subject for a nonnull and similar /// attributes. By default, we look through references (the behavior used by /// nonnull), but if the second parameter is true, then we treat a reference diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 086998cf16bb7..00795cd29dff8 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3407,6 +3407,62 @@ uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) { return llvm::getPointerAuthStableSipHash(Str); } +std::pair +ASTContext::getPointerAuthStructKey(const RecordDecl *RD) const { + if (auto *Attr = RD->getAttr()) { + Expr *E = Attr->getKey(); + if (E->isValueDependent()) + return {false, 0}; + std::optional V = E->getIntegerConstantExpr(*this); + return {true, static_cast(V->getExtValue())}; + } + + return {true, PointerAuthKeyNone}; +} + +std::pair +ASTContext::getPointerAuthStructDisc(const RecordDecl *RD) const { + if (auto *Attr = RD->getAttr()) { + Expr *E = Attr->getDiscriminator(); + if (E->isValueDependent()) + return {false, 0}; + std::optional V = E->getIntegerConstantExpr(*this); + return {true, V->getExtValue()}; + } + + return {true, 0}; +} + +bool ASTContext::hasPointerAuthStructMismatch(const RecordDecl *RD0, + const RecordDecl *RD1) const { + if (RD0 == RD1) + return true; + + unsigned Key0, Key1; + unsigned Disc0, Disc1; + bool Known0, Known1; + std::tie(Known0, Key0) = getPointerAuthStructKey(RD0); + std::tie(Known1, Key1) = getPointerAuthStructKey(RD1); + + if (!Known0 || !Known1) + return false; + + if (Key0 != Key1) + return true; + + // If the key is none, skip checking the discriminators. + if (Key0 == PointerAuthKeyNone) + return false; + + std::tie(Known0, Disc0) = getPointerAuthStructDisc(RD0); + std::tie(Known1, Disc1) = getPointerAuthStructDisc(RD1); + + if (!Known0 || !Known1) + return false; + + return Disc0 != Disc1; +} + std::optional ASTContext::tryTypeContainsAuthenticatedNull(QualType T) const { if (!LangOpts.PointerAuthIntrinsics) diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 0e86c24981e6c..d50a2615485f5 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -109,9 +109,9 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) ImplicitCopyAssignmentHasConstParam(true), HasDeclaredCopyConstructorWithConstParam(false), HasDeclaredCopyAssignmentWithConstParam(false), - IsAnyDestructorNoReturn(false), IsLambda(false), - IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false), - HasODRHash(false), Definition(D) {} + IsAnyDestructorNoReturn(false), HasSignedClassInHierarchy(false), + IsLambda(false), IsParsingBaseSpecifiers(false), + ComputedVisibleConversions(false), HasODRHash(false), Definition(D) {} CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const { return Bases.get(Definition->getASTContext().getExternalSource()); @@ -439,6 +439,9 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, if (!BaseClassDecl->hasCopyAssignmentWithConstParam()) data().ImplicitCopyAssignmentHasConstParam = false; + if (BaseClassDecl->hasSignedClassInHierarchy()) + setHasSignedClassInHierarchy(); + // A class has an Objective-C object member if... or any of its bases // has an Objective-C object member. if (BaseClassDecl->hasObjectMember()) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index a09c223d896c8..3a05bd1609e42 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -11523,13 +11523,15 @@ class IntExprEvaluator return Success(I, E, Result); } - bool Success(uint64_t Value, const Expr *E, APValue &Result) { + template ::value>> + bool Success(T Value, const Expr *E, APValue &Result) { assert(E->getType()->isIntegralOrEnumerationType() && "Invalid evaluation result."); Result = APValue(Info.Ctx.MakeIntValue(Value, E->getType())); return true; } - bool Success(uint64_t Value, const Expr *E) { + template ::value>> + bool Success(T Value, const Expr *E) { return Success(Value, E, Result); } @@ -14038,6 +14040,24 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( return Success( Info.Ctx.getPointerAuthTypeDiscriminator(E->getArgumentType()), E); } + case UETT_PtrAuthStructKey: { + if (E->getArgumentType()->isDependentType()) + return false; + std::pair Key = Info.Ctx.getPointerAuthStructKey( + E->getArgumentType()->getAsRecordDecl()); + assert(Key.first && + "key argument of ptrauth_struct shouldn't be dependent"); + return Success(Key.second, E); + } + case UETT_PtrAuthStructDiscriminator: { + if (E->getArgumentType()->isDependentType()) + return false; + std::pair Disc = Info.Ctx.getPointerAuthStructDisc( + E->getArgumentType()->getAsRecordDecl()); + assert(Disc.first && + "discriminator argument of ptrauth_struct shouldn't be dependent"); + return Success(Disc.second, E); + } case UETT_VecStep: { QualType Ty = E->getTypeOfArgument(); diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 97eeaa6007b29..ce6976a410539 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -525,6 +525,7 @@ class CXXNameMangler { void mangleUnscopedTemplateName(GlobalDecl GD, const DeclContext *DC, const AbiTagList *AdditionalAbiTags); void mangleSourceName(const IdentifierInfo *II); + void mangleSourceName(StringRef Name); void mangleRegCallName(const IdentifierInfo *II); void mangleDeviceStubName(const IdentifierInfo *II); void mangleSourceNameWithAbiTags( @@ -1762,10 +1763,14 @@ void CXXNameMangler::mangleDeviceStubName(const IdentifierInfo *II) { } void CXXNameMangler::mangleSourceName(const IdentifierInfo *II) { + mangleSourceName(II->getName()); +} + +void CXXNameMangler::mangleSourceName(StringRef Name) { // ::= // ::= [n] // ::= - Out << II->getLength() << II->getName(); + Out << Name.size() << Name; } void CXXNameMangler::mangleNestedName(GlobalDecl GD, @@ -5190,6 +5195,15 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, Diags.Report(E->getExprLoc(), DiagID); return; } + case UETT_PtrAuthStructKey: + case UETT_PtrAuthStructDiscriminator: { + // ::= u * E + Out << "u"; + mangleSourceName(getTraitSpelling(SAE->getKind())); + mangleType(SAE->getArgumentType()); + Out << "E"; + break; + } case UETT_VecStep: { DiagnosticsEngine &Diags = Context.getDiags(); unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index 38e565ca8f836..48ed28f2f4f3d 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -70,6 +70,33 @@ uint16_t CodeGen::getPointerAuthDeclDiscriminator(CodeGenModule &CGM, return CGM.getPointerAuthDeclDiscriminator(Declaration); } +CGPointerAuthInfo CodeGenModule::EmitPointerAuthInfo(const RecordDecl *RD) { + assert(RD && "null RecordDecl passed"); + auto *Attr = RD->getAttr(); + + if (!Attr) + return CGPointerAuthInfo(); + + Expr::EvalResult Result; + bool Succeeded = Attr->getKey()->EvaluateAsRValue(Result, getContext()); + assert(Succeeded); + (void)Succeeded; + int Key = Result.Val.getInt().getExtValue(); + + if (static_cast(Key) == PointerAuthKeyNone) + return CGPointerAuthInfo(); + + Succeeded = Attr->getDiscriminator()->EvaluateAsRValue(Result, getContext()); + assert(Succeeded); + (void)Succeeded; + unsigned DiscVal = Result.Val.getInt().getExtValue(); + + auto *Discriminator = llvm::ConstantInt::get(IntPtrTy, DiscVal); + return CGPointerAuthInfo(Key, PointerAuthenticationMode::SignAndAuth, + /* isIsaPointer */ false, + /* authenticatesNullValues */ false, Discriminator); +} + /// Return the "other" decl-specific discriminator for the given decl. uint16_t CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl Declaration) { @@ -146,6 +173,12 @@ getPointerAuthInfoForPointeeType(CodeGenModule &CGM, QualType PointeeType) { if (PointeeType->isFunctionType()) return CGM.getFunctionPointerAuthInfo(PointeeType); + // Records pointers may have the ptrauth_struct attribute. + if (auto RecordTy = PointeeType->getAs()) + if (CGPointerAuthInfo AuthInfo = + CGM.EmitPointerAuthInfo(RecordTy->getDecl())) + return AuthInfo; + // Normal data pointers never use direct pointer authentication by default. return CGPointerAuthInfo(); } diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 8852b39f46be1..bca9cc40f90b1 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -3006,6 +3006,8 @@ void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, if (D.isNoDestroy(CGM.getContext())) return; + addr = CGM.getConstantSignedPointer(addr, D.getType()); + // OpenMP offloading supports C++ constructors and destructors but we do not // always have 'atexit' available. Instead lower these to use the LLVM global // destructors which we can handle directly in the runtime. Note that this is @@ -4881,16 +4883,18 @@ static void InitCatchParam(CodeGenFunction &CGF, llvm::Type *PtrTy = CGF.ConvertTypeForMem(CaughtType); // Create the temporary and write the adjusted pointer into it. - Address ExnPtrTmp = - CGF.CreateTempAlloca(PtrTy, CGF.getPointerAlign(), "exn.byref.tmp"); + RawAddress ExnPtrTmp = + CGF.CreateTempAlloca(PtrTy, CGF.getPointerAlign(), "exn.byref.tmp"); + AdjustedExn = CGF.EmitPointerAuthSign(PointeeType, AdjustedExn); llvm::Value *Casted = CGF.Builder.CreateBitCast(AdjustedExn, PtrTy); CGF.Builder.CreateStore(Casted, ExnPtrTmp); // Bind the reference to the temporary. - AdjustedExn = ExnPtrTmp.emitRawPointer(CGF); + AdjustedExn = ExnPtrTmp.getPointer(); } } + AdjustedExn = CGF.EmitPointerAuthSign(CaughtType, AdjustedExn); llvm::Value *ExnCast = CGF.Builder.CreateBitCast(AdjustedExn, LLVMCatchTy, "exn.byref"); CGF.Builder.CreateStore(ExnCast, ParamAddr); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 52ca9c9935ae3..a5aaeb9325a81 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -780,6 +780,29 @@ ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() { /*isType=*/true, Ty.get().getAsOpaquePtr(), SourceRange(Loc, EndLoc)); } +ExprResult Parser::ParseBuiltinPtrauthStructKeyOrDisc(bool ParseKey) { + 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(); + UnaryExprOrTypeTrait ExprKind = + ParseKey ? UETT_PtrAuthStructKey : UETT_PtrAuthStructDiscriminator; + + return Actions.ActOnUnaryExprOrTypeTraitExpr( + Loc, ExprKind, + /*isType=*/true, Ty.get().getAsOpaquePtr(), SourceRange(Loc, EndLoc)); +} + /// Parse a cast-expression, or, if \pisUnaryExpression is true, parse /// a unary-expression. /// @@ -1832,6 +1855,12 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw___builtin_ptrauth_type_discriminator: return ParseBuiltinPtrauthTypeDiscriminator(); + case tok::kw___builtin_ptrauth_struct_key: + return ParseBuiltinPtrauthStructKeyOrDisc(true); + + case tok::kw___builtin_ptrauth_struct_disc: + return ParseBuiltinPtrauthStructKeyOrDisc(false); + case tok::kw___is_lvalue_expr: case tok::kw___is_rvalue_expr: if (NotPrimaryExpression) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index f2546dcf58d37..87271d010106d 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1554,9 +1554,11 @@ bool Sema::checkPointerAuthDiscriminatorArg(Expr *arg, return true; } + bool isPtrAuthStruct = kind == PADAK_TypeDiscPtrAuthStruct; + std::optional result = arg->getIntegerConstantExpr(Context); if (!result) { - Diag(arg->getExprLoc(), diag::err_ptrauth_arg_not_ice); + Diag(arg->getExprLoc(), diag::err_ptrauth_arg_not_ice) << isPtrAuthStruct; return false; } @@ -1569,6 +1571,7 @@ bool Sema::checkPointerAuthDiscriminatorArg(Expr *arg, isAddrDiscArg = true; break; case PADAK_ExtraDiscPtrAuth: + case PADAK_TypeDiscPtrAuthStruct: max = PointerAuthQualifier::MaxDiscriminator; break; }; @@ -1585,7 +1588,7 @@ bool Sema::checkPointerAuthDiscriminatorArg(Expr *arg, << value; else Diag(arg->getExprLoc(), diag::err_ptrauth_extra_discriminator_invalid) - << value << max; + << value << max << isPtrAuthStruct; return false; }; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 3cadda56d7ed9..4a93a356615a5 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -3060,6 +3060,19 @@ static void diagnoseMissingConstinit(Sema &S, const VarDecl *InitDecl, } } +static void checkPtrAuthStructAttr(const NamedDecl *New, const Decl *Prev, + Sema &S) { + if (auto *NewRD = dyn_cast(New)) + if (auto *PrevRD = dyn_cast(Prev)) + if (S.Context.hasPointerAuthStructMismatch(NewRD, PrevRD)) { + S.Diag(NewRD->getLocation(), diag::err_ptrauth_struct_signing_mismatch) + << NewRD->getDeclName(); + S.Diag(PrevRD->getLocation(), + diag::note_previous_ptrauth_struct_declaration) + << NewRD->getDeclName(); + } +} + void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, AvailabilityMergeKind AMK) { if (UsedAttr *OldAttr = Old->getMostRecentDecl()->getAttr()) { @@ -3165,6 +3178,8 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, Diag(Old->getLocation(), diag::note_previous_declaration); } + checkPtrAuthStructAttr(New, Old, *this); + if (!Old->hasAttrs()) return; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 9f2a9475a475a..1ec5388c22f30 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -482,6 +482,28 @@ static bool checkAcquireOrderAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL, return true; } +static void handlePointerAuthStructAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + S.addPointerAuthStructAttr(D, AL, AL.getArgAsExpr(0), AL.getArgAsExpr(1)); +} + +void Sema::addPointerAuthStructAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *Key, Expr *Disc) { + int KeyValue = 0; + unsigned DiscValue; + + if (!Key->isValueDependent()) + if (checkConstantPointerAuthKey(Key, KeyValue)) + return; + + if (!Disc->isValueDependent()) + if (!checkPointerAuthDiscriminatorArg(Disc, PADAK_TypeDiscPtrAuthStruct, + DiscValue)) + return; + + D->addAttr(::new (Context) PointerAuthStructAttr(Context, CI, Key, Disc)); +} + static void handleAcquiredAfterAttr(Sema &S, Decl *D, const ParsedAttr &AL) { SmallVector Args; if (!checkAcquireOrderAttrCommon(S, D, AL, Args)) @@ -6701,6 +6723,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_Mode: handleModeAttr(S, D, AL); break; + case ParsedAttr::AT_PointerAuthStruct: + handlePointerAuthStructAttr(S, D, AL); + break; case ParsedAttr::AT_NonNull: if (auto *PVD = dyn_cast(D)) handleNonNullAttrParameter(S, PVD, AL); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 4ce12e989a67a..7431d7cfb850d 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -6663,6 +6663,32 @@ Sema::getDefaultedFunctionKind(const FunctionDecl *FD) { return DefaultedFunctionKind(); } +/// Set flag HasSignedClassInHierarchy and diagnose dynamic classes that have +/// signed classes in their hierarchies. +static void setHasSignedClassInHierarchy(CXXRecordDecl *RD, Sema &S) { + bool Known; + unsigned Key; + std::tie(Known, Key) = S.Context.getPointerAuthStructKey(RD); + + if (Known && Key != PointerAuthKeyNone) { + RD->setHasSignedClassInHierarchy(); + } else { + for (auto &B : RD->bases()) { + auto *Base = B.getType()->getAsCXXRecordDecl(); + if (Base && !Base->isDependentType() && + Base->hasSignedClassInHierarchy()) { + RD->setHasSignedClassInHierarchy(); + break; + } + } + } + + if (RD->hasSignedClassInHierarchy() && RD->isDynamicClass()) + S.Diag(RD->getLocation(), + diag::err_ptrauth_invalid_struct_attr_dynamic_class) + << RD->getDeclName(); +} + static void DefineDefaultedFunction(Sema &S, FunctionDecl *FD, SourceLocation DefaultLoc) { Sema::DefaultedFunctionKind DFK = S.getDefaultedFunctionKind(FD); @@ -6888,6 +6914,8 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) { } } + setHasSignedClassInHierarchy(Record, *this); + // Warn if the class has virtual methods but non-virtual public destructor. if (Record->isPolymorphic() && !Record->isDependentType()) { CXXDestructorDecl *dtor = Record->getDestructor(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index ca835c779b947..0b0db4bc5a4bc 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4122,6 +4122,15 @@ static bool CheckPtrAuthTypeDiscriminatorOperandType(Sema &S, QualType T, return false; } +static bool CheckPtrAuthStructKeyOrDiscrminatorOperandType( + Sema &S, QualType T, SourceLocation Loc, SourceRange ArgRange) { + if (!T->isRecordType()) { + S.Diag(Loc, diag::err_ptrauth_struct_key_disc_not_record) << ArgRange; + return true; + } + return false; +} + static bool CheckExtensionTraitOperandType(Sema &S, QualType T, SourceLocation Loc, SourceRange ArgRange, @@ -4515,6 +4524,11 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType, return CheckPtrAuthTypeDiscriminatorOperandType( *this, ExprType, OpLoc, ExprRange); + if (ExprKind == UETT_PtrAuthStructKey || + ExprKind == UETT_PtrAuthStructDiscriminator) + return CheckPtrAuthStructKeyOrDiscrminatorOperandType(*this, ExprType, + OpLoc, ExprRange); + // Explicitly list some types as extensions. if (!CheckExtensionTraitOperandType(*this, ExprType, OpLoc, ExprRange, ExprKind)) @@ -4590,9 +4604,16 @@ ExprResult Sema::CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo, TInfo->getType()->isVariablyModifiedType()) TInfo = TransformToPotentiallyEvaluated(TInfo); + QualType ResultTy; + if (ExprKind == UETT_PtrAuthStructKey) { + ResultTy = Context.getSignedSizeType(); + } else { + ResultTy = Context.getSizeType(); + } + // C99 6.5.3.4p4: the type (an unsigned integer type) is size_t. return new (Context) UnaryExprOrTypeTraitExpr( - ExprKind, TInfo, Context.getSizeType(), OpLoc, R.getEnd()); + ExprKind, TInfo, ResultTy, OpLoc, R.getEnd()); } ExprResult diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 64f6b01bed229..0e23493acc943 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -644,6 +644,26 @@ static void instantiateDependentSYCLKernelAttr( New->addAttr(Attr.clone(S.getASTContext())); } +static void instantiatePointerAuthStructAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const PointerAuthStructAttr &Attr, Decl *New) { + EnterExpressionEvaluationContext Unevaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + + Expr *Key, *Disc; + ExprResult Result = S.SubstExpr(Attr.getKey(), TemplateArgs); + if (Result.isInvalid()) + return; + Key = Result.getAs(); + + Result = S.SubstExpr(Attr.getDiscriminator(), TemplateArgs); + if (Result.isInvalid()) + return; + Disc = Result.getAs(); + + S.addPointerAuthStructAttr(New, Attr, Key, Disc); +} + /// Determine whether the attribute A might be relevant to the declaration D. /// If not, we can skip instantiating it. The attribute may or may not have /// been instantiated yet. @@ -870,6 +890,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, continue; } + if (auto *PointerAuthStruct = dyn_cast(TmplAttr)) { + instantiatePointerAuthStructAttr(*this, TemplateArgs, *PointerAuthStruct, + New); + continue; + } + assert(!TmplAttr->isPackExpansion()); if (TmplAttr->isLateParsed() && LateAttrs) { // Late parsed attributes must be instantiated and attached after the diff --git a/clang/test/CodeGen/ptrauth-function-lvalue-cast-disc.c b/clang/test/CodeGen/ptrauth-function-lvalue-cast-disc.c index 6712a6cf417b9..df90ab16c3271 100644 --- a/clang/test/CodeGen/ptrauth-function-lvalue-cast-disc.c +++ b/clang/test/CodeGen/ptrauth-function-lvalue-cast-disc.c @@ -5,6 +5,9 @@ typedef void (*fptr_t)(void); char *cptr; void (*fptr)(void); +typedef struct __attribute__((ptrauth_struct(0,42))) S {} S; +S *sptr; + // CHECK-LABEL: define void @test1 void test1() { // CHECK: [[LOAD:%.*]] = load ptr, ptr @cptr @@ -26,6 +29,16 @@ char test2() { // CHECK: call i64 @llvm.ptrauth.resign(i64 [[TOINT]], i32 0, i64 18983, i32 0, i64 0) } +// CHECK-LABEL: define void @test3 +void test3() { + (S *)fptr; + // CHECK: [[LOAD:%.*]] = load ptr, ptr @fptr + // CHECK: [[CMP:%.*]] = icmp ne ptr [[LOAD]], null + + // CHECK: [[TOINT:%.*]] = ptrtoint ptr [[LOAD]] to i64 + // CHECK: call i64 @llvm.ptrauth.resign(i64 [[TOINT]], i32 0, i64 18983, i32 0, i64 42) +} + // CHECK-LABEL: define void @test4 void test4() { (*((fptr_t)(&*((char *)(&*(fptr_t)cptr)))))(); diff --git a/clang/test/CodeGen/ptrauth-function-lvalue-cast.c b/clang/test/CodeGen/ptrauth-function-lvalue-cast.c index 8d8af18fcafbe..5ee9c32c244b5 100644 --- a/clang/test/CodeGen/ptrauth-function-lvalue-cast.c +++ b/clang/test/CodeGen/ptrauth-function-lvalue-cast.c @@ -6,6 +6,9 @@ typedef void (*fptr_t)(void); char *cptr; void (*fptr)(void); +typedef struct __attribute__((ptrauth_struct(0,42))) S {} S; +S *sptr; + // CHECK: define {{(dso_local )?}}void @test1 void test1() { // CHECK: [[LOAD:%.*]] = load ptr, ptr @cptr @@ -22,3 +25,13 @@ char test2() { // CHECK: [[LOAD1:%.*]] = load i8, ptr [[LOAD]] // CHECK: ret i8 [[LOAD1]] } + +// CHECK-LABEL: define {{(dso_local )?}}void @test3 +void test3() { + (S *)fptr; + // CHECK: [[LOAD:%.*]] = load ptr, ptr @fptr + // CHECK: [[CMP:%.*]] = icmp ne ptr [[LOAD]], null + + // CHECK: [[TOINT:%.*]] = ptrtoint ptr [[LOAD]] to i64 + // CHECK: call i64 @llvm.ptrauth.resign(i64 [[TOINT]], i32 0, i64 0, i32 0, i64 42) +} diff --git a/clang/test/CodeGenCXX/ptrauth-struct-attr-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-struct-attr-wrapper-globals.cpp new file mode 100644 index 0000000000000..b7fb362c8d7b0 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-struct-attr-wrapper-globals.cpp @@ -0,0 +1,820 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-returns -fptrauth-intrinsics -emit-llvm -std=c++17 -O1 -disable-llvm-passes -no-enable-noundef-analysis -fexceptions -fcxx-exceptions -o - %s | FileCheck %s + +// CHECK: %[[STRUCT_S0:.*]] = type { i32, i32, [4 x i32] } +// CHECK: %[[STRUCT_S5:.*]] = type { %[[STRUCT_S2:.*]] } +// CHECK: %[[STRUCT_S2]] = type { i32, %[[STRUCT_S0]] } +// CHECK: %[[STRUCT_S6:.*]] = type { i8 } +// CHECK: %[[STRUCT_S7:.*]] = type { i8 } +// CHECK: %[[STRUCT_S8:.*]] = type { i8 } +// CHECK: %[[STRUCT_S9:.*]] = type { i8 } +// CHECK: %[[STRUCT_S12:.*]] = type { i8 } +// CHECK: %[[STRUCT_S14:.*]] = type { i8 } +// CHECK: %[[STRUCT_S13:.*]] = type { i8 } +// CHECK: %[[STRUCT_S1:.*]] = type { i32, i32, [4 x i32] } +// CHECK: %[[STRUCT_S3:.*]] = type { [2 x i32], %[[STRUCT_S0]] } +// CHECK: %[[CLASS_ANON:.*]] = type { ptr, ptr } +// CHECK: %[[CLASS_ANON_0:.*]] = type { %[[STRUCT_S0]], i32 } + +#include + +typedef __SIZE_TYPE__ size_t; +void* operator new(size_t count, void* ptr); + +#define ATTR_NONE __attribute__((ptrauth_struct(ptrauth_key_none, 100))) +#define ATTR0 __attribute__((ptrauth_struct(1, 100))) +#define ATTR1 __attribute__((ptrauth_struct(1, 101))) +#define ATTR2 __attribute__((ptrauth_struct(1, 102))) +#define ATTR4 __attribute__((ptrauth_struct(1, 104))) + +struct ATTR0 S0 { + int f0, f1, f2[4]; + S0() {} + S0(const S0 &); + S0 &operator=(const S0 &); + ~S0(); + void nonvirtual0() { f1 = 1; } + int lambda0(int i) { + return [&](){ return f1 + i; }(); + } + int lambda1(int i) { + return [*this, i](){ return f1 + i; }(); + } +}; + +struct ATTR1 S1 { + int f0, f1, f2[4]; + void nonvirtual1(); + S1(); + ~S1() noexcept(false); +}; + +struct ATTR2 S2 { + int f0; + void nonvirtual2(); + S0 f1; + S2(S0); +}; + +struct S3 { + int f0[2]; + S0 f1; +}; + +struct ATTR4 S4 { + int f0, f1; +}; + +struct ATTR2 S5 : S2 { + using S2::S2; +}; + +struct ATTR_NONE S6 { +}; + +template +struct __attribute__((ptrauth_struct(k, 100))) S7 { +}; + +template +struct __attribute__((ptrauth_struct(1, d))) S8 { +}; + +template +struct __attribute__((ptrauth_struct((k + 2) % 4, (d + 1)))) S9 { +}; + +struct ATTR0 S11 : S1, S2 { +}; + +struct __attribute__((ptrauth_struct(__builtin_ptrauth_struct_key(S0) + 1, __builtin_ptrauth_struct_disc(S1) + 1))) S12 { +}; + +template +struct __attribute__((ptrauth_struct(__builtin_ptrauth_struct_key(Derived) + 1, __builtin_ptrauth_struct_disc(Derived) + 1))) S13 { +}; + +struct ATTR4 S14 : S13 { +}; + +// CHECK: @gs0.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @gs0, i32 1, i64 0, i64 100 }, +// CHECK: @gs2 = constant ptr @gs0.ptrauth, align 8 +// CHECK: @gs3 = constant ptr @gs0.ptrauth, align 8 +// CHECK: @gs6 = global %[[STRUCT_S6]] zeroinitializer, align 1 +// CHECK: @gs7 = global ptr @gs6, align 8 +// CHECK: @gs8 = global %[[STRUCT_S7]] zeroinitializer, align 1 +// CHECK: @gs8.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @gs8, i32 1, i64 0, i64 100 }, section "llvm.ptrauth", align 8 +// CHECK: @gs9 = global ptr @gs8.ptrauth, align 8 +// CHECK: @gs10 = global %[[STRUCT_S8]] zeroinitializer, align 1 +// CHECK: @gs10.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @gs10, i32 1, i64 0, i64 200 }, section "llvm.ptrauth", align 8 +// CHECK: @gs11 = global ptr @gs10.ptrauth, align 8 +// CHECK: @gs12 = global %[[STRUCT_S9]] zeroinitializer, align 1 +// CHECK: @gs12.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @gs12, i32 0, i64 0, i64 202 }, section "llvm.ptrauth", align 8 +// CHECK: @gs13 = global ptr @gs12.ptrauth, align 8 +// CHECK: @gs16 = global %[[STRUCT_S12]] zeroinitializer, align 1 +// CHECK: @gs16.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @gs16, i32 2, i64 0, i64 102 }, section "llvm.ptrauth", align 8 +// CHECK: @gs17 = global ptr @gs16.ptrauth, align 8 +// CHECK: @gs18 = global %[[STRUCT_S14]] zeroinitializer, align 1 +// CHECK: @gs18.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @gs18, i32 1, i64 0, i64 104 }, section "llvm.ptrauth", align 8 +// CHECK: @gs19 = global ptr @gs18.ptrauth, align 8 +// CHECK: @gs20 = global %[[STRUCT_S13]] zeroinitializer, align 1 +// CHECK: @gs20.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @gs20, i32 2, i64 0, i64 105 }, section "llvm.ptrauth", align 8 +// CHECK: @gs21 = global ptr @gs20.ptrauth, align 8 + +// CHECK: define internal void @__cxx_global_var_init() +// CHECK: %[[V0:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @gs0 to i64), i32 1, i64 100) +// CHECK: %[[V1:.*]] = inttoptr i64 %[[V0]] to ptr +// CHECK: call ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V1]]) +// CHECK: call i32 @__cxa_atexit(ptr @_ZN2S0D1Ev.ptrauth, ptr @gs0.ptrauth, ptr @__dso_handle) +// CHECK: ret void +// CHECK: } + +// CHECK: define linkonce_odr ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: call ptr @_ZN2S0C2Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[THIS1]]) +// CHECK: ret ptr %[[THIS1]] + +S0 gs0; + +// CHECK: define linkonce_odr ptr @_ZN2S5CI12S2E2S0(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS:.*]], ptr %[[V0:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: call ptr @_ZN2S5CI22S2E2S0(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS1]], ptr %[[V0]]) +// CHECK: ret ptr %[[THIS1]] + +S5 gs1(gs0); + +S0 &gs2 = gs0; +S0 &gs3 = gs2; + +S6 gs6, *gs7 = &gs6; + +S7<1> gs8, *gs9 = &gs8; +S8<200> gs10, *gs11 = &gs10; +S9<2, 201> gs12, *gs13 = &gs12; +S12 gs16, *gs17 = &gs16; +S14 gs18, *gs19 = &gs18; +S13 gs20, *gs21 = &gs20; + +// CHECK: define void @_Z16test_nonvirtual0P2S0(ptr %[[S:.*]]) +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// CHECK: call void @_ZN2S011nonvirtual0Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V0]]) + +// CHECK: define linkonce_odr void @_ZN2S011nonvirtual0Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[THIS1]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V0]], i32 1, i64 100) +// CHECK: %[[V2:.*]] = inttoptr i64 %[[V1]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V2]], i64 4 +// CHECK: store i32 1, ptr %[[RESIGNEDGEP]], align 4 + +void test_nonvirtual0(S0 *s) { + s->nonvirtual0(); +} + +// CHECK: define ptr @_Z9test_new0v() +// CHECK: entry: +// CHECK: %[[EXN_SLOT:.*]] = alloca ptr +// CHECK: %[[EHSELECTOR_SLOT:.*]] = alloca i32 +// CHECK: %[[CALL:.*]] = call noalias nonnull ptr @_Znwm(i64 24) +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[CALL]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 101) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[CALL1:.*]] = invoke ptr @_ZN2S1C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) + +// CHECK: ret ptr %[[CALL]] + +// CHECK: landingpad { ptr, i32 } +// CHECK: call void @_ZdlPvm(ptr %[[CALL]], i64 24) + +S1 *test_new0() { + return new S1(); +} + +// CHECK: define ptr @_Z9test_new1Pv(ptr %[[P:.*]]) +// CHECK: %[[P_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[P]], ptr %[[P_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[P_ADDR]], align 8 +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V1]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V2]], i32 1, i64 101) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: %[[CALL:.*]] = call ptr @_ZN2S1C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V4]]) +// CHECK: ret ptr %[[V1]] + +S1 *test_new1(void *p) { + return new (p) S1; +} + +// CHECK: define ptr @_Z9test_new2v() +// CHECK: %[[CALL:.*]] = call noalias nonnull ptr @_Znam(i64 112) +// CHECK: %[[V2:.*]] = getelementptr inbounds i8, ptr %[[CALL]], i64 16 +// CHECK: %[[ARRAYCTOR_END:.*]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[V2]], i64 4 + +// CHECK: %[[ARRAYCTOR_CUR:.*]] = phi ptr [ %[[V2]], %{{.*}} ], [ %[[ARRAYCTOR_NEXT:.*]], %{{.*}} ] +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[ARRAYCTOR_CUR]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V4]], i32 1, i64 101) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[CALL1:.*]] = invoke ptr @_ZN2S1C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V6]]) + +// CHECK: %[[ARRAYCTOR_NEXT]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[ARRAYCTOR_CUR]], i64 1 +// CHECK: %[[ARRAYCTOR_DONE:.*]] = icmp eq ptr %[[ARRAYCTOR_NEXT]], %[[ARRAYCTOR_END]] + +// CHECK: ret ptr %[[V2]] + +// CHECK: landingpad { ptr, i32 } + +// CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi ptr [ %[[ARRAYCTOR_CUR]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT:.*]], %{{.*}} ] +// CHECK: %[[ARRAYDESTROY_ELEMENT:.*]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1 +// CHECK: %[[V10:.*]] = ptrtoint ptr %[[ARRAYDESTROY_ELEMENT]] to i64 +// CHECK: %[[V11:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V10]], i32 1, i64 101) +// CHECK: %[[V12:.*]] = inttoptr i64 %[[V11]] to ptr +// CHECK: invoke ptr @_ZN2S1D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V12]]) + +// CHECK: icmp eq ptr %[[ARRAYDESTROY_ELEMENT]], %[[V2]] + +// CHECK: call void @_ZdaPvm(ptr %[[CALL]], i64 112) + +S1 *test_new2() { + return new S1[4]; +} + +// CHECK: define void @_Z12test_delete0P2S1(ptr %{{.*}}) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0]] = load ptr, ptr %[[A_ADDR]], align 8 + +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 101) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: invoke ptr @_ZN2S1D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V0]]) + +// CHECK: call void @_ZdlPvm(ptr %[[V3]], i64 24) + +// CHECK: landingpad { ptr, i32 } +// CHECK: call void @_ZdlPvm(ptr %[[V3]], i64 24) + +void test_delete0(S1 *a) { + delete a; +} + +// CHECK: define void @_Z12test_delete1P2S1(ptr %{{.*}}) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 + +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 101) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V4]], i64 -16 +// CHECK: %[[V5:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V5]], i32 1, i64 101) +// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr +// CHECK: %[[RESIGNEDGEP1:.*]] = getelementptr i8, ptr %[[V7]], i64 -8 +// CHECK: %[[V9:.*]] = load i64, ptr %[[RESIGNEDGEP1]], align 4 +// CHECK: %[[V10:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V11:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V10]], i32 1, i64 101) +// CHECK: %[[V12:.*]] = inttoptr i64 %[[V11]] to ptr +// CHECK: %[[DELETE_END:.*]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[V12]], i64 %[[V9]] +// CHECK: icmp eq ptr %[[V12]], %[[DELETE_END]] + +// CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi ptr [ %[[DELETE_END]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT]], %{{.*}} ] +// CHECK: %[[ARRAYDESTROY_ELEMENT:.*]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1 +// CHECK: %[[V13:.*]] = ptrtoint ptr %[[ARRAYDESTROY_ELEMENT]] to i64 +// CHECK: %[[V14:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V13]], i32 1, i64 101) +// CHECK: %[[V15:.*]] = inttoptr i64 %[[V14]] to ptr +// CHECK: invoke ptr @_ZN2S1D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V15]]) + +// CHECK: icmp eq ptr %[[ARRAYDESTROY_ELEMENT]], %[[V12]] + +// CHECK: call void @_ZdaPvm(ptr %[[RESIGNEDGEP]], i64 %{{.*}}) + +// CHECK: landingpad { ptr, i32 } + +// CHECK: %[[ARRAYDESTROY_ELEMENTPAST4:.*]] = phi ptr [ %[[ARRAYDESTROY_ELEMENT]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT5:.*]], %{{.*}} ] +// CHECK: %[[ARRAYDESTROY_ELEMENT5:.*]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[ARRAYDESTROY_ELEMENTPAST4]], i64 -1 +// CHECK: %[[V19:.*]] = ptrtoint ptr %[[ARRAYDESTROY_ELEMENT5]] to i64 +// CHECK: %[[V20:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V19]], i32 1, i64 101) +// CHECK: %[[V21:.*]] = inttoptr i64 %[[V20]] to ptr +// CHECK: %[[CALL7:.*]] = invoke ptr @_ZN2S1D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V21]]) + +// CHECK: icmp eq ptr %[[ARRAYDESTROY_ELEMENT5]], %[[V12]] + +// CHECK: call void @_ZdaPvm(ptr %[[RESIGNEDGEP]], i64 %{{.*}}) + +void test_delete1(S1 *a) { + delete [] a; +} + +// CHECK: define void @_Z16test_assignment0P2S0S0_(ptr %{{.*}}, ptr %{{.*}}) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[B_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[B_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: call nonnull align 4 dereferenceable(24) ptr @_ZN2S0aSERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V1]], ptr nonnull align 4 dereferenceable(24) %[[V0]]) + +void test_assignment0(S0 *a, S0 *b) { + *a = *b; +} + +// CHECK: define void @_Z16test_assignment1v() +// CHECK: %[[T0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: call ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) +// CHECK: %[[V5:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V5]], i32 1, i64 100) +// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr +// CHECK: invoke ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V7]]) + +// CHECK: %[[V8:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V8]], i32 1, i64 100) +// CHECK: %[[V10:.*]] = inttoptr i64 %[[V9]] to ptr +// CHECK: %[[V11:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V12:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V11]], i32 1, i64 100) +// CHECK: %[[V13:.*]] = inttoptr i64 %[[V12]] to ptr +// CHECK: invoke nonnull align 4 dereferenceable(24) ptr @_ZN2S0aSERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V13]], ptr nonnull align 4 dereferenceable(24) %[[V10]]) + +// CHECK: %[[V14:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V15:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V14]], i32 1, i64 100) +// CHECK: %[[V16:.*]] = inttoptr i64 %[[V15]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V16]]) +// CHECK: %[[V18:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V19:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V18]], i32 1, i64 100) +// CHECK: %[[V20:.*]] = inttoptr i64 %[[V19]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V20]]) +// CHECK: ret void + +// CHECK: landingpad { ptr, i32 } +// CHECK: landingpad { ptr, i32 } +// CHECK: %[[V28:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V29:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V28]], i32 1, i64 100) +// CHECK: %[[V30:.*]] = inttoptr i64 %[[V29]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V30]]) + +// CHECK: %[[V32:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V33:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V32]], i32 1, i64 100) +// CHECK: %[[V34:.*]] = inttoptr i64 %[[V33]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V34]]) + +void test_assignment1() { + S0 t0, t1; + t0 = t1; +} + +// CHECK: define void @_Z16test_assignment2P2S4S0_( +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[B_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[B_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[V2]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 1, i64 104) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[V7:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V8:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V7]], i32 1, i64 104) +// CHECK: %[[V9:.*]] = inttoptr i64 %[[V8]] to ptr +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[V6]], ptr align 4 %[[V9]], i64 8, i1 false) + +void test_assignment2(S4 *a, S4 *b) { + *a = *b; +} + +// CHECK: define void @_Z28test_constructor_destructor0v() +// CHECK: %[[T0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: call ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) +// CHECK: %[[V5:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V5]], i32 1, i64 100) +// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr +// CHECK: %[[V8:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V8]], i32 1, i64 100) +// CHECK: %[[V10:.*]] = inttoptr i64 %[[V9]] to ptr +// CHECK: invoke ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V7]], ptr nonnull align 4 dereferenceable(24) %[[V10]]) + +// CHECK: %[[V11:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V12:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V11]], i32 1, i64 100) +// CHECK: %[[V13:.*]] = inttoptr i64 %[[V12]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V13]]) +// CHECK: %[[V15:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V16:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V15]], i32 1, i64 100) +// CHECK: %[[V17:.*]] = inttoptr i64 %[[V16]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V17]]) +// CHECK: ret void + +// CHECK: landingpad { ptr, i32 } +// CHECK: %[[V23:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V24:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V23]], i32 1, i64 100) +// CHECK: %[[V25:.*]] = inttoptr i64 %[[V24]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V25]]) + +void test_constructor_destructor0() { + S0 t0, t1 = t0; +} + +// CHECK: define void @_Z19test_member_access1P2S0i(ptr %{{.*}}, i32 %{{.*}}) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[I_ADDR:.*]] = alloca i32, align 4 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i32, ptr %[[I_ADDR]], align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V1]], 2 +// CHECK: %[[IDXPROM:.*]] = sext i32 %[[ADD]] to i64 +// CHECK: %[[ARRAYIDX_OFFS:.*]] = mul nsw i64 %[[IDXPROM]], 4 +// CHECK: %[[ADD1:.*]] = add i64 8, %[[ARRAYIDX_OFFS]] +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 100) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V4]], i64 %[[ADD1]] +// CHECK: store i32 123, ptr %[[RESIGNEDGEP]], align 4 + +void test_member_access1(S0 *a, int i) { + a->f2[i + 2] = 123; +} + +// CHECK: define nonnull align 4 dereferenceable(24) ptr @_Z15test_reference0R2S0(ptr nonnull align 4 dereferenceable(24) %[[A:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1:.*]] = alloca ptr, align 8 +// CHECK: %[[R0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: alloca ptr, align 8 +// CHECK: %[[R1:.*]] = alloca ptr, align 8 +// CHECK: %[[V11:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V21:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V11]], i32 1, i64 100) +// CHECK: %[[V31:.*]] = inttoptr i64 %[[V21]] to ptr +// CHECK: %[[V41:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: call ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V31]], ptr nonnull align 4 dereferenceable(24) %[[V41]]) +// CHECK: %[[V6:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: store ptr %[[V6]], ptr %[[T1]], align 8 +// CHECK: %[[V8:.*]] = ptrtoint ptr %[[R0]] to i64 +// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V8]], i32 1, i64 100) +// CHECK: %[[V10:.*]] = inttoptr i64 %[[V9]] to ptr +// CHECK: %[[V11:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V12:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V11]], i32 1, i64 100) +// CHECK: %[[V13:.*]] = inttoptr i64 %[[V12]] to ptr +// CHECK: %[[CALL1:.*]] = invoke nonnull align 4 dereferenceable(24) ptr @_Z5func0R2S0(ptr nonnull align 4 dereferenceable(24) %[[V13]]) + +// CHECK: invoke ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V10]], ptr nonnull align 4 dereferenceable(24) %[[CALL1]]) + +// CHECK: %[[V15:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V16:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V15]], i32 1, i64 100) +// CHECK: %[[V17:.*]] = inttoptr i64 %[[V16]] to ptr +// CHECK: %[[CALL6:.*]] = invoke nonnull align 4 dereferenceable(24) ptr @_Z5func0R2S0(ptr nonnull align 4 dereferenceable(24) %[[V17]]) + +// CHECK: store ptr %[[CALL6]], ptr %[[R1]], align 8 +// CHECK: %[[V18:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @gs0 to i64), i32 1, i64 100) +// CHECK: %[[V19:.*]] = inttoptr i64 %[[V18]] to ptr +// CHECK: %[[V21:.*]] = ptrtoint ptr %[[R0]] to i64 +// CHECK: %[[V22:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V21]], i32 1, i64 100) +// CHECK: %[[V23:.*]] = inttoptr i64 %[[V22]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V23]]) +// CHECK: %[[V26:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V27:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V26]], i32 1, i64 100) +// CHECK: %[[V28:.*]] = inttoptr i64 %[[V27]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V28]]) +// CHECK: ret ptr %[[V19]] + +// CHECK: landingpad { ptr, i32 } + +// CHECK: landingpad { ptr, i32 } +// CHECK: %[[V37:.*]] = ptrtoint ptr %[[R0]] to i64 +// CHECK: %[[V38:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V37]], i32 1, i64 100) +// CHECK: %[[V39:.*]] = inttoptr i64 %[[V38]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V39]]) + +// CHECK: %[[V42:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V43:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V42]], i32 1, i64 100) +// CHECK: %[[V44:.*]] = inttoptr i64 %[[V43]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V44]]) + +S0 &func0(S0 &); + +S0 &test_reference0(S0 &a) { + S0 t0 = a; + S0 &t1 = a; + S0 r0 = func0(t0); + S0 &r1 = func0(t0); + return gs0; +} + +// CHECK: define void @_Z17test_conditional0bR2S0S0_(ptr dead_on_unwind noalias writable sret(%struct.S0) align 4 %[[AGG_RESULT:.*]], i1 %{{.*}}, ptr nonnull align 4 dereferenceable(24) %{{.*}}, ptr nonnull align 4 dereferenceable(24) %{{.*}}) +// CHECK: alloca ptr, align 8 +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[B_ADDR:.*]] = alloca ptr, align 8 + +// CHECK: %[[V5:.*]] = load ptr, ptr %[[A_ADDR]], align 8 + +// CHECK: %[[V6:.*]] = load ptr, ptr %[[B_ADDR]], align 8 + +// CHECK: %[[V7:.*]] = phi ptr [ %[[V5]], %{{.*}} ], [ %[[V6]], %{{.*}} ] +// CHECK: call ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[AGG_RESULT]], ptr nonnull align 4 dereferenceable(24) %[[V7]]) +// CHECK: ret void + +S0 test_conditional0(bool c, S0 &a, S0 &b) { + return c ? a : b; +} + +// CHECK: define i32 @_Z17test_conditional1b( +// CHECK: %[[A:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[B:.*]] = alloca %[[STRUCT_S0]], align 4 + +// CHECK: %[[V9:.*]] = phi ptr [ %[[A]], %{{.*}} ], [ %[[B]], %{{.*}} ] +// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[V9]], i32 0, i32 1 +// CHECK: %[[V10:.*]] = load i32, ptr %[[F1]], align 4 +// CHECK: ret i32 %[[V10]] + +int test_conditional1(bool c) { + S0 a, b; + return (c ? a : b).f1; +} + +// CHECK: define void @_Z17test_conditional2bR2S2R2S3(ptr dead_on_unwind noalias writable sret(%struct.S0) align 4 %[[AGG_RESULT]], i1 %{{.*}}, ptr nonnull align 4 dereferenceable(28) %{{.*}}, ptr nonnull align 4 dereferenceable(32) %{{.*}}) +// CHECK: alloca ptr, align 8 +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[B_ADDR:.*]] = alloca ptr, align 8 + +// CHECK: %[[V5:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[V6:.*]] = ptrtoint ptr %[[V5]] to i64 +// CHECK: %[[V7:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V6]], i32 1, i64 102) +// CHECK: %[[V8:.*]] = inttoptr i64 %[[V7]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V8]], i64 4 +// CHECK: %[[V10:.*]] = ptrtoint ptr %[[RESIGNEDGEP]] to i64 +// CHECK: %[[V11:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V10]], i32 1, i64 100) +// CHECK: %[[V12:.*]] = inttoptr i64 %[[V11]] to ptr + +// CHECK: %[[V14:.*]] = load ptr, ptr %[[B_ADDR]], align 8 +// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_S3]], ptr %[[V14]], i32 0, i32 1 +// CHECK: %[[V15:.*]] = ptrtoint ptr %[[F1]] to i64 +// CHECK: %[[V16:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V15]], i32 1, i64 100) +// CHECK: %[[V17:.*]] = inttoptr i64 %[[V16]] to ptr + +// CHECK: %[[V18:.*]] = phi ptr [ %[[V12]], %{{.*}} ], [ %[[V17]], %{{.*}} ] +// CHECK: call ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[AGG_RESULT]], ptr nonnull align 4 dereferenceable(24) %[[V18]]) +// CHECK: ret void + +S0 test_conditional2(bool c, S2 &a, S3 &b) { + return c ? a.f1 : b.f1; +} + +// CHECK: define void @_Z17test_inheritance0P3S11(ptr %[[A:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, +// CHECK: store ptr %[[A]], ptr %[[A_ADDR]], +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V2]], i32 1, i64 100, i32 1, i64 101) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: call void @_ZN2S111nonvirtual1Ev(ptr nonnull align 4 {{.*}} %[[V4]]) + +void test_inheritance0(S11 *a) { + a->nonvirtual1(); +} + +// CHECK: define void @_Z17test_inheritance1P3S11(ptr %[[A:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, +// CHECK: store ptr %[[A]], ptr %[[A_ADDR]], +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V4]], i64 24 +// CHECK: %[[V6:.*]] = ptrtoint ptr %[[ADD_PTR]] to i64 +// CHECK: %[[V7:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V6]], i32 1, i64 102) +// CHECK: %[[V8:.*]] = inttoptr i64 %[[V7]] to ptr +// CHECK: call void @_ZN2S211nonvirtual2Ev(ptr nonnull align 4 {{.*}} %[[V8]]) + +void test_inheritance1(S11 *a) { + a->nonvirtual2(); +} + +// CHECK: define void @_Z15test_exception0v() +// CHECK: alloca ptr, align 8 +// CHECK: %[[S0:.*]] = alloca ptr, align 8 +// CHECK: %[[I:.*]] = alloca i32, align 4 +// CHECK: %[[EXCEPTION:.*]] = call ptr @__cxa_allocate_exception(i64 24) +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[EXCEPTION]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: invoke ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) + +// CHECK: invoke void @__cxa_throw(ptr %[[EXCEPTION]], ptr @_ZTI2S0, ptr @_ZN2S0D1Ev.ptrauth) + +// CHECK: landingpad { ptr, i32 } +// CHECK: call void @__cxa_free_exception(ptr %[[EXCEPTION]]) + +// CHECK: %[[V12:.*]] = call ptr @__cxa_begin_catch( +// CHECK: %[[V13:.*]] = ptrtoint ptr %[[V12]] to i64 +// CHECK: %[[V14:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V13]], i32 1, i64 100) +// CHECK: %[[V15:.*]] = inttoptr i64 %[[V14]] to ptr +// CHECK: store ptr %[[V15]], ptr %[[S0]], align 8 +// CHECK: %[[V17:.*]] = load ptr, ptr %[[S0]], align 8 +// CHECK: %[[V18:.*]] = ptrtoint ptr %[[V17]] to i64 +// CHECK: %[[V19:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V18]], i32 1, i64 100) +// CHECK: %[[V20:.*]] = inttoptr i64 %[[V19]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V20]], i64 4 +// CHECK: %[[V23:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 +// CHECK: store i32 %[[V23]], ptr %[[I]], align 4 + +void test_exception0() { + try { + throw S0(); + } catch (const S0 &s0) { + int i = s0.f1; + } +} + +// CHECK: define void @_Z15test_exception1v() +// CHECK: %[[S0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[I:.*]] = alloca i32, align 4 +// CHECK: %[[EXCEPTION:.*]] = call ptr @__cxa_allocate_exception(i64 24) +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[EXCEPTION]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: invoke ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) + +// CHECK: %[[V12:.*]] = call ptr @__cxa_get_exception_ptr( +// CHECK: %[[V14:.*]] = ptrtoint ptr %[[S0]] to i64 +// CHECK: %[[V15:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V14]], i32 1, i64 100) +// CHECK: %[[V16:.*]] = inttoptr i64 %[[V15]] to ptr +// CHECK: %[[V17:.*]] = ptrtoint ptr %[[V12]] to i64 +// CHECK: %[[V18:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V17]], i32 1, i64 100) +// CHECK: %[[V19:.*]] = inttoptr i64 %[[V18]] to ptr +// CHECK: invoke ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V16]], ptr nonnull align 4 dereferenceable(24) %[[V19]]) + +// CHECK: %[[V20:.*]] = call ptr @__cxa_begin_catch( +// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[S0]], i32 0, i32 1 +// CHECK: %[[V22:.*]] = load i32, ptr %[[F1]], align 4 +// CHECK: store i32 %[[V22]], ptr %[[I]], align 4 +// CHECK: %[[V24:.*]] = ptrtoint ptr %[[S0]] to i64 +// CHECK: %[[V25:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V24]], i32 1, i64 100) +// CHECK: %[[V26:.*]] = inttoptr i64 %[[V25]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V26]]) + +void test_exception1() { + try { + throw S0(); + } catch (S0 s0) { + int i = s0.f1; + } +} + +// CHECK: define void @_Z15test_exception2v() +// CHECK: alloca ptr, align 8 +// CHECK: %[[S0:.*]] = alloca ptr, align 8 +// CHECK: %[[EXN_BYREF_TMP:.*]] = alloca ptr, align 8 +// CHECK: %[[I:.*]] = alloca i32, align 4 +// CHECK: %[[EXCEPTION:.*]] = call ptr @__cxa_allocate_exception(i64 24) +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[EXCEPTION]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: invoke ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) + +// CHECK: %[[V12:.*]] = call ptr @__cxa_begin_catch( +// CHECK: %[[V13:.*]] = ptrtoint ptr %[[V12]] to i64 +// CHECK: %[[V14:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V13]], i32 1, i64 100) +// CHECK: %[[V15:.*]] = inttoptr i64 %[[V14]] to ptr +// CHECK: store ptr %[[V15]], ptr %[[EXN_BYREF_TMP]], align 8 +// CHECK: store ptr %[[EXN_BYREF_TMP]], ptr %[[S0]], align 8 +// CHECK: %[[V18:.*]] = load ptr, ptr %[[S0]], align 8 +// CHECK: %[[V19:.*]] = load ptr, ptr %[[V18]], align 8 +// CHECK: %[[V20:.*]] = ptrtoint ptr %[[V19]] to i64 +// CHECK: %[[V21:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V20]], i32 1, i64 100) +// CHECK: %[[V22:.*]] = inttoptr i64 %[[V21]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V23]], i64 4 +// CHECK: %[[V25:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 +// CHECK: store i32 %[[V25]], ptr %[[I]], align 4 + +void test_exception2() { + try { + throw S0(); + } catch (S0 *&s0) { + int i = s0->f1; + } +} + +// CHECK: define linkonce_odr i32 @_ZN2S07lambda0Ei( +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[I_ADDR:.*]] = alloca i32, align 4 +// CHECK: %[[REF_TMP:.*]] = alloca %[[CLASS_ANON]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V1:.*]] = getelementptr inbounds %[[CLASS_ANON]], ptr %[[REF_TMP]], i32 0, i32 0 +// CHECK: store ptr %[[THIS1]], ptr %[[V1]], align 8 +// CHECK: %[[V2:.*]] = getelementptr inbounds %[[CLASS_ANON]], ptr %[[REF_TMP]], i32 0, i32 1 +// CHECK: store ptr %[[I_ADDR]], ptr %[[V2]], align 8 +// CHECK: call i32 @_ZZN2S07lambda0EiENKUlvE_clEv(ptr nonnull align {{[0-9]+}} dereferenceable(16) %[[REF_TMP]]) + +void test_lambda0(S0 *a) { + a->lambda0(1); +} + +// CHECK: define linkonce_odr i32 @_ZN2S07lambda1Ei( +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[I_ADDR:.*]] = alloca i32, align 4 +// CHECK: %[[REF_TMP:.*]] = alloca %[[CLASS_ANON_0]], align 4 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V1:.*]] = getelementptr inbounds %[[CLASS_ANON_0]], ptr %[[REF_TMP]], i32 0, i32 0 +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V1]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V2]], i32 1, i64 100) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: call ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V4]], ptr nonnull align 4 dereferenceable(24) %[[THIS1]]) +// CHECK: %[[V5:.*]] = getelementptr inbounds %[[CLASS_ANON_0]], ptr %[[REF_TMP]], i32 0, i32 1 +// CHECK: %[[V6:.*]] = load i32, ptr %[[I_ADDR]], align 4 +// CHECK: store i32 %[[V6]], ptr %[[V5]], align 4 +// CHECK: %[[CALL2:.*]] = invoke i32 @_ZZN2S07lambda1EiENKUlvE_clEv(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[REF_TMP]]) + +// CHECK: call ptr @_ZZN2S07lambda1EiENUlvE_D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[REF_TMP]]) +// CHECK: ret i32 %[[CALL2]] + +// CHECK: landingpad { ptr, i32 } +// CHECK: call ptr @_ZZN2S07lambda1EiENUlvE_D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[REF_TMP]]) + +void test_lambda1(S0 *a) { + a->lambda1(1); +} + +// CHECK-LABEL: define void @_ZN36test_builtin_ptrauth_struct_mangling4testEv() +// CHECK: call void @_ZN36test_builtin_ptrauth_struct_mangling7foo_keyI2S0EEvNS_1SIXu28__builtin_ptrauth_struct_keyT_EEEE( +// CHECK: call void @_ZN36test_builtin_ptrauth_struct_mangling8foo_discI2S0EEvNS_1SIXu29__builtin_ptrauth_struct_discT_EEEE( + +namespace test_builtin_ptrauth_struct_mangling { +template +struct S { +}; + +template +void foo_key(S<__builtin_ptrauth_struct_key(C)> a) { +} +template +void foo_disc(S<__builtin_ptrauth_struct_disc(C)> a) { +} + +void test() { + foo_key(S<__builtin_ptrauth_struct_key(S0)>()); + foo_disc(S<__builtin_ptrauth_struct_disc(S0)>()); +} +} + +// CHECK: define linkonce_odr ptr @_ZN2S0C2Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: ret ptr %[[THIS1]] + +// CHECK: define linkonce_odr ptr @_ZN2S5CI22S2E2S0(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS:.*]], ptr %[[V0:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: call ptr @_ZN2S2C2E2S0(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS1]], ptr %[[V0]]) +// CHECK: ret ptr %[[THIS1]] + +// CHECK: define linkonce_odr i32 @_ZZN2S07lambda0EiENKUlvE_clEv( +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V0:.*]] = getelementptr inbounds %[[CLASS_ANON]], ptr %[[THIS1]], i32 0, i32 0 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[V0]], align 8 +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V1]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 100) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V5]], i64 4 +// CHECK: %[[V7:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 +// CHECK: %[[V8:.*]] = getelementptr inbounds %[[CLASS_ANON]], ptr %[[THIS1]], i32 0, i32 1 +// CHECK: %[[V9:.*]] = load ptr, ptr %[[V8]], align 8 +// CHECK: %[[V10:.*]] = load i32, ptr %[[V9]], align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V7]], %[[V10]] +// CHECK: ret i32 %[[ADD]] + +// CHECK: define linkonce_odr i32 @_ZZN2S07lambda1EiENKUlvE_clEv( +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V0:.*]] = getelementptr inbounds %[[CLASS_ANON_0]], ptr %[[THIS1]], i32 0, i32 0 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[V3]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 1, i64 100) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V6]], i64 4 +// CHECK: %[[V9:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 +// CHECK: %[[V10:.*]] = getelementptr inbounds %[[CLASS_ANON_0]], ptr %[[THIS1]], i32 0, i32 1 +// CHECK: %[[V11:.*]] = load i32, ptr %[[V10]], align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V9]], %[[V11]] +// CHECK: ret i32 %[[ADD]] + +// CHECK: define linkonce_odr ptr @_ZZN2S07lambda1EiENUlvE_D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS]]) unnamed_addr +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: ptr @_ZZN2S07lambda1EiENUlvE_D2Ev(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS1]]) + +// CHECK: define linkonce_odr ptr @_ZZN2S07lambda1EiENUlvE_D2Ev(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS]]) unnamed_addr +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V0:.*]] = getelementptr inbounds %[[CLASS_ANON_0]], ptr %[[THIS1]], i32 0, i32 0 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) diff --git a/clang/test/CodeGenCXX/ptrauth-struct-attr.cpp b/clang/test/CodeGenCXX/ptrauth-struct-attr.cpp new file mode 100644 index 0000000000000..ff50050d5fbac --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-struct-attr.cpp @@ -0,0 +1,813 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-returns -fptrauth-intrinsics -emit-llvm -std=c++17 -O1 -disable-llvm-passes -no-enable-noundef-analysis -fexceptions -fcxx-exceptions -o - %s | FileCheck %s + +// CHECK: %[[STRUCT_S0:.*]] = type { i32, i32, [4 x i32] } +// CHECK: %[[STRUCT_S5:.*]] = type { %[[STRUCT_S2:.*]] } +// CHECK: %[[STRUCT_S2]] = type { i32, %[[STRUCT_S0]] } +// CHECK: %[[STRUCT_S6:.*]] = type { i8 } +// CHECK: %[[STRUCT_S7:.*]] = type { i8 } +// CHECK: %[[STRUCT_S8:.*]] = type { i8 } +// CHECK: %[[STRUCT_S9:.*]] = type { i8 } +// CHECK: %[[STRUCT_S12:.*]] = type { i8 } +// CHECK: %[[STRUCT_S14:.*]] = type { i8 } +// CHECK: %[[STRUCT_S13:.*]] = type { i8 } +// CHECK: %[[STRUCT_S1:.*]] = type { i32, i32, [4 x i32] } +// CHECK: %[[STRUCT_S3:.*]] = type { [2 x i32], %[[STRUCT_S0]] } +// CHECK: %[[CLASS_ANON:.*]] = type { ptr, ptr } +// CHECK: %[[CLASS_ANON_0:.*]] = type { %[[STRUCT_S0]], i32 } + +#include + +typedef __SIZE_TYPE__ size_t; +void* operator new(size_t count, void* ptr); + +#define ATTR_NONE __attribute__((ptrauth_struct(ptrauth_key_none, 100))) +#define ATTR0 __attribute__((ptrauth_struct(1, 100))) +#define ATTR1 __attribute__((ptrauth_struct(1, 101))) +#define ATTR2 __attribute__((ptrauth_struct(1, 102))) +#define ATTR4 __attribute__((ptrauth_struct(1, 104))) + +struct ATTR0 S0 { + int f0, f1, f2[4]; + S0() {} + S0(const S0 &); + S0 &operator=(const S0 &); + ~S0(); + void nonvirtual0() { f1 = 1; } + int lambda0(int i) { + return [&](){ return f1 + i; }(); + } + int lambda1(int i) { + return [*this, i](){ return f1 + i; }(); + } +}; + +struct ATTR1 S1 { + int f0, f1, f2[4]; + void nonvirtual1(); + S1(); + ~S1() noexcept(false); +}; + +struct ATTR2 S2 { + int f0; + void nonvirtual2(); + S0 f1; + S2(S0); +}; + +struct S3 { + int f0[2]; + S0 f1; +}; + +struct ATTR4 S4 { + int f0, f1; +}; + +struct ATTR2 S5 : S2 { + using S2::S2; +}; + +struct ATTR_NONE S6 { +}; + +template +struct __attribute__((ptrauth_struct(k, 100))) S7 { +}; + +template +struct __attribute__((ptrauth_struct(1, d))) S8 { +}; + +template +struct __attribute__((ptrauth_struct((k + 2) % 4, (d + 1)))) S9 { +}; + +struct ATTR0 S11 : S1, S2 { +}; + +struct __attribute__((ptrauth_struct(__builtin_ptrauth_struct_key(S0) + 1, __builtin_ptrauth_struct_disc(S1) + 1))) S12 { +}; + +template +struct __attribute__((ptrauth_struct(__builtin_ptrauth_struct_key(Derived) + 1, __builtin_ptrauth_struct_disc(Derived) + 1))) S13 { +}; + +struct ATTR4 S14 : S13 { +}; + +// CHECK: @gs2 = constant ptr ptrauth (ptr @gs0, i32 1, i64 100), align 8 +// CHECK: @gs3 = constant ptr ptrauth (ptr @gs0, i32 1, i64 100), align 8 +// CHECK: @gs6 = global %[[STRUCT_S6]] zeroinitializer, align 1 +// CHECK: @gs7 = global ptr @gs6, align 8 +// CHECK: @gs8 = global %[[STRUCT_S7]] zeroinitializer, align 1 +// CHECK: @gs9 = global ptr ptrauth (ptr @gs8, i32 1, i64 100), align 8 +// CHECK: @gs10 = global %[[STRUCT_S8]] zeroinitializer, align 1 +// CHECK: @gs11 = global ptr ptrauth (ptr @gs10, i32 1, i64 200), align 8 +// CHECK: @gs12 = global %[[STRUCT_S9]] zeroinitializer, align 1 +// CHECK: @gs13 = global ptr ptrauth (ptr @gs12, i32 0, i64 202), align 8 +// CHECK: @gs16 = global %[[STRUCT_S12]] zeroinitializer, align 1 +// CHECK: @gs17 = global ptr ptrauth (ptr @gs16, i32 2, i64 102), align 8 +// CHECK: @gs18 = global %[[STRUCT_S14]] zeroinitializer, align 1 +// CHECK: @gs19 = global ptr ptrauth (ptr @gs18, i32 1, i64 104), align 8 +// CHECK: @gs20 = global %[[STRUCT_S13]] zeroinitializer, align 1 +// CHECK: @gs21 = global ptr ptrauth (ptr @gs20, i32 2, i64 105), align 8 + +// CHECK: define internal void @__cxx_global_var_init() +// CHECK: %[[V0:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @gs0 to i64), i32 1, i64 100) +// CHECK: %[[V1:.*]] = inttoptr i64 %[[V0]] to ptr +// CHECK: call ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V1]]) +// CHECK: call i32 @__cxa_atexit(ptr ptrauth (ptr @_ZN2S0D1Ev, i32 0), ptr ptrauth (ptr @gs0, i32 1, i64 100), ptr @__dso_handle) +// CHECK: ret void +// CHECK: } + +// CHECK: define linkonce_odr ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: call ptr @_ZN2S0C2Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[THIS1]]) +// CHECK: ret ptr %[[THIS1]] + +S0 gs0; + +// CHECK: define linkonce_odr ptr @_ZN2S5CI12S2E2S0(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS:.*]], ptr %[[V0:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: call ptr @_ZN2S5CI22S2E2S0(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS1]], ptr %[[V0]]) +// CHECK: ret ptr %[[THIS1]] + +S5 gs1(gs0); + +S0 &gs2 = gs0; +S0 &gs3 = gs2; + +S6 gs6, *gs7 = &gs6; + +S7<1> gs8, *gs9 = &gs8; +S8<200> gs10, *gs11 = &gs10; +S9<2, 201> gs12, *gs13 = &gs12; +S12 gs16, *gs17 = &gs16; +S14 gs18, *gs19 = &gs18; +S13 gs20, *gs21 = &gs20; + +// CHECK: define void @_Z16test_nonvirtual0P2S0(ptr %[[S:.*]]) +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// CHECK: call void @_ZN2S011nonvirtual0Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V0]]) + +// CHECK: define linkonce_odr void @_ZN2S011nonvirtual0Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[THIS1]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V0]], i32 1, i64 100) +// CHECK: %[[V2:.*]] = inttoptr i64 %[[V1]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V2]], i64 4 +// CHECK: store i32 1, ptr %[[RESIGNEDGEP]], align 4 + +void test_nonvirtual0(S0 *s) { + s->nonvirtual0(); +} + +// CHECK: define ptr @_Z9test_new0v() +// CHECK: entry: +// CHECK: %[[EXN_SLOT:.*]] = alloca ptr +// CHECK: %[[EHSELECTOR_SLOT:.*]] = alloca i32 +// CHECK: %[[CALL:.*]] = call noalias nonnull ptr @_Znwm(i64 24) +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[CALL]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 101) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[CALL1:.*]] = invoke ptr @_ZN2S1C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) + +// CHECK: ret ptr %[[CALL]] + +// CHECK: landingpad { ptr, i32 } +// CHECK: call void @_ZdlPvm(ptr %[[CALL]], i64 24) + +S1 *test_new0() { + return new S1(); +} + +// CHECK: define ptr @_Z9test_new1Pv(ptr %[[P:.*]]) +// CHECK: %[[P_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[P]], ptr %[[P_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[P_ADDR]], align 8 +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V1]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V2]], i32 1, i64 101) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: %[[CALL:.*]] = call ptr @_ZN2S1C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V4]]) +// CHECK: ret ptr %[[V1]] + +S1 *test_new1(void *p) { + return new (p) S1; +} + +// CHECK: define ptr @_Z9test_new2v() +// CHECK: %[[CALL:.*]] = call noalias nonnull ptr @_Znam(i64 112) +// CHECK: %[[V2:.*]] = getelementptr inbounds i8, ptr %[[CALL]], i64 16 +// CHECK: %[[ARRAYCTOR_END:.*]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[V2]], i64 4 + +// CHECK: %[[ARRAYCTOR_CUR:.*]] = phi ptr [ %[[V2]], %{{.*}} ], [ %[[ARRAYCTOR_NEXT:.*]], %{{.*}} ] +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[ARRAYCTOR_CUR]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V4]], i32 1, i64 101) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[CALL1:.*]] = invoke ptr @_ZN2S1C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V6]]) + +// CHECK: %[[ARRAYCTOR_NEXT]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[ARRAYCTOR_CUR]], i64 1 +// CHECK: %[[ARRAYCTOR_DONE:.*]] = icmp eq ptr %[[ARRAYCTOR_NEXT]], %[[ARRAYCTOR_END]] + +// CHECK: ret ptr %[[V2]] + +// CHECK: landingpad { ptr, i32 } + +// CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi ptr [ %[[ARRAYCTOR_CUR]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT:.*]], %{{.*}} ] +// CHECK: %[[ARRAYDESTROY_ELEMENT:.*]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1 +// CHECK: %[[V10:.*]] = ptrtoint ptr %[[ARRAYDESTROY_ELEMENT]] to i64 +// CHECK: %[[V11:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V10]], i32 1, i64 101) +// CHECK: %[[V12:.*]] = inttoptr i64 %[[V11]] to ptr +// CHECK: invoke ptr @_ZN2S1D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V12]]) + +// CHECK: icmp eq ptr %[[ARRAYDESTROY_ELEMENT]], %[[V2]] + +// CHECK: call void @_ZdaPvm(ptr %[[CALL]], i64 112) + +S1 *test_new2() { + return new S1[4]; +} + +// CHECK: define void @_Z12test_delete0P2S1(ptr %{{.*}}) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0]] = load ptr, ptr %[[A_ADDR]], align 8 + +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 101) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: invoke ptr @_ZN2S1D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V0]]) + +// CHECK: call void @_ZdlPvm(ptr %[[V3]], i64 24) + +// CHECK: landingpad { ptr, i32 } +// CHECK: call void @_ZdlPvm(ptr %[[V3]], i64 24) + +void test_delete0(S1 *a) { + delete a; +} + +// CHECK: define void @_Z12test_delete1P2S1(ptr %{{.*}}) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 + +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 101) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V4]], i64 -16 +// CHECK: %[[V5:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V5]], i32 1, i64 101) +// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr +// CHECK: %[[RESIGNEDGEP1:.*]] = getelementptr i8, ptr %[[V7]], i64 -8 +// CHECK: %[[V9:.*]] = load i64, ptr %[[RESIGNEDGEP1]], align 4 +// CHECK: %[[V10:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V11:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V10]], i32 1, i64 101) +// CHECK: %[[V12:.*]] = inttoptr i64 %[[V11]] to ptr +// CHECK: %[[DELETE_END:.*]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[V12]], i64 %[[V9]] +// CHECK: icmp eq ptr %[[V12]], %[[DELETE_END]] + +// CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi ptr [ %[[DELETE_END]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT]], %{{.*}} ] +// CHECK: %[[ARRAYDESTROY_ELEMENT:.*]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1 +// CHECK: %[[V13:.*]] = ptrtoint ptr %[[ARRAYDESTROY_ELEMENT]] to i64 +// CHECK: %[[V14:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V13]], i32 1, i64 101) +// CHECK: %[[V15:.*]] = inttoptr i64 %[[V14]] to ptr +// CHECK: invoke ptr @_ZN2S1D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V15]]) + +// CHECK: icmp eq ptr %[[ARRAYDESTROY_ELEMENT]], %[[V12]] + +// CHECK: call void @_ZdaPvm(ptr %[[RESIGNEDGEP]], i64 %{{.*}}) + +// CHECK: landingpad { ptr, i32 } + +// CHECK: %[[ARRAYDESTROY_ELEMENTPAST4:.*]] = phi ptr [ %[[ARRAYDESTROY_ELEMENT]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT5:.*]], %{{.*}} ] +// CHECK: %[[ARRAYDESTROY_ELEMENT5:.*]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[ARRAYDESTROY_ELEMENTPAST4]], i64 -1 +// CHECK: %[[V19:.*]] = ptrtoint ptr %[[ARRAYDESTROY_ELEMENT5]] to i64 +// CHECK: %[[V20:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V19]], i32 1, i64 101) +// CHECK: %[[V21:.*]] = inttoptr i64 %[[V20]] to ptr +// CHECK: %[[CALL7:.*]] = invoke ptr @_ZN2S1D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V21]]) + +// CHECK: icmp eq ptr %[[ARRAYDESTROY_ELEMENT5]], %[[V12]] + +// CHECK: call void @_ZdaPvm(ptr %[[RESIGNEDGEP]], i64 %{{.*}}) + +void test_delete1(S1 *a) { + delete [] a; +} + +// CHECK: define void @_Z16test_assignment0P2S0S0_(ptr %{{.*}}, ptr %{{.*}}) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[B_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[B_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: call nonnull align 4 dereferenceable(24) ptr @_ZN2S0aSERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V1]], ptr nonnull align 4 dereferenceable(24) %[[V0]]) + +void test_assignment0(S0 *a, S0 *b) { + *a = *b; +} + +// CHECK: define void @_Z16test_assignment1v() +// CHECK: %[[T0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: call ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) +// CHECK: %[[V5:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V5]], i32 1, i64 100) +// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr +// CHECK: invoke ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V7]]) + +// CHECK: %[[V8:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V8]], i32 1, i64 100) +// CHECK: %[[V10:.*]] = inttoptr i64 %[[V9]] to ptr +// CHECK: %[[V11:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V12:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V11]], i32 1, i64 100) +// CHECK: %[[V13:.*]] = inttoptr i64 %[[V12]] to ptr +// CHECK: invoke nonnull align 4 dereferenceable(24) ptr @_ZN2S0aSERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V13]], ptr nonnull align 4 dereferenceable(24) %[[V10]]) + +// CHECK: %[[V14:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V15:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V14]], i32 1, i64 100) +// CHECK: %[[V16:.*]] = inttoptr i64 %[[V15]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V16]]) +// CHECK: %[[V18:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V19:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V18]], i32 1, i64 100) +// CHECK: %[[V20:.*]] = inttoptr i64 %[[V19]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V20]]) +// CHECK: ret void + +// CHECK: landingpad { ptr, i32 } +// CHECK: landingpad { ptr, i32 } +// CHECK: %[[V28:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V29:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V28]], i32 1, i64 100) +// CHECK: %[[V30:.*]] = inttoptr i64 %[[V29]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V30]]) + +// CHECK: %[[V32:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V33:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V32]], i32 1, i64 100) +// CHECK: %[[V34:.*]] = inttoptr i64 %[[V33]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V34]]) + +void test_assignment1() { + S0 t0, t1; + t0 = t1; +} + +// CHECK: define void @_Z16test_assignment2P2S4S0_( +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[B_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[B_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[V2]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 1, i64 104) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[V7:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V8:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V7]], i32 1, i64 104) +// CHECK: %[[V9:.*]] = inttoptr i64 %[[V8]] to ptr +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[V6]], ptr align 4 %[[V9]], i64 8, i1 false) + +void test_assignment2(S4 *a, S4 *b) { + *a = *b; +} + +// CHECK: define void @_Z28test_constructor_destructor0v() +// CHECK: %[[T0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: call ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) +// CHECK: %[[V5:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V5]], i32 1, i64 100) +// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr +// CHECK: %[[V8:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V8]], i32 1, i64 100) +// CHECK: %[[V10:.*]] = inttoptr i64 %[[V9]] to ptr +// CHECK: invoke ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V7]], ptr nonnull align 4 dereferenceable(24) %[[V10]]) + +// CHECK: %[[V11:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V12:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V11]], i32 1, i64 100) +// CHECK: %[[V13:.*]] = inttoptr i64 %[[V12]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V13]]) +// CHECK: %[[V15:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V16:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V15]], i32 1, i64 100) +// CHECK: %[[V17:.*]] = inttoptr i64 %[[V16]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V17]]) +// CHECK: ret void + +// CHECK: landingpad { ptr, i32 } +// CHECK: %[[V23:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V24:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V23]], i32 1, i64 100) +// CHECK: %[[V25:.*]] = inttoptr i64 %[[V24]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V25]]) + +void test_constructor_destructor0() { + S0 t0, t1 = t0; +} + +// CHECK: define void @_Z19test_member_access1P2S0i(ptr %{{.*}}, i32 %{{.*}}) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[I_ADDR:.*]] = alloca i32, align 4 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load i32, ptr %[[I_ADDR]], align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V1]], 2 +// CHECK: %[[IDXPROM:.*]] = sext i32 %[[ADD]] to i64 +// CHECK: %[[ARRAYIDX_OFFS:.*]] = mul nsw i64 %[[IDXPROM]], 4 +// CHECK: %[[ADD1:.*]] = add i64 8, %[[ARRAYIDX_OFFS]] +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 100) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V4]], i64 %[[ADD1]] +// CHECK: store i32 123, ptr %[[RESIGNEDGEP]], align 4 + +void test_member_access1(S0 *a, int i) { + a->f2[i + 2] = 123; +} + +// CHECK: define nonnull align 4 dereferenceable(24) ptr @_Z15test_reference0R2S0(ptr nonnull align 4 dereferenceable(24) %[[A:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1:.*]] = alloca ptr, align 8 +// CHECK: %[[R0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: alloca ptr, align 8 +// CHECK: %[[R1:.*]] = alloca ptr, align 8 +// CHECK: %[[V11:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V21:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V11]], i32 1, i64 100) +// CHECK: %[[V31:.*]] = inttoptr i64 %[[V21]] to ptr +// CHECK: %[[V41:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: call ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V31]], ptr nonnull align 4 dereferenceable(24) %[[V41]]) +// CHECK: %[[V6:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: store ptr %[[V6]], ptr %[[T1]], align 8 +// CHECK: %[[V8:.*]] = ptrtoint ptr %[[R0]] to i64 +// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V8]], i32 1, i64 100) +// CHECK: %[[V10:.*]] = inttoptr i64 %[[V9]] to ptr +// CHECK: %[[V11:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V12:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V11]], i32 1, i64 100) +// CHECK: %[[V13:.*]] = inttoptr i64 %[[V12]] to ptr +// CHECK: %[[CALL1:.*]] = invoke nonnull align 4 dereferenceable(24) ptr @_Z5func0R2S0(ptr nonnull align 4 dereferenceable(24) %[[V13]]) + +// CHECK: invoke ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V10]], ptr nonnull align 4 dereferenceable(24) %[[CALL1]]) + +// CHECK: %[[V15:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V16:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V15]], i32 1, i64 100) +// CHECK: %[[V17:.*]] = inttoptr i64 %[[V16]] to ptr +// CHECK: %[[CALL6:.*]] = invoke nonnull align 4 dereferenceable(24) ptr @_Z5func0R2S0(ptr nonnull align 4 dereferenceable(24) %[[V17]]) + +// CHECK: store ptr %[[CALL6]], ptr %[[R1]], align 8 +// CHECK: %[[V18:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @gs0 to i64), i32 1, i64 100) +// CHECK: %[[V19:.*]] = inttoptr i64 %[[V18]] to ptr +// CHECK: %[[V21:.*]] = ptrtoint ptr %[[R0]] to i64 +// CHECK: %[[V22:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V21]], i32 1, i64 100) +// CHECK: %[[V23:.*]] = inttoptr i64 %[[V22]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V23]]) +// CHECK: %[[V26:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V27:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V26]], i32 1, i64 100) +// CHECK: %[[V28:.*]] = inttoptr i64 %[[V27]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V28]]) +// CHECK: ret ptr %[[V19]] + +// CHECK: landingpad { ptr, i32 } + +// CHECK: landingpad { ptr, i32 } +// CHECK: %[[V37:.*]] = ptrtoint ptr %[[R0]] to i64 +// CHECK: %[[V38:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V37]], i32 1, i64 100) +// CHECK: %[[V39:.*]] = inttoptr i64 %[[V38]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V39]]) + +// CHECK: %[[V42:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V43:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V42]], i32 1, i64 100) +// CHECK: %[[V44:.*]] = inttoptr i64 %[[V43]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V44]]) + +S0 &func0(S0 &); + +S0 &test_reference0(S0 &a) { + S0 t0 = a; + S0 &t1 = a; + S0 r0 = func0(t0); + S0 &r1 = func0(t0); + return gs0; +} + +// CHECK: define void @_Z17test_conditional0bR2S0S0_(ptr dead_on_unwind noalias writable sret(%struct.S0) align 4 %[[AGG_RESULT:.*]], i1 %{{.*}}, ptr nonnull align 4 dereferenceable(24) %{{.*}}, ptr nonnull align 4 dereferenceable(24) %{{.*}}) +// CHECK: alloca ptr, align 8 +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[B_ADDR:.*]] = alloca ptr, align 8 + +// CHECK: %[[V5:.*]] = load ptr, ptr %[[A_ADDR]], align 8 + +// CHECK: %[[V6:.*]] = load ptr, ptr %[[B_ADDR]], align 8 + +// CHECK: %[[V7:.*]] = phi ptr [ %[[V5]], %{{.*}} ], [ %[[V6]], %{{.*}} ] +// CHECK: call ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[AGG_RESULT]], ptr nonnull align 4 dereferenceable(24) %[[V7]]) +// CHECK: ret void + +S0 test_conditional0(bool c, S0 &a, S0 &b) { + return c ? a : b; +} + +// CHECK: define i32 @_Z17test_conditional1b( +// CHECK: %[[A:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[B:.*]] = alloca %[[STRUCT_S0]], align 4 + +// CHECK: %[[V9:.*]] = phi ptr [ %[[A]], %{{.*}} ], [ %[[B]], %{{.*}} ] +// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[V9]], i32 0, i32 1 +// CHECK: %[[V10:.*]] = load i32, ptr %[[F1]], align 4 +// CHECK: ret i32 %[[V10]] + +int test_conditional1(bool c) { + S0 a, b; + return (c ? a : b).f1; +} + +// CHECK: define void @_Z17test_conditional2bR2S2R2S3(ptr dead_on_unwind noalias writable sret(%struct.S0) align 4 %[[AGG_RESULT]], i1 %{{.*}}, ptr nonnull align 4 dereferenceable(28) %{{.*}}, ptr nonnull align 4 dereferenceable(32) %{{.*}}) +// CHECK: alloca ptr, align 8 +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[B_ADDR:.*]] = alloca ptr, align 8 + +// CHECK: %[[V5:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[V6:.*]] = ptrtoint ptr %[[V5]] to i64 +// CHECK: %[[V7:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V6]], i32 1, i64 102) +// CHECK: %[[V8:.*]] = inttoptr i64 %[[V7]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V8]], i64 4 +// CHECK: %[[V10:.*]] = ptrtoint ptr %[[RESIGNEDGEP]] to i64 +// CHECK: %[[V11:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V10]], i32 1, i64 100) +// CHECK: %[[V12:.*]] = inttoptr i64 %[[V11]] to ptr + +// CHECK: %[[V14:.*]] = load ptr, ptr %[[B_ADDR]], align 8 +// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_S3]], ptr %[[V14]], i32 0, i32 1 +// CHECK: %[[V15:.*]] = ptrtoint ptr %[[F1]] to i64 +// CHECK: %[[V16:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V15]], i32 1, i64 100) +// CHECK: %[[V17:.*]] = inttoptr i64 %[[V16]] to ptr + +// CHECK: %[[V18:.*]] = phi ptr [ %[[V12]], %{{.*}} ], [ %[[V17]], %{{.*}} ] +// CHECK: call ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[AGG_RESULT]], ptr nonnull align 4 dereferenceable(24) %[[V18]]) +// CHECK: ret void + +S0 test_conditional2(bool c, S2 &a, S3 &b) { + return c ? a.f1 : b.f1; +} + +// CHECK: define void @_Z17test_inheritance0P3S11(ptr %[[A:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, +// CHECK: store ptr %[[A]], ptr %[[A_ADDR]], +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V2]], i32 1, i64 100, i32 1, i64 101) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: call void @_ZN2S111nonvirtual1Ev(ptr nonnull align 4 {{.*}} %[[V4]]) + +void test_inheritance0(S11 *a) { + a->nonvirtual1(); +} + +// CHECK: define void @_Z17test_inheritance1P3S11(ptr %[[A:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, +// CHECK: store ptr %[[A]], ptr %[[A_ADDR]], +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V4]], i64 24 +// CHECK: %[[V6:.*]] = ptrtoint ptr %[[ADD_PTR]] to i64 +// CHECK: %[[V7:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V6]], i32 1, i64 102) +// CHECK: %[[V8:.*]] = inttoptr i64 %[[V7]] to ptr +// CHECK: call void @_ZN2S211nonvirtual2Ev(ptr nonnull align 4 {{.*}} %[[V8]]) + +void test_inheritance1(S11 *a) { + a->nonvirtual2(); +} + +// CHECK: define void @_Z15test_exception0v() +// CHECK: alloca ptr, align 8 +// CHECK: %[[S0:.*]] = alloca ptr, align 8 +// CHECK: %[[I:.*]] = alloca i32, align 4 +// CHECK: %[[EXCEPTION:.*]] = call ptr @__cxa_allocate_exception(i64 24) +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[EXCEPTION]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: invoke ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) + +// CHECK: invoke void @__cxa_throw(ptr %exception, ptr @_ZTI2S0, ptr ptrauth (ptr @_ZN2S0D1Ev, i32 0)) + +// CHECK: landingpad { ptr, i32 } +// CHECK: call void @__cxa_free_exception(ptr %[[EXCEPTION]]) + +// CHECK: %[[V12:.*]] = call ptr @__cxa_begin_catch( +// CHECK: %[[V13:.*]] = ptrtoint ptr %[[V12]] to i64 +// CHECK: %[[V14:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V13]], i32 1, i64 100) +// CHECK: %[[V15:.*]] = inttoptr i64 %[[V14]] to ptr +// CHECK: store ptr %[[V15]], ptr %[[S0]], align 8 +// CHECK: %[[V17:.*]] = load ptr, ptr %[[S0]], align 8 +// CHECK: %[[V18:.*]] = ptrtoint ptr %[[V17]] to i64 +// CHECK: %[[V19:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V18]], i32 1, i64 100) +// CHECK: %[[V20:.*]] = inttoptr i64 %[[V19]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V20]], i64 4 +// CHECK: %[[V23:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 +// CHECK: store i32 %[[V23]], ptr %[[I]], align 4 + +void test_exception0() { + try { + throw S0(); + } catch (const S0 &s0) { + int i = s0.f1; + } +} + +// CHECK: define void @_Z15test_exception1v() +// CHECK: %[[S0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[I:.*]] = alloca i32, align 4 +// CHECK: %[[EXCEPTION:.*]] = call ptr @__cxa_allocate_exception(i64 24) +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[EXCEPTION]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: invoke ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) + +// CHECK: %[[V12:.*]] = call ptr @__cxa_get_exception_ptr( +// CHECK: %[[V14:.*]] = ptrtoint ptr %[[S0]] to i64 +// CHECK: %[[V15:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V14]], i32 1, i64 100) +// CHECK: %[[V16:.*]] = inttoptr i64 %[[V15]] to ptr +// CHECK: %[[V17:.*]] = ptrtoint ptr %[[V12]] to i64 +// CHECK: %[[V18:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V17]], i32 1, i64 100) +// CHECK: %[[V19:.*]] = inttoptr i64 %[[V18]] to ptr +// CHECK: invoke ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V16]], ptr nonnull align 4 dereferenceable(24) %[[V19]]) + +// CHECK: %[[V20:.*]] = call ptr @__cxa_begin_catch( +// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[S0]], i32 0, i32 1 +// CHECK: %[[V22:.*]] = load i32, ptr %[[F1]], align 4 +// CHECK: store i32 %[[V22]], ptr %[[I]], align 4 +// CHECK: %[[V24:.*]] = ptrtoint ptr %[[S0]] to i64 +// CHECK: %[[V25:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V24]], i32 1, i64 100) +// CHECK: %[[V26:.*]] = inttoptr i64 %[[V25]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V26]]) + +void test_exception1() { + try { + throw S0(); + } catch (S0 s0) { + int i = s0.f1; + } +} + +// CHECK: define void @_Z15test_exception2v() +// CHECK: alloca ptr, align 8 +// CHECK: %[[S0:.*]] = alloca ptr, align 8 +// CHECK: %[[EXN_BYREF_TMP:.*]] = alloca ptr, align 8 +// CHECK: %[[I:.*]] = alloca i32, align 4 +// CHECK: %[[EXCEPTION:.*]] = call ptr @__cxa_allocate_exception(i64 24) +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[EXCEPTION]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: invoke ptr @_ZN2S0C1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) + +// CHECK: %[[V12:.*]] = call ptr @__cxa_begin_catch( +// CHECK: %[[V13:.*]] = ptrtoint ptr %[[V12]] to i64 +// CHECK: %[[V14:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V13]], i32 1, i64 100) +// CHECK: %[[V15:.*]] = inttoptr i64 %[[V14]] to ptr +// CHECK: store ptr %[[V15]], ptr %[[EXN_BYREF_TMP]], align 8 +// CHECK: store ptr %[[EXN_BYREF_TMP]], ptr %[[S0]], align 8 +// CHECK: %[[V18:.*]] = load ptr, ptr %[[S0]], align 8 +// CHECK: %[[V19:.*]] = load ptr, ptr %[[V18]], align 8 +// CHECK: %[[V20:.*]] = ptrtoint ptr %[[V19]] to i64 +// CHECK: %[[V21:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V20]], i32 1, i64 100) +// CHECK: %[[V22:.*]] = inttoptr i64 %[[V21]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V23]], i64 4 +// CHECK: %[[V25:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 +// CHECK: store i32 %[[V25]], ptr %[[I]], align 4 + +void test_exception2() { + try { + throw S0(); + } catch (S0 *&s0) { + int i = s0->f1; + } +} + +// CHECK: define linkonce_odr i32 @_ZN2S07lambda0Ei( +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[I_ADDR:.*]] = alloca i32, align 4 +// CHECK: %[[REF_TMP:.*]] = alloca %[[CLASS_ANON]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V1:.*]] = getelementptr inbounds %[[CLASS_ANON]], ptr %[[REF_TMP]], i32 0, i32 0 +// CHECK: store ptr %[[THIS1]], ptr %[[V1]], align 8 +// CHECK: %[[V2:.*]] = getelementptr inbounds %[[CLASS_ANON]], ptr %[[REF_TMP]], i32 0, i32 1 +// CHECK: store ptr %[[I_ADDR]], ptr %[[V2]], align 8 +// CHECK: call i32 @_ZZN2S07lambda0EiENKUlvE_clEv(ptr nonnull align {{[0-9]+}} dereferenceable(16) %[[REF_TMP]]) + +void test_lambda0(S0 *a) { + a->lambda0(1); +} + +// CHECK: define linkonce_odr i32 @_ZN2S07lambda1Ei( +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[I_ADDR:.*]] = alloca i32, align 4 +// CHECK: %[[REF_TMP:.*]] = alloca %[[CLASS_ANON_0]], align 4 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V1:.*]] = getelementptr inbounds %[[CLASS_ANON_0]], ptr %[[REF_TMP]], i32 0, i32 0 +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V1]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V2]], i32 1, i64 100) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: call ptr @_ZN2S0C1ERKS_(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V4]], ptr nonnull align 4 dereferenceable(24) %[[THIS1]]) +// CHECK: %[[V5:.*]] = getelementptr inbounds %[[CLASS_ANON_0]], ptr %[[REF_TMP]], i32 0, i32 1 +// CHECK: %[[V6:.*]] = load i32, ptr %[[I_ADDR]], align 4 +// CHECK: store i32 %[[V6]], ptr %[[V5]], align 4 +// CHECK: %[[CALL2:.*]] = invoke i32 @_ZZN2S07lambda1EiENKUlvE_clEv(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[REF_TMP]]) + +// CHECK: call ptr @_ZZN2S07lambda1EiENUlvE_D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[REF_TMP]]) +// CHECK: ret i32 %[[CALL2]] + +// CHECK: landingpad { ptr, i32 } +// CHECK: call ptr @_ZZN2S07lambda1EiENUlvE_D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[REF_TMP]]) + +void test_lambda1(S0 *a) { + a->lambda1(1); +} + +// CHECK-LABEL: define void @_ZN36test_builtin_ptrauth_struct_mangling4testEv() +// CHECK: call void @_ZN36test_builtin_ptrauth_struct_mangling7foo_keyI2S0EEvNS_1SIXu28__builtin_ptrauth_struct_keyT_EEEE( +// CHECK: call void @_ZN36test_builtin_ptrauth_struct_mangling8foo_discI2S0EEvNS_1SIXu29__builtin_ptrauth_struct_discT_EEEE( + +namespace test_builtin_ptrauth_struct_mangling { +template +struct S { +}; + +template +void foo_key(S<__builtin_ptrauth_struct_key(C)> a) { +} +template +void foo_disc(S<__builtin_ptrauth_struct_disc(C)> a) { +} + +void test() { + foo_key(S<__builtin_ptrauth_struct_key(S0)>()); + foo_disc(S<__builtin_ptrauth_struct_disc(S0)>()); +} +} + +// CHECK: define linkonce_odr ptr @_ZN2S0C2Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: ret ptr %[[THIS1]] + +// CHECK: define linkonce_odr ptr @_ZN2S5CI22S2E2S0(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS:.*]], ptr %[[V0:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: call ptr @_ZN2S2C2E2S0(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS1]], ptr %[[V0]]) +// CHECK: ret ptr %[[THIS1]] + +// CHECK: define linkonce_odr i32 @_ZZN2S07lambda0EiENKUlvE_clEv( +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V0:.*]] = getelementptr inbounds %[[CLASS_ANON]], ptr %[[THIS1]], i32 0, i32 0 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[V0]], align 8 +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V1]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 100) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V5]], i64 4 +// CHECK: %[[V7:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 +// CHECK: %[[V8:.*]] = getelementptr inbounds %[[CLASS_ANON]], ptr %[[THIS1]], i32 0, i32 1 +// CHECK: %[[V9:.*]] = load ptr, ptr %[[V8]], align 8 +// CHECK: %[[V10:.*]] = load i32, ptr %[[V9]], align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V7]], %[[V10]] +// CHECK: ret i32 %[[ADD]] + +// CHECK: define linkonce_odr i32 @_ZZN2S07lambda1EiENKUlvE_clEv( +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V0:.*]] = getelementptr inbounds %[[CLASS_ANON_0]], ptr %[[THIS1]], i32 0, i32 0 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[V3]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 1, i64 100) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V6]], i64 4 +// CHECK: %[[V9:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 +// CHECK: %[[V10:.*]] = getelementptr inbounds %[[CLASS_ANON_0]], ptr %[[THIS1]], i32 0, i32 1 +// CHECK: %[[V11:.*]] = load i32, ptr %[[V10]], align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V9]], %[[V11]] +// CHECK: ret i32 %[[ADD]] + +// CHECK: define linkonce_odr ptr @_ZZN2S07lambda1EiENUlvE_D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS]]) unnamed_addr +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: ptr @_ZZN2S07lambda1EiENUlvE_D2Ev(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS1]]) + +// CHECK: define linkonce_odr ptr @_ZZN2S07lambda1EiENUlvE_D2Ev(ptr nonnull align {{[0-9]+}} dereferenceable(28) %[[THIS]]) unnamed_addr +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V0:.*]] = getelementptr inbounds %[[CLASS_ANON_0]], ptr %[[THIS1]], i32 0, i32 0 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: call ptr @_ZN2S0D1Ev(ptr nonnull align {{[0-9]+}} dereferenceable(24) %[[V3]]) diff --git a/clang/test/CodeGenObjC/ptrauth-struct-attr-wrapper-globals.m b/clang/test/CodeGenObjC/ptrauth-struct-attr-wrapper-globals.m new file mode 100644 index 0000000000000..f6e57f3a1c60a --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-struct-attr-wrapper-globals.m @@ -0,0 +1,720 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-returns -fptrauth-intrinsics -fobjc-arc -fobjc-runtime-has-weak -fblocks -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s + +#define ATTR0 __attribute__((ptrauth_struct(1, 100))) +#define ATTR1 __attribute__((ptrauth_struct(1, 101))) +#define ATTR2 __attribute__((ptrauth_struct(1, 102))) +#define ATTR3 __attribute__((ptrauth_struct(1, 103))) +#define ATTR4 __attribute__((ptrauth_struct(1, 104))) + +// CHECK: %[[STRUCT_S0:.*]] = type { i32, i32 } +// CHECK: %[[STRUCT_S1:.*]] = type { i32, ptr, ptr, %[[STRUCT_S0]] } +// CHECK: %[[STRUCT_S4:.*]] = type { i32, i32, ptr, <4 x float> } + +// CHECK: @g0 = global %[[STRUCT_S0]] zeroinitializer, align 4 +// CHECK: @[[G0_PTRAUTH:.*]] = private constant { ptr, i32, i64, i64 } { ptr @g0, i32 1, i64 0, i64 100 }, section "llvm.ptrauth", align 8 +// CHECK: @gp0 = global ptr @[[G0_PTRAUTH]], align 8 +// CHECK: @[[PTRAUTH:.*]] = private constant { ptr, i32, i64, i64 } { ptr getelementptr (i8, ptr @g1, i64 24), i32 1, i64 0, i64 100 }, section "llvm.ptrauth", align 8 +// CHECK: @gp1 = global ptr @[[PTRAUTH]], align 8 +// CHECK: @ga2 = global [4 x i32] zeroinitializer, align 4 +// CHECK: @gf0 = global ptr @ga2, align 8 +// CHECK: @[[CONST_TEST_COMPOUND_LITERAL0_T0:.*]] = private unnamed_addr constant %[[STRUCT_S0]] { i32 1, i32 2 }, align 4 + +typedef long intptr_t; + +struct ATTR0 S0 { + int a0, a1; +}; + +typedef struct S0 S0; + +struct ATTR1 S1 { + int a; + id b; + S0 *f0; + S0 f1; +}; + +typedef struct S1 S1; + +struct ATTR2 S2; +typedef struct S2 S2; + +struct ATTR3 S3 { + int f0; + __attribute__((annotate("abc"))) S0 f1; +}; + +typedef struct S3 S3; + +typedef __attribute__((ext_vector_type(4))) float float4; + +struct ATTR4 S4 { + int f0, f1; + __weak id f2; + float4 extv0; +}; + +typedef struct S4 S4; + +S0 getS0(void); +S0 *func0(S0 *); +S0 func1(S0); +S4 func2(S4); + +S0 g0; +S0 *gp0 = &g0; +S1 g1; +S0 *gp1 = &g1.f1; + +S0 ga0[10][10][10]; +S0 ga1[2]; + +int ga2[4] = {0}; +void (*gf0)(void) = (void (*)(void))ga2; + +// CHECK-LABEL: define void @test_member_access0( +// CHECK-NOT: @llvm.ptrauth +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %{{.*}}, i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V3]], i64 4 +// CHECK: load i32, ptr %[[RESIGNEDGEP]], align 4 + +void test_member_access0(S0 *s) { + int t = s->a1; +} + +// CHECK-LABEL: define void @test_member_access1( +// CHECK-NOT: @llvm.ptrauth +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %{{.*}}, i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V3]], i64 4 +// CHECK: load i32, ptr %[[RESIGNEDGEP]], align 4 + +void test_member_access1(S0 *s) { + int t = (*s).a1; +} + +// CHECK-LABEL: define void @test_member_access2( +// CHECK-NOT: @llvm.ptrauth + +void test_member_access2(S0 s) { + int t = s.a1; +} + +// CHECK-LABEL: define void @test_member_initialization( +// CHECK: %[[T:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1:.*]] = alloca %[[STRUCT_S1]], align 8 +// CHECK: %[[S0:.*]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[T1]], i32 0, i32 2 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[T]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V0]], i32 1, i64 100) +// CHECK: %[[V2:.*]] = inttoptr i64 %[[V1]] to ptr +// CHECK: store ptr %[[V2]], ptr %[[S0]], align 8 + +void test_member_initialization() { + S0 t; + S1 t1 = {1, 0, &t}; +} + +// CHECK-LABEL: define i32 @test_array0( +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V3]], i64 84 +// CHECK: load i32, ptr %[[RESIGNEDGEP]], align 4 + +int test_array0(S0 *a) { + return a[10].a1; +} + +// CHECK-LABEL: define i32 @test_array1( +// CHECK: %[[V1:.*]] = load i32, ptr %{{.*}}, align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V1]], 2 +// CHECK: %[[IDXPROM:.*]] = sext i32 %[[ADD]] to i64 +// CHECK: %[[ARRAYIDX_IDX:.*]] = mul nsw i64 %[[IDXPROM]], 8 +// CHECK: %[[ADD:.*]] = add i64 %[[ARRAYIDX_IDX]], 4 +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 100) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V4]], i64 %[[ADD]] +// CHECK: load i32, ptr %[[RESIGNEDGEP]], align 4 + +int test_array1(S0 *a, int i) { + return a[i + 2].a1; +} + +// CHECK-LABEL: define i32 @test_array2( +// CHECK: %[[V0:.*]] = load i32, ptr %{{.*}}, align 4 +// CHECK: %[[IDXPROM:.*]] = sext i32 %[[V0]] to i64 +// CHECK: %[[ARRAYIDX:.*]] = getelementptr inbounds [10 x [10 x [10 x %[[STRUCT_S0]]]]], ptr @ga0, i64 0, i64 %[[IDXPROM]] +// CHECK: %[[V1:.*]] = load i32, ptr %{{.*}}, align 4 +// CHECK: %[[IDXPROM1:.*]] = sext i32 %[[V1]] to i64 +// CHECK: %[[ARRAYIDX2:.*]] = getelementptr inbounds [10 x [10 x %[[STRUCT_S0]]]], ptr %[[ARRAYIDX]], i64 0, i64 %[[IDXPROM1]] +// CHECK: %[[V2:.*]] = load i32, ptr %{{.*}}, align 4 +// CHECK: %[[IDXPROM3:.*]] = sext i32 %[[V2]] to i64 +// CHECK: %[[ARRAYIDX4:.*]] = getelementptr inbounds [10 x %[[STRUCT_S0]]], ptr %[[ARRAYIDX2]], i64 0, i64 %[[IDXPROM3]] +// CHECK: %[[A1:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[ARRAYIDX4]], i32 0, i32 1 +// CHECK: load i32, ptr %[[A1]], align 4 + +int test_array2(int i, int j, int k) { + return ga0[i][j][k].a1; +} + +// CHECK: define ptr @test_array3( +// CHECK: %[[V0:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @ga1 to i64), i32 1, i64 100) +// CHECK: %[[V1:.*]] = inttoptr i64 %[[V0]] to ptr +// CHECK: ret ptr %[[V1]] + +S0 *test_array3(void) { + return ga1; +} + +// CHECK-LABEL: define i32 @test_pointer_arithmetic0( +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %{{.*}}, i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[V3]], i64 10 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[ADD_PTR]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V4]], i32 1, i64 100) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[V7:.*]] = ptrtoint ptr %[[V6]] to i64 +// CHECK: %[[V8:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V7]], i32 1, i64 100) +// CHECK: %[[V9:.*]] = inttoptr i64 %[[V8]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V9]], i64 4 +// CHECK: %[[V12:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 + +int test_pointer_arithmetic0(S0 *a) { + return (a + 10)->a1; +} + +// CHECK: define ptr @test_pointer_arithmetic1( +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[INCDEC_PTR:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[V3]], i32 1 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[INCDEC_PTR]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V4]], i32 1, i64 100) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: store ptr %[[V6]], ptr %[[A_ADDR]], align 8 +// CHECK: ret ptr %[[V6]] + +S0 *test_pointer_arithmetic1(S0 *a) { + return ++a; +} + +// CHECK: define ptr @test_pointer_arithmetic2( +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[INCDEC_PTR:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[V3]], i32 1 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[INCDEC_PTR]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V4]], i32 1, i64 100) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: store ptr %[[V6]], ptr %[[A_ADDR]], align 8 +// CHECK: ret ptr %[[V0]] + +S0 *test_pointer_arithmetic2(S0 *a) { + return a++; +} + +// CHECK-LABEL: define void @test_dereference0( +// CHECK: %[[A0_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[A1_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A0_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[A1_ADDR]], align 8 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 1, i64 100) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[V7:.*]] = ptrtoint ptr %[[V1]] to i64 +// CHECK: %[[V8:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V7]], i32 1, i64 100) +// CHECK: %[[V9:.*]] = inttoptr i64 %[[V8]] to ptr +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[V6]], ptr align 4 %[[V9]], i64 8, i1 false) + +void test_dereference0(S0 *a0, S0 *a1) { + *a0 = *a1; +} + +// CHECK-LABEL: define void @test_dereference1( +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T:.*]] = alloca %[[STRUCT_S1]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V3]], i32 1, i64 101) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr +// CHECK: call void @__copy_constructor_8_8_t0w4_s8_t16w16(ptr %[[T]], ptr %[[V5]]) + +void test_dereference1(S1 *s) { + S1 t = *s; +} + +// CHECK-LABEL: define void @test_address_of0( +// CHECK: %[[T:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[T]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V0]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: store ptr %[[V3]], ptr %{{.*}}, align 8 + +void test_address_of0(void) { + S0 t = getS0(); + S0 *p = &t; +} + +// CHECK-LABEL: define void @test_address_of1( +// CHECK: %[[V0:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @g0 to i64), i32 1, i64 100) +// CHECK: %[[V1:.*]] = inttoptr i64 %[[V0]] to ptr +// CHECK: store ptr %[[V1]], ptr %{{.*}}, align 8 + +void test_address_of1(void) { + S0 *p = &g0; +} + +// CHECK-LABEL: define void @test_address_of2( +// CHECK: %[[T:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[CALL:.*]] = call i64 @getS0() +// CHECK: store i64 %[[CALL]], ptr %[[T]], align 4 +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[T1]], ptr align 4 %[[T]], i64 8, i1 false) + +void test_address_of2(void) { + S0 t = getS0(); + S0 t1 = *&t; +} + +// CHECK-LABEL: define void @test_conversion0( +// CHECK: %[[P_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T:.*]] = alloca ptr, align 8 +// CHECK: %[[T2:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[P_ADDR]], align 8 + +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V3]], i32 1, i64 100) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr + +// CHECK: %[[V6:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V5]], %{{.*}} ] +// CHECK: store ptr %[[V6]], ptr %[[T]], align 8 +// CHECK: %[[V7:.*]] = load ptr, ptr %[[T]], align 8 + +// CHECK: %[[V10:.*]] = ptrtoint ptr %[[V8]] to i64 +// CHECK: %[[V11:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V10]], i32 1, i64 100) +// CHECK: %[[V12:.*]] = inttoptr i64 %[[V11]] to ptr + +// CHECK: %[[V13:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V12]], %{{.*}} ] +// CHECK: store ptr %[[V13]], ptr %[[T2]], align 8 + +void test_conversion0(void *p) { + S0 *t = (S0 *)p; + void *t2 = t; +} + +// CHECK-LABEL: define void @test_conversion1( +// CHECK: %[[P_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[P_ADDR]], align 8 + +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V3]], i32 1, i64 100, i32 1, i64 101) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr + +// CHECK: %[[V6:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V5]], %{{.*}} ] +// CHECK: store ptr %[[V6]], ptr %[[T]], align 8 + +void test_conversion1(S0 *p) { + S1 *t = (S1 *)p; +} + +// CHECK-LABEL: define void @test_conversion2( +// CHECK: %[[P_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[P_ADDR]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[T]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V1]], i64 1000) + +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V4]], i32 1, i64 100, i32 1, i64 %[[V2]]) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr + +// CHECK: %[[V7:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V6]], %{{.*}} ] +// CHECK: store ptr %[[V7]], ptr %[[T]], align 8 +// CHECK: %[[V8:.*]] = load ptr, ptr %[[T]], align 8 +// CHECK: %[[V9:.*]] = ptrtoint ptr %[[T]] to i64 +// CHECK: %[[V10:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V9]], i64 1000) + +// CHECK: %[[V12:.*]] = ptrtoint ptr %[[V8]] to i64 +// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V12]], i32 1, i64 %[[V10]], i32 1, i64 100) +// CHECK: %[[V14:.*]] = inttoptr i64 %[[V13]] to ptr + +// CHECK: %[[V15:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V14]], %{{.*}} ] +// CHECK: store ptr %[[V15]], ptr %{{.*}}, align 8 + +void test_conversion2(S0 *p) { + S0 *__ptrauth(1, 1, 1000) t = p; + S0 *t2 = t; +} + +// CHECK-LABEL: define void @test_conversion3( +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 + +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V3]], i32 1, i64 102, i32 1, i64 100) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr + +// CHECK: %[[V6:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V5]], %{{.*}} ] +// CHECK: store ptr %[[V6]], ptr %[[T]], align 8 + +void test_conversion3(S2 *s) { + S0 *t = (S0 *)s; +} + +// CHECK-LABEL: define void @test_conversion4( +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 + +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 100) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr + +// CHECK: %[[V5:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V4]], %{{.*}} ] +// CHECK: %[[V6:.*]] = ptrtoint ptr %[[V5]] to i64 +// CHECK: store i64 %[[V6]], ptr %{{.*}}, align 8 + +void test_conversion4(S0 *s) { + intptr_t i = (intptr_t)s; +} + +// CHECK-LABEL: define void @test_conversion5( +// CHECK: %[[I_ADDR:.*]] = alloca i64, align 8 +// CHECK: %[[V0:.*]] = load i64, ptr %[[I_ADDR]], align 8 +// CHECK: %[[V1:.*]] = inttoptr i64 %[[V0]] to ptr + +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[V1]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V3]], i32 1, i64 100) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr + +// CHECK: %[[V6:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V5]], %{{.*}} ] +// CHECK: store ptr %[[V6]], ptr %{{.*}}, align 8 + +void test_conversion5(intptr_t i) { + S0 *s = (S0 *)i; +} + +// CHECK: define i32 @test_comparison0(ptr %[[S:.*]]) +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @g0 to i64), i32 1, i64 100) +// CHECK: %[[V2:.*]] = inttoptr i64 %[[V1]] to ptr +// CHECK: icmp eq ptr %[[V0]], %[[V2]] + +int test_comparison0(S0 *s) { + if (s == &g0) + return 1; + return 2; +} + +// CHECK-LABEL: define void @test_call0() +// CHECK: %[[T:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[P:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[T]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V0]], i32 1, i64 100) +// CHECK: %[[V2:.*]] = inttoptr i64 %[[V1]] to ptr +// CHECK: store ptr %[[V2]], ptr %[[P]], align 8 +// CHECK: %[[V3:.*]] = load ptr, ptr %[[P]], align 8 +// CHECK: call ptr @func0(ptr %[[V3]]) + +void test_call0(void) { + S0 t; + S0 *p = &t; + func0(p); +} + +// CHECK-LABEL: define void @test_call1() +// CHECK: %[[T0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1:.*]] = alloca ptr, align 8 +// CHECK: %[[T2:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V0]], i32 1, i64 100) +// CHECK: %[[V2:.*]] = inttoptr i64 %[[V1]] to ptr +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V3]], i64 1000) +// CHECK: %[[V5:.*]] = ptrtoint ptr %[[V2]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V5]], i32 1, i64 100, i32 1, i64 %[[V4]]) +// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr +// CHECK: store ptr %[[V7]], ptr %[[T1]], align 8 +// CHECK: %[[V8:.*]] = load ptr, ptr %[[T1]], align 8 +// CHECK: %[[V9:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V10:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V9]], i64 1000) + +// CHECK: %[[V12:.*]] = ptrtoint ptr %[[V8]] to i64 +// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V12]], i32 1, i64 %[[V10]], i32 1, i64 100) +// CHECK: %[[V14:.*]] = inttoptr i64 %[[V13]] to ptr + +// CHECK: %[[V15:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V14]], %{{.*}} ] +// CHECK-NEXT: %[[CALL:.*]] = call ptr @func0(ptr %[[V15]]) +// CHECK-NEXT: store ptr %[[CALL]], ptr %[[T2]], align 8 +// CHECK-NEXT: ret void + +void test_call1(void) { + S0 t0; + S0 *__ptrauth(1, 1, 1000) t1 = &t0; + S0 *t2 = func0(t1); +} + +// CHECK-LABEL: define void @test_call2() +// CHECK: %[[P:.*]] = alloca ptr, align 8 +// CHECK: %[[CALL:.*]] = call ptr @func0(ptr null) +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[P]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V0:.*]], i64 1000) + +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[CALL]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V3]], i32 1, i64 100, i32 1, i64 %[[V1]]) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr + +// CHECK: %[[V6:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V5]], %{{.*}} ] +// CHECK: store ptr %[[V6]], ptr %[[P]], align 8 + +void test_call2(void) { + S0 *__ptrauth(1, 1, 1000) p = func0(0); +} + +// CHECK-LABEL: define i64 @test_call3( +// CHECK-NOT: @llvm.ptrauth + +S0 test_call3(S0 a) { + S0 t = a; + S0 t1 = func1(t); + S0 t2 = t1; + return t2; +} + +// NOTE: The aggregate temporary created to pass 't' to 'func2' isn't +// destructed. This is a pre-existing bug. + +// FIXME: Shouldn't pass raw pointers to non-trivial C struct special functions. + +// CHECK: define void @test_call4(ptr dead_on_unwind noalias writable sret(%struct.S4) align 16 %[[AGG_RESULT:.*]], ptr %[[A:.*]]) +// CHECK: %[[T:.*]] = alloca %[[STRUCT_S4]], align 16 +// CHECK: %[[T1:.*]] = alloca %[[STRUCT_S4]], align 16 +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_S4]], align 16 +// CHECK: %[[NRVO:.*]] = alloca i1, align 1 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[A]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V0]], i32 1, i64 104) +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[A]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 104) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: call void @__copy_constructor_16_16_t0w8_w8_t16w16(ptr %[[T]], ptr %[[V4]]) +// CHECK: call void @__copy_constructor_16_16_t0w8_w8_t16w16(ptr %[[AGG_TMP]], ptr %[[T]]) +// CHECK: %[[V7:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V8:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V7]], i32 1, i64 104) +// CHECK: %[[V9:.*]] = inttoptr i64 %[[V8]] to ptr +// CHECK: %[[V10:.*]] = ptrtoint ptr %[[AGG_TMP]] to i64 +// CHECK: %[[V11:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V10]], i32 1, i64 104) +// CHECK: %[[V12:.*]] = inttoptr i64 %[[V11]] to ptr +// CHECK: call void @func2(ptr dead_on_unwind writable sret(%struct.S4) align 16 %[[V9]], ptr %[[V12]]) +// CHECK: %[[V12:.*]] = ptrtoint ptr %[[AGG_RESULT]] to i64 +// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V12]], i32 1, i64 104) +// CHECK: %[[V14:.*]] = inttoptr i64 %[[V13]] to ptr +// CHECK: store i1 false, ptr %[[NRVO]], align 1 +// CHECK: %[[V15:.*]] = ptrtoint ptr %[[AGG_RESULT]] to i64 +// CHECK: %[[V16:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V15]], i32 1, i64 104) +// CHECK: %[[V17:.*]] = inttoptr i64 %[[V16]] to ptr +// CHECK: call void @__copy_constructor_16_16_t0w8_w8_t16w16(ptr %[[V17]], ptr %[[T1]]) + +// CHECK: call void @__destructor_16_w8(ptr %[[T1]]) +// CHECK: call void @__destructor_16_w8(ptr %[[T]]) +// CHECK: %[[V25:.*]] = ptrtoint ptr %[[A]] to i64 +// CHECK: %[[V26:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V25]], i32 1, i64 104) +// CHECK: %[[V27:.*]] = inttoptr i64 %[[V26]] to ptr +// CHECK: call void @__destructor_16_w8(ptr %[[V27]]) + +S4 test_call4(S4 a) { + S4 t = a; + S4 t1 = func2(t); + S4 t2 = t1; + return t2; +} + +// CHECK: define ptr @test_return0(ptr %[[P:.*]]) +// CHECK: %[[P_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[P_ADDR]], align 8 +// CHECK: %[[V2:.*]] = icmp ne ptr %[[V0]], null + +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V3]], i32 1, i64 100) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr + +// CHECK: %[[V6:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V5]], %{{.*}} ] +// CHECK: ret ptr %[[V6]] + +S0 *test_return0(void *p) { + return (S0 *)p; +} + +// CHECK-LABEL: define void @test_assignment0( +// CHECK-NOT: call {{.*}}ptrauth + +void test_assignment0(S0 *s) { + S0 *t = s; +} + +// CHECK-LABEL: define void @test_compound_literal0( +// CHECK: %[[T0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1]] = alloca i32, align 4 +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[T0]], ptr align 4 @[[CONST_TEST_COMPOUND_LITERAL0_T0]], i64 8, i1 false) +// CHECK: %[[A0:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[T0]], i32 0, i32 0 +// CHECK: %[[V1:.*]] = load i32, ptr %[[A0]], align 4 +// CHECK: %[[A1:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[T0]], i32 0, i32 1 +// CHECK: %[[V2:.*]] = load i32, ptr %[[A1]], align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V1]], %[[V2]] + +void test_compound_literal0() { + S0 t0 = (S0){1, 2}; + int t1 = t0.a0 + t0.a1; +} + +// CHECK-LABEL: define void @test_compound_literal1( +// CHECK: %[[T0:.*]] = alloca ptr, align 8 +// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[_COMPOUNDLITERAL]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V0]], i32 1, i64 100) +// CHECK: %[[V2:.*]] = inttoptr i64 %[[V1]] to ptr +// CHECK: store ptr %[[V2]], ptr %[[T0]], align 8 +// CHECK: %[[V3:.*]] = load ptr, ptr %[[T0]], align 8 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[V3]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 1, i64 100) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V6]], i64 0 +// CHECK: %[[V9:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 +// CHECK: %[[V10:.*]] = load ptr, ptr %[[T0]], align 8 +// CHECK: %[[V11:.*]] = ptrtoint ptr %[[V10]] to i64 +// CHECK: %[[V12:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V11]], i32 1, i64 100) +// CHECK: %[[V13:.*]] = inttoptr i64 %[[V12]] to ptr +// CHECK: %[[RESIGNEDGEP1:.*]] = getelementptr i8, ptr %[[V13]], i64 4 +// CHECK: %[[V16:.*]] = load i32, ptr %[[RESIGNEDGEP1]], align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V9]], %[[V16]] + +void test_compound_literal1() { + S0 *t0 = &(S0){1, 2}; + int t1 = t0->a0 + t0->a1; +} + +// CHECK: define void @test_block0(ptr %[[S:.*]]) +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[BLOCK:.*]] = alloca <{ ptr, i32, i32, ptr, ptr, ptr, %[[STRUCT_S0]] }>, align 8 +// CHECK: %[[BLOCK_CAPTURED:.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr, %[[STRUCT_S0]] }>, ptr %[[BLOCK]], i32 0, i32 5 +// CHECK: %[[V4:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// CHECK: store ptr %[[V4]], ptr %[[BLOCK_CAPTURED]], align 8 +// CHECK: %[[BLOCK_CAPTURED1:.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr, %[[STRUCT_S0]] }>, ptr %[[BLOCK]], i32 0, i32 6 +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %[[BLOCK_CAPTURED1]], ptr align 4 %[[T0]], i64 8, i1 false) + +// CHECK: define internal i32 @__test_block0_block_invoke(ptr %[[_BLOCK_DESCRIPTOR:.*]]) +// CHECK: %[[BLOCK_CAPTURE_ADDR:.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr, %[[STRUCT_S0]] }>, ptr %[[_BLOCK_DESCRIPTOR]], i32 0, i32 5 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[BLOCK_CAPTURE_ADDR]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V3]], i64 4 +// CHECK: %[[V6:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 +// CHECK: %[[BLOCK_CAPTURE_ADDR1:.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr, %[[STRUCT_S0]] }>, ptr %[[_BLOCK_DESCRIPTOR]], i32 0, i32 6 +// CHECK: %[[A1:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[BLOCK_CAPTURE_ADDR1]], i32 0, i32 1 +// CHECK: %[[V7:.*]] = load i32, ptr %[[A1]], align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V6]], %[[V7]] +// CHECK: ret i32 %[[ADD]] + +void test_block0(S0 *s) { + S0 t0 = {1, 2}; + int t1 = ^{ + return s->a1 + t0.a1; + }(); +} + +// CHECK: define ptr @test_atomic0( +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[P:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: store ptr %[[V0]], ptr %[[P]], align 8 +// CHECK: %[[ATOMIC_LOAD:.*]] = load atomic ptr, ptr %[[P]] seq_cst, align 8 + +// CHECK: %[[V1:.*]] = phi ptr [ %[[ATOMIC_LOAD]], %{{.*}} ], [ %[[V16:.*]], %{{.*}} ] +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V1]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 100) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: %[[INCDEC_PTR:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[V4]], i32 1 +// CHECK: %[[V5:.*]] = ptrtoint ptr %[[INCDEC_PTR]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V5]], i32 1, i64 100) +// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr +// CHECK: %[[V8:.*]] = cmpxchg ptr %[[P]], ptr %[[V1]], ptr %[[V7]] seq_cst seq_cst +// CHECK: %[[V9:.*]] = extractvalue { ptr, i1 } %[[V8]], 0 +// CHECK: %[[V10:.*]] = extractvalue { ptr, i1 } %[[V8]], 1 + +// CHECK: ret ptr %[[V7]] + +S0 *test_atomic0(S0 *a) { + S0 * _Atomic p = a; + return ++p; +} + +// CHECK: define ptr @test_atomic1( +// CHECK: alloca ptr, align 8 +// CHECK: %[[P:.*]] = alloca ptr, align 8 +// CHECK: %[[ATOMIC_LOAD:.*]] = load atomic ptr, ptr %[[P]] seq_cst, align 8 + +// CHECK: ret ptr %[[ATOMIC_LOAD]] + +S0 *test_atomic1(S0 *a) { + S0 * _Atomic p = a; + return p++; +} + +// CHECK-LABEL: define i32 @test_address_space0( +// CHECK: %[[V1:.*]] = ptrtoint ptr addrspace(1) %{{.*}} to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr addrspace(1) +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr addrspace(1) %[[V3]], i64 4 +// CHECK: load i32, ptr addrspace(1) %[[RESIGNEDGEP]], align 4 + +int test_address_space0(__attribute__((address_space(1))) S0 *s) { + return s->a1; +} + +// CHECK: define i32 @test_attr_annotate(ptr %[[S:.*]]) +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[S]], ptr %[[S_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// CHECK: %[[V1:.*]] = icmp ne ptr %[[V0]], null +// CHECK: br i1 %[[V1]], label %[[RESIGN_NONNULL:.*]], label %[[RESIGN_CONT:.*]] + +// CHECK: [[RESIGN_NONNULL]]: +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 103) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: br label %[[RESIGN_CONT]] + +// CHECK: [[RESIGN_CONT]]: +// CHECK: %[[V5:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V4]], %[[RESIGN_NONNULL]] ] +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V5]], i64 4 +// CHECK: %[[V9:.*]] = call ptr @llvm.ptr.annotation.p0.p0(ptr %[[RESIGNEDGEP]], +// CHECK: %[[A1:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[V9]], i32 0, i32 1 +// CHECK: load i32, ptr %[[A1]], align 4 + +int test_attr_annotate(S3 *s) { + return s->f1.a1; +} + +// CHECK-LABEL: define void @test_ext_vector0( +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 104) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V3]], i64 24 +// CHECK: store float 3.000000e+00, ptr %[[RESIGNEDGEP]], align 8 + +void test_ext_vector0(S4 *s) { + s->extv0.hi[0] = 3.0; +} diff --git a/clang/test/CodeGenObjC/ptrauth-struct-attr.m b/clang/test/CodeGenObjC/ptrauth-struct-attr.m new file mode 100644 index 0000000000000..a086a319fee3c --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-struct-attr.m @@ -0,0 +1,718 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-returns -fptrauth-intrinsics -fobjc-arc -fobjc-runtime-has-weak -fblocks -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s + +#define ATTR0 __attribute__((ptrauth_struct(1, 100))) +#define ATTR1 __attribute__((ptrauth_struct(1, 101))) +#define ATTR2 __attribute__((ptrauth_struct(1, 102))) +#define ATTR3 __attribute__((ptrauth_struct(1, 103))) +#define ATTR4 __attribute__((ptrauth_struct(1, 104))) + +// CHECK: %[[STRUCT_S0:.*]] = type { i32, i32 } +// CHECK: %[[STRUCT_S1:.*]] = type { i32, ptr, ptr, %[[STRUCT_S0]] } +// CHECK: %[[STRUCT_S4:.*]] = type { i32, i32, ptr, <4 x float> } + +// CHECK: @g0 = global %[[STRUCT_S0]] zeroinitializer, align 4 +// CHECK: @gp0 = global ptr ptrauth (ptr @g0, i32 1, i64 100), align 8 +// CHECK: @gp1 = global ptr ptrauth (ptr getelementptr (i8, ptr @g1, i64 24), i32 1, i64 100), align 8 +// CHECK: @ga2 = global [4 x i32] zeroinitializer, align 4 +// CHECK: @gf0 = global ptr @ga2, align 8 +// CHECK: @[[CONST_TEST_COMPOUND_LITERAL0_T0:.*]] = private unnamed_addr constant %[[STRUCT_S0]] { i32 1, i32 2 }, align 4 + +typedef long intptr_t; + +struct ATTR0 S0 { + int a0, a1; +}; + +typedef struct S0 S0; + +struct ATTR1 S1 { + int a; + id b; + S0 *f0; + S0 f1; +}; + +typedef struct S1 S1; + +struct ATTR2 S2; +typedef struct S2 S2; + +struct ATTR3 S3 { + int f0; + __attribute__((annotate("abc"))) S0 f1; +}; + +typedef struct S3 S3; + +typedef __attribute__((ext_vector_type(4))) float float4; + +struct ATTR4 S4 { + int f0, f1; + __weak id f2; + float4 extv0; +}; + +typedef struct S4 S4; + +S0 getS0(void); +S0 *func0(S0 *); +S0 func1(S0); +S4 func2(S4); + +S0 g0; +S0 *gp0 = &g0; +S1 g1; +S0 *gp1 = &g1.f1; + +S0 ga0[10][10][10]; +S0 ga1[2]; + +int ga2[4] = {0}; +void (*gf0)(void) = (void (*)(void))ga2; + +// CHECK-LABEL: define void @test_member_access0( +// CHECK-NOT: @llvm.ptrauth +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %{{.*}}, i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V3]], i64 4 +// CHECK: load i32, ptr %[[RESIGNEDGEP]], align 4 + +void test_member_access0(S0 *s) { + int t = s->a1; +} + +// CHECK-LABEL: define void @test_member_access1( +// CHECK-NOT: @llvm.ptrauth +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %{{.*}}, i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V3]], i64 4 +// CHECK: load i32, ptr %[[RESIGNEDGEP]], align 4 + +void test_member_access1(S0 *s) { + int t = (*s).a1; +} + +// CHECK-LABEL: define void @test_member_access2( +// CHECK-NOT: @llvm.ptrauth + +void test_member_access2(S0 s) { + int t = s.a1; +} + +// CHECK-LABEL: define void @test_member_initialization( +// CHECK: %[[T:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1:.*]] = alloca %[[STRUCT_S1]], align 8 +// CHECK: %[[S0:.*]] = getelementptr inbounds %[[STRUCT_S1]], ptr %[[T1]], i32 0, i32 2 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[T]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V0]], i32 1, i64 100) +// CHECK: %[[V2:.*]] = inttoptr i64 %[[V1]] to ptr +// CHECK: store ptr %[[V2]], ptr %[[S0]], align 8 + +void test_member_initialization() { + S0 t; + S1 t1 = {1, 0, &t}; +} + +// CHECK-LABEL: define i32 @test_array0( +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V3]], i64 84 +// CHECK: load i32, ptr %[[RESIGNEDGEP]], align 4 + +int test_array0(S0 *a) { + return a[10].a1; +} + +// CHECK-LABEL: define i32 @test_array1( +// CHECK: %[[V1:.*]] = load i32, ptr %{{.*}}, align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V1]], 2 +// CHECK: %[[IDXPROM:.*]] = sext i32 %[[ADD]] to i64 +// CHECK: %[[ARRAYIDX_IDX:.*]] = mul nsw i64 %[[IDXPROM]], 8 +// CHECK: %[[ADD:.*]] = add i64 %[[ARRAYIDX_IDX]], 4 +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 100) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V4]], i64 %[[ADD]] +// CHECK: load i32, ptr %[[RESIGNEDGEP]], align 4 + +int test_array1(S0 *a, int i) { + return a[i + 2].a1; +} + +// CHECK-LABEL: define i32 @test_array2( +// CHECK: %[[V0:.*]] = load i32, ptr %{{.*}}, align 4 +// CHECK: %[[IDXPROM:.*]] = sext i32 %[[V0]] to i64 +// CHECK: %[[ARRAYIDX:.*]] = getelementptr inbounds [10 x [10 x [10 x %[[STRUCT_S0]]]]], ptr @ga0, i64 0, i64 %[[IDXPROM]] +// CHECK: %[[V1:.*]] = load i32, ptr %{{.*}}, align 4 +// CHECK: %[[IDXPROM1:.*]] = sext i32 %[[V1]] to i64 +// CHECK: %[[ARRAYIDX2:.*]] = getelementptr inbounds [10 x [10 x %[[STRUCT_S0]]]], ptr %[[ARRAYIDX]], i64 0, i64 %[[IDXPROM1]] +// CHECK: %[[V2:.*]] = load i32, ptr %{{.*}}, align 4 +// CHECK: %[[IDXPROM3:.*]] = sext i32 %[[V2]] to i64 +// CHECK: %[[ARRAYIDX4:.*]] = getelementptr inbounds [10 x %[[STRUCT_S0]]], ptr %[[ARRAYIDX2]], i64 0, i64 %[[IDXPROM3]] +// CHECK: %[[A1:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[ARRAYIDX4]], i32 0, i32 1 +// CHECK: load i32, ptr %[[A1]], align 4 + +int test_array2(int i, int j, int k) { + return ga0[i][j][k].a1; +} + +// CHECK: define ptr @test_array3( +// CHECK: %[[V0:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @ga1 to i64), i32 1, i64 100) +// CHECK: %[[V1:.*]] = inttoptr i64 %[[V0]] to ptr +// CHECK: ret ptr %[[V1]] + +S0 *test_array3(void) { + return ga1; +} + +// CHECK-LABEL: define i32 @test_pointer_arithmetic0( +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %{{.*}}, i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[V3]], i64 10 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[ADD_PTR]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V4]], i32 1, i64 100) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[V7:.*]] = ptrtoint ptr %[[V6]] to i64 +// CHECK: %[[V8:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V7]], i32 1, i64 100) +// CHECK: %[[V9:.*]] = inttoptr i64 %[[V8]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V9]], i64 4 +// CHECK: %[[V12:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 + +int test_pointer_arithmetic0(S0 *a) { + return (a + 10)->a1; +} + +// CHECK: define ptr @test_pointer_arithmetic1( +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[INCDEC_PTR:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[V3]], i32 1 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[INCDEC_PTR]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V4]], i32 1, i64 100) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: store ptr %[[V6]], ptr %[[A_ADDR]], align 8 +// CHECK: ret ptr %[[V6]] + +S0 *test_pointer_arithmetic1(S0 *a) { + return ++a; +} + +// CHECK: define ptr @test_pointer_arithmetic2( +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[INCDEC_PTR:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[V3]], i32 1 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[INCDEC_PTR]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V4]], i32 1, i64 100) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: store ptr %[[V6]], ptr %[[A_ADDR]], align 8 +// CHECK: ret ptr %[[V0]] + +S0 *test_pointer_arithmetic2(S0 *a) { + return a++; +} + +// CHECK-LABEL: define void @test_dereference0( +// CHECK: %[[A0_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[A1_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A0_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[A1_ADDR]], align 8 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 1, i64 100) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[V7:.*]] = ptrtoint ptr %[[V1]] to i64 +// CHECK: %[[V8:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V7]], i32 1, i64 100) +// CHECK: %[[V9:.*]] = inttoptr i64 %[[V8]] to ptr +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[V6]], ptr align 4 %[[V9]], i64 8, i1 false) + +void test_dereference0(S0 *a0, S0 *a1) { + *a0 = *a1; +} + +// CHECK-LABEL: define void @test_dereference1( +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T:.*]] = alloca %[[STRUCT_S1]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V3]], i32 1, i64 101) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr +// CHECK: call void @__copy_constructor_8_8_t0w4_s8_t16w16(ptr %[[T]], ptr %[[V5]]) + +void test_dereference1(S1 *s) { + S1 t = *s; +} + +// CHECK-LABEL: define void @test_address_of0( +// CHECK: %[[T:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[T]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V0]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: store ptr %[[V3]], ptr %{{.*}}, align 8 + +void test_address_of0(void) { + S0 t = getS0(); + S0 *p = &t; +} + +// CHECK-LABEL: define void @test_address_of1( +// CHECK: %[[V0:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @g0 to i64), i32 1, i64 100) +// CHECK: %[[V1:.*]] = inttoptr i64 %[[V0]] to ptr +// CHECK: store ptr %[[V1]], ptr %{{.*}}, align 8 + +void test_address_of1(void) { + S0 *p = &g0; +} + +// CHECK-LABEL: define void @test_address_of2( +// CHECK: %[[T:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[CALL:.*]] = call i64 @getS0() +// CHECK: store i64 %[[CALL]], ptr %[[T]], align 4 +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[T1]], ptr align 4 %[[T]], i64 8, i1 false) + +void test_address_of2(void) { + S0 t = getS0(); + S0 t1 = *&t; +} + +// CHECK-LABEL: define void @test_conversion0( +// CHECK: %[[P_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T:.*]] = alloca ptr, align 8 +// CHECK: %[[T2:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[P_ADDR]], align 8 + +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V3]], i32 1, i64 100) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr + +// CHECK: %[[V6:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V5]], %{{.*}} ] +// CHECK: store ptr %[[V6]], ptr %[[T]], align 8 +// CHECK: %[[V7:.*]] = load ptr, ptr %[[T]], align 8 + +// CHECK: %[[V10:.*]] = ptrtoint ptr %[[V8]] to i64 +// CHECK: %[[V11:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V10]], i32 1, i64 100) +// CHECK: %[[V12:.*]] = inttoptr i64 %[[V11]] to ptr + +// CHECK: %[[V13:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V12]], %{{.*}} ] +// CHECK: store ptr %[[V13]], ptr %[[T2]], align 8 + +void test_conversion0(void *p) { + S0 *t = (S0 *)p; + void *t2 = t; +} + +// CHECK-LABEL: define void @test_conversion1( +// CHECK: %[[P_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[P_ADDR]], align 8 + +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V3]], i32 1, i64 100, i32 1, i64 101) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr + +// CHECK: %[[V6:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V5]], %{{.*}} ] +// CHECK: store ptr %[[V6]], ptr %[[T]], align 8 + +void test_conversion1(S0 *p) { + S1 *t = (S1 *)p; +} + +// CHECK-LABEL: define void @test_conversion2( +// CHECK: %[[P_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[P_ADDR]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[T]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V1]], i64 1000) + +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V4]], i32 1, i64 100, i32 1, i64 %[[V2]]) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr + +// CHECK: %[[V7:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V6]], %{{.*}} ] +// CHECK: store ptr %[[V7]], ptr %[[T]], align 8 +// CHECK: %[[V8:.*]] = load ptr, ptr %[[T]], align 8 +// CHECK: %[[V9:.*]] = ptrtoint ptr %[[T]] to i64 +// CHECK: %[[V10:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V9]], i64 1000) + +// CHECK: %[[V12:.*]] = ptrtoint ptr %[[V8]] to i64 +// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V12]], i32 1, i64 %[[V10]], i32 1, i64 100) +// CHECK: %[[V14:.*]] = inttoptr i64 %[[V13]] to ptr + +// CHECK: %[[V15:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V14]], %{{.*}} ] +// CHECK: store ptr %[[V15]], ptr %{{.*}}, align 8 + +void test_conversion2(S0 *p) { + S0 *__ptrauth(1, 1, 1000) t = p; + S0 *t2 = t; +} + +// CHECK-LABEL: define void @test_conversion3( +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 + +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V3]], i32 1, i64 102, i32 1, i64 100) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr + +// CHECK: %[[V6:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V5]], %{{.*}} ] +// CHECK: store ptr %[[V6]], ptr %[[T]], align 8 + +void test_conversion3(S2 *s) { + S0 *t = (S0 *)s; +} + +// CHECK-LABEL: define void @test_conversion4( +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 + +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 100) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr + +// CHECK: %[[V5:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V4]], %{{.*}} ] +// CHECK: %[[V6:.*]] = ptrtoint ptr %[[V5]] to i64 +// CHECK: store i64 %[[V6]], ptr %{{.*}}, align 8 + +void test_conversion4(S0 *s) { + intptr_t i = (intptr_t)s; +} + +// CHECK-LABEL: define void @test_conversion5( +// CHECK: %[[I_ADDR:.*]] = alloca i64, align 8 +// CHECK: %[[V0:.*]] = load i64, ptr %[[I_ADDR]], align 8 +// CHECK: %[[V1:.*]] = inttoptr i64 %[[V0]] to ptr + +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[V1]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V3]], i32 1, i64 100) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr + +// CHECK: %[[V6:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V5]], %{{.*}} ] +// CHECK: store ptr %[[V6]], ptr %{{.*}}, align 8 + +void test_conversion5(intptr_t i) { + S0 *s = (S0 *)i; +} + +// CHECK: define i32 @test_comparison0(ptr %[[S:.*]]) +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @g0 to i64), i32 1, i64 100) +// CHECK: %[[V2:.*]] = inttoptr i64 %[[V1]] to ptr +// CHECK: icmp eq ptr %[[V0]], %[[V2]] + +int test_comparison0(S0 *s) { + if (s == &g0) + return 1; + return 2; +} + +// CHECK-LABEL: define void @test_call0() +// CHECK: %[[T:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[P:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[T]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V0]], i32 1, i64 100) +// CHECK: %[[V2:.*]] = inttoptr i64 %[[V1]] to ptr +// CHECK: store ptr %[[V2]], ptr %[[P]], align 8 +// CHECK: %[[V3:.*]] = load ptr, ptr %[[P]], align 8 +// CHECK: call ptr @func0(ptr %[[V3]]) + +void test_call0(void) { + S0 t; + S0 *p = &t; + func0(p); +} + +// CHECK-LABEL: define void @test_call1() +// CHECK: %[[T0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1:.*]] = alloca ptr, align 8 +// CHECK: %[[T2:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V0]], i32 1, i64 100) +// CHECK: %[[V2:.*]] = inttoptr i64 %[[V1]] to ptr +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V3]], i64 1000) +// CHECK: %[[V5:.*]] = ptrtoint ptr %[[V2]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V5]], i32 1, i64 100, i32 1, i64 %[[V4]]) +// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr +// CHECK: store ptr %[[V7]], ptr %[[T1]], align 8 +// CHECK: %[[V8:.*]] = load ptr, ptr %[[T1]], align 8 +// CHECK: %[[V9:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V10:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V9]], i64 1000) + +// CHECK: %[[V12:.*]] = ptrtoint ptr %[[V8]] to i64 +// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V12]], i32 1, i64 %[[V10]], i32 1, i64 100) +// CHECK: %[[V14:.*]] = inttoptr i64 %[[V13]] to ptr + +// CHECK: %[[V15:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V14]], %{{.*}} ] +// CHECK-NEXT: %[[CALL:.*]] = call ptr @func0(ptr %[[V15]]) +// CHECK-NEXT: store ptr %[[CALL]], ptr %[[T2]], align 8 +// CHECK-NEXT: ret void + +void test_call1(void) { + S0 t0; + S0 *__ptrauth(1, 1, 1000) t1 = &t0; + S0 *t2 = func0(t1); +} + +// CHECK-LABEL: define void @test_call2() +// CHECK: %[[P:.*]] = alloca ptr, align 8 +// CHECK: %[[CALL:.*]] = call ptr @func0(ptr null) +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[P]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V0:.*]], i64 1000) + +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[CALL]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V3]], i32 1, i64 100, i32 1, i64 %[[V1]]) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr + +// CHECK: %[[V6:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V5]], %{{.*}} ] +// CHECK: store ptr %[[V6]], ptr %[[P]], align 8 + +void test_call2(void) { + S0 *__ptrauth(1, 1, 1000) p = func0(0); +} + +// CHECK-LABEL: define i64 @test_call3( +// CHECK-NOT: @llvm.ptrauth + +S0 test_call3(S0 a) { + S0 t = a; + S0 t1 = func1(t); + S0 t2 = t1; + return t2; +} + +// NOTE: The aggregate temporary created to pass 't' to 'func2' isn't +// destructed. This is a pre-existing bug. + +// FIXME: Shouldn't pass raw pointers to non-trivial C struct special functions. + +// CHECK: define void @test_call4(ptr dead_on_unwind noalias writable sret(%struct.S4) align 16 %[[AGG_RESULT:.*]], ptr %[[A:.*]]) +// CHECK: %[[T:.*]] = alloca %[[STRUCT_S4]], align 16 +// CHECK: %[[T1:.*]] = alloca %[[STRUCT_S4]], align 16 +// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_S4]], align 16 +// CHECK: %[[NRVO:.*]] = alloca i1, align 1 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[A]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V0]], i32 1, i64 104) +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[A]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 104) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: call void @__copy_constructor_16_16_t0w8_w8_t16w16(ptr %[[T]], ptr %[[V4]]) +// CHECK: call void @__copy_constructor_16_16_t0w8_w8_t16w16(ptr %[[AGG_TMP]], ptr %[[T]]) +// CHECK: %[[V7:.*]] = ptrtoint ptr %[[T1]] to i64 +// CHECK: %[[V8:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V7]], i32 1, i64 104) +// CHECK: %[[V9:.*]] = inttoptr i64 %[[V8]] to ptr +// CHECK: %[[V10:.*]] = ptrtoint ptr %[[AGG_TMP]] to i64 +// CHECK: %[[V11:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V10]], i32 1, i64 104) +// CHECK: %[[V12:.*]] = inttoptr i64 %[[V11]] to ptr +// CHECK: call void @func2(ptr dead_on_unwind writable sret(%struct.S4) align 16 %[[V9]], ptr %[[V12]]) +// CHECK: %[[V12:.*]] = ptrtoint ptr %[[AGG_RESULT]] to i64 +// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V12]], i32 1, i64 104) +// CHECK: %[[V14:.*]] = inttoptr i64 %[[V13]] to ptr +// CHECK: store i1 false, ptr %[[NRVO]], align 1 +// CHECK: %[[V15:.*]] = ptrtoint ptr %[[AGG_RESULT]] to i64 +// CHECK: %[[V16:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V15]], i32 1, i64 104) +// CHECK: %[[V17:.*]] = inttoptr i64 %[[V16]] to ptr +// CHECK: call void @__copy_constructor_16_16_t0w8_w8_t16w16(ptr %[[V17]], ptr %[[T1]]) + +// CHECK: call void @__destructor_16_w8(ptr %[[T1]]) +// CHECK: call void @__destructor_16_w8(ptr %[[T]]) +// CHECK: %[[V25:.*]] = ptrtoint ptr %[[A]] to i64 +// CHECK: %[[V26:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V25]], i32 1, i64 104) +// CHECK: %[[V27:.*]] = inttoptr i64 %[[V26]] to ptr +// CHECK: call void @__destructor_16_w8(ptr %[[V27]]) + +S4 test_call4(S4 a) { + S4 t = a; + S4 t1 = func2(t); + S4 t2 = t1; + return t2; +} + +// CHECK: define ptr @test_return0(ptr %[[P:.*]]) +// CHECK: %[[P_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[P_ADDR]], align 8 +// CHECK: %[[V2:.*]] = icmp ne ptr %[[V0]], null + +// CHECK: %[[V3:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V4:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V3]], i32 1, i64 100) +// CHECK: %[[V5:.*]] = inttoptr i64 %[[V4]] to ptr + +// CHECK: %[[V6:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V5]], %{{.*}} ] +// CHECK: ret ptr %[[V6]] + +S0 *test_return0(void *p) { + return (S0 *)p; +} + +// CHECK-LABEL: define void @test_assignment0( +// CHECK-NOT: call {{.*}}ptrauth + +void test_assignment0(S0 *s) { + S0 *t = s; +} + +// CHECK-LABEL: define void @test_compound_literal0( +// CHECK: %[[T0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[T1]] = alloca i32, align 4 +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[T0]], ptr align 4 @[[CONST_TEST_COMPOUND_LITERAL0_T0]], i64 8, i1 false) +// CHECK: %[[A0:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[T0]], i32 0, i32 0 +// CHECK: %[[V1:.*]] = load i32, ptr %[[A0]], align 4 +// CHECK: %[[A1:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[T0]], i32 0, i32 1 +// CHECK: %[[V2:.*]] = load i32, ptr %[[A1]], align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V1]], %[[V2]] + +void test_compound_literal0() { + S0 t0 = (S0){1, 2}; + int t1 = t0.a0 + t0.a1; +} + +// CHECK-LABEL: define void @test_compound_literal1( +// CHECK: %[[T0:.*]] = alloca ptr, align 8 +// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[V0:.*]] = ptrtoint ptr %[[_COMPOUNDLITERAL]] to i64 +// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V0]], i32 1, i64 100) +// CHECK: %[[V2:.*]] = inttoptr i64 %[[V1]] to ptr +// CHECK: store ptr %[[V2]], ptr %[[T0]], align 8 +// CHECK: %[[V3:.*]] = load ptr, ptr %[[T0]], align 8 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[V3]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 1, i64 100) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V6]], i64 0 +// CHECK: %[[V9:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 +// CHECK: %[[V10:.*]] = load ptr, ptr %[[T0]], align 8 +// CHECK: %[[V11:.*]] = ptrtoint ptr %[[V10]] to i64 +// CHECK: %[[V12:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V11]], i32 1, i64 100) +// CHECK: %[[V13:.*]] = inttoptr i64 %[[V12]] to ptr +// CHECK: %[[RESIGNEDGEP1:.*]] = getelementptr i8, ptr %[[V13]], i64 4 +// CHECK: %[[V16:.*]] = load i32, ptr %[[RESIGNEDGEP1]], align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V9]], %[[V16]] + +void test_compound_literal1() { + S0 *t0 = &(S0){1, 2}; + int t1 = t0->a0 + t0->a1; +} + +// CHECK: define void @test_block0(ptr %[[S:.*]]) +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[T0:.*]] = alloca %[[STRUCT_S0]], align 4 +// CHECK: %[[BLOCK:.*]] = alloca <{ ptr, i32, i32, ptr, ptr, ptr, %[[STRUCT_S0]] }>, align 8 +// CHECK: %[[BLOCK_CAPTURED:.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr, %[[STRUCT_S0]] }>, ptr %[[BLOCK]], i32 0, i32 5 +// CHECK: %[[V4:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// CHECK: store ptr %[[V4]], ptr %[[BLOCK_CAPTURED]], align 8 +// CHECK: %[[BLOCK_CAPTURED1:.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr, %[[STRUCT_S0]] }>, ptr %[[BLOCK]], i32 0, i32 6 +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %[[BLOCK_CAPTURED1]], ptr align 4 %[[T0]], i64 8, i1 false) + +// CHECK: define internal i32 @__test_block0_block_invoke(ptr %[[_BLOCK_DESCRIPTOR:.*]]) +// CHECK: %[[BLOCK_CAPTURE_ADDR:.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr, %[[STRUCT_S0]] }>, ptr %[[_BLOCK_DESCRIPTOR]], i32 0, i32 5 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[BLOCK_CAPTURE_ADDR]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V3]], i64 4 +// CHECK: %[[V6:.*]] = load i32, ptr %[[RESIGNEDGEP]], align 4 +// CHECK: %[[BLOCK_CAPTURE_ADDR1:.*]] = getelementptr inbounds <{ ptr, i32, i32, ptr, ptr, ptr, %[[STRUCT_S0]] }>, ptr %[[_BLOCK_DESCRIPTOR]], i32 0, i32 6 +// CHECK: %[[A1:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[BLOCK_CAPTURE_ADDR1]], i32 0, i32 1 +// CHECK: %[[V7:.*]] = load i32, ptr %[[A1]], align 4 +// CHECK: %[[ADD:.*]] = add nsw i32 %[[V6]], %[[V7]] +// CHECK: ret i32 %[[ADD]] + +void test_block0(S0 *s) { + S0 t0 = {1, 2}; + int t1 = ^{ + return s->a1 + t0.a1; + }(); +} + +// CHECK: define ptr @test_atomic0( +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[P:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: store ptr %[[V0]], ptr %[[P]], align 8 +// CHECK: %[[ATOMIC_LOAD:.*]] = load atomic ptr, ptr %[[P]] seq_cst, align 8 + +// CHECK: %[[V1:.*]] = phi ptr [ %[[ATOMIC_LOAD]], %{{.*}} ], [ %[[V16:.*]], %{{.*}} ] +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V1]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 100) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: %[[INCDEC_PTR:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[V4]], i32 1 +// CHECK: %[[V5:.*]] = ptrtoint ptr %[[INCDEC_PTR]] to i64 +// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.sign(i64 %[[V5]], i32 1, i64 100) +// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr +// CHECK: %[[V8:.*]] = cmpxchg ptr %[[P]], ptr %[[V1]], ptr %[[V7]] seq_cst seq_cst +// CHECK: %[[V9:.*]] = extractvalue { ptr, i1 } %[[V8]], 0 +// CHECK: %[[V10:.*]] = extractvalue { ptr, i1 } %[[V8]], 1 + +// CHECK: ret ptr %[[V7]] + +S0 *test_atomic0(S0 *a) { + S0 * _Atomic p = a; + return ++p; +} + +// CHECK: define ptr @test_atomic1( +// CHECK: alloca ptr, align 8 +// CHECK: %[[P:.*]] = alloca ptr, align 8 +// CHECK: %[[ATOMIC_LOAD:.*]] = load atomic ptr, ptr %[[P]] seq_cst, align 8 + +// CHECK: ret ptr %[[ATOMIC_LOAD]] + +S0 *test_atomic1(S0 *a) { + S0 * _Atomic p = a; + return p++; +} + +// CHECK-LABEL: define i32 @test_address_space0( +// CHECK: %[[V1:.*]] = ptrtoint ptr addrspace(1) %{{.*}} to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 100) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr addrspace(1) +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr addrspace(1) %[[V3]], i64 4 +// CHECK: load i32, ptr addrspace(1) %[[RESIGNEDGEP]], align 4 + +int test_address_space0(__attribute__((address_space(1))) S0 *s) { + return s->a1; +} + +// CHECK: define i32 @test_attr_annotate(ptr %[[S:.*]]) +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[S]], ptr %[[S_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// CHECK: %[[V1:.*]] = icmp ne ptr %[[V0]], null +// CHECK: br i1 %[[V1]], label %[[RESIGN_NONNULL:.*]], label %[[RESIGN_CONT:.*]] + +// CHECK: [[RESIGN_NONNULL]]: +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 1, i64 103) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: br label %[[RESIGN_CONT]] + +// CHECK: [[RESIGN_CONT]]: +// CHECK: %[[V5:.*]] = phi ptr [ null, %{{.*}} ], [ %[[V4]], %[[RESIGN_NONNULL]] ] +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V5]], i64 4 +// CHECK: %[[V9:.*]] = call ptr @llvm.ptr.annotation.p0.p0(ptr %[[RESIGNEDGEP]], +// CHECK: %[[A1:.*]] = getelementptr inbounds %[[STRUCT_S0]], ptr %[[V9]], i32 0, i32 1 +// CHECK: load i32, ptr %[[A1]], align 4 + +int test_attr_annotate(S3 *s) { + return s->f1.a1; +} + +// CHECK-LABEL: define void @test_ext_vector0( +// CHECK: %[[S_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[S_ADDR]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V0]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 1, i64 104) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[RESIGNEDGEP:.*]] = getelementptr i8, ptr %[[V3]], i64 24 +// CHECK: store float 3.000000e+00, ptr %[[RESIGNEDGEP]], align 8 + +void test_ext_vector0(S4 *s) { + s->extv0.hi[0] = 3.0; +} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 98249e69baca5..0a19f503b00bd 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -165,6 +165,7 @@ // CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: PatchableFunctionEntry (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union) +// CHECK-NEXT: PointerAuthStruct (SubjectMatchRule_record) // CHECK-NEXT: PreserveNone (SubjectMatchRule_hasType_functionType) // CHECK-NEXT: RandomizeLayout (SubjectMatchRule_record) // CHECK-NEXT: ReadOnlyPlacement (SubjectMatchRule_record) diff --git a/clang/test/Sema/ptrauth-struct-attr.c b/clang/test/Sema/ptrauth-struct-attr.c new file mode 100644 index 0000000000000..5114701f7b398 --- /dev/null +++ b/clang/test/Sema/ptrauth-struct-attr.c @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -verify %s + +#include + +#define VALID_KEY 2 +#define INVALID_KEY 200 +#define VALID_DISC 100 +#define INVALID_DISC 100000 +#define VALID_KEY1 1 +#define VALID_DISC1 101 + +int nonconstval0; + +int *__attribute__((ptrauth_struct(VALID_KEY, VALID_DISC))) intPtr; // expected-warning {{only applies to structs, unions, and classes}} + +struct __attribute__((ptrauth_struct(nonconstval0, VALID_DISC))) NonConstIntKeyS {}; // expected-error {{not an integer constant expression}} +struct __attribute__((ptrauth_struct(VALID_KEY, nonconstval0))) NonConstIntDiscS0 {}; // expected-error {{ptrauth_struct must be an integer constant expression}} +struct __attribute__((ptrauth_struct(VALID_KEY, "val"))) NonConstIntDiscS1 {}; // expected-error {{ptrauth_struct must be an integer constant expression}} +struct __attribute__((ptrauth_struct(VALID_KEY))) NoDiscS {}; // expected-error {{requires exactly 2 arguments}} + +struct __attribute__((ptrauth_struct(VALID_KEY, VALID_DISC))) ValidS {}; // expected-note {{previous declaration of}} +struct __attribute__((ptrauth_struct(INVALID_KEY, VALID_DISC))) InvalidKeyS {}; // expected-error {{200 does not identify a valid pointer authentication key}} +struct __attribute__((ptrauth_struct(VALID_KEY, INVALID_DISC))) InvalidDiscS0 {}; // expected-error {{for ptrauth_struct must be between 0 and 65535; value is 100000}} + +struct __attribute__((ptrauth_struct(VALID_KEY1, VALID_DISC))) ValidS; // expected-error {{is signed differently from the previous declaration}} expected-note {{previous declaration of}} +struct __attribute__((ptrauth_struct(VALID_KEY1, VALID_DISC1))) ValidS; // expected-error {{is signed differently from the previous declaration}} +struct __attribute__((ptrauth_struct(VALID_KEY1, VALID_DISC1))) ValidS; // expected-note {{previous declaration of}} +struct ValidS; // expected-error {{is signed differently from the previous declaration}} + +struct __attribute__((ptrauth_struct(ptrauth_key_none, VALID_DISC))) NoneS {}; +struct __attribute__((ptrauth_struct(ptrauth_key_none, VALID_DISC1))) NoneS; diff --git a/clang/test/SemaCXX/ptrauth-struct-attr.cpp b/clang/test/SemaCXX/ptrauth-struct-attr.cpp new file mode 100644 index 0000000000000..c82812c2b4eea --- /dev/null +++ b/clang/test/SemaCXX/ptrauth-struct-attr.cpp @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -verify %s + +#include + +#define DISC0 100 +#define DISC1 101 + +struct __attribute__((ptrauth_struct(2, DISC0))) SignedBase0 {}; +static_assert(__builtin_ptrauth_struct_key(SignedBase0) == 2); +static_assert(__builtin_ptrauth_struct_disc(SignedBase0) == DISC0); + +struct __attribute__((ptrauth_struct(2, DISC1))) SignedBase1 {}; +struct UnsignedBase0 {}; +struct __attribute__((ptrauth_struct(2, DISC0))) SignedDerivded0 : SignedBase0 {}; +struct __attribute__((ptrauth_struct(2, DISC1))) SignedDerivded1 : SignedBase0 {}; +struct __attribute__((ptrauth_struct(2, DISC0))) SignedDerivded2 : SignedBase0, SignedBase1 {}; + +struct __attribute__((ptrauth_struct(2, DISC0))) SignedDynamic0 { // expected-error {{cannot be used on class 'SignedDynamic0' or its subclasses because it is a dynamic class}} + virtual void m0(); +}; +struct __attribute__((ptrauth_struct(2, DISC1))) SignedDynamic1 : virtual SignedBase1 {}; // expected-error {{cannot be used on class 'SignedDynamic1' or its subclasses because it is a dynamic class}} +struct UnsignedDynamic2 : virtual SignedBase0 {}; // expected-error {{because it is a dynamic class}} + +template +struct __attribute__((ptrauth_struct(2, DISC0))) Template0 : T { + static_assert(__builtin_ptrauth_struct_key(Template0) == 2); + static_assert(__builtin_ptrauth_struct_disc(Template0) == DISC0); +}; + +Template0 g0; +Template0 g1; +Template0 g2; + +struct DynamicBase0 { + virtual void m0(); +}; + +struct DynamicBase1 : virtual UnsignedBase0 { +}; + +struct DynamicBase2 : SignedBase0 { // expected-error {{because it is a dynamic class}} + virtual void m0(); +}; + +template +struct __attribute__((ptrauth_struct(2, DISC0))) Template1 { // expected-error {{because it is a dynamic class}} + virtual void m0(); +}; + +template +struct __attribute__((ptrauth_struct(2, DISC0))) Template2 : DynamicBase0 {}; // expected-error {{because it is a dynamic class}} + +template +struct __attribute__((ptrauth_struct(2, DISC0))) Template3 : DynamicBase1 {}; // expected-error {{because it is a dynamic class}} + +template +struct __attribute__((ptrauth_struct(2, DISC1))) Template4 { +}; + +template +struct __attribute__((ptrauth_struct(2, DISC0))) Template5 : Template4 {}; + +template +struct __attribute__((ptrauth_struct(2, DISC0))) Template6 : Template4 {}; + +template <> +struct __attribute__((ptrauth_struct(2, DISC0))) Template4 { +}; + +template +struct __attribute__((ptrauth_struct(k, d))) Template7 { // expected-error 3 {{because it is a dynamic class}} + virtual void m0(); +}; + +template +struct Template8 : Template7<2, d> { // expected-error {{because it is a dynamic class}} expected-note {{in instantiation of template class}} +}; + +template +struct Template9 : Template7<2, 100> { // expected-error {{because it is a dynamic class}} expected-note {{in instantiation of template class}} +}; + +Template5 g3; +Template5 g4; +Template7<3, 100> g5; // expected-note {{in instantiation of template class}} +Template8<103> g6; // expected-note 2 {{in instantiation of template class}} +SignedBase0 * __ptrauth(ptrauth_key_none, 0, 0) g7; // expected-error {{signed pointer types may not be qualified}} +Template7 * __ptrauth(ptrauth_key_none, 0, 0) g8; diff --git a/clang/test/SemaCXX/ptrauth-template-parameters.cpp b/clang/test/SemaCXX/ptrauth-template-parameters.cpp index f848a3df53fd4..0f8f5bf6b1ccf 100644 --- a/clang/test/SemaCXX/ptrauth-template-parameters.cpp +++ b/clang/test/SemaCXX/ptrauth-template-parameters.cpp @@ -63,4 +63,18 @@ void f3() { // expected-note@-1{{in instantiation of template class 'Indirect' requested here}} } + +template struct __attribute__((ptrauth_struct(0,1236))) AuthenticatedStruct { + P ptr; +}; + +void f4(void* __attribute__((nonnull)) v) { + AuthenticatedStruct no_ptrauth; + AuthenticatedStruct basic_auth; + AuthenticatedStruct explicit_null_auth; + no_ptrauth.ptr=v; + basic_auth.ptr=v; + explicit_null_auth.ptr=v; +} + #endif From c14d38e3a92007a630403156bfae18562bb202f0 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Wed, 3 Jul 2024 10:18:06 -0700 Subject: [PATCH 50/58] [clang] Add __ptrauth macros to ptrauth.h for the arm64e ABI. --- clang/lib/Headers/ptrauth.h | 127 +++++++++++++++++++++ clang/test/Sema/ptrauth-intrinsics-macro.c | 8 +- 2 files changed, 132 insertions(+), 3 deletions(-) diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h index a741aeed20f86..1d93ac8e45aa1 100644 --- a/clang/lib/Headers/ptrauth.h +++ b/clang/lib/Headers/ptrauth.h @@ -109,6 +109,38 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; __ptrauth qualifier; the compiler will perform this check automatically. */ +#if __has_feature(ptrauth_qualifier_authentication_mode) +/* We define these macros including the comma so we can no-op them in the future + without version locking with downstream projects +*/ + #if __has_feature(ptrauth_objc_isa_signs) + #if __has_feature(ptrauth_objc_isa_authenticates) + #define __ptrauth_isa_signing_mode , "sign-and-auth" + #elif __has_feature(ptrauth_objc_isa_strips) + #define __ptrauth_isa_signing_mode , "sign-and-strip" + #endif + #else + #if __has_feature(ptrauth_objc_isa_strips) + #define __ptrauth_isa_signing_mode , "strip" + #endif + #endif +#endif + +#ifdef __ptrauth_isa_signing_mode + #if __has_feature(ptrauth_objc_isa_masking) + #define __ptrauth_isa_masking_mode ",isa-pointer" + #else + #define __ptrauth_isa_masking_mode + #endif +#else + #define __ptrauth_isa_signing_mode + #if __has_feature(ptrauth_objc_isa_masking) + #define __ptrauth_isa_masking_mode "isa-pointer" + #else + #define __ptrauth_isa_masking_mode + #endif +#endif + #if __has_feature(ptrauth_intrinsics) /* Strip the signature from a value without authenticating it. @@ -352,6 +384,78 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; [[clang::ptrauth_vtable_pointer(key, address_discrimination, \ extra_discrimination)]] + +/* Define some standard __ptrauth qualifiers used in the ABI. */ +#define __ptrauth_function_pointer(__typekey) \ + __ptrauth(ptrauth_key_function_pointer,0,__typekey) +#define __ptrauth_return_address \ + __ptrauth(ptrauth_key_return_address,1,0) +#define __ptrauth_block_invocation_pointer \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_block_copy_helper \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_block_destroy_helper \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_block_byref_copy_helper \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_block_byref_destroy_helper \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#if __has_feature(ptrauth_signed_block_descriptors) +#define __ptrauth_block_descriptor_pointer \ + __ptrauth(ptrauth_key_block_descriptor_pointer,1,0xC0BB) +#else +#define __ptrauth_block_descriptor_pointer +#endif +#define __ptrauth_objc_method_list_imp \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#if __has_feature(ptrauth_objc_method_list_pointer) +#define __ptrauth_objc_method_list_pointer \ + __ptrauth(ptrauth_key_method_list_pointer,1,0xC310) +#else +#define __ptrauth_objc_method_list_pointer +#endif +#if !__has_feature(ptrauth_qualifier_authentication_mode) || \ + (__has_feature(ptrauth_objc_isa_signs) || \ + __has_feature(ptrauth_objc_isa_strips)) +#define __ptrauth_isa_discriminator 0x6AE1 +#define __ptrauth_super_discriminator 0xB5AB +#define __ptrauth_objc_isa_pointer \ + __ptrauth(ptrauth_key_objc_isa_pointer, 1, \ + __ptrauth_isa_discriminator __ptrauth_isa_signing_mode \ + __ptrauth_isa_masking_mode) +#define __ptrauth_objc_isa_uintptr \ + __ptrauth_restricted_intptr( \ + ptrauth_key_objc_isa_pointer, 1, \ + __ptrauth_isa_discriminator __ptrauth_isa_signing_mode \ + __ptrauth_isa_masking_mode) +#define __ptrauth_objc_super_pointer \ + __ptrauth(ptrauth_key_objc_super_pointer, 1, \ + __ptrauth_super_discriminator __ptrauth_isa_signing_mode) +#else +#define __ptrauth_objc_isa_pointer +#define __ptrauth_objc_isa_uintptr +#define __ptrauth_objc_super_pointer +#endif +#define __ptrauth_cxx_vtable_pointer \ + __ptrauth(ptrauth_key_cxx_vtable_pointer,0,0) +#define __ptrauth_cxx_vtt_vtable_pointer \ + __ptrauth(ptrauth_key_cxx_vtable_pointer,0,0) +#define __ptrauth_swift_heap_object_destructor \ + __ptrauth(ptrauth_key_function_pointer,1,0xbbbf) + +/* Some situations in the C++ and Swift ABIs use declaration-specific + or type-specific extra discriminators. */ +#define __ptrauth_cxx_virtual_function_pointer(__declkey) \ + __ptrauth(ptrauth_key_function_pointer,1,__declkey) +#define __ptrauth_swift_function_pointer(__typekey) \ + __ptrauth(ptrauth_key_function_pointer,0,__typekey) +#define __ptrauth_swift_class_method_pointer(__declkey) \ + __ptrauth(ptrauth_key_function_pointer,1,__declkey) +#define __ptrauth_swift_protocol_witness_function_pointer(__declkey) \ + __ptrauth(ptrauth_key_function_pointer,1,__declkey) +#define __ptrauth_swift_value_witness_function_pointer(__key) \ + __ptrauth(ptrauth_key_function_pointer,1,__key) + #else #define ptrauth_strip(__value, __key) \ @@ -448,6 +552,29 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; #define ptrauth_cxx_vtable_pointer(key, address_discrimination, \ extra_discrimination...) + +#define __ptrauth_function_pointer(__typekey) +#define __ptrauth_return_address +#define __ptrauth_block_invocation_pointer +#define __ptrauth_block_copy_helper +#define __ptrauth_block_destroy_helper +#define __ptrauth_block_byref_copy_helper +#define __ptrauth_block_byref_destroy_helper +#define __ptrauth_block_descriptor_pointer +#define __ptrauth_objc_method_list_imp +#define __ptrauth_objc_method_list_pointer +#define __ptrauth_objc_isa_pointer +#define __ptrauth_objc_isa_uintptr +#define __ptrauth_objc_super_pointer +#define __ptrauth_cxx_vtable_pointer +#define __ptrauth_cxx_vtt_vtable_pointer +#define __ptrauth_swift_heap_object_destructor +#define __ptrauth_cxx_virtual_function_pointer(__declkey) +#define __ptrauth_swift_function_pointer(__typekey) +#define __ptrauth_swift_class_method_pointer(__declkey) +#define __ptrauth_swift_protocol_witness_function_pointer(__declkey) +#define __ptrauth_swift_value_witness_function_pointer(__key) + #endif /* __has_feature(ptrauth_intrinsics) */ #endif /* __PTRAUTH_H */ diff --git a/clang/test/Sema/ptrauth-intrinsics-macro.c b/clang/test/Sema/ptrauth-intrinsics-macro.c index 033315d4e63c6..87e9d3b8b0710 100644 --- a/clang/test/Sema/ptrauth-intrinsics-macro.c +++ b/clang/test/Sema/ptrauth-intrinsics-macro.c @@ -29,10 +29,8 @@ void test(int *dp, int (*fp)(int), int value) { int t2 = ptrauth_sign_generic_data(dp, 0); (void)t2; - t0 = ptrauth_type_discriminator(int (*)(int)); - fp = ptrauth_auth_function(fp, VALID_CODE_KEY, 0); - void * __ptrauth_function_pointer p0; + void * __ptrauth_function_pointer(0) p0; (void)p0; void * __ptrauth_return_address p1; (void)p1; @@ -62,6 +60,10 @@ void test(int *dp, int (*fp)(int), int value) { (void)p13; void * __ptrauth_swift_value_witness_function_pointer(VALID_CODE_KEY) p14; (void)p14; + void * __ptrauth_objc_method_list_pointer p15; + (void)p15; + void * __ptrauth_block_descriptor_pointer p16; + (void)p16; } void test_string_discriminator(int *dp) { From 9f4fe03d8f8730738cc6d0a07626eef913bd7580 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Thu, 23 May 2024 16:03:59 -0700 Subject: [PATCH 51/58] [AArch64] Add -fbranch-target-identification alias to -mbranch-target-enforce. --- clang/include/clang/Driver/Options.td | 4 ++++ clang/lib/Driver/ToolChains/Clang.cpp | 4 ++++ clang/test/CodeGen/ptrauth-function-attributes.c | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 982d7b9e50fc2..9ab193817c032 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4282,6 +4282,10 @@ defm ptrauth_objc_isa_masking : OptInCC1FFlag<"ptrauth-objc-isa-masking", } let Group = f_Group in { + def fbranch_target_identification : Flag<["-"], "fbranch-target-identification">, + HelpText<"Emit branch target identification instructions for indirect branch destinations">; + def fno_branch_target_identification : Flag<["-"], "fno-branch-target-identification">; + def fptrauth_objc_isa_mode : Joined<["-"], "fptrauth-objc-isa-mode=">, Visibility<[ClangOption, CC1Option, CC1AsOption]>, HelpText<"Authentication mode for ObjC isa field. Full auth if unspecified">, Values<"strip,sign-and-strip,sign-and-auth">; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 4d79b6d794fc7..21e7433d3a6b5 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -1809,6 +1809,10 @@ void Clang::AddAArch64TargetArgs(const ArgList &Args, Args.addOptInFlag(CmdArgs, options::OPT_fptrauth_indirect_gotos, options::OPT_fno_ptrauth_indirect_gotos); + + if (Args.hasArg(options::OPT_fbranch_target_identification, + options::OPT_fno_branch_target_identification, false)) + CmdArgs.push_back("-mbranch-target-enforce"); } void Clang::AddLoongArchTargetArgs(const ArgList &Args, diff --git a/clang/test/CodeGen/ptrauth-function-attributes.c b/clang/test/CodeGen/ptrauth-function-attributes.c index ed9d13faf556e..0f89d43228a11 100644 --- a/clang/test/CodeGen/ptrauth-function-attributes.c +++ b/clang/test/CodeGen/ptrauth-function-attributes.c @@ -16,6 +16,8 @@ // RUN: %clang_cc1 -triple arm64e-apple-ios -fptrauth-auth-traps -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,TRAPS // RUN: %clang_cc1 -triple arm64e-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,OFF +// RUN: %clang_cc1 -triple arm64e-apple-ios -mbranch-target-enforce -emit-llvm %s -o - | FileCheck %s --check-prefixes=ALL,BTI + // ALL: define {{(dso_local )?}}void @test() #0 void test() { } @@ -26,4 +28,7 @@ void test() { // GOTOS: attributes #0 = {{{.*}} "ptrauth-indirect-gotos" {{.*}}} // TRAPS: attributes #0 = {{{.*}} "ptrauth-auth-traps" {{.*}}} +// BTI: !1 = !{i32 8, !"branch-target-enforcement", i32 1} + // OFF-NOT: attributes {{.*}} "ptrauth- +// OFF-NOT: !"branch-target-enforcement" From bb49e428dd08453d152938752c51b55e348a15d1 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Wed, 3 Jul 2024 10:26:35 -0700 Subject: [PATCH 52/58] [clang][Driver] Enable -fptrauth- ABI flags for arm64e-apple-darwin. --- clang/lib/Driver/ToolChains/Darwin.cpp | 99 ++++++++++++++++++- clang/test/Driver/arch-arm64e.c | 61 +++++------- clang/test/Driver/ptrauth-block-descriptor.m | 8 ++ .../Driver/ptrauth-objc-isa-mode-default.c | 32 ++++++ 4 files changed, 165 insertions(+), 35 deletions(-) create mode 100644 clang/test/Driver/ptrauth-block-descriptor.m create mode 100644 clang/test/Driver/ptrauth-objc-isa-mode-default.c diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index bb70d7c4f1539..c92f8b4555ce4 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -1283,13 +1283,48 @@ void DarwinClang::addClangWarningOptions(ArgStringList &CC1Args) const { } } +static bool useObjCIsaPtrauth(const Darwin &D) { + VersionTuple TargetVersion = D.getTripleTargetVersion(); + if (D.isTargetMacCatalyst() || D.TargetEnvironment == Darwin::Simulator) + return true; + switch (D.TargetPlatform) { + case Darwin::IPhoneOS: + case Darwin::TvOS: + return TargetVersion >= VersionTuple(14, 5, 0); + case Darwin::WatchOS: + case Darwin::MacOS: + case Darwin::DriverKit: + return true; + case Darwin::XROS: + return true; + } +} + +static bool useCXXVTablePtrTypeAddressDiscrimination(const Darwin& D) { + VersionTuple TargetVersion = D.getTripleTargetVersion(); + if (D.isTargetMacCatalyst()) + return true; + switch (D.TargetPlatform) { + case Darwin::IPhoneOS: + case Darwin::TvOS: + return TargetVersion >= VersionTuple(15, 0, 0); + case Darwin::WatchOS: + case Darwin::MacOS: + case Darwin::DriverKit: + return true; + case Darwin::XROS: + return true; + } +} + void DarwinClang::addClangTargetOptions( const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args, Action::OffloadKind DeviceOffloadKind) const{ Darwin::addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadKind); - // On arm64e, enable pointer authentication intrinsics. + // On arm64e, enable pointer authentication (for the return address and + // indirect calls), as well as usage of the intrinsics. if (getTriple().isArm64e()) { // The ptrauth ABI version is 0 by default, but can be overridden. static const constexpr unsigned DefaultPtrauthABIVersion = 0; @@ -1342,6 +1377,68 @@ void DarwinClang::addClangTargetOptions( if (!DriverArgs.hasArg(options::OPT_fptrauth_auth_traps, options::OPT_fno_ptrauth_auth_traps)) CC1Args.push_back("-fptrauth-auth-traps"); + + if (!DriverArgs.hasArg(options::OPT_fassume_unique_vtables, + options::OPT_fno_assume_unique_vtables)) + CC1Args.push_back("-fno-assume-unique-vtables"); + + if (useCXXVTablePtrTypeAddressDiscrimination(*this)) { + if (!DriverArgs.hasArg( + options::OPT_fptrauth_vtable_pointer_address_discrimination, + options::OPT_fno_ptrauth_vtable_pointer_address_discrimination)) + CC1Args.push_back("-fptrauth-vtable-pointer-address-discrimination"); + + if (!DriverArgs.hasArg( + options::OPT_fptrauth_vtable_pointer_type_discrimination, + options::OPT_fno_ptrauth_vtable_pointer_type_discrimination)) + CC1Args.push_back("-fptrauth-vtable-pointer-type-discrimination"); + } + + if (!DriverArgs.hasArg(options::OPT_fptrauth_objc_isa, + options::OPT_fno_ptrauth_objc_isa)) { + if (useObjCIsaPtrauth(*this)) + CC1Args.push_back("-fptrauth-objc-isa-mode=sign-and-auth"); + else + CC1Args.push_back("-fptrauth-objc-isa-mode=sign-and-strip"); + } + + if (DriverArgs.hasArg(options::OPT_fapple_kext) || + DriverArgs.hasArg(options::OPT_mkernel) || isTargetDriverKit()) { + if (!DriverArgs.hasArg( + options::OPT_fptrauth_block_descriptor_pointers, + options::OPT_fno_ptrauth_block_descriptor_pointers)) + CC1Args.push_back("-fptrauth-block-descriptor-pointers"); + + if (!DriverArgs.hasArg( + options::OPT_fptrauth_vtable_pointer_address_discrimination, + options::OPT_fno_ptrauth_vtable_pointer_address_discrimination)) + CC1Args.push_back("-fptrauth-vtable-pointer-address-discrimination"); + + if (!DriverArgs.hasArg( + options::OPT_fptrauth_vtable_pointer_type_discrimination, + options::OPT_fno_ptrauth_vtable_pointer_type_discrimination)) + CC1Args.push_back("-fptrauth-vtable-pointer-type-discrimination"); + + if (!DriverArgs.hasArg( + options::OPT_fptrauth_function_pointer_type_discrimination, + options::OPT_fno_ptrauth_function_pointer_type_discrimination)) + CC1Args.push_back("-fptrauth-function-pointer-type-discrimination"); + } + + if (DriverArgs.hasArg(options::OPT_fapple_kext) || + DriverArgs.hasArg(options::OPT_mkernel)) { + // -fbranch-target-identification is a driver flag and isn't honored + // by -cc1. For arm64e, -mbranch-target-enforce is the -cc1 spelling, + // derived from non-arm64e -mbranch-protection=. + // Catch all 3 here anyway. + if (!DriverArgs.hasArg(options::OPT_fbranch_target_identification, + options::OPT_fno_branch_target_identification) && + !DriverArgs.hasArg(options::OPT_mbranch_target_enforce) && + !DriverArgs.hasArg(options::OPT_mbranch_protection_EQ)) { + CC1Args.push_back("-mbranch-target-enforce"); + } + } + } } diff --git a/clang/test/Driver/arch-arm64e.c b/clang/test/Driver/arch-arm64e.c index ad8b6c92bb6bc..6fc7b3a71517c 100644 --- a/clang/test/Driver/arch-arm64e.c +++ b/clang/test/Driver/arch-arm64e.c @@ -1,67 +1,60 @@ // Check that we can manually enable specific ptrauth features. -// RUN: %clang -target arm64-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix NONE +// RUN: %clang -target arm64-apple-ios15 -c %s -### 2>&1 | FileCheck %s --check-prefix NONE // NONE: "-cc1" // NONE-NOT: "-fptrauth-intrinsics" // NONE-NOT: "-fptrauth-calls" // NONE-NOT: "-fptrauth-returns" // NONE-NOT: "-fptrauth-indirect-gotos" // NONE-NOT: "-fptrauth-auth-traps" +// NONE-NOT: "-mbranch-target-enforce" // NONE-NOT: "-fptrauth-soft" -// RUN: %clang -target arm64-apple-darwin -fptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix CALL +// RUN: %clang -target arm64-apple-ios15 -fptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix CALL // CALL: "-cc1"{{.*}} {{.*}} "-fptrauth-calls" -// RUN: %clang -target arm64-apple-darwin -fptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix INTRIN +// RUN: %clang -target arm64-apple-ios15 -fptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix INTRIN // INTRIN: "-cc1"{{.*}} {{.*}} "-fptrauth-intrinsics" -// RUN: %clang -target arm64-apple-darwin -fptrauth-returns -c %s -### 2>&1 | FileCheck %s --check-prefix RETURN +// RUN: %clang -target arm64-apple-ios15 -fptrauth-returns -c %s -### 2>&1 | FileCheck %s --check-prefix RETURN // RETURN: "-cc1"{{.*}} {{.*}} "-fptrauth-returns" -// RUN: %clang -target arm64-apple-darwin -fptrauth-indirect-gotos -c %s -### 2>&1 | FileCheck %s --check-prefix INDGOTO +// RUN: %clang -target arm64-apple-ios15 -fptrauth-indirect-gotos -c %s -### 2>&1 | FileCheck %s --check-prefix INDGOTO // INDGOTO: "-cc1"{{.*}} {{.*}} "-fptrauth-indirect-gotos" -// RUN: %clang -target arm64-apple-darwin -fptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix TRAPS +// RUN: %clang -target arm64-apple-ios15 -fptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix TRAPS // TRAPS: "-cc1"{{.*}} {{.*}} "-fptrauth-auth-traps" -// RUN: %clang -target arm64-apple-darwin -fptrauth-soft -c %s -### 2>&1 | FileCheck %s --check-prefix SOFT +// RUN: %clang -target arm64-apple-ios15 -fbranch-target-identification -c %s -### 2>&1 | FileCheck %s --check-prefix BTI +// BTI: "-cc1"{{.*}} {{.*}} "-mbranch-target-enforce" + +// RUN: %clang -target arm64-apple-ios -fptrauth-soft -c %s -### 2>&1 | FileCheck %s --check-prefix SOFT // SOFT: "-cc1"{{.*}} {{.*}} "-fptrauth-soft" // Check the arm64e defaults. +// isa signing depends on the target OS and is tested elsewhere. -// RUN: %clang -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT -// RUN: %clang -mkernel -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT -// RUN: %clang -fapple-kext -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT -// DEFAULT: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "apple-a12"{{.*}} - +// RUN: %clang -target arm64e-apple-ios15 -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT +// RUN: %clang -mkernel -target arm64e-apple-ios15 -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-KERN +// RUN: %clang -fapple-kext -target arm64e-apple-ios15 -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-KERN +// DEFAULT: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-fno-assume-unique-vtables" "-fptrauth-vtable-pointer-address-discrimination" "-fptrauth-vtable-pointer-type-discrimination" "-fptrauth-objc-isa-mode=sign-and-auth" "-target-cpu" "apple-a12"{{.*}} +// DEFAULT-KERN: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-fno-assume-unique-vtables" "-fptrauth-vtable-pointer-address-discrimination" "-fptrauth-vtable-pointer-type-discrimination" "-fptrauth-objc-isa-mode=sign-and-auth" "-fptrauth-block-descriptor-pointers" "-fptrauth-vtable-pointer-address-discrimination" "-fptrauth-vtable-pointer-type-discrimination" "-fptrauth-function-pointer-type-discrimination" "-mbranch-target-enforce" "-target-cpu" "apple-a12"{{.*}} -// RUN: %clang -target arm64e-apple-ios -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL -// RUN: %clang -mkernel -target arm64e-apple-ios -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL -// RUN: %clang -fapple-kext -target arm64e-apple-ios -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL +// RUN: %clang -target arm64e-apple-ios15 -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-NOCALL +// RUN: %clang -mkernel -target arm64e-apple-ios15 -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-KERN-NOCALL +// RUN: %clang -fapple-kext -target arm64e-apple-ios15 -fno-ptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix DEFAULT-KERN-NOCALL // DEFAULT-NOCALL-NOT: "-fptrauth-calls" -// DEFAULT-NOCALL: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "apple-a12" +// DEFAULT-KERN-NOCALL-NOT: "-fptrauth-calls" +// DEFAULT-NOCALL: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-fno-assume-unique-vtables" "-fptrauth-vtable-pointer-address-discrimination" "-fptrauth-vtable-pointer-type-discrimination" "-fptrauth-objc-isa-mode=sign-and-auth" "-target-cpu" "apple-a12"{{.*}} +// DEFAULT-KERN-NOCALL: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-fno-assume-unique-vtables" "-fptrauth-vtable-pointer-address-discrimination" "-fptrauth-vtable-pointer-type-discrimination" "-fptrauth-objc-isa-mode=sign-and-auth" "-fptrauth-block-descriptor-pointers" "-fptrauth-vtable-pointer-address-discrimination" "-fptrauth-vtable-pointer-type-discrimination" "-fptrauth-function-pointer-type-discrimination" "-mbranch-target-enforce" "-target-cpu" "apple-a12"{{.*}} -// RUN: %clang -target arm64e-apple-ios -fno-ptrauth-returns -c %s -### 2>&1 | FileCheck %s --check-prefix NORET - +// RUN: %clang -target arm64e-apple-ios15 -fno-ptrauth-returns -c %s -### 2>&1 | FileCheck %s --check-prefix NORET // NORET-NOT: "-fptrauth-returns" -// NORET: "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "apple-a12" - -// RUN: %clang -target arm64e-apple-ios -fno-ptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix NOINTRIN -// NOINTRIN: "-fptrauth-returns" +// RUN: %clang -target arm64e-apple-ios15 -fno-ptrauth-intrinsics -c %s -### 2>&1 | FileCheck %s --check-prefix NOINTRIN // NOINTRIN-NOT: "-fptrauth-intrinsics" -// NOINTRIN: "-fptrauth-calls" "-fptrauth-indirect-gotos" "-fptrauth-auth-traps" "-target-cpu" "apple-a12"{{.*}} - - -// RUN: %clang -target arm64e-apple-ios -fno-ptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix NOTRAP -// NOTRAP: "-fptrauth-returns" "-fptrauth-intrinsics" "-fptrauth-calls" "-fptrauth-indirect-gotos" "-target-cpu" "apple-a12" - - -// Check the CPU defaults and overrides. -// RUN: %clang -target arm64e-apple-ios -c %s -### 2>&1 | FileCheck %s --check-prefix APPLE-A12 -// RUN: %clang -target arm64e-apple-ios -mcpu=apple-a13 -c %s -### 2>&1 | FileCheck %s --check-prefix APPLE-A13 -// APPLE-A12: "-cc1"{{.*}} "-target-cpu" "apple-a12" -// APPLE-A13: "-cc1"{{.*}} "-target-cpu" "apple-a13" +// RUN: %clang -target arm64e-apple-ios15 -fno-ptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix NOTRAP +// NOTRAP-NOT: "-fptrauth-auth-traps" diff --git a/clang/test/Driver/ptrauth-block-descriptor.m b/clang/test/Driver/ptrauth-block-descriptor.m new file mode 100644 index 0000000000000..e859ffed0f1a5 --- /dev/null +++ b/clang/test/Driver/ptrauth-block-descriptor.m @@ -0,0 +1,8 @@ +// RUN: %clang -target arm64e-apple-ios %s -mkernel -### 2>&1 | FileCheck %s --check-prefix=YES +// RUN: %clang -target arm64e-apple-ios %s -fapple-kext -### 2>&1 | FileCheck %s --check-prefix=YES +// RUN: %clang -target arm64e-apple-ios %s -fapple-kext -fptrauth-block-descriptor-pointers -### 2>&1 | FileCheck %s --check-prefix=YES +// RUN: %clang -target arm64e-apple-ios %s -### 2>&1 | FileCheck %s --check-prefix=NO +// RUN: %clang -target arm64e-apple-ios %s -mkernel -fno-ptrauth-block-descriptor-pointers -### 2>&1 | FileCheck %s --check-prefix=NO + +// YES: "-cc1"{{.*}} "-fptrauth-block-descriptor-pointers" +// NO-NOT: "-cc1"{{.*}} "-fptrauth-block-descriptor-pointers" diff --git a/clang/test/Driver/ptrauth-objc-isa-mode-default.c b/clang/test/Driver/ptrauth-objc-isa-mode-default.c new file mode 100644 index 0000000000000..88e5b39857c33 --- /dev/null +++ b/clang/test/Driver/ptrauth-objc-isa-mode-default.c @@ -0,0 +1,32 @@ +// Test objc isa signing default based on target. + +// AUTH: "-fptrauth-objc-isa-mode=sign-and-auth" +// STRIP: "-fptrauth-objc-isa-mode=sign-and-strip" + +// Enabled on iOS14.5+, tvOS14.5+. +// RUN: %clang -target arm64e-apple-ios14.5 -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH +// RUN: %clang -target arm64e-apple-tvos14.5 -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH + +// Hence, enabled on iOS15+, tvOS15+. +// RUN: %clang -target arm64e-apple-ios15 -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH +// RUN: %clang -target arm64e-apple-tvos15 -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH + +// Disabled on older OS versions +// RUN: %clang -target arm64e-apple-ios14.4.0 -c %s -### 2>&1 | FileCheck %s --check-prefix STRIP +// RUN: %clang -target arm64e-apple-tvos14.4.0 -c %s -### 2>&1 | FileCheck %s --check-prefix STRIP + +// Enabled on any macOS, watchOS, MacABI. +// RUN: %clang -target arm64e-apple-macos12 -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH +// RUN: %clang -target arm64e-apple-macos11 -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH +// RUN: %clang -target arm64e-apple-ios15-macabi -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH +// RUN: %clang -target arm64e-apple-ios14-macabi -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH +// RUN: %clang -target arm64e-apple-watchos5 -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH + +// Enabled on any simulator targets as well. +// RUN: %clang -target arm64e-apple-ios14-simulator -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH +// RUN: %clang -target arm64e-apple-tvos14-simulator -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH +// RUN: %clang -target arm64e-apple-watchos7-simulator -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH + +// Kernel doesn't care (but is exposed to block isa) +// RUN: %clang -mkernel -target arm64e-apple-ios15 -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH +// RUN: %clang -fapple-kext -target arm64e-apple-ios15 -c %s -### 2>&1 | FileCheck %s --check-prefix AUTH From 9e7c3f7a9f21b0f1566c5cf1f4e2c95afecfb766 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 27 Sep 2021 08:00:00 -0700 Subject: [PATCH 53/58] [clang] Use has_feature ptrauth in Apple Silicon host triple recognition. --- llvm/cmake/config.guess | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/cmake/config.guess b/llvm/cmake/config.guess index 2444ed7f5792b..c37108039496b 100644 --- a/llvm/cmake/config.guess +++ b/llvm/cmake/config.guess @@ -1304,7 +1304,7 @@ EOF (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then - if (echo '#ifdef __PTRAUTH_INTRINSICS__'; echo HAS_AUTH; echo '#endif') | \ + if (echo '#if __has_feature(ptrauth_intrinsics) || defined(__PTRAUTH_INTRINSICS__'; echo HAS_AUTH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep HAS_AUTH >/dev/null then From f0a40acfa244230c6732aa23cd75bcda787e6016 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Tue, 12 Dec 2023 14:28:45 -0800 Subject: [PATCH 54/58] [clang] Annotate Cleanup to support ptrauth vtable diversity. --- clang/lib/CodeGen/EHScopeStack.h | 2 +- llvm/include/llvm/Support/Compiler.h | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/EHScopeStack.h b/clang/lib/CodeGen/EHScopeStack.h index 0c667e80bb6d8..f08f7886963cc 100644 --- a/clang/lib/CodeGen/EHScopeStack.h +++ b/clang/lib/CodeGen/EHScopeStack.h @@ -138,7 +138,7 @@ class EHScopeStack { /// /// Cleanup implementations should generally be declared in an /// anonymous namespace. - class Cleanup { + class LLVM_MOVABLE_POLYMORPHIC_TYPE Cleanup { // Anchor the construction vtable. virtual void anchor(); diff --git a/llvm/include/llvm/Support/Compiler.h b/llvm/include/llvm/Support/Compiler.h index d8e3794babc74..d82fc238bbb24 100644 --- a/llvm/include/llvm/Support/Compiler.h +++ b/llvm/include/llvm/Support/Compiler.h @@ -604,4 +604,23 @@ void AnnotateIgnoreWritesEnd(const char *file, int line); #define LLVM_PREFERRED_TYPE(T) #endif +#ifdef __cplusplus +namespace llvm { + +/// \macro LLVM_MOVABLE_POLYMORPHIC_TYPE +/// Configures vtable pointer authentication for a struct to allow the structs +/// to be moved +#if LLVM_HAS_CPP_ATTRIBUTE(clang::ptrauth_vtable_pointer) && \ + __has_feature(ptrauth_intrinsics) +#define LLVM_MOVABLE_POLYMORPHIC_TYPE \ + [[clang::ptrauth_vtable_pointer(default_key, no_address_discrimination, \ + default_extra_discrimination)]] +#else +#define LLVM_MOVABLE_POLYMORPHIC_TYPE +#endif + +} // End namespace llvm + +#endif // __cplusplus + #endif From 71c9ac490c42343d48242a597abefb7154646191 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Wed, 3 Jul 2024 10:55:37 -0700 Subject: [PATCH 55/58] Implement SoftPointerAuth prototype. --- clang/include/clang/Basic/LangOptions.def | 3 +- .../include/clang/Basic/PointerAuthOptions.h | 4 +- clang/include/clang/Driver/Options.td | 13 +++---- clang/lib/CodeGen/BackendUtil.cpp | 2 +- clang/lib/CodeGen/CGPointerAuth.cpp | 16 +++++--- clang/lib/Frontend/CompilerInvocation.cpp | 37 ++++++++++++++++--- .../Instrumentation/SoftPointerAuth.cpp | 1 - .../Transforms/SoftPointerAuth/intrinsics.ll | 23 +++++++----- .../test/Transforms/SoftPointerAuth/relocs.ll | 8 ++-- 9 files changed, 69 insertions(+), 38 deletions(-) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 954b1bfe32355..f3662cdf4aae6 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -177,12 +177,13 @@ LANGOPT(PointerAuthBlockDescriptorPointers, 1, 0, "enable signed block descripto ENUM_LANGOPT(PointerAuthObjcIsaAuthentication, PointerAuthenticationMode, 2, PointerAuthenticationMode::None, "authentication mode for objc isa") LANGOPT(PointerAuthObjcIsaMasking, 1, 0, "pre- and post-authentication masking for Objective-C isa pointer") -LANGOPT(SoftPointerAuth , 1, 0, "software emulation of pointer authentication") VALUE_LANGOPT(PointerAuthABIVersion, 32, 0, "pointer authentication ABI version") LANGOPT(PointerAuthKernelABIVersion, 1, 0, "controls whether the pointer auth abi version represents a kernel ABI") LANGOPT(PointerAuthABIVersionEncoded, 1, 0, "controls whether the pointer auth abi version should be encoded in the IR") +LANGOPT(SoftPointerAuth , 1, 0, "software emulation of pointer authentication") + LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") LANGOPT(ExperimentalLateParseAttributes, 1, 0, "experimental late parsing of attributes") diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index 68412e41c19eb..cf219b368561c 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -59,6 +59,9 @@ class PointerAuthSchema { CXXVTablePointers = 4, CXXVirtualFunctionPointers = 5, CXXMemberFunctionPointers = 6, + ObjCMethodListPointer = 7, + ObjCIsaPointer = 8, + BlockDescriptorPointers = 9, }; /// Hardware pointer-signing keys in ARM8.3. @@ -155,7 +158,6 @@ class PointerAuthSchema { OtherDiscrimination, ConstantDiscriminatorOrNone, IsIsaPointer, AuthenticatesNullValues) {} - Kind getKind() const { return TheKind; } explicit operator bool() const { return isEnabled(); } diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 9ab193817c032..2ec2da694bf54 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4251,14 +4251,6 @@ defm strict_return : BoolFOption<"strict-return", " of a non-void function as unreachable">, PosFlag>; -let Group = f_Group in { - let Visibility = [ClangOption, CC1Option] in { - def fptrauth_soft : Flag<["-"], "fptrauth-soft">, - HelpText<"Enable software lowering of pointer authentication">; - } - def fno_ptrauth_soft : Flag<["-"], "fno-ptrauth-soft">; -} - let Flags = [TargetSpecific] in { defm ptrauth_intrinsics : OptInCC1FFlag<"ptrauth-intrinsics", "Enable pointer authentication intrinsics">; defm ptrauth_calls : OptInCC1FFlag<"ptrauth-calls", "Enable signing and authentication of all indirect calls">; @@ -4301,6 +4293,11 @@ let Group = f_Group in { HelpText<"Enable Pointer Authentication kernel ABI version">; def fno_ptrauth_kernel_abi_version : Flag<["-"], "fno-ptrauth-kernel-abi-version">, HelpText<"Disable Pointer Authentication kernel ABI versioning">; + + def fptrauth_soft : Flag<["-"], "fptrauth-soft">, + Visibility<[ClangOption, CC1Option, CC1AsOption]>, + HelpText<"Enable software lowering of pointer authentication">; + def fno_ptrauth_soft : Flag<["-"], "fno-ptrauth-soft">; } def fenable_matrix : Flag<["-"], "fenable-matrix">, Group, diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index c2d777df23c58..b6ac85bc4d3ae 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -79,8 +79,8 @@ #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" #include "llvm/Transforms/Instrumentation/PGOInstrumentation.h" #include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h" -#include "llvm/Transforms/Instrumentation/SoftPointerAuth.h" #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" +#include "llvm/Transforms/Instrumentation/SoftPointerAuth.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/ObjCARC.h" #include "llvm/Transforms/Scalar/EarlyCSE.h" diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index 48ed28f2f4f3d..25976b4bf9580 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -850,12 +850,16 @@ CodeGenModule::computeVTPointerAuthentication(const CXXRecordDecl *ThisClass) { return std::nullopt; if (ExplicitKey != VTablePointerAuthenticationAttr::DefaultKey) { - if (ExplicitKey == VTablePointerAuthenticationAttr::ProcessIndependent) - Key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDA; - else { - assert(ExplicitKey == - VTablePointerAuthenticationAttr::ProcessDependent); - Key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDB; + if (Context.getLangOpts().SoftPointerAuth) { + Key = (unsigned)PointerAuthSchema::SoftKey::CXXVirtualFunctionPointers; + } else { + if (ExplicitKey == VTablePointerAuthenticationAttr::ProcessIndependent) + Key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDA; + else { + assert(ExplicitKey == + VTablePointerAuthenticationAttr::ProcessDependent); + Key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDB; + } } } diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 5d32a8eb8f0da..1092a9e497dd1 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1605,14 +1605,19 @@ void CompilerInvocation::setDefaultPointerAuthOptions( if (LangOpts.PointerAuthCalls) { using Key = PointerAuthSchema::SoftKey; using Discrimination = PointerAuthSchema::Discrimination; - Opts.FunctionPointers = PointerAuthSchema( - Key::FunctionPointers, false, Discrimination::None); + Opts.FunctionPointers = + PointerAuthSchema(Key::FunctionPointers, false, + LangOpts.PointerAuthFunctionTypeDiscrimination + ? Discrimination::Type + : Discrimination::None); Opts.CXXVTablePointers = PointerAuthSchema( Key::CXXVTablePointers, LangOpts.PointerAuthVTPtrAddressDiscrimination, LangOpts.PointerAuthVTPtrTypeDiscrimination ? Discrimination::Type : Discrimination::None); + Opts.CXXTypeInfoVTablePointer = PointerAuthSchema( + Key::CXXVTablePointers, false, Discrimination::None); Opts.CXXVTTVTablePointers = PointerAuthSchema( Key::CXXVTablePointers, false, Discrimination::None); Opts.CXXVirtualFunctionPointers = @@ -1627,9 +1632,27 @@ void CompilerInvocation::setDefaultPointerAuthOptions( Key::BlockHelperFunctionPointers, true, Discrimination::None); Opts.BlockByrefHelperFunctionPointers = PointerAuthSchema( Key::BlockHelperFunctionPointers, true, Discrimination::None); + if (LangOpts.PointerAuthBlockDescriptorPointers) + Opts.BlockDescriptorPointers = PointerAuthSchema( + Key::BlockDescriptorPointers, true, Discrimination::Constant, + BlockDescriptorConstantDiscriminator); + Opts.ObjCMethodListFunctionPointers = PointerAuthSchema( Key::ObjCMethodListFunctionPointers, true, Discrimination::None); - + Opts.ObjCMethodListPointer = PointerAuthSchema( + Key::ObjCMethodListPointer, true, Discrimination::Constant, + MethodListPointerConstantDiscriminator); + + auto IsaAuthenticationMode = + LangOpts.getPointerAuthObjcIsaAuthentication(); + if (IsaAuthenticationMode != PointerAuthenticationMode::None) { + Opts.ObjCIsaPointers = PointerAuthSchema( + Key::ObjCIsaPointer, true, IsaAuthenticationMode, + Discrimination::Constant, IsaPointerConstantDiscriminator, true); + Opts.ObjCSuperPointers = PointerAuthSchema( + Key::ObjCIsaPointer, true, IsaAuthenticationMode, + Discrimination::Constant, SuperPointerConstantDiscriminator); + } } Opts.ReturnAddresses = LangOpts.PointerAuthReturns; Opts.IndirectGotos = LangOpts.PointerAuthIndirectGotos; @@ -3748,8 +3771,6 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts, if (Opts.PointerAuthIndirectGotos) GenerateArg(Consumer, OPT_fptrauth_indirect_gotos); - if (Opts.SoftPointerAuth) - GenerateArg(Consumer, OPT_fptrauth_soft); if (Opts.PointerAuthABIVersionEncoded) { GenerateArg(Consumer, OPT_fptrauth_abi_version_EQ, @@ -3758,6 +3779,9 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts, GenerateArg(Consumer, OPT_fptrauth_kernel_abi_version); } + if (Opts.SoftPointerAuth) + GenerateArg(Consumer, OPT_fptrauth_soft); + { StringRef Value; switch (Opts.getPointerAuthObjcIsaAuthentication()) { @@ -3818,7 +3842,6 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, Opts.PointerAuthObjcIsaMasking = Args.hasArg(OPT_fptrauth_objc_isa_masking); Opts.PointerAuthIndirectGotos = Args.hasArg(OPT_fptrauth_indirect_gotos); - Opts.SoftPointerAuth = Args.hasArg(OPT_fptrauth_soft); Opts.PointerAuthABIVersionEncoded = Args.hasArg(OPT_fptrauth_abi_version_EQ) || @@ -3826,6 +3849,8 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, Opts.PointerAuthABIVersion = getLastArgIntValue(Args, OPT_fptrauth_abi_version_EQ, 0, Diags); Opts.PointerAuthKernelABIVersion = Args.hasArg(OPT_fptrauth_kernel_abi_version); + + Opts.SoftPointerAuth = Args.hasArg(OPT_fptrauth_soft); } /// Check if input file kind and language standard are compatible. diff --git a/llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp b/llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp index 7d0cde0daaf15..513261c8b2dc7 100644 --- a/llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp +++ b/llvm/lib/Transforms/Instrumentation/SoftPointerAuth.cpp @@ -48,7 +48,6 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" #include -#include #define DEBUG_TYPE "soft-ptrauth" diff --git a/llvm/test/Transforms/SoftPointerAuth/intrinsics.ll b/llvm/test/Transforms/SoftPointerAuth/intrinsics.ll index 76185ccce66c1..27a5c1cd999e6 100644 --- a/llvm/test/Transforms/SoftPointerAuth/intrinsics.ll +++ b/llvm/test/Transforms/SoftPointerAuth/intrinsics.ll @@ -1,27 +1,30 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt < %s -passes=soft-ptrauth -S | FileCheck %s target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.12.0" +%struct.__block_descriptor = type { i64, i64 } %struct.__block_literal_generic = type { ptr, i32, i32, ptr, ptr } @blockptr = common global ptr null, align 8 define internal void @test1() { +; CHECK-LABEL: @test1( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[BLOCK:%.*]] = load ptr, ptr @blockptr, align 8 +; CHECK-NEXT: [[FNPTR_ADDR:%.*]] = getelementptr inbounds [[STRUCT___BLOCK_LITERAL_GENERIC:%.*]], ptr [[BLOCK]], i32 0, i32 3 +; CHECK-NEXT: [[FNPTR:%.*]] = load ptr, ptr [[FNPTR_ADDR]], align 8 +; CHECK-NEXT: [[DISCRIMINATOR:%.*]] = ptrtoint ptr [[FNPTR_ADDR]] to i64 +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__ptrauth_auth(ptr [[FNPTR]], i32 1, i64 [[DISCRIMINATOR]]) #[[ATTR0:[0-9]+]] +; CHECK-NEXT: call void [[TMP0]](ptr [[FNPTR_ADDR]]) +; CHECK-NEXT: ret void +; entry: %block = load ptr, ptr @blockptr, align 8 %fnptr_addr = getelementptr inbounds %struct.__block_literal_generic, ptr %block, i32 0, i32 3 %fnptr = load ptr, ptr %fnptr_addr, align 8 %discriminator = ptrtoint ptr %fnptr_addr to i64 - call void %fnptr(ptr %block) [ "ptrauth"(i32 1, i64 %discriminator) ] + call void %fnptr(ptr %fnptr_addr) [ "ptrauth"(i32 1, i64 %discriminator) ] ret void } - -; CHECK: define internal void @test1() { -; CHECK: %fnptr_addr = getelementptr inbounds %struct.__block_literal_generic, ptr %block, i32 0, i32 3 -; CHECK-NEXT: %fnptr = load ptr, ptr %fnptr_addr, align 8 -; CHECK-NEXT: %discriminator = ptrtoint ptr %fnptr_addr to i64 -; CHECK-NEXT: [[FNPTR_AUTH:%.*]] = call ptr @__ptrauth_auth(ptr %fnptr, i32 1, i64 %discriminator) [[NOUNWIND:#[0-9]+]] -; CHECK-NEXT: call void [[FNPTR_AUTH]](ptr %block){{$}} - -; CHECK: attributes [[NOUNWIND]] = { nounwind } diff --git a/llvm/test/Transforms/SoftPointerAuth/relocs.ll b/llvm/test/Transforms/SoftPointerAuth/relocs.ll index 05157f82132cc..c57c3cbda87c5 100644 --- a/llvm/test/Transforms/SoftPointerAuth/relocs.ll +++ b/llvm/test/Transforms/SoftPointerAuth/relocs.ll @@ -3,13 +3,13 @@ target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.12.0" -; CHECK-NOT: @test1_reloc -; CHECK: @test1 = internal global { ptr, i32, i32, ptr } { ptr null, i32 1342177280, i32 0, ptr null }, align 8 - @test1_reloc = private constant { ptr, i32, i64, i64 } { ptr @test1_function, i32 1, i64 ptrtoint (ptr getelementptr inbounds ({ ptr, i32, i32, ptr }, ptr @test1, i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth", align 8 @test1 = internal constant { ptr, i32, i32, ptr } { ptr null, i32 1342177280, i32 0, ptr @test1_reloc }, align 8 -define internal void @test1_function(ptr %0) { +; CHECK-NOT: @test1_reloc +; CHECK: @test1 = internal global { ptr, i32, i32, ptr } { ptr null, i32 1342177280, i32 0, ptr null }, align 8 + +define internal void @test1_function(ptr) { entry: ret void } From 5675e40cb1d8332030df61dab69d8d122c91a3bd Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Mon, 18 Mar 2024 23:00:51 -0700 Subject: [PATCH 56/58] [IR] Autoupgrade llvm.ptrauth globals to ptrauth constant. --- clang/lib/CodeGen/CGPointerAuth.cpp | 9 +-- llvm/include/llvm/IR/AutoUpgrade.h | 2 +- llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 9 ++- llvm/lib/IR/AutoUpgrade.cpp | 69 +++++++++++++++++++-- llvm/test/Bitcode/upgrade-ptrauth-global.ll | 27 ++++++++ 5 files changed, 102 insertions(+), 14 deletions(-) create mode 100644 llvm/test/Bitcode/upgrade-ptrauth-global.ll diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index 25976b4bf9580..ab7ca9f35f1a8 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -24,9 +24,10 @@ using namespace clang; using namespace CodeGen; // FIXME: Temporarily allow both ConstantPtrAuth and llvm.ptrauth emission. -static llvm::cl::opt PtrauthEmitWrapperGlobals( - "ptrauth-emit-wrapper-globals", llvm::cl::init(true), llvm::cl::Hidden, - llvm::cl::desc("Emit llvm.ptrauth globals rather than ptrauth Constants")); +// Defined in llvm AutoUpgrade.cpp. +namespace llvm { +extern cl::opt PtrauthEmitWrapperGlobals; +} /// Given a pointer-authentication schema, return a concrete "other" /// discriminator for it. @@ -615,7 +616,7 @@ llvm::Constant * CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, llvm::Constant *StorageAddress, llvm::ConstantInt *OtherDiscriminator) { - if (PtrauthEmitWrapperGlobals) { + if (llvm::PtrauthEmitWrapperGlobals) { // Unique based on the underlying value, not a signing of it. auto stripped = Pointer->stripPointerCasts(); diff --git a/llvm/include/llvm/IR/AutoUpgrade.h b/llvm/include/llvm/IR/AutoUpgrade.h index 97c3e4d7589d7..3f14ef1f6d137 100644 --- a/llvm/include/llvm/IR/AutoUpgrade.h +++ b/llvm/include/llvm/IR/AutoUpgrade.h @@ -55,7 +55,7 @@ namespace llvm { /// This checks for global variables which should be upgraded. If it requires /// upgrading, returns a pointer to the upgraded variable. - GlobalVariable *UpgradeGlobalVariable(GlobalVariable *GV); + bool UpgradeGlobalVariable(GlobalVariable *GV, GlobalVariable *&NewGV); /// This checks for module flags which should be upgraded. It returns true if /// module is modified. diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 05c9697123371..6ba9d3d2e4bfe 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -3874,12 +3874,15 @@ Error BitcodeReader::globalCleanup() { // Look for global variables which need to be renamed. std::vector> UpgradedVariables; - for (GlobalVariable &GV : TheModule->globals()) - if (GlobalVariable *Upgraded = UpgradeGlobalVariable(&GV)) + for (GlobalVariable &GV : TheModule->globals()) { + GlobalVariable *Upgraded = nullptr; + if (UpgradeGlobalVariable(&GV, Upgraded)) UpgradedVariables.emplace_back(&GV, Upgraded); + } for (auto &Pair : UpgradedVariables) { Pair.first->eraseFromParent(); - TheModule->insertGlobalVariable(Pair.second); + if (Pair.second) + TheModule->insertGlobalVariable(Pair.second); } // Force deallocation of memory for these vectors to favor the client that diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp index b1553e664427f..a1ededd20b7d9 100644 --- a/llvm/lib/IR/AutoUpgrade.cpp +++ b/llvm/lib/IR/AutoUpgrade.cpp @@ -45,6 +45,13 @@ using namespace llvm; +namespace llvm { +// FIXME: Temporarily allow both ConstantPtrAuth and llvm.ptrauth emission. +cl::opt PtrauthEmitWrapperGlobals( + "ptrauth-emit-wrapper-globals", llvm::cl::init(true), llvm::cl::Hidden, + llvm::cl::desc("Emit llvm.ptrauth globals rather than ptrauth Constants")); +} // namespace llvm + static cl::opt DisableAutoUpgradeDebugInfo("disable-auto-upgrade-debug-info", cl::desc("Disable autoupgrade of debug info")); @@ -1474,17 +1481,66 @@ bool llvm::UpgradeIntrinsicFunction(Function *F, Function *&NewFn, return Upgraded; } -GlobalVariable *llvm::UpgradeGlobalVariable(GlobalVariable *GV) { +bool llvm::UpgradeGlobalVariable(GlobalVariable *GV, GlobalVariable *&NewGV) { + if (GV->getSection() == "llvm.ptrauth") { + auto &Ctx = GV->getContext(); + + // Temporarily allow opt-out from wrapper global to constant transition. + if (PtrauthEmitWrapperGlobals) + return false; + + if (!GV->hasInitializer()) + return false; + + auto *Init = GV->getInitializer(); + auto *Ty = dyn_cast(GV->getInitializer()->getType()); + if (!Ty) + return false; + + auto *I64Ty = Type::getInt64Ty(Ctx); + auto *I32Ty = Type::getInt32Ty(Ctx); + auto *PtrTy = PointerType::get(Ctx, 0); + // Check that the struct matches its expected shape: + // { ptr, i32, i64, i64 } + if (!Ty->isLayoutIdentical( + StructType::get(Ctx, {PtrTy, I32Ty, I64Ty, I64Ty}))) + return false; + + auto Pointer = cast(Init->getOperand(0)); + Pointer = Pointer->stripPointerCasts(); + + auto *Key = dyn_cast(Init->getOperand(1)); + if (!Key) + return false; + + auto *AddrDiscriminator = cast(Init->getOperand(2)); + if (!isa(AddrDiscriminator) && + !isa(AddrDiscriminator)) + return false; + + auto *Discriminator = dyn_cast(Init->getOperand(3)); + if (!Discriminator) + return false; + + AddrDiscriminator = ConstantExpr::getIntToPtr(AddrDiscriminator, PtrTy); + Discriminator = ConstantInt::get(I64Ty, Discriminator->getZExtValue()); + auto *SP = + ConstantPtrAuth::get(Pointer, Key, Discriminator, AddrDiscriminator); + + GV->replaceAllUsesWith(ConstantExpr::getBitCast(SP, GV->getType())); + return true; + } + if (!(GV->hasName() && (GV->getName() == "llvm.global_ctors" || GV->getName() == "llvm.global_dtors")) || !GV->hasInitializer()) - return nullptr; + return false; ArrayType *ATy = dyn_cast(GV->getValueType()); if (!ATy) - return nullptr; + return false; StructType *STy = dyn_cast(ATy->getElementType()); if (!STy || STy->getNumElements() != 2) - return nullptr; + return false; LLVMContext &C = GV->getContext(); IRBuilder<> IRB(C); @@ -1501,8 +1557,9 @@ GlobalVariable *llvm::UpgradeGlobalVariable(GlobalVariable *GV) { } Constant *NewInit = ConstantArray::get(ArrayType::get(EltTy, N), NewCtors); - return new GlobalVariable(NewInit->getType(), false, GV->getLinkage(), - NewInit, GV->getName()); + NewGV = new GlobalVariable(NewInit->getType(), false, GV->getLinkage(), + NewInit, GV->getName()); + return true; } // Handles upgrading SSE2/AVX2/AVX512BW PSLLDQ intrinsics by converting them diff --git a/llvm/test/Bitcode/upgrade-ptrauth-global.ll b/llvm/test/Bitcode/upgrade-ptrauth-global.ll new file mode 100644 index 0000000000000..086e844304a60 --- /dev/null +++ b/llvm/test/Bitcode/upgrade-ptrauth-global.ll @@ -0,0 +1,27 @@ +; RUN: llvm-as < %s | llvm-dis -ptrauth-emit-wrapper-globals=0 | FileCheck %s +; RUN: llvm-as < %s | llvm-dis -ptrauth-emit-wrapper-globals=1 | FileCheck %s --check-prefix=NOUPGRADE + +@var = global i32 0 + +@var.ptrauth1 = constant { i8*, i32, i64, i64 } { i8* bitcast(i32* @var to i8*), + i32 0, + i64 0, + i64 1234 }, section "llvm.ptrauth" +@var_auth1 = global i32* bitcast({i8*, i32, i64, i64}* @var.ptrauth1 to i32*) +; CHECK: @var_auth1 = global ptr ptrauth (ptr @var, i32 0, i64 1234) + +; NOUPGRADE: @var.ptrauth1 = constant { ptr, i32, i64, i64 } { ptr @var, i32 0, i64 0, i64 1234 }, section "llvm.ptrauth" +; NOUPGRADE: @var_auth1 = global ptr @var.ptrauth1 + + +@dummy_addrdisc = global i64* null + +@var.ptrauth2 = constant { i8*, i32, i64, i64 } { i8* bitcast(i32* @var to i8*), + i32 2, + i64 ptrtoint(i64** @dummy_addrdisc to i64), + i64 5678 }, section "llvm.ptrauth" +@var_auth2 = global i32* bitcast({i8*, i32, i64, i64}* @var.ptrauth2 to i32*) +; CHECK: @var_auth2 = global ptr ptrauth (ptr @var, i32 2, i64 5678, ptr @dummy_addrdisc) + +; NOUPGRADE: @var.ptrauth2 = constant { ptr, i32, i64, i64 } { ptr @var, i32 2, i64 ptrtoint (ptr @dummy_addrdisc to i64), i64 5678 }, section "llvm.ptrauth" +; NOUPGRADE: @var_auth2 = global ptr @var.ptrauth2 From 99ccc72144f805e952b7c91ca244ae9f509d2d70 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Wed, 3 Jul 2024 11:56:50 -0700 Subject: [PATCH 57/58] [clang] Remove StableHash, now replaced by libSupport SipHash. --- clang/include/clang/AST/StableHash.h | 46 ------- clang/lib/AST/ASTContext.cpp | 1 - clang/lib/AST/CMakeLists.txt | 1 - clang/lib/AST/ExprConstant.cpp | 1 - clang/lib/AST/StableHash.cpp | 178 --------------------------- clang/lib/CodeGen/CGPointerAuth.cpp | 1 - 6 files changed, 228 deletions(-) delete mode 100644 clang/include/clang/AST/StableHash.h delete mode 100644 clang/lib/AST/StableHash.cpp diff --git a/clang/include/clang/AST/StableHash.h b/clang/include/clang/AST/StableHash.h deleted file mode 100644 index 5c6f900a37289..0000000000000 --- a/clang/include/clang/AST/StableHash.h +++ /dev/null @@ -1,46 +0,0 @@ -//===--- StableHash.h - An ABI-stable string hash ---------------*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// The interface to an ABI-stable string hash algorithm. -// -//===----------------------------------------------------------------------===// - -#ifndef CLANG_AST_STABLEHASH_H -#define CLANG_AST_STABLEHASH_H - -#include - -namespace llvm { -class StringRef; -} - -namespace clang { -class ASTContext; - -/// Compute a stable 64-bit hash of the given string. -/// -/// The exact algorithm is the little-endian interpretation of the -/// non-doubled (i.e. 64-bit) result of applying a SipHash-2-4 using -/// a specific key value which can be found in the source. -/// -/// By "stable" we mean that the result of this hash algorithm will -/// the same across different compiler versions and target platforms. -uint64_t getStableStringHash(llvm::StringRef string); - -/// Compute a pointer-auth extra discriminator for the given string, -/// suitable for both the blend operation and the __ptrauth qualifier. -/// -/// The result of this hash will be the same across different compiler -/// versions but may vary between targets due to differences in the -/// range of discriminators desired by the target. -uint64_t getPointerAuthStringDiscriminator(const ASTContext &ctx, - llvm::StringRef string); - -} // end namespace clang - -#endif diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 00795cd29dff8..423856d37899a 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -40,7 +40,6 @@ #include "clang/AST/ParentMapContext.h" #include "clang/AST/RawCommentList.h" #include "clang/AST/RecordLayout.h" -#include "clang/AST/StableHash.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtOpenACC.h" #include "clang/AST/TemplateBase.h" diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index 9c3a91e0923af..ceaad8d3c5a86 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -110,7 +110,6 @@ add_clang_library(clangAST RecordLayoutBuilder.cpp ScanfFormatString.cpp SelectorLocationsKind.cpp - StableHash.cpp Stmt.cpp StmtCXX.cpp StmtIterator.cpp diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 3a05bd1609e42..722393e70b3ba 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -48,7 +48,6 @@ #include "clang/AST/OSLog.h" #include "clang/AST/OptionalDiagnostic.h" #include "clang/AST/RecordLayout.h" -#include "clang/AST/StableHash.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" diff --git a/clang/lib/AST/StableHash.cpp b/clang/lib/AST/StableHash.cpp deleted file mode 100644 index f1bdf97ff6b0e..0000000000000 --- a/clang/lib/AST/StableHash.cpp +++ /dev/null @@ -1,178 +0,0 @@ -//===--- StableHash.cpp - Context to hold long-lived AST nodes ------------===// -// -// 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 implements an ABI-stable string hash based on SipHash. -// -//===----------------------------------------------------------------------===// - -#include "clang/AST/StableHash.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/Support/Debug.h" -#include -#include - -using namespace clang; - -#define DEBUG_TYPE "clang-stable-hash" - -#define SIPHASH_ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) - -#define SIPHASH_U8TO64_LE(p) \ - (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \ - ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \ - ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \ - ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56)) - -#define SIPHASH_SIPROUND \ - do { \ - v0 += v1; \ - v1 = SIPHASH_ROTL(v1, 13); \ - v1 ^= v0; \ - v0 = SIPHASH_ROTL(v0, 32); \ - v2 += v3; \ - v3 = SIPHASH_ROTL(v3, 16); \ - v3 ^= v2; \ - v0 += v3; \ - v3 = SIPHASH_ROTL(v3, 21); \ - v3 ^= v0; \ - v2 += v1; \ - v1 = SIPHASH_ROTL(v1, 17); \ - v1 ^= v2; \ - v2 = SIPHASH_ROTL(v2, 32); \ - } while (0) - -template -static inline ResultTy siphash(const uint8_t *in, uint64_t inlen, - const uint8_t (&k)[16]) { - static_assert(sizeof(ResultTy) == 8 || sizeof(ResultTy) == 16, - "result type should be uint64_t or uint128_t"); - uint64_t v0 = 0x736f6d6570736575ULL; - uint64_t v1 = 0x646f72616e646f6dULL; - uint64_t v2 = 0x6c7967656e657261ULL; - uint64_t v3 = 0x7465646279746573ULL; - uint64_t b; - uint64_t k0 = SIPHASH_U8TO64_LE(k); - uint64_t k1 = SIPHASH_U8TO64_LE(k + 8); - uint64_t m; - int i; - const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t)); - const int left = inlen & 7; - b = ((uint64_t)inlen) << 56; - v3 ^= k1; - v2 ^= k0; - v1 ^= k1; - v0 ^= k0; - - if (sizeof(ResultTy) == 16) { - v1 ^= 0xee; - } - - for (; in != end; in += 8) { - m = SIPHASH_U8TO64_LE(in); - v3 ^= m; - - for (i = 0; i < cROUNDS; ++i) - SIPHASH_SIPROUND; - - v0 ^= m; - } - - switch (left) { - case 7: - b |= ((uint64_t)in[6]) << 48; - LLVM_FALLTHROUGH; - case 6: - b |= ((uint64_t)in[5]) << 40; - LLVM_FALLTHROUGH; - case 5: - b |= ((uint64_t)in[4]) << 32; - LLVM_FALLTHROUGH; - case 4: - b |= ((uint64_t)in[3]) << 24; - LLVM_FALLTHROUGH; - case 3: - b |= ((uint64_t)in[2]) << 16; - LLVM_FALLTHROUGH; - case 2: - b |= ((uint64_t)in[1]) << 8; - LLVM_FALLTHROUGH; - case 1: - b |= ((uint64_t)in[0]); - break; - case 0: - break; - } - - v3 ^= b; - - for (i = 0; i < cROUNDS; ++i) - SIPHASH_SIPROUND; - - v0 ^= b; - - if (sizeof(ResultTy) == 8) { - v2 ^= 0xff; - } else { - v2 ^= 0xee; - } - - for (i = 0; i < dROUNDS; ++i) - SIPHASH_SIPROUND; - - b = v0 ^ v1 ^ v2 ^ v3; - - // This mess with the result type would be easier with 'if constexpr'. - - uint64_t firstHalf = b; - if (sizeof(ResultTy) == 8) - return firstHalf; - - v1 ^= 0xdd; - - for (i = 0; i < dROUNDS; ++i) - SIPHASH_SIPROUND; - - b = v0 ^ v1 ^ v2 ^ v3; - uint64_t secondHalf = b; - - return firstHalf - | (ResultTy(secondHalf) << (sizeof(ResultTy) == 8 ? 0 : 64)); -} - -/// Compute an ABI-stable hash of the given string. -uint64_t clang::getStableStringHash(llvm::StringRef string) { - static const uint8_t K[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, 0x4a, 0x79, - 0x6f, 0xec, 0x8b, 0x1b, 0x42, 0x87, 0x81, 0xd4}; - - // The aliasing is fine here because of omnipotent char. - auto data = reinterpret_cast(string.data()); - return siphash<2, 4, uint64_t>(data, string.size(), K); -} - -uint64_t clang::getPointerAuthStringDiscriminator(const ASTContext &ctxt, - llvm::StringRef string) { - auto rawHash = getStableStringHash(string); - - // Don't do anything target-specific yet. - - // Produce a non-zero 16-bit discriminator. - // We use a 16-bit discriminator because ARM64 can efficiently load - // a 16-bit immediate into the high bits of a register without disturbing - // the remainder of the value, which serves as a nice blend operation. - // 16 bits is also sufficiently compact to not inflate a loader relocation. - // We disallow zero to guarantee a different discriminator from the places - // in the ABI that use a constant zero. - uint64_t discriminator = (rawHash % 0xFFFF) + 1; - LLVM_DEBUG( - llvm::dbgs() << "Ptrauth string disc: " << llvm::utostr(discriminator) - << " (0x" << llvm::utohexstr(discriminator) << ")" - << " of: " << string << "\n"); - return discriminator; -} diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index ab7ca9f35f1a8..0caaa0d3e4404 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -14,7 +14,6 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "CGCall.h" -#include "clang/AST/StableHash.h" #include "clang/CodeGen/CodeGenABITypes.h" #include "clang/CodeGen/ConstantInitBuilder.h" #include "llvm/Analysis/ValueTracking.h" From 878d4f709678645f0e3b0f71615bc0fbbe03aa60 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Wed, 3 Jul 2024 11:57:49 -0700 Subject: [PATCH 58/58] [clang] Remove CodeGen:: ptrauth helpers. I couldn't find users after all, including in lldb. --- clang/include/clang/CodeGen/CodeGenABITypes.h | 21 ---------------- clang/lib/CodeGen/CGPointerAuth.cpp | 24 ------------------- 2 files changed, 45 deletions(-) diff --git a/clang/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h index c959bb8747847..e72c8decd47fc 100644 --- a/clang/include/clang/CodeGen/CodeGenABITypes.h +++ b/clang/include/clang/CodeGen/CodeGenABITypes.h @@ -106,27 +106,6 @@ llvm::Type *convertTypeForMemory(CodeGenModule &CGM, QualType T); unsigned getLLVMFieldNumber(CodeGenModule &CGM, const RecordDecl *RD, const FieldDecl *FD); -/// Compute a stable hash of the given string. -/// -/// The exact algorithm is the little-endian interpretation of the -/// non-doubled (i.e. 64-bit) result of applying a SipHash-2-4 using -/// a specific key value which can be found in the source. -uint64_t computeStableStringHash(StringRef string); - -/// Return a declaration discriminator for the given global decl. -uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD); - -/// Return a type discriminator for the given function type. -uint16_t getPointerAuthTypeDiscriminator(CodeGenModule &CGM, - QualType FunctionType); - -/// Return a signed constant pointer. -llvm::Constant *getConstantSignedPointer(CodeGenModule &CGM, - llvm::Constant *pointer, - unsigned key, - llvm::Constant *storageAddress, - llvm::ConstantInt *otherDiscriminator); - /// Given the language and code-generation options that Clang was configured /// with, set the default LLVM IR attributes for a function definition. /// The attributes set here are mostly global target-configuration and diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index 0caaa0d3e4404..20eb3c150954b 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -55,21 +55,6 @@ CodeGenModule::getPointerAuthOtherDiscriminator(const PointerAuthSchema &Schema, llvm_unreachable("bad discrimination kind"); } -uint16_t CodeGen::getPointerAuthTypeDiscriminator(CodeGenModule &CGM, - QualType FunctionType) { - return CGM.getContext().getPointerAuthTypeDiscriminator(FunctionType); -} - -/// Compute an ABI-stable hash of the given string. -uint64_t CodeGen::computeStableStringHash(StringRef string) { - return clang::getStableStringHash(string); -} - -uint16_t CodeGen::getPointerAuthDeclDiscriminator(CodeGenModule &CGM, - GlobalDecl Declaration) { - return CGM.getPointerAuthDeclDiscriminator(Declaration); -} - CGPointerAuthInfo CodeGenModule::EmitPointerAuthInfo(const RecordDecl *RD) { assert(RD && "null RecordDecl passed"); auto *Attr = RD->getAttr(); @@ -702,15 +687,6 @@ llvm::Constant *CodeGenModule::getConstantSignedPointer( OtherDiscriminator); } -llvm::Constant * -CodeGen::getConstantSignedPointer(CodeGenModule &CGM, - llvm::Constant *pointer, unsigned key, - llvm::Constant *storageAddress, - llvm::ConstantInt *otherDiscriminator) { - return CGM.getConstantSignedPointer(pointer, key, storageAddress, - otherDiscriminator); -} - void CodeGenModule::destroyConstantSignedPointerCaches() { destroyCache(ConstantSignedPointersByConstant); destroyCache(ConstantSignedPointersByDecl);