-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[clang][PAC] Support trivially_relocating polymorphic objects #144420
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
base: main
Are you sure you want to change the base?
Changes from all commits
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 |
---|---|---|
|
@@ -4467,17 +4467,25 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, | |
case Builtin::BI__builtin_trivially_relocate: | ||
case Builtin::BImemmove: | ||
case Builtin::BI__builtin_memmove: { | ||
QualType CopiedType = E->getArg(0)->getType()->getPointeeType(); | ||
Address Dest = EmitPointerWithAlignment(E->getArg(0)); | ||
Address Src = EmitPointerWithAlignment(E->getArg(1)); | ||
Value *SizeVal = EmitScalarExpr(E->getArg(2)); | ||
if (BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_trivially_relocate) | ||
if (BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_trivially_relocate) { | ||
QualType BaseElementType = getContext().getBaseElementType(CopiedType); | ||
if (getContext().containsAddressDiscriminatedPointerAuth( | ||
BaseElementType)) { | ||
llvm::Instruction *LastFixupInstruction = | ||
EmitPointerAuthRelocationFixup(CopiedType, Dest, Src, SizeVal); | ||
addInstToNewSourceAtom(LastFixupInstruction, nullptr); | ||
Comment on lines
+4477
to
+4480
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. It's not really a fixup if it's doing the reallocation inside the fixup. 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 was so confused by this comment, to the extent I thought I had managed to break this and not catch it, but yet I agree now the name should be changed :D When I first wrote this the memmove was not part of that function, so it was purely a fixup, but that is obviously no longer the case. |
||
return RValue::get(Dest, *this); | ||
} | ||
SizeVal = Builder.CreateMul( | ||
SizeVal, | ||
ConstantInt::get( | ||
SizeVal->getType(), | ||
getContext() | ||
.getTypeSizeInChars(E->getArg(0)->getType()->getPointeeType()) | ||
.getQuantity())); | ||
getContext().getTypeSizeInChars(CopiedType).getQuantity())); | ||
} | ||
EmitArgCheck(TCK_Store, Dest, E->getArg(0), 0); | ||
EmitArgCheck(TCK_Load, Src, E->getArg(1), 1); | ||
auto *I = Builder.CreateMemMove(Dest, Src, SizeVal, false); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -420,6 +420,219 @@ void CodeGenFunction::EmitPointerAuthCopy(PointerAuthQualifier Qual, QualType T, | |
Builder.CreateStore(Value, DestAddress); | ||
} | ||
|
||
static const ConstantArrayType *tryGetTypeAsConstantArrayType(QualType T) { | ||
if (!T->isConstantArrayType()) | ||
return nullptr; | ||
return cast<ConstantArrayType>(T->castAsArrayTypeUnsafe()); | ||
} | ||
|
||
using FixupErrorTy = std::pair<const CXXRecordDecl *, CXXBaseSpecifier>; | ||
class CodeGenFunction::FixupFinder { | ||
public: | ||
using FixupVectorTy = CodeGenFunction::FixupVectorTy; | ||
static FixupVectorTy findFixups(CodeGenFunction &CGF, QualType T) { | ||
FixupFinder Finder(CGF); | ||
FixupVectorTy Result; | ||
Finder.findFixups(Result, T, CharUnits::Zero()); | ||
std::sort(Result.begin(), Result.end(), | ||
[](const auto &L, const auto &R) { return L.Offset < R.Offset; }); | ||
return Result; | ||
} | ||
|
||
private: | ||
explicit FixupFinder(CodeGenFunction &CGF) | ||
: CGF(CGF), Context(CGF.getContext()) {} | ||
|
||
void findVTablePointerFixups(FixupVectorTy &Output, CXXRecordDecl *RD, | ||
CharUnits Offset) { | ||
CodeGenFunction::VPtrsVector VPtrs = CGF.getVTablePointers(RD); | ||
for (auto VPtr : VPtrs) { | ||
std::optional<PointerAuthQualifier> PointerAuth = | ||
CGF.CGM.getVTablePointerAuthentication(VPtr.Base.getBase()); | ||
if (PointerAuth && PointerAuth->isAddressDiscriminated()) | ||
Output.push_back( | ||
{Offset + VPtr.Base.getBaseOffset(), KnownNonNull, *PointerAuth}); | ||
} | ||
} | ||
void findObjectFixups(FixupVectorTy &Output, CXXRecordDecl *RD, | ||
CharUnits Offset) { | ||
if (RD->isPolymorphic()) | ||
findVTablePointerFixups(Output, RD, Offset); | ||
findFixups(Output, RD, Offset, /*SubobjectIsBase=*/true); | ||
} | ||
|
||
void findFixups(FixupVectorTy &Output, CXXRecordDecl *RD, | ||
CharUnits SubobjectOffset, bool SubobjectIsBase) { | ||
// If we've found a union it by definition cannot contain | ||
// address discriminated fields. | ||
if (RD->isUnion()) | ||
return; | ||
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); | ||
if (Layout.hasOwnVFPtr() && RD == Layout.getPrimaryBase()) | ||
findVTablePointerFixups(Output, RD, SubobjectOffset); | ||
|
||
for (auto Base : RD->bases()) { | ||
CXXRecordDecl *BaseDecl = | ||
Base.getType()->getAsCXXRecordDecl()->getDefinition(); | ||
assert(!Base.isVirtual()); | ||
CharUnits BaseOffset = Layout.getBaseClassOffset(BaseDecl); | ||
findFixups(Output, BaseDecl, SubobjectOffset + BaseOffset, | ||
/*SubobjectIsBase=*/true); | ||
} | ||
|
||
for (const FieldDecl *Field : RD->fields()) { | ||
if (Field->isBitField()) | ||
continue; | ||
unsigned FieldBitOffset = Layout.getFieldOffset(Field->getFieldIndex()); | ||
CharUnits FieldOffset = Context.toCharUnitsFromBits(FieldBitOffset); | ||
findFixups(Output, Field->getType(), SubobjectOffset + FieldOffset); | ||
} | ||
} | ||
void findFixups(FixupVectorTy &Output, QualType T, CharUnits Offset) { | ||
T = T.getCanonicalType(); | ||
if (!Context.containsAddressDiscriminatedPointerAuth(T)) | ||
return; | ||
|
||
if (const ConstantArrayType *CAT = tryGetTypeAsConstantArrayType(T)) { | ||
if (CAT->getSize() == 0) | ||
return; | ||
Output.push_back({Offset, CAT}); | ||
return; | ||
} | ||
|
||
if (PointerAuthQualifier Q = T.getPointerAuth(); | ||
Q && Q.isAddressDiscriminated()) { | ||
// FIXME: Would it be reasonable to consider nullability? | ||
Output.push_back({Offset, NotKnownNonNull, Q}); | ||
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 think this is a reasonable question, but it would also turn an incorrectly null value become a runtime error? |
||
return; | ||
} | ||
|
||
CXXRecordDecl *RD = T->getAsCXXRecordDecl(); | ||
if (!RD) | ||
return; | ||
findObjectFixups(Output, RD, Offset); | ||
} | ||
CodeGenFunction &CGF; | ||
ASTContext &Context; | ||
}; | ||
|
||
void CodeGenFunction::EmitSingleObjectPointerAuthRelocationFixup( | ||
const FixupVectorTy &Fixups, QualType ElementType, Address Dst, | ||
Address Src) { | ||
auto GetFixupAddress = [&](Address BaseAddress, CharUnits Offset, | ||
KnownNonNull_t IsKnownNonNull) { | ||
llvm::Value *BasePtr = BaseAddress.emitRawPointer(*this); | ||
llvm::Value *OffsetValue = | ||
llvm::ConstantInt::get(PtrDiffTy, Offset.getQuantity()); | ||
llvm::Value *FixupAddress = | ||
Builder.CreateInBoundsGEP(Int8Ty, BasePtr, OffsetValue); | ||
return Address(FixupAddress, VoidPtrPtrTy, | ||
BaseAddress.getAlignment().alignmentAtOffset(Offset), | ||
IsKnownNonNull); | ||
}; | ||
for (auto &Fixup : Fixups) { | ||
if (const ConstantArrayType *CAT = Fixup.getAsConstantArrayType()) { | ||
llvm::Value *CountValue = llvm::ConstantInt::get(SizeTy, 1); | ||
EmitArrayPointerAuthRelocationFixup(QualType(CAT, 0), Dst, Src, | ||
CountValue); | ||
continue; | ||
} | ||
auto [IsKnownNonNull, Qualifier] = Fixup.getValueFixup(); | ||
|
||
// We don't use the existing copy helpers as we'll be resigning a | ||
// value in place assuming the old address for the read. | ||
Address FixupDst = GetFixupAddress(Dst, Fixup.Offset, IsKnownNonNull); | ||
CGPointerAuthInfo DstPtrAuth = EmitPointerAuthInfo(Qualifier, FixupDst); | ||
|
||
Address FixupSrc = GetFixupAddress(Src, Fixup.Offset, IsKnownNonNull); | ||
CGPointerAuthInfo SrcPtrAuth = EmitPointerAuthInfo(Qualifier, FixupSrc); | ||
|
||
// We're loading from the destination here as we've already performed the | ||
// copy from src to dst, and as relocation has memmove semantics, the src | ||
// address may have been overwritten. | ||
llvm::Value *Value = Builder.CreateLoad(FixupDst); | ||
Value = emitPointerAuthResign(Value, QualType(), SrcPtrAuth, DstPtrAuth, | ||
IsKnownNonNull); | ||
Builder.CreateStore(Value, FixupDst); | ||
} | ||
} | ||
|
||
llvm::Instruction *CodeGenFunction::EmitArrayPointerAuthRelocationFixup( | ||
QualType ElementType, Address Dst, Address Src, llvm::Value *Count) { | ||
// Preemptively flatten array types so we don't end up with multiple levels | ||
// of loops unnecessarily | ||
if (const ConstantArrayType *CAT = | ||
tryGetTypeAsConstantArrayType(ElementType)) { | ||
uint64_t ElementCount = getContext().getConstantArrayElementCount(CAT); | ||
llvm::Value *ElementCountValue = | ||
llvm::ConstantInt::get(SizeTy, ElementCount); | ||
Count = Builder.CreateMul(Count, ElementCountValue); | ||
ElementType = getContext().getBaseElementType(QualType(CAT, 0)); | ||
} | ||
|
||
FixupVectorTy *Fixups; | ||
if (const auto Existing = FixupLists.find(ElementType); | ||
Existing != FixupLists.end()) | ||
Fixups = Existing->second.get(); | ||
else { | ||
auto FoundFixups = FixupFinder::findFixups(*this, ElementType); | ||
auto [EntryPoint, Inserted] = FixupLists.try_emplace( | ||
ElementType, std::make_unique<FixupVectorTy>(std::move(FoundFixups))); | ||
(void)Inserted; | ||
Fixups = EntryPoint->second.get(); | ||
} | ||
|
||
CharUnits ElementSize = getContext().getTypeSizeInChars(ElementType); | ||
CharUnits ElementAlign = | ||
Src.getAlignment().alignmentOfArrayElement(ElementSize); | ||
llvm::Type *LLVMElemType = ConvertTypeForMem(ElementType); | ||
|
||
llvm::BasicBlock *RelocationFixupEntry = Builder.GetInsertBlock(); | ||
llvm::BasicBlock *RelocationFixupBody = | ||
createBasicBlock("relocation_ptrauth_fixup.body"); | ||
EmitBlock(RelocationFixupBody); | ||
llvm::Value *Zero = llvm::ConstantInt::get(SizeTy, 0); | ||
llvm::PHINode *Index = | ||
Builder.CreatePHI(SizeTy, 2, "relocation_ptrauth_fixup.index"); | ||
Index->addIncoming(Zero, RelocationFixupEntry); | ||
llvm::Value *DstElement = | ||
Builder.CreateInBoundsGEP(LLVMElemType, Dst.emitRawPointer(*this), Index, | ||
"relocation_ptrauth_fixup.dstobject"); | ||
Address DstElementAddress = Address(DstElement, LLVMElemType, ElementAlign); | ||
llvm::Value *SrcElement = | ||
Builder.CreateInBoundsGEP(LLVMElemType, Src.emitRawPointer(*this), Index, | ||
"relocation_ptrauth_fixup.srcobject"); | ||
Address SrcElementAddress = Address(SrcElement, LLVMElemType, ElementAlign); | ||
|
||
// Do the fixup | ||
EmitSingleObjectPointerAuthRelocationFixup( | ||
*Fixups, ElementType, DstElementAddress, SrcElementAddress); | ||
|
||
llvm::Value *NextIndex = | ||
Builder.CreateNUWAdd(Index, llvm::ConstantInt::get(Index->getType(), 1), | ||
"relocation_ptrauth_fixup.next_index"); | ||
Index->addIncoming(NextIndex, Builder.GetInsertBlock()); | ||
llvm::Value *IsComplete = Builder.CreateICmpEQ( | ||
NextIndex, Count, "relocation_ptrauth_fixup.is_complete"); | ||
llvm::BasicBlock *RelocationFixupFinished = | ||
createBasicBlock("relocation_ptrauth_fixup.end"); | ||
Builder.CreateCondBr(IsComplete, RelocationFixupFinished, | ||
RelocationFixupBody); | ||
EmitBlock(RelocationFixupFinished); | ||
return RelocationFixupFinished->getTerminator(); | ||
} | ||
|
||
llvm::Instruction *CodeGenFunction::EmitPointerAuthRelocationFixup( | ||
QualType ElementType, Address Dst, Address Src, llvm::Value *Count) { | ||
size_t ElementSize = | ||
getContext().getTypeSizeInChars(ElementType).getQuantity(); | ||
llvm::Value *ElementSizeValue = | ||
llvm::ConstantInt::get(Count->getType(), ElementSize); | ||
llvm::Value *Size = Builder.CreateMul(Count, ElementSizeValue); | ||
Builder.CreateMemMove(Dst, Src, Size, false); | ||
return EmitArrayPointerAuthRelocationFixup(ElementType, Dst, Src, Count); | ||
} | ||
|
||
llvm::Constant * | ||
CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, | ||
llvm::Constant *StorageAddress, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding this simplifies the logic in codegen, and to an extent removes a footgun in the form of not requiring every caller to lower the type to the base element prior to calling.