Skip to content

Commit b43a7e9

Browse files
committed
[clang][PAC] Support trivially_relocating polymorphic objects
Adds support for trivial relocation of polymorphic objects with address discriminated vtable pointers. This is implemented as a post-memmove fixup pass over the impacted objects. We do this by traversing the object graph and finding all the vtable slots in the type being relocated, or any of the subobjects.
1 parent 267b859 commit b43a7e9

File tree

6 files changed

+645
-4
lines changed

6 files changed

+645
-4
lines changed

clang/lib/AST/ASTContext.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,11 @@ ASTContext::PointerAuthContent ASTContext::findPointerAuthContent(QualType T) {
17271727
T = T.getCanonicalType();
17281728
if (T.hasAddressDiscriminatedPointerAuth())
17291729
return PointerAuthContent::AddressDiscriminatedData;
1730+
1731+
T = getBaseElementType(T).getCanonicalType();
1732+
if (T.hasAddressDiscriminatedPointerAuth())
1733+
return PointerAuthContent::AddressDiscriminatedData;
1734+
17301735
const RecordDecl *RD = T->getAsRecordDecl();
17311736
if (!RD)
17321737
return PointerAuthContent::None;

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4467,17 +4467,25 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
44674467
case Builtin::BI__builtin_trivially_relocate:
44684468
case Builtin::BImemmove:
44694469
case Builtin::BI__builtin_memmove: {
4470+
QualType CopiedType = E->getArg(0)->getType()->getPointeeType();
44704471
Address Dest = EmitPointerWithAlignment(E->getArg(0));
44714472
Address Src = EmitPointerWithAlignment(E->getArg(1));
44724473
Value *SizeVal = EmitScalarExpr(E->getArg(2));
4473-
if (BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_trivially_relocate)
4474+
if (BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_trivially_relocate) {
4475+
QualType BaseElementType = getContext().getBaseElementType(CopiedType);
4476+
if (getContext().containsAddressDiscriminatedPointerAuth(
4477+
BaseElementType)) {
4478+
llvm::Instruction *LastFixupInstruction =
4479+
EmitPointerAuthRelocationFixup(CopiedType, Dest, Src, SizeVal);
4480+
addInstToNewSourceAtom(LastFixupInstruction, nullptr);
4481+
return RValue::get(Dest, *this);
4482+
}
44744483
SizeVal = Builder.CreateMul(
44754484
SizeVal,
44764485
ConstantInt::get(
44774486
SizeVal->getType(),
4478-
getContext()
4479-
.getTypeSizeInChars(E->getArg(0)->getType()->getPointeeType())
4480-
.getQuantity()));
4487+
getContext().getTypeSizeInChars(CopiedType).getQuantity()));
4488+
}
44814489
EmitArgCheck(TCK_Store, Dest, E->getArg(0), 0);
44824490
EmitArgCheck(TCK_Load, Src, E->getArg(1), 1);
44834491
auto *I = Builder.CreateMemMove(Dest, Src, SizeVal, false);

clang/lib/CodeGen/CGPointerAuth.cpp

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,222 @@ void CodeGenFunction::EmitPointerAuthCopy(PointerAuthQualifier Qual, QualType T,
420420
Builder.CreateStore(Value, DestAddress);
421421
}
422422

423+
static const ConstantArrayType *tryGetTypeAsConstantArrayType(QualType T) {
424+
if (!T->isConstantArrayType())
425+
return nullptr;
426+
return cast<ConstantArrayType>(T->castAsArrayTypeUnsafe());
427+
}
428+
429+
using FixupErrorTy = std::pair<const CXXRecordDecl *, CXXBaseSpecifier>;
430+
class CodeGenFunction::FixupFinder {
431+
public:
432+
using FixupVectorTy = CodeGenFunction::FixupVectorTy;
433+
static FixupVectorTy findFixups(CodeGenFunction &CGF, QualType T) {
434+
FixupFinder Finder(CGF);
435+
FixupVectorTy Result;
436+
Finder.findFixups(Result, T, CharUnits::Zero());
437+
std::sort(Result.begin(), Result.end(),
438+
[](const auto &L, const auto &R) { return L.Offset < R.Offset; });
439+
return Result;
440+
}
441+
442+
private:
443+
explicit FixupFinder(CodeGenFunction &CGF)
444+
: CGF(CGF), Context(CGF.getContext()) {}
445+
446+
void findVTablePointerFixups(FixupVectorTy &Output, CXXRecordDecl *RD,
447+
CharUnits Offset) {
448+
CodeGenFunction::VPtrsVector VPtrs = CGF.getVTablePointers(RD);
449+
for (auto VPtr : VPtrs) {
450+
std::optional<PointerAuthQualifier> PointerAuth =
451+
CGF.CGM.getVTablePointerAuthentication(VPtr.Base.getBase());
452+
if (PointerAuth && PointerAuth->isAddressDiscriminated())
453+
Output.push_back(
454+
{Offset + VPtr.Base.getBaseOffset(), KnownNonNull, *PointerAuth});
455+
}
456+
}
457+
void findObjectFixups(FixupVectorTy &Output, CXXRecordDecl *RD,
458+
CharUnits Offset) {
459+
if (RD->isPolymorphic())
460+
findVTablePointerFixups(Output, RD, Offset);
461+
findFixups(Output, RD, Offset, /*SubobjectIsBase=*/true);
462+
}
463+
464+
void findFixups(FixupVectorTy &Output, CXXRecordDecl *RD,
465+
CharUnits SubobjectOffset, bool SubobjectIsBase) {
466+
// If we've found a union it by definition cannot contain
467+
// address discriminated fields.
468+
if (RD->isUnion())
469+
return;
470+
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
471+
if (Layout.hasOwnVFPtr() && RD == Layout.getPrimaryBase())
472+
findVTablePointerFixups(Output, RD, SubobjectOffset);
473+
474+
for (auto Base : RD->bases()) {
475+
CXXRecordDecl *BaseDecl =
476+
Base.getType()->getAsCXXRecordDecl()->getDefinition();
477+
assert(!Base.isVirtual());
478+
CharUnits BaseOffset = Layout.getBaseClassOffset(BaseDecl);
479+
findFixups(Output, BaseDecl, SubobjectOffset + BaseOffset,
480+
/*SubobjectIsBase=*/true);
481+
}
482+
483+
for (const FieldDecl *Field : RD->fields()) {
484+
if (Field->isBitField())
485+
continue;
486+
unsigned FieldBitOffset = Layout.getFieldOffset(Field->getFieldIndex());
487+
CharUnits FieldOffset = Context.toCharUnitsFromBits(FieldBitOffset);
488+
findFixups(Output, Field->getType(), SubobjectOffset + FieldOffset);
489+
}
490+
}
491+
void findFixups(FixupVectorTy &Output, QualType T, CharUnits Offset) {
492+
T = T.getCanonicalType();
493+
if (!Context.containsAddressDiscriminatedPointerAuth(T))
494+
return;
495+
496+
if (const ConstantArrayType *CAT = tryGetTypeAsConstantArrayType(T)) {
497+
if (CAT->getSize() == 0)
498+
return;
499+
Output.push_back({Offset, CAT});
500+
return;
501+
}
502+
503+
if (PointerAuthQualifier Q = T.getPointerAuth();
504+
Q && Q.isAddressDiscriminated()) {
505+
// FIXME: Would it be reasonable to consider nullability?
506+
Output.push_back({Offset, NotKnownNonNull, Q});
507+
return;
508+
}
509+
510+
CXXRecordDecl *RD = T->getAsCXXRecordDecl();
511+
if (!RD)
512+
return;
513+
findObjectFixups(Output, RD, Offset);
514+
}
515+
CodeGenFunction &CGF;
516+
ASTContext &Context;
517+
};
518+
519+
void CodeGenFunction::EmitSingleObjectPointerAuthRelocationFixup(
520+
const FixupVectorTy &Fixups, QualType ElementType, Address Dst,
521+
Address Src) {
522+
auto GetFixupAddress = [&](Address BaseAddress, CharUnits Offset,
523+
KnownNonNull_t IsKnownNonNull,
524+
const char *Reason) {
525+
llvm::Value *BasePtr = BaseAddress.emitRawPointer(*this);
526+
llvm::Value *OffsetValue =
527+
llvm::ConstantInt::get(PtrDiffTy, Offset.getQuantity());
528+
llvm::Value *FixupAddress =
529+
Builder.CreateInBoundsGEP(Int8Ty, BasePtr, OffsetValue, Reason);
530+
return Address(FixupAddress, VoidPtrPtrTy,
531+
BaseAddress.getAlignment().alignmentAtOffset(Offset),
532+
IsKnownNonNull);
533+
};
534+
for (auto &Fixup : Fixups) {
535+
if (const ConstantArrayType *CAT = Fixup.getAsConstantArrayType()) {
536+
llvm::Value *CountValue = llvm::ConstantInt::get(SizeTy, 1);
537+
EmitArrayPointerAuthRelocationFixup(QualType(CAT, 0), Dst, Src,
538+
CountValue);
539+
continue;
540+
}
541+
auto [IsKnownNonNull, Qualifier] = Fixup.getValueFixup();
542+
543+
// We don't use the existing copy helpers as we'll be resigning a
544+
// value in place assuming the old address for the read.
545+
Address FixupDst = GetFixupAddress(Dst, Fixup.Offset, IsKnownNonNull,
546+
"fixup.dst.with.offset");
547+
CGPointerAuthInfo DstPtrAuth = EmitPointerAuthInfo(Qualifier, FixupDst);
548+
549+
Address FixupSrc = GetFixupAddress(Src, Fixup.Offset, IsKnownNonNull,
550+
"fixup.src.with.offset");
551+
CGPointerAuthInfo SrcPtrAuth = EmitPointerAuthInfo(Qualifier, FixupSrc);
552+
553+
// We're loading from the destination here as we've already performed the
554+
// copy from src to dst, and as relocation has memmove semantics, the src
555+
// address may have been overwritten.
556+
llvm::Value *Value = Builder.CreateLoad(FixupDst);
557+
Value = emitPointerAuthResign(Value, QualType(), SrcPtrAuth, DstPtrAuth,
558+
IsKnownNonNull);
559+
Builder.CreateStore(Value, FixupDst);
560+
}
561+
}
562+
563+
llvm::Instruction *CodeGenFunction::EmitArrayPointerAuthRelocationFixup(
564+
QualType ElementType, Address Dst, Address Src, llvm::Value *Count) {
565+
// Preemptively flatten array types so we don't end up with multiple levels
566+
// of loops unnecessarily
567+
if (const ConstantArrayType *CAT =
568+
tryGetTypeAsConstantArrayType(ElementType)) {
569+
uint64_t ElementCount = getContext().getConstantArrayElementCount(CAT);
570+
llvm::Value *ElementCountValue =
571+
llvm::ConstantInt::get(SizeTy, ElementCount);
572+
Count = Builder.CreateMul(Count, ElementCountValue);
573+
ElementType = getContext().getBaseElementType(QualType(CAT, 0));
574+
}
575+
576+
FixupVectorTy *Fixups;
577+
if (const auto Existing = FixupLists.find(ElementType);
578+
Existing != FixupLists.end())
579+
Fixups = Existing->second.get();
580+
else {
581+
auto FoundFixups = FixupFinder::findFixups(*this, ElementType);
582+
auto [EntryPoint, Inserted] = FixupLists.try_emplace(
583+
ElementType, std::make_unique<FixupVectorTy>(std::move(FoundFixups)));
584+
(void)Inserted;
585+
Fixups = EntryPoint->second.get();
586+
}
587+
588+
CharUnits ElementSize = getContext().getTypeSizeInChars(ElementType);
589+
CharUnits ElementAlign =
590+
Src.getAlignment().alignmentOfArrayElement(ElementSize);
591+
llvm::Type *LLVMElemType = ConvertTypeForMem(ElementType);
592+
593+
llvm::BasicBlock *RelocationFixupEntry = Builder.GetInsertBlock();
594+
llvm::BasicBlock *RelocationFixupBody =
595+
createBasicBlock("relocation_ptrauth_fixup.body");
596+
EmitBlock(RelocationFixupBody);
597+
llvm::Value *Zero = llvm::ConstantInt::get(SizeTy, 0);
598+
llvm::PHINode *Index =
599+
Builder.CreatePHI(SizeTy, 2, "relocation_ptrauth_fixup.index");
600+
Index->addIncoming(Zero, RelocationFixupEntry);
601+
llvm::Value *DstElement =
602+
Builder.CreateInBoundsGEP(LLVMElemType, Dst.emitRawPointer(*this), Index,
603+
"relocation_ptrauth_fixup.dstobject");
604+
Address DstElementAddress = Address(DstElement, LLVMElemType, ElementAlign);
605+
llvm::Value *SrcElement =
606+
Builder.CreateInBoundsGEP(LLVMElemType, Src.emitRawPointer(*this), Index,
607+
"relocation_ptrauth_fixup.srcobject");
608+
Address SrcElementAddress = Address(SrcElement, LLVMElemType, ElementAlign);
609+
610+
// Do the fixup
611+
EmitSingleObjectPointerAuthRelocationFixup(
612+
*Fixups, ElementType, DstElementAddress, SrcElementAddress);
613+
614+
llvm::Value *NextIndex =
615+
Builder.CreateNUWAdd(Index, llvm::ConstantInt::get(Index->getType(), 1),
616+
"relocation_ptrauth_fixup.next_index");
617+
Index->addIncoming(NextIndex, Builder.GetInsertBlock());
618+
llvm::Value *IsComplete = Builder.CreateICmpEQ(
619+
NextIndex, Count, "relocation_ptrauth_fixup.is_complete");
620+
llvm::BasicBlock *RelocationFixupFinished =
621+
createBasicBlock("relocation_ptrauth_fixup.end");
622+
Builder.CreateCondBr(IsComplete, RelocationFixupFinished,
623+
RelocationFixupBody);
624+
EmitBlock(RelocationFixupFinished);
625+
return RelocationFixupFinished->getTerminator();
626+
}
627+
628+
llvm::Instruction *CodeGenFunction::EmitPointerAuthRelocationFixup(
629+
QualType ElementType, Address Dst, Address Src, llvm::Value *Count) {
630+
size_t ElementSize =
631+
getContext().getTypeSizeInChars(ElementType).getQuantity();
632+
llvm::Value *ElementSizeValue =
633+
llvm::ConstantInt::get(Count->getType(), ElementSize);
634+
llvm::Value *Size = Builder.CreateMul(Count, ElementSizeValue);
635+
Builder.CreateMemMove(Dst, Src, Size, false);
636+
return EmitArrayPointerAuthRelocationFixup(ElementType, Dst, Src, Count);
637+
}
638+
423639
llvm::Constant *
424640
CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
425641
llvm::Constant *StorageAddress,

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4610,6 +4610,10 @@ class CodeGenFunction : public CodeGenTypeCache {
46104610
void EmitPointerAuthCopy(PointerAuthQualifier Qualifier, QualType Type,
46114611
Address DestField, Address SrcField);
46124612

4613+
llvm::Instruction *EmitPointerAuthRelocationFixup(QualType ElementType,
4614+
Address Dst, Address Src,
4615+
llvm::Value *SrcEnd);
4616+
46134617
std::pair<llvm::Value *, CGPointerAuthInfo>
46144618
EmitOrigPointerRValue(const Expr *E);
46154619

@@ -4618,6 +4622,46 @@ class CodeGenFunction : public CodeGenTypeCache {
46184622
Address authPointerToPointerCast(Address Ptr, QualType SourceType,
46194623
QualType DestType);
46204624

4625+
private:
4626+
llvm::Instruction *EmitArrayPointerAuthRelocationFixup(QualType ElementType,
4627+
Address Dst,
4628+
Address Src,
4629+
llvm::Value *Count);
4630+
4631+
struct RelocatedAddressDiscriminatedPointerAuthFixup {
4632+
using FieldFixup = std::pair<KnownNonNull_t, PointerAuthQualifier>;
4633+
CharUnits Offset;
4634+
RelocatedAddressDiscriminatedPointerAuthFixup(
4635+
CharUnits Offset, KnownNonNull_t IsKnownNonNull,
4636+
PointerAuthQualifier Qualifier)
4637+
: Offset(Offset), Info(FieldFixup{IsKnownNonNull, Qualifier}) {}
4638+
RelocatedAddressDiscriminatedPointerAuthFixup(
4639+
CharUnits Offset, const ConstantArrayType *ArrayType)
4640+
: Offset(Offset), Info(ArrayType) {}
4641+
const ConstantArrayType *getAsConstantArrayType() const {
4642+
if (!std::holds_alternative<const ConstantArrayType *>(Info))
4643+
return nullptr;
4644+
return std::get<const ConstantArrayType *>(Info);
4645+
}
4646+
std::pair<KnownNonNull_t, PointerAuthQualifier> getValueFixup() const {
4647+
assert(std::holds_alternative<FieldFixup>(Info));
4648+
return std::get<FieldFixup>(Info);
4649+
}
4650+
4651+
private:
4652+
std::variant<FieldFixup, const ConstantArrayType *> Info;
4653+
};
4654+
4655+
using FixupVectorTy =
4656+
llvm::SmallVector<RelocatedAddressDiscriminatedPointerAuthFixup>;
4657+
class FixupFinder;
4658+
llvm::DenseMap<QualType, std::unique_ptr<FixupVectorTy>> FixupLists;
4659+
4660+
void EmitSingleObjectPointerAuthRelocationFixup(const FixupVectorTy &Fixups,
4661+
QualType ElementType,
4662+
Address Dst, Address Src);
4663+
4664+
public:
46214665
Address getAsNaturalAddressOf(Address Addr, QualType PointeeTy);
46224666

46234667
llvm::Value *getAsNaturalPointerTo(Address Addr, QualType PointeeType) {

0 commit comments

Comments
 (0)