Skip to content

Commit f6b06b4

Browse files
ahatanakahmedbougacharjmccall
authored
[PAC] Implement function pointer re-signing (#98847)
Re-signing occurs when function type discrimination is enabled and a function pointer is converted to another function pointer type that requires signing using a different discriminator. A function pointer is re-signed using discriminator zero when it's converted to a pointer to a non-function type such as `void*`. --------- Co-authored-by: Ahmed Bougacha <[email protected]> Co-authored-by: John McCall <[email protected]>
1 parent 9711f6b commit f6b06b4

11 files changed

+513
-42
lines changed

clang/lib/CodeGen/Address.h

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef LLVM_CLANG_LIB_CODEGEN_ADDRESS_H
1515
#define LLVM_CLANG_LIB_CODEGEN_ADDRESS_H
1616

17+
#include "CGPointerAuthInfo.h"
1718
#include "clang/AST/CharUnits.h"
1819
#include "clang/AST/Type.h"
1920
#include "llvm/ADT/PointerIntPair.h"
@@ -108,6 +109,22 @@ class RawAddress {
108109

109110
/// Like RawAddress, an abstract representation of an aligned address, but the
110111
/// pointer contained in this class is possibly signed.
112+
///
113+
/// This is designed to be an IR-level abstraction, carrying just the
114+
/// information necessary to perform IR operations on an address like loads and
115+
/// stores. In particular, it doesn't carry C type information or allow the
116+
/// representation of things like bit-fields; clients working at that level
117+
/// should generally be using `LValue`.
118+
///
119+
/// An address may be either *raw*, meaning that it's an ordinary machine
120+
/// pointer, or *signed*, meaning that the pointer carries an embedded
121+
/// pointer-authentication signature. Representing signed pointers directly in
122+
/// this abstraction allows the authentication to be delayed as long as possible
123+
/// without forcing IRGen to use totally different code paths for signed and
124+
/// unsigned values or to separately propagate signature information through
125+
/// every API that manipulates addresses. Pointer arithmetic on signed addresses
126+
/// (e.g. drilling down to a struct field) is accumulated into a separate offset
127+
/// which is applied when the address is finally accessed.
111128
class Address {
112129
friend class CGBuilderTy;
113130

@@ -121,7 +138,11 @@ class Address {
121138

122139
CharUnits Alignment;
123140

124-
/// Offset from the base pointer.
141+
/// The ptrauth information needed to authenticate the base pointer.
142+
CGPointerAuthInfo PtrAuthInfo;
143+
144+
/// Offset from the base pointer. This is non-null only when the base
145+
/// pointer is signed.
125146
llvm::Value *Offset = nullptr;
126147

127148
llvm::Value *emitRawPointerSlow(CodeGenFunction &CGF) const;
@@ -140,12 +161,14 @@ class Address {
140161
}
141162

142163
Address(llvm::Value *BasePtr, llvm::Type *ElementType, CharUnits Alignment,
143-
llvm::Value *Offset, KnownNonNull_t IsKnownNonNull = NotKnownNonNull)
164+
CGPointerAuthInfo PtrAuthInfo, llvm::Value *Offset,
165+
KnownNonNull_t IsKnownNonNull = NotKnownNonNull)
144166
: Pointer(BasePtr, IsKnownNonNull), ElementType(ElementType),
145-
Alignment(Alignment), Offset(Offset) {}
167+
Alignment(Alignment), PtrAuthInfo(PtrAuthInfo), Offset(Offset) {}
146168

147169
Address(RawAddress RawAddr)
148-
: Pointer(RawAddr.isValid() ? RawAddr.getPointer() : nullptr),
170+
: Pointer(RawAddr.isValid() ? RawAddr.getPointer() : nullptr,
171+
RawAddr.isValid() ? RawAddr.isKnownNonNull() : NotKnownNonNull),
149172
ElementType(RawAddr.isValid() ? RawAddr.getElementType() : nullptr),
150173
Alignment(RawAddr.isValid() ? RawAddr.getAlignment()
151174
: CharUnits::Zero()) {}
@@ -192,13 +215,18 @@ class Address {
192215
/// Return the IR name of the pointer value.
193216
llvm::StringRef getName() const { return Pointer.getPointer()->getName(); }
194217

218+
const CGPointerAuthInfo &getPointerAuthInfo() const { return PtrAuthInfo; }
219+
void setPointerAuthInfo(const CGPointerAuthInfo &Info) { PtrAuthInfo = Info; }
220+
195221
// This function is called only in CGBuilderBaseTy::CreateElementBitCast.
196222
void setElementType(llvm::Type *Ty) {
197223
assert(hasOffset() &&
198224
"this funcion shouldn't be called when there is no offset");
199225
ElementType = Ty;
200226
}
201227

228+
bool isSigned() const { return PtrAuthInfo.isSigned(); }
229+
202230
/// Whether the pointer is known not to be null.
203231
KnownNonNull_t isKnownNonNull() const {
204232
assert(isValid());
@@ -215,6 +243,9 @@ class Address {
215243

216244
llvm::Value *getOffset() const { return Offset; }
217245

246+
Address getResignedAddress(const CGPointerAuthInfo &NewInfo,
247+
CodeGenFunction &CGF) const;
248+
218249
/// Return the pointer contained in this class after authenticating it and
219250
/// adding offset to it if necessary.
220251
llvm::Value *emitRawPointer(CodeGenFunction &CGF) const {
@@ -240,7 +271,8 @@ class Address {
240271
/// alignment.
241272
Address withElementType(llvm::Type *ElemTy) const {
242273
if (!hasOffset())
243-
return Address(getBasePointer(), ElemTy, getAlignment(), nullptr,
274+
return Address(getBasePointer(), ElemTy, getAlignment(),
275+
getPointerAuthInfo(), /*Offset=*/nullptr,
244276
isKnownNonNull());
245277
Address A(*this);
246278
A.ElementType = ElemTy;

clang/lib/CodeGen/CGBuilder.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ class CGBuilderTy : public CGBuilderBaseTy {
190190
const llvm::Twine &Name = "") {
191191
if (!Addr.hasOffset())
192192
return Address(CreateAddrSpaceCast(Addr.getBasePointer(), Ty, Name),
193-
ElementTy, Addr.getAlignment(), nullptr,
194-
Addr.isKnownNonNull());
193+
ElementTy, Addr.getAlignment(), Addr.getPointerAuthInfo(),
194+
/*Offset=*/nullptr, Addr.isKnownNonNull());
195195
// Eagerly force a raw address if these is an offset.
196196
return RawAddress(
197197
CreateAddrSpaceCast(Addr.emitRawPointer(*getCGF()), Ty, Name),

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1312,7 +1312,8 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo,
13121312
if (CE->getCastKind() == CK_AddressSpaceConversion)
13131313
Addr = CGF.Builder.CreateAddrSpaceCast(
13141314
Addr, CGF.ConvertType(E->getType()), ElemTy);
1315-
return Addr;
1315+
return CGF.authPointerToPointerCast(Addr, CE->getSubExpr()->getType(),
1316+
CE->getType());
13161317
}
13171318
break;
13181319

clang/lib/CodeGen/CGExprScalar.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2374,7 +2374,9 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
23742374
DestLV.setTBAAInfo(TBAAAccessInfo::getMayAliasInfo());
23752375
return EmitLoadOfLValue(DestLV, CE->getExprLoc());
23762376
}
2377-
return Builder.CreateBitCast(Src, DstTy);
2377+
2378+
llvm::Value *Result = Builder.CreateBitCast(Src, DstTy);
2379+
return CGF.authPointerToPointerCast(Result, E->getType(), DestTy);
23782380
}
23792381
case CK_AddressSpaceConversion: {
23802382
Expr::EvalResult Result;
@@ -2524,6 +2526,8 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
25242526
if (DestTy.mayBeDynamicClass())
25252527
IntToPtr = Builder.CreateLaunderInvariantGroup(IntToPtr);
25262528
}
2529+
2530+
IntToPtr = CGF.authPointerToPointerCast(IntToPtr, E->getType(), DestTy);
25272531
return IntToPtr;
25282532
}
25292533
case CK_PointerToIntegral: {
@@ -2539,6 +2543,7 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
25392543
PtrExpr = Builder.CreateStripInvariantGroup(PtrExpr);
25402544
}
25412545

2546+
PtrExpr = CGF.authPointerToPointerCast(PtrExpr, E->getType(), DestTy);
25422547
return Builder.CreatePtrToInt(PtrExpr, ConvertType(DestTy));
25432548
}
25442549
case CK_ToVoid: {

clang/lib/CodeGen/CGPointerAuth.cpp

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "CodeGenModule.h"
1616
#include "clang/CodeGen/CodeGenABITypes.h"
1717
#include "clang/CodeGen/ConstantInitBuilder.h"
18+
#include "llvm/Analysis/ValueTracking.h"
1819
#include "llvm/Support/SipHash.h"
1920

2021
using namespace clang;
@@ -165,6 +166,128 @@ CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForType(QualType T) {
165166
return ::getPointerAuthInfoForType(*this, T);
166167
}
167168

169+
static bool isZeroConstant(const llvm::Value *Value) {
170+
if (const auto *CI = dyn_cast<llvm::ConstantInt>(Value))
171+
return CI->isZero();
172+
return false;
173+
}
174+
175+
static bool equalAuthPolicies(const CGPointerAuthInfo &Left,
176+
const CGPointerAuthInfo &Right) {
177+
assert((Left.isSigned() || Right.isSigned()) &&
178+
"shouldn't be called if neither is signed");
179+
if (Left.isSigned() != Right.isSigned())
180+
return false;
181+
return Left.getKey() == Right.getKey() &&
182+
Left.getAuthenticationMode() == Right.getAuthenticationMode();
183+
}
184+
185+
// Return the discriminator or return zero if the discriminator is null.
186+
static llvm::Value *getDiscriminatorOrZero(const CGPointerAuthInfo &Info,
187+
CGBuilderTy &Builder) {
188+
llvm::Value *Discriminator = Info.getDiscriminator();
189+
return Discriminator ? Discriminator : Builder.getSize(0);
190+
}
191+
192+
llvm::Value *
193+
CodeGenFunction::emitPointerAuthResignCall(llvm::Value *Value,
194+
const CGPointerAuthInfo &CurAuth,
195+
const CGPointerAuthInfo &NewAuth) {
196+
assert(CurAuth && NewAuth);
197+
198+
if (CurAuth.getAuthenticationMode() !=
199+
PointerAuthenticationMode::SignAndAuth ||
200+
NewAuth.getAuthenticationMode() !=
201+
PointerAuthenticationMode::SignAndAuth) {
202+
llvm::Value *AuthedValue = EmitPointerAuthAuth(CurAuth, Value);
203+
return EmitPointerAuthSign(NewAuth, AuthedValue);
204+
}
205+
// Convert the pointer to intptr_t before signing it.
206+
auto *OrigType = Value->getType();
207+
Value = Builder.CreatePtrToInt(Value, IntPtrTy);
208+
209+
auto *CurKey = Builder.getInt32(CurAuth.getKey());
210+
auto *NewKey = Builder.getInt32(NewAuth.getKey());
211+
212+
llvm::Value *CurDiscriminator = getDiscriminatorOrZero(CurAuth, Builder);
213+
llvm::Value *NewDiscriminator = getDiscriminatorOrZero(NewAuth, Builder);
214+
215+
// call i64 @llvm.ptrauth.resign(i64 %pointer,
216+
// i32 %curKey, i64 %curDiscriminator,
217+
// i32 %newKey, i64 %newDiscriminator)
218+
auto *Intrinsic = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_resign);
219+
Value = EmitRuntimeCall(
220+
Intrinsic, {Value, CurKey, CurDiscriminator, NewKey, NewDiscriminator});
221+
222+
// Convert back to the original type.
223+
Value = Builder.CreateIntToPtr(Value, OrigType);
224+
return Value;
225+
}
226+
227+
llvm::Value *CodeGenFunction::emitPointerAuthResign(
228+
llvm::Value *Value, QualType Type, const CGPointerAuthInfo &CurAuthInfo,
229+
const CGPointerAuthInfo &NewAuthInfo, bool IsKnownNonNull) {
230+
// Fast path: if neither schema wants a signature, we're done.
231+
if (!CurAuthInfo && !NewAuthInfo)
232+
return Value;
233+
234+
llvm::Value *Null = nullptr;
235+
// If the value is obviously null, we're done.
236+
if (auto *PointerValue = dyn_cast<llvm::PointerType>(Value->getType())) {
237+
Null = CGM.getNullPointer(PointerValue, Type);
238+
} else {
239+
assert(Value->getType()->isIntegerTy());
240+
Null = llvm::ConstantInt::get(IntPtrTy, 0);
241+
}
242+
if (Value == Null)
243+
return Value;
244+
245+
// If both schemas sign the same way, we're done.
246+
if (equalAuthPolicies(CurAuthInfo, NewAuthInfo)) {
247+
const llvm::Value *CurD = CurAuthInfo.getDiscriminator();
248+
const llvm::Value *NewD = NewAuthInfo.getDiscriminator();
249+
if (CurD == NewD)
250+
return Value;
251+
252+
if ((CurD == nullptr && isZeroConstant(NewD)) ||
253+
(NewD == nullptr && isZeroConstant(CurD)))
254+
return Value;
255+
}
256+
257+
llvm::BasicBlock *InitBB = Builder.GetInsertBlock();
258+
llvm::BasicBlock *ResignBB = nullptr, *ContBB = nullptr;
259+
260+
// Null pointers have to be mapped to null, and the ptrauth_resign
261+
// intrinsic doesn't do that.
262+
if (!IsKnownNonNull && !llvm::isKnownNonZero(Value, CGM.getDataLayout())) {
263+
ContBB = createBasicBlock("resign.cont");
264+
ResignBB = createBasicBlock("resign.nonnull");
265+
266+
auto *IsNonNull = Builder.CreateICmpNE(Value, Null);
267+
Builder.CreateCondBr(IsNonNull, ResignBB, ContBB);
268+
EmitBlock(ResignBB);
269+
}
270+
271+
// Perform the auth/sign/resign operation.
272+
if (!NewAuthInfo)
273+
Value = EmitPointerAuthAuth(CurAuthInfo, Value);
274+
else if (!CurAuthInfo)
275+
Value = EmitPointerAuthSign(NewAuthInfo, Value);
276+
else
277+
Value = emitPointerAuthResignCall(Value, CurAuthInfo, NewAuthInfo);
278+
279+
// Clean up with a phi if we branched before.
280+
if (ContBB) {
281+
EmitBlock(ContBB);
282+
auto *Phi = Builder.CreatePHI(Value->getType(), 2);
283+
Phi->addIncoming(Null, InitBB);
284+
Phi->addIncoming(Value, ResignBB);
285+
Value = Phi;
286+
}
287+
288+
return Value;
289+
}
290+
168291
llvm::Constant *
169292
CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
170293
llvm::Constant *StorageAddress,
@@ -351,3 +474,110 @@ CodeGenModule::getVTablePointerAuthInfo(CodeGenFunction *CGF,
351474
/* IsIsaPointer */ false,
352475
/* AuthenticatesNullValues */ false, Discriminator);
353476
}
477+
478+
llvm::Value *CodeGenFunction::authPointerToPointerCast(llvm::Value *ResultPtr,
479+
QualType SourceType,
480+
QualType DestType) {
481+
CGPointerAuthInfo CurAuthInfo, NewAuthInfo;
482+
if (SourceType->isSignableType())
483+
CurAuthInfo = getPointerAuthInfoForType(CGM, SourceType);
484+
485+
if (DestType->isSignableType())
486+
NewAuthInfo = getPointerAuthInfoForType(CGM, DestType);
487+
488+
if (!CurAuthInfo && !NewAuthInfo)
489+
return ResultPtr;
490+
491+
// If only one side of the cast is a function pointer, then we still need to
492+
// resign to handle casts to/from opaque pointers.
493+
if (!CurAuthInfo && DestType->isFunctionPointerType())
494+
CurAuthInfo = CGM.getFunctionPointerAuthInfo(SourceType);
495+
496+
if (!NewAuthInfo && SourceType->isFunctionPointerType())
497+
NewAuthInfo = CGM.getFunctionPointerAuthInfo(DestType);
498+
499+
return emitPointerAuthResign(ResultPtr, DestType, CurAuthInfo, NewAuthInfo,
500+
/*IsKnownNonNull=*/false);
501+
}
502+
503+
Address CodeGenFunction::authPointerToPointerCast(Address Ptr,
504+
QualType SourceType,
505+
QualType DestType) {
506+
CGPointerAuthInfo CurAuthInfo, NewAuthInfo;
507+
if (SourceType->isSignableType())
508+
CurAuthInfo = getPointerAuthInfoForType(CGM, SourceType);
509+
510+
if (DestType->isSignableType())
511+
NewAuthInfo = getPointerAuthInfoForType(CGM, DestType);
512+
513+
if (!CurAuthInfo && !NewAuthInfo)
514+
return Ptr;
515+
516+
if (!CurAuthInfo && DestType->isFunctionPointerType()) {
517+
// When casting a non-signed pointer to a function pointer, just set the
518+
// auth info on Ptr to the assumed schema. The pointer will be resigned to
519+
// the effective type when used.
520+
Ptr.setPointerAuthInfo(CGM.getFunctionPointerAuthInfo(SourceType));
521+
return Ptr;
522+
}
523+
524+
if (!NewAuthInfo && SourceType->isFunctionPointerType()) {
525+
NewAuthInfo = CGM.getFunctionPointerAuthInfo(DestType);
526+
Ptr = Ptr.getResignedAddress(NewAuthInfo, *this);
527+
Ptr.setPointerAuthInfo(CGPointerAuthInfo());
528+
return Ptr;
529+
}
530+
531+
return Ptr;
532+
}
533+
534+
Address CodeGenFunction::getAsNaturalAddressOf(Address Addr,
535+
QualType PointeeTy) {
536+
CGPointerAuthInfo Info =
537+
PointeeTy.isNull() ? CGPointerAuthInfo()
538+
: CGM.getPointerAuthInfoForPointeeType(PointeeTy);
539+
return Addr.getResignedAddress(Info, *this);
540+
}
541+
542+
Address Address::getResignedAddress(const CGPointerAuthInfo &NewInfo,
543+
CodeGenFunction &CGF) const {
544+
assert(isValid() && "pointer isn't valid");
545+
CGPointerAuthInfo CurInfo = getPointerAuthInfo();
546+
llvm::Value *Val;
547+
548+
// Nothing to do if neither the current or the new ptrauth info needs signing.
549+
if (!CurInfo.isSigned() && !NewInfo.isSigned())
550+
return Address(getBasePointer(), getElementType(), getAlignment(),
551+
isKnownNonNull());
552+
553+
assert(ElementType && "Effective type has to be set");
554+
assert(!Offset && "unexpected non-null offset");
555+
556+
// If the current and the new ptrauth infos are the same and the offset is
557+
// null, just cast the base pointer to the effective type.
558+
if (CurInfo == NewInfo && !hasOffset())
559+
Val = getBasePointer();
560+
else
561+
Val = CGF.emitPointerAuthResign(getBasePointer(), QualType(), CurInfo,
562+
NewInfo, isKnownNonNull());
563+
564+
Val = CGF.Builder.CreateBitCast(Val, getType());
565+
return Address(Val, getElementType(), getAlignment(), NewInfo,
566+
/*Offset=*/nullptr, isKnownNonNull());
567+
}
568+
569+
llvm::Value *LValue::getPointer(CodeGenFunction &CGF) const {
570+
assert(isSimple());
571+
return emitResignedPointer(getType(), CGF);
572+
}
573+
574+
llvm::Value *LValue::emitResignedPointer(QualType PointeeTy,
575+
CodeGenFunction &CGF) const {
576+
assert(isSimple());
577+
return CGF.getAsNaturalAddressOf(Addr, PointeeTy).getBasePointer();
578+
}
579+
580+
llvm::Value *LValue::emitRawPointer(CodeGenFunction &CGF) const {
581+
assert(isSimple());
582+
return Addr.isValid() ? Addr.emitRawPointer(CGF) : nullptr;
583+
}

0 commit comments

Comments
 (0)