diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index 5b398d3b75f59..28e63c7065b99 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3048,6 +3048,34 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) { Intrinsic::getOrInsertDeclaration(II->getModule(), NewIntrin); return CallInst::Create(NewFn, CallArgs); } + case Intrinsic::ptrauth_sign: { + // Replace auth+sign with a single resign intrinsic. + // When auth and sign operations are performed separately, later compiler + // passes may spill intermediate result to memory as a raw, unprotected + // pointer, which makes it possible for an attacker to replace it under + // PAuth threat model. On the other hand, resign intrinsic is not expanded + // until AsmPrinter, when it is emitted as a contiguous, non-attackable + // sequence of instructions. + Value *Ptr = II->getArgOperand(0); + Value *SignKey = II->getArgOperand(1); + Value *SignDisc = II->getArgOperand(2); + + const auto *CI = dyn_cast(Ptr); + if (!CI || CI->getIntrinsicID() != Intrinsic::ptrauth_auth) + break; + + Value *BasePtr = CI->getOperand(0); + Value *AuthKey = CI->getArgOperand(1); + Value *AuthDisc = CI->getArgOperand(2); + + // Not replacing auth+sign using the same schema with nop, as auth+sign + // pair traps on authentication failure. + + Function *NewFn = Intrinsic::getOrInsertDeclaration( + II->getModule(), Intrinsic::ptrauth_resign); + return CallInst::Create(NewFn, + {BasePtr, AuthKey, AuthDisc, SignKey, SignDisc}); + } case Intrinsic::arm_neon_vtbl1: case Intrinsic::aarch64_neon_tbl1: if (Value *V = simplifyNeonTbl1(*II, Builder)) diff --git a/llvm/test/Transforms/InstCombine/ptrauth-intrinsics.ll b/llvm/test/Transforms/InstCombine/ptrauth-intrinsics.ll index 208e162ac9416..15dd8bff19f2c 100644 --- a/llvm/test/Transforms/InstCombine/ptrauth-intrinsics.ll +++ b/llvm/test/Transforms/InstCombine/ptrauth-intrinsics.ll @@ -160,6 +160,64 @@ define i64 @test_ptrauth_resign_ptrauth_constant(ptr %p) { ret i64 %authed } +define i64 @test_ptrauth_auth_sign_same_schema(ptr %p) { +; CHECK-LABEL: @test_ptrauth_auth_sign_same_schema( +; CHECK-NEXT: [[P_INT:%.*]] = ptrtoint ptr [[P:%.*]] to i64 +; CHECK-NEXT: [[RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[P_INT]], i32 1, i64 1234, i32 1, i64 1234) +; CHECK-NEXT: ret i64 [[RESIGNED]] +; + %p.int = ptrtoint ptr %p to i64 + %authed = call i64 @llvm.ptrauth.auth(i64 %p.int, i32 1, i64 1234) + %resigned = call i64 @llvm.ptrauth.sign(i64 %authed, i32 1, i64 1234) + ret i64 %resigned +} + +define i64 @test_ptrauth_auth_sign_opaque_disc_same_schema(ptr %p, i64 %disc) { +; CHECK-LABEL: @test_ptrauth_auth_sign_opaque_disc_same_schema( +; CHECK-NEXT: [[P_INT:%.*]] = ptrtoint ptr [[P:%.*]] to i64 +; CHECK-NEXT: [[RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[P_INT]], i32 1, i64 [[DISC:%.*]], i32 1, i64 [[DISC]]) +; CHECK-NEXT: ret i64 [[RESIGNED]] +; + %p.int = ptrtoint ptr %p to i64 + %authed = call i64 @llvm.ptrauth.auth(i64 %p.int, i32 1, i64 %disc) + %resigned = call i64 @llvm.ptrauth.sign(i64 %authed, i32 1, i64 %disc) + ret i64 %resigned +} + +define i64 @test_ptrauth_auth_sign_different_disc(ptr %p, i64 %disc) { +; CHECK-LABEL: @test_ptrauth_auth_sign_different_disc( +; CHECK-NEXT: [[P_INT:%.*]] = ptrtoint ptr [[P:%.*]] to i64 +; CHECK-NEXT: [[RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[P_INT]], i32 1, i64 [[DISC:%.*]], i32 1, i64 1234) +; CHECK-NEXT: ret i64 [[RESIGNED]] +; + %p.int = ptrtoint ptr %p to i64 + %authed = call i64 @llvm.ptrauth.auth(i64 %p.int, i32 1, i64 %disc) + %resigned = call i64 @llvm.ptrauth.sign(i64 %authed, i32 1, i64 1234) + ret i64 %resigned +} + +define i64 @test_ptrauth_auth_sign_different_key(ptr %p) { +; CHECK-LABEL: @test_ptrauth_auth_sign_different_key( +; CHECK-NEXT: [[P_INT:%.*]] = ptrtoint ptr [[P:%.*]] to i64 +; CHECK-NEXT: [[RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[P_INT]], i32 0, i64 1234, i32 1, i64 1234) +; CHECK-NEXT: ret i64 [[RESIGNED]] +; + %p.int = ptrtoint ptr %p to i64 + %authed = call i64 @llvm.ptrauth.auth(i64 %p.int, i32 0, i64 1234) + %resigned = call i64 @llvm.ptrauth.sign(i64 %authed, i32 1, i64 1234) + ret i64 %resigned +} + +define i64 @test_ptrauth_sign_nonauth_nonconst_disc(i64 %disc) { +; CHECK-LABEL: @test_ptrauth_sign_nonauth_nonconst_disc( +; CHECK-NEXT: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @foo to i64), i32 1, i64 [[DISC:%.*]]) +; CHECK-NEXT: ret i64 [[SIGNED]] +; + %foo.int = ptrtoint ptr @foo to i64 + %signed = call i64 @llvm.ptrauth.sign(i64 %foo.int, i32 1, i64 %disc) + ret i64 %signed +} + 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)