llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-backend-aarch64 Author: Abhay Kanhere (AbhayKanhere) <details> <summary>Changes</summary> ptrauth intrinsic to safely construct relative ptr for swift coroutines. A ptrauth intrinsic for swift co-routine support that allows creation of signed pointer from offset stored at address relative to the pointer. Following C-like pseudo code (ignoring keys,discriminators) explains its operation: let rawptr = PACauth(inputptr); return PACsign( rawptr + signextend64( *(int32*)(rawptr+addend) )) What: Authenticate a signed pointer, load a 32bit value at offset 'addend' from pointer, add this value to pointer, sign this new pointer. builtin: __builtin_ptrauth_auth_load_relative_and_sign intrinsic: ptrauth_auth_resign_load_relative Also this PR removes ptr_auth intrinsics noop form (!__has_feature(ptrauth_intrinsics) path) --- Patch is 40.37 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/142047.diff 10 Files Affected: - (modified) clang/include/clang/Basic/Builtins.td (+6) - (modified) clang/lib/CodeGen/CGBuiltin.cpp (+5-1) - (modified) clang/lib/Headers/ptrauth.h (+25-72) - (modified) llvm/include/llvm/IR/Intrinsics.td (+13) - (modified) llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp (+70-4) - (modified) llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp (+27-10) - (modified) llvm/lib/Target/AArch64/AArch64InstrInfo.td (+20) - (modified) llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp (+39) - (modified) llvm/lib/Transforms/Utils/Local.cpp (+1) - (added) llvm/test/CodeGen/AArch64/ptrauth-intrinsic-auth-resign-relative-load.ll (+516) ``````````diff diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index e43b87fb3c131..44bbe651891a7 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -4595,6 +4595,12 @@ def PtrauthAuthAndResign : Builtin { let Prototype = "void*(void*,int,void*,int,void*)"; } +def PtrauthAuthLoadRelativeAndSign : Builtin { + let Spellings = ["__builtin_ptrauth_auth_load_relative_and_sign"]; + let Attributes = [CustomTypeChecking, NoThrow]; + let Prototype = "void*(void*,int,void*,int,void*,ptrdiff_t)"; +} + def PtrauthAuth : Builtin { let Spellings = ["__builtin_ptrauth_auth"]; let Attributes = [CustomTypeChecking, NoThrow]; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 89b321090f2d8..fa1d778db7caa 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -5543,12 +5543,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__builtin_ptrauth_auth: case Builtin::BI__builtin_ptrauth_auth_and_resign: + case Builtin::BI__builtin_ptrauth_auth_load_relative_and_sign: case Builtin::BI__builtin_ptrauth_blend_discriminator: case Builtin::BI__builtin_ptrauth_sign_generic_data: case Builtin::BI__builtin_ptrauth_sign_unauthenticated: case Builtin::BI__builtin_ptrauth_strip: { // Emit the arguments. - SmallVector<llvm::Value *, 5> Args; + SmallVector<llvm::Value *, 6> Args; for (auto argExpr : E->arguments()) Args.push_back(EmitScalarExpr(argExpr)); @@ -5559,6 +5560,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, switch (BuiltinID) { case Builtin::BI__builtin_ptrauth_auth_and_resign: + case Builtin::BI__builtin_ptrauth_auth_load_relative_and_sign: if (Args[4]->getType()->isPointerTy()) Args[4] = Builder.CreatePtrToInt(Args[4], IntPtrTy); [[fallthrough]]; @@ -5586,6 +5588,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, return Intrinsic::ptrauth_auth; case Builtin::BI__builtin_ptrauth_auth_and_resign: return Intrinsic::ptrauth_resign; + case Builtin::BI__builtin_ptrauth_auth_load_relative_and_sign: + return Intrinsic::ptrauth_resign_load_relative; case Builtin::BI__builtin_ptrauth_blend_discriminator: return Intrinsic::ptrauth_blend; case Builtin::BI__builtin_ptrauth_sign_generic_data: diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h index d489a67c533d4..cf47b7ae959b3 100644 --- a/clang/lib/Headers/ptrauth.h +++ b/clang/lib/Headers/ptrauth.h @@ -165,6 +165,31 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; __builtin_ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, \ __new_data) +/* Authenticate a pointer using one scheme, load 32bit value at offset addend + from the pointer, and add this value to the pointer, sign using specified + scheme. + + If the result is subsequently authenticated using the new scheme, that + authentication is guaranteed to fail if and only if the initial + authentication failed. + + The value must be an expression of pointer type. + The key must be a constant expression of type ptrauth_key. + The extra data must be an expression of pointer or integer type; + if an integer, it will be coerced to ptrauth_extra_data_t. + The addend must be an immediate ptrdiff_t value. + The result will have the same type as the original value. + + This operation is guaranteed to not leave the intermediate value + available for attack before it is re-signed. + + Do not pass a null pointer to this function. A null pointer + will not successfully authenticate. */ +#define ptrauth_auth_load_relative_and_sign(__value, __old_key, __old_data, \ + __new_key, __new_data, __addend) \ + __builtin_ptrauth_auth_load_relative_and_sign( \ + __value, __old_key, __old_data, __new_key, __new_data, __addend) + /* Authenticate a pointer using one scheme and resign it as a C function pointer. @@ -259,78 +284,6 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; /* The value is ptrauth_string_discriminator("init_fini") */ #define __ptrauth_init_fini_discriminator 0xd9d4 -#else - -#define ptrauth_strip(__value, __key) \ - ({ \ - (void)__key; \ - __value; \ - }) - -#define ptrauth_blend_discriminator(__pointer, __integer) \ - ({ \ - (void)__pointer; \ - (void)__integer; \ - ((ptrauth_extra_data_t)0); \ - }) - -#define ptrauth_sign_constant(__value, __key, __data) \ - ({ \ - (void)__key; \ - (void)__data; \ - __value; \ - }) - -#define ptrauth_sign_unauthenticated(__value, __key, __data) \ - ({ \ - (void)__key; \ - (void)__data; \ - __value; \ - }) - -#define ptrauth_auth_and_resign(__value, __old_key, __old_data, __new_key, \ - __new_data) \ - ({ \ - (void)__old_key; \ - (void)__old_data; \ - (void)__new_key; \ - (void)__new_data; \ - __value; \ - }) - -#define ptrauth_auth_function(__value, __old_key, __old_data) \ - ({ \ - (void)__old_key; \ - (void)__old_data; \ - __value; \ - }) - -#define ptrauth_auth_data(__value, __old_key, __old_data) \ - ({ \ - (void)__old_key; \ - (void)__old_data; \ - __value; \ - }) - -#define ptrauth_string_discriminator(__string) \ - ({ \ - (void)__string; \ - ((ptrauth_extra_data_t)0); \ - }) - -#define ptrauth_type_discriminator(__type) ((ptrauth_extra_data_t)0) - -#define ptrauth_sign_generic_data(__value, __data) \ - ({ \ - (void)__value; \ - (void)__data; \ - ((ptrauth_generic_signature_t)0); \ - }) - - -#define ptrauth_cxx_vtable_pointer(key, address_discrimination, \ - extra_discrimination...) - #endif /* __has_feature(ptrauth_intrinsics) */ #endif /* __PTRAUTH_H */ diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index d3899056bc240..bcf6fb0f9bbd6 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -2799,6 +2799,19 @@ def int_ptrauth_resign : Intrinsic<[llvm_i64_ty], [IntrNoMem, ImmArg<ArgIndex<1>>, ImmArg<ArgIndex<3>>]>; +// Authenticate a signed pointer, load 32bit value at offset from pointer, add +// both, and sign it. The second (key) and third (discriminator) arguments +// specify the signing schema used for authenticating. The fourth and fifth +// arguments specify the schema used for signing. The sixth argument is addend +// added to pointer to load the relative offset. The signature must be valid. +// This is a combined form of int_ptrauth_resign for relative pointers +def int_ptrauth_resign_load_relative + : Intrinsic<[llvm_i64_ty], + [llvm_i64_ty, llvm_i32_ty, llvm_i64_ty, llvm_i32_ty, + llvm_i64_ty, llvm_i64_ty], + [IntrReadMem, ImmArg<ArgIndex<1>>, ImmArg<ArgIndex<3>>, + ImmArg<ArgIndex<5>>]>; + // Strip the embedded signature out of a signed pointer. // The second argument specifies the key. // This behaves like @llvm.ptrauth.auth, but doesn't require the signature to diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index e9ccc35f34612..c92ecab17194b 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -167,7 +167,7 @@ class AArch64AsmPrinter : public AsmPrinter { // Check authenticated LR before tail calling. void emitPtrauthTailCallHardening(const MachineInstr *TC); - // Emit the sequence for AUT or AUTPAC. + // Emit the sequence for AUT, AUTPAC, or AUTRELLOADPAC. void emitPtrauthAuthResign(const MachineInstr *MI); // Emit the sequence to compute the discriminator. @@ -2065,8 +2065,9 @@ void AArch64AsmPrinter::emitPtrauthTailCallHardening(const MachineInstr *TC) { } void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) { - const bool IsAUTPAC = MI->getOpcode() == AArch64::AUTPAC; - + const bool IsAUTPAC = MI->getOpcode() == AArch64::AUTPAC || + MI->getOpcode() == AArch64::AUTRELLOADPAC; + const bool HasLoad = MI->getOpcode() == AArch64::AUTRELLOADPAC; // We expand AUT/AUTPAC into a sequence of the form // // ; authenticate x16 @@ -2141,11 +2142,75 @@ void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr *MI) { } // We already emitted unchecked and checked-but-non-trapping AUTs. - // That left us with trapping AUTs, and AUTPACs. + // That left us with trapping AUTs, and AUTPA/AUTRELLOADPACs. // Trapping AUTs don't need PAC: we're done. if (!IsAUTPAC) return; + if (HasLoad) { + int64_t Addend = MI->getOperand(6).getImm(); + // incoming rawpointer in X16, X17 is not live at this point. + // LDSRWpre x17, x16, simm9 ; note: x16+simm9 used later. + if (isInt<9>(Addend)) { + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRSWpre) + .addReg(AArch64::X16) + .addReg(AArch64::X17) + .addReg(AArch64::X16) + .addImm(/*simm9:*/ Addend)); + } else { + // x16 = x16 + Addend computation has 2 variants + if (isUInt<24>(Addend)) { + // variant 1: add x16, x16, Addend >> shift12 ls shift12 + // This can take upto 2 instructions. + for (int BitPos = 0; BitPos != 24 && (Addend >> BitPos); BitPos += 12) { + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXri) + .addReg(AArch64::X16) + .addReg(AArch64::X16) + .addImm((Addend >> BitPos) & 0xfff) + .addImm(AArch64_AM::getShifterImm( + AArch64_AM::LSL, BitPos))); + } + } else { + // variant 2: accumulate constant in X17 16 bits at a time, and add to + // X16 This can take 2-5 instructions. + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::MOVZXi) + .addReg(AArch64::X17) + .addImm(Addend & 0xffff) + .addImm(AArch64_AM::getShifterImm( + AArch64_AM::LSL, 0))); + + for (int Offset = 16; Offset < 64; Offset += 16) { + uint16_t Fragment = static_cast<uint16_t>(Addend >> Offset); + if (!Fragment) + continue; + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::MOVKXi) + .addReg(AArch64::X17) + .addReg(AArch64::X17) + .addImm(Fragment) + .addImm(/*shift:*/ Offset)); + } + // addx x16, x16, x17 + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXrs) + .addReg(AArch64::X16) + .addReg(AArch64::X16) + .addReg(AArch64::X17) + .addImm(0)); + } + // ldrsw x17,x16(0) + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRSWui) + .addReg(AArch64::X17) + .addReg(AArch64::X16) + .addImm(0)); + } + // addx x16, x16, x17 + EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXrs) + .addReg(AArch64::X16) + .addReg(AArch64::X16) + .addReg(AArch64::X17) + .addImm(0)); + + } /* HasLoad == true */ + auto PACKey = (AArch64PACKey::ID)MI->getOperand(3).getImm(); uint64_t PACDisc = MI->getOperand(4).getImm(); unsigned PACAddrDisc = MI->getOperand(5).getReg(); @@ -2863,6 +2928,7 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { case AArch64::AUT: case AArch64::AUTPAC: + case AArch64::AUTRELLOADPAC: emitPtrauthAuthResign(MI); return; diff --git a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp index 2eb8c6008db0f..67559e8705b4c 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelDAGToDAG.cpp @@ -1544,12 +1544,15 @@ void AArch64DAGToDAGISel::SelectPtrauthAuth(SDNode *N) { 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); + // IntrinsicID is operand #0, if W_CHAIN it is #1 + int OffsetBase = N->getOpcode() == ISD::INTRINSIC_W_CHAIN ? 1 : 0; + SDValue Val = N->getOperand(OffsetBase + 1); + SDValue AUTKey = N->getOperand(OffsetBase + 2); + SDValue AUTDisc = N->getOperand(OffsetBase + 3); + SDValue PACKey = N->getOperand(OffsetBase + 4); + SDValue PACDisc = N->getOperand(OffsetBase + 5); + uint32_t IntNum = N->getConstantOperandVal(OffsetBase + 0); + bool HasLoad = IntNum == Intrinsic::ptrauth_resign_load_relative; unsigned AUTKeyC = cast<ConstantSDNode>(AUTKey)->getZExtValue(); unsigned PACKeyC = cast<ConstantSDNode>(PACKey)->getZExtValue(); @@ -1568,11 +1571,22 @@ void AArch64DAGToDAGISel::SelectPtrauthResign(SDNode *N) { SDValue X16Copy = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, AArch64::X16, Val, SDValue()); - SDValue Ops[] = {AUTKey, AUTConstDisc, AUTAddrDisc, PACKey, - PACConstDisc, PACAddrDisc, X16Copy.getValue(1)}; + if (HasLoad) { + SDValue Addend = N->getOperand(OffsetBase + 6); + SDValue Ops[] = {AUTKey, AUTConstDisc, AUTAddrDisc, + PACKey, PACConstDisc, PACAddrDisc, + Addend, X16Copy.getValue(1)}; - SDNode *AUTPAC = CurDAG->getMachineNode(AArch64::AUTPAC, DL, MVT::i64, Ops); - ReplaceNode(N, AUTPAC); + SDNode *AUTRELLOADPAC = CurDAG->getMachineNode(AArch64::AUTRELLOADPAC, DL, + MVT::i64, MVT::Other, Ops); + ReplaceNode(N, AUTRELLOADPAC); + } else { + SDValue Ops[] = {AUTKey, AUTConstDisc, AUTAddrDisc, PACKey, + PACConstDisc, PACAddrDisc, X16Copy.getValue(1)}; + + SDNode *AUTPAC = CurDAG->getMachineNode(AArch64::AUTPAC, DL, MVT::i64, Ops); + ReplaceNode(N, AUTPAC); + } } bool AArch64DAGToDAGISel::tryIndexedLoad(SDNode *N) { @@ -5634,6 +5648,9 @@ void AArch64DAGToDAGISel::Select(SDNode *Node) { {AArch64::BF2CVT_2ZZ_BtoH, AArch64::F2CVT_2ZZ_BtoH})) SelectCVTIntrinsicFP8(Node, 2, Opc); return; + case Intrinsic::ptrauth_resign_load_relative: + SelectPtrauthResign(Node); + return; } } break; case ISD::INTRINSIC_WO_CHAIN: { diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index 72445172059bf..a0f5cff5c77d0 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -2124,6 +2124,26 @@ let Predicates = [HasPAuth] in { let Uses = [X16]; } + // Similiar to AUTPAC, except a 32bit value is loaded at Addend offset from + // pointer and this value is added to the pointer before signing. This + // directly manipulates x16/x17, which are the only registers the OS + // guarantees are safe to use for sensitive operations. + def AUTRELLOADPAC + : Pseudo<(outs), + (ins i32imm:$AUTKey, i64imm:$AUTDisc, GPR64:$AUTAddrDisc, + i32imm:$PACKey, i64imm:$PACDisc, GPR64noip:$PACAddrDisc, + i64imm:$Addend), + []>, + Sched<[WriteI, ReadI]> { + let isCodeGenOnly = 1; + let hasSideEffects = 1; + let mayStore = 0; + let mayLoad = 1; + let Size = 84; + let Defs = [X16, X17, NZCV]; + let Uses = [X16]; + } + // Materialize a signed global address, with adrp+add and PAC. def MOVaddrPAC : Pseudo<(outs), (ins i64imm:$Addr, i32imm:$Key, diff --git a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp index e0c693bff3c0a..4e4b34bc348c9 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp @@ -6640,6 +6640,45 @@ bool AArch64InstructionSelector::selectIntrinsicWithSideEffects( constrainSelectedInstRegOperands(*Memset, TII, TRI, RBI); break; } + case Intrinsic::ptrauth_resign_load_relative: { + 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(); + int64_t Addend = I.getOperand(7).getImm(); + + Register AUTAddrDisc = AUTDisc; + uint16_t AUTConstDiscC = 0; + std::tie(AUTConstDiscC, AUTAddrDisc) = + extractPtrauthBlendDiscriminators(AUTDisc, MRI); + + Register PACAddrDisc = PACDisc; + uint16_t PACConstDiscC = 0; + st... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/142047 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits