-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[PAC] Implement function pointer re-signing #98847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
938d8f0
a147bce
4110867
d6a54ad
868ec8a
aa7e4ec
227382d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
#include "CodeGenModule.h" | ||
#include "clang/CodeGen/CodeGenABITypes.h" | ||
#include "clang/CodeGen/ConstantInitBuilder.h" | ||
#include "llvm/Analysis/ValueTracking.h" | ||
#include "llvm/Support/SipHash.h" | ||
|
||
using namespace clang; | ||
|
@@ -165,6 +166,128 @@ CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForType(QualType T) { | |
return ::getPointerAuthInfoForType(*this, T); | ||
} | ||
|
||
static bool isZeroConstant(const llvm::Value *Value) { | ||
if (const auto *CI = dyn_cast<llvm::ConstantInt>(Value)) | ||
return CI->isZero(); | ||
return false; | ||
} | ||
|
||
static bool equalAuthPolicies(const CGPointerAuthInfo &Left, | ||
const CGPointerAuthInfo &Right) { | ||
assert((Left.isSigned() || Right.isSigned()) && | ||
"shouldn't be called if neither is signed"); | ||
if (Left.isSigned() != Right.isSigned()) | ||
return false; | ||
return Left.getKey() == Right.getKey() && | ||
Left.getAuthenticationMode() == Right.getAuthenticationMode(); | ||
} | ||
|
||
// Return the discriminator or return zero if the discriminator is null. | ||
static llvm::Value *getDiscriminatorOrZero(const CGPointerAuthInfo &Info, | ||
CGBuilderTy &Builder) { | ||
llvm::Value *Discriminator = Info.getDiscriminator(); | ||
return Discriminator ? Discriminator : Builder.getSize(0); | ||
} | ||
|
||
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) { | ||
llvm::Value *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 = getDiscriminatorOrZero(CurAuth, Builder); | ||
llvm::Value *NewDiscriminator = getDiscriminatorOrZero(NewAuth, Builder); | ||
|
||
// 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::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<llvm::PointerType>(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)) { | ||
const llvm::Value *CurD = CurAuthInfo.getDiscriminator(); | ||
const llvm::Value *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, | ||
|
@@ -351,3 +474,110 @@ 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::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(getBasePointer(), getElementType(), getAlignment(), | ||
isKnownNonNull()); | ||
|
||
assert(ElementType && "Effective type has to be set"); | ||
assert(!Offset && "unexpected non-null offset"); | ||
|
||
// 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()) | ||
ahatanak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Val = getBasePointer(); | ||
else | ||
Val = CGF.emitPointerAuthResign(getBasePointer(), QualType(), CurInfo, | ||
NewInfo, isKnownNonNull()); | ||
|
||
Val = CGF.Builder.CreateBitCast(Val, getType()); | ||
return Address(Val, getElementType(), getAlignment(), NewInfo, | ||
/*Offset=*/nullptr, isKnownNonNull()); | ||
} | ||
|
||
llvm::Value *LValue::getPointer(CodeGenFunction &CGF) const { | ||
assert(isSimple()); | ||
return emitResignedPointer(getType(), CGF); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose it might be worth to have a "short-path" for non-signed pointers. Most code is compile w/o pauth, but we'll always have overhead of calling these pauth-related functions (while having the same observable behavior as previously for non-pauth case). The same applies to Anyway, I suggest to leave this out of scope of the patch and do performance testing with https://llvm-compile-time-tracker.com/ after merging the PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we can check whether there is a noticeable regression in compile time. |
||
} | ||
|
||
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; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.