diff options
38 files changed, 1204 insertions, 37 deletions
diff --git a/clang/docs/StructureProtection.rst b/clang/docs/StructureProtection.rst new file mode 100644 index 0000000..b6a0ea3 --- /dev/null +++ b/clang/docs/StructureProtection.rst @@ -0,0 +1,70 @@ +==================== +Structure Protection +==================== + +.. contents:: + :local: + + +Introduction +============ + +Structure protection is an *experimental* mitigation +against use-after-free vulnerabilities. For +more information, please see the original `RFC +<https://discourse.llvm.org/t/rfc-structure-protection-a-family-of-uaf-mitigation-techniques/85555>`_. +An independent set of documentation will be contributed when the feature +is promoted to stable. + +Usage +===== + +To use structure protection, build your program using one or more of these flags: + +- ``-fexperimental-pointer-field-protection``: Enable pointer + field protection on all types that are not considered standard-layout + according to the C++ rules for standard layout. Specifying this flag + also defines the predefined macro ``__POINTER_FIELD_PROTECTION__``. + +- ``-fexperimental-pointer-field-protection-tagged``: On architectures + that support it (currently only AArch64), for types that are not considered + trivially copyable, use the address of the object to compute the pointer + encoding. Specifying this flag also defines the predefined macro + ``__POINTER_FIELD_PROTECTION_TAGGED__``. + +It is also possible to specify the attribute +``[[clang::pointer_field_protection]]`` on a struct type to opt the +struct's pointer fields into pointer field protection, even if the type is +standard layout or none of the command line flags are specified. Note that +this means that the type will not comply with pointer interconvertibility +and other standard layout rules. + +Pointer field protection is inherited from bases and non-static data +members. + +In order to avoid ABI breakage, the entire C++ part +of the program must be built with a consistent set of +``-fexperimental-pointer-field-protection*`` flags, and the C++ standard +library must also be built with the same flags and statically linked +into the program. + +To build libc++ with pointer field protection support, pass the following +CMake flags: + +.. code-block:: console + + "-DRUNTIMES_${triple}_LIBCXXABI_ENABLE_SHARED=OFF" \ + "-DRUNTIMES_${triple}_LIBCXX_USE_COMPILER_RT=ON" \ + "-DRUNTIMES_${triple}_LIBCXX_PFP=untagged" \ + "-DRUNTIMES_${triple}_LIBCXX_ENABLE_SHARED=OFF" \ + "-DRUNTIMES_${triple}_LIBCXX_TEST_CONFIG=llvm-libc++-static.cfg.in" \ + "-DRUNTIMES_${triple}_LIBUNWIND_ENABLE_SHARED=OFF" \ + +where ``${triple}`` is your target triple, such as +``aarch64-unknown-linux``. + +The resulting toolchain may then be used to build programs +with pointer field protection by passing ``-stdlib=libc++ +-fexperimental-pointer-field-protection`` at compile time +and ``-Wl,-Bstatic -lc++ -lc++abi -Wl,-Bdynamic -lm -fuse-ld=lld +-static-libstdc++`` at link time. diff --git a/clang/docs/index.rst b/clang/docs/index.rst index 542bfc9..dbc1d20 100644 --- a/clang/docs/index.rst +++ b/clang/docs/index.rst @@ -47,6 +47,7 @@ Using Clang as a Compiler LTOVisibility SafeStack ShadowCallStack + StructureProtection SourceBasedCodeCoverage StandardCPlusPlusModules Modules diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 1c17333..c4c62b6 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -183,6 +183,12 @@ struct TypeInfoChars { } }; +struct PFPField { + CharUnits offset; + FieldDecl *field; + bool isWithinUnion; +}; + /// Holds long-lived AST nodes (such as types and decls) that can be /// referred to throughout the semantic analysis of a file. class ASTContext : public RefCountedBase<ASTContext> { @@ -3729,6 +3735,22 @@ public: StringRef getCUIDHash() const; + void findPFPFields(QualType Ty, CharUnits Offset, + std::vector<PFPField> &Fields, bool IncludeVBases, + bool IsWithinUnion = false) const; + bool hasPFPFields(QualType ty) const; + bool isPFPField(const FieldDecl *field) const; + + /// Returns whether this record's PFP fields (if any) are trivially + /// copyable (i.e. may be memcpy'd). This may also return true if the + /// record does not have any PFP fields, so it may be necessary for the caller + /// to check for PFP fields, e.g. by calling hasPFPFields(). + bool arePFPFieldsTriviallyCopyable(const RecordDecl *RD) const; + + llvm::SetVector<const FieldDecl *> PFPFieldsWithEvaluatedOffset; + void recordMemberDataPointerEvaluation(const ValueDecl *VD); + void recordOffsetOfEvaluation(const OffsetOfExpr *E); + private: /// All OMPTraitInfo objects live in this collection, one per /// `pragma omp [begin] declare variant` directive. diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def index 6620840..83673dc 100644 --- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def +++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def @@ -253,4 +253,10 @@ FIELD(IsAnyDestructorNoReturn, 1, NO_MERGE) /// type that is intangible). HLSL only. FIELD(IsHLSLIntangible, 1, NO_MERGE) +/// Whether the pointer fields in this class should have pointer field +/// protection (PFP) by default, either because of an attribute, the +/// -fexperimental-pointer-field-protection compiler flag or inheritance from +/// a base or member with PFP. +FIELD(IsPFPType, 1, NO_MERGE) + #undef FIELD diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 00d8f724..c1db6b3 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1235,6 +1235,12 @@ public: /// Determine whether this class has any variant members. bool hasVariantMembers() const { return data().HasVariantMembers; } + /// Returns whether the pointer fields in this class should have pointer field + /// protection (PFP) by default, either because of an attribute, the + /// -fexperimental-pointer-field-protection compiler flag or inheritance from + /// a base or member with PFP. + bool isPFPType() const { return data().IsPFPType; } + /// Determine whether this class has a trivial default constructor /// (C++11 [class.ctor]p5). bool hasTrivialDefaultConstructor() const { diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 29364c5..fc37e06 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2597,6 +2597,19 @@ def CountedByOrNull : DeclOrTypeAttr { let LangOpts = [COnly]; } +def NoFieldProtection : DeclOrTypeAttr { + let Spellings = [Clang<"no_field_protection">]; + let Subjects = SubjectList<[Field], ErrorDiag>; + let Documentation = [Undocumented]; +} + +def PointerFieldProtection : DeclOrTypeAttr { + let Spellings = [Clang<"pointer_field_protection">]; + let Subjects = SubjectList<[CXXRecord], ErrorDiag>; + let Documentation = [Undocumented]; + let SimpleHandler = 1; +} + def SizedBy : DeclOrTypeAttr { let Spellings = [Clang<"sized_by">]; let Subjects = SubjectList<[Field], ErrorDiag>; diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index f094ba1..2a75af4 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -458,6 +458,11 @@ LANGOPT(RelativeCXXABIVTables, 1, 0, NotCompatible, LANGOPT(OmitVTableRTTI, 1, 0, NotCompatible, "Use an ABI-incompatible v-table layout that omits the RTTI component") +LANGOPT(PointerFieldProtection, 1, 0, NotCompatible, + "Encode struct pointer fields to protect against UAF vulnerabilities") +LANGOPT(PointerFieldProtectionTagged, 1, 0, NotCompatible, + "Use pointer identity (tag) to discriminate pointers of non-trivially-copyable types") + LANGOPT(VScaleMin, 32, 0, NotCompatible, "Minimum vscale value") LANGOPT(VScaleMax, 32, 0, NotCompatible, "Maximum vscale value") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index b1ae3cf..50f6933 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3077,6 +3077,17 @@ defm experimental_omit_vtable_rtti : BoolFOption<"experimental-omit-vtable-rtti" NegFlag<SetFalse, [], [CC1Option], "Do not omit">, BothFlags<[], [CC1Option], " the RTTI component from virtual tables">>; +defm experimental_pointer_field_protection : BoolFOption<"experimental-pointer-field-protection", + LangOpts<"PointerFieldProtection">, DefaultFalse, + PosFlag<SetTrue, [], [CC1Option], "Enable">, + NegFlag<SetFalse, [], [], "Do not enable">, + BothFlags<[], [ClangOption], " pointer field protection on all non-standard layout struct types">>; +defm experimental_pointer_field_protection_tagged : BoolFOption<"experimental-pointer-field-protection-tagged", + LangOpts<"PointerFieldProtectionTagged">, DefaultFalse, + PosFlag<SetTrue, [], [CC1Option], "Use">, + NegFlag<SetFalse, [], [], "Do not use">, + BothFlags<[], [ClangOption], " pointer identity (tag) to discriminate pointers of non-trivially-copyable types">>; + def fcxx_abi_EQ : Joined<["-"], "fc++-abi=">, Group<f_clang_Group>, Visibility<[ClangOption, CC1Option]>, HelpText<"C++ ABI to use. This will override the target C++ ABI.">; diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index dca05b4..7e8f4f2 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -15256,3 +15256,93 @@ bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl, ThunksToBeAbbreviated[VirtualMethodDecl] = std::move(SimplifiedThunkNames); return Result; } + +bool ASTContext::arePFPFieldsTriviallyCopyable(const RecordDecl *RD) const { + bool IsPAuthSupported = + getTargetInfo().getTriple().getArch() == llvm::Triple::aarch64; + if (!IsPAuthSupported) + return true; + if (getLangOpts().PointerFieldProtectionTagged) + return !isa<CXXRecordDecl>(RD) || + cast<CXXRecordDecl>(RD)->hasTrivialDestructor(); + return true; +} + +void ASTContext::findPFPFields(QualType Ty, CharUnits Offset, + std::vector<PFPField> &Fields, + bool IncludeVBases, bool IsWithinUnion) const { + if (auto *AT = getAsConstantArrayType(Ty)) { + if (auto *ElemDecl = AT->getElementType()->getAsCXXRecordDecl()) { + const ASTRecordLayout &ElemRL = getASTRecordLayout(ElemDecl); + for (unsigned i = 0; i != AT->getSize(); ++i) { + findPFPFields(AT->getElementType(), Offset + i * ElemRL.getSize(), + Fields, true); + } + } + } + auto *Decl = Ty->getAsCXXRecordDecl(); + // isPFPType() is inherited from bases and members (including via arrays), so + // we can early exit if it is false. + if (!Decl || !Decl->isPFPType()) + return; + IsWithinUnion |= Decl->isUnion(); + const ASTRecordLayout &RL = getASTRecordLayout(Decl); + for (FieldDecl *field : Decl->fields()) { + CharUnits fieldOffset = + Offset + toCharUnitsFromBits(RL.getFieldOffset(field->getFieldIndex())); + if (isPFPField(field)) + Fields.push_back({fieldOffset, field, IsWithinUnion}); + findPFPFields(field->getType(), fieldOffset, Fields, true, IsWithinUnion); + } + for (auto &Base : Decl->bases()) { + if (Base.isVirtual()) + continue; + CharUnits BaseOffset = + Offset + RL.getBaseClassOffset(Base.getType()->getAsCXXRecordDecl()); + findPFPFields(Base.getType(), BaseOffset, Fields, false, IsWithinUnion); + } + if (IncludeVBases) { + for (auto &Base : Decl->vbases()) { + CharUnits BaseOffset = + Offset + RL.getVBaseClassOffset(Base.getType()->getAsCXXRecordDecl()); + findPFPFields(Base.getType(), BaseOffset, Fields, false, IsWithinUnion); + } + } +} + +bool ASTContext::hasPFPFields(QualType Ty) const { + std::vector<PFPField> PFPFields; + findPFPFields(Ty, CharUnits::Zero(), PFPFields, true); + return !PFPFields.empty(); +} + +bool ASTContext::isPFPField(const FieldDecl *FD) const { + auto *RD = dyn_cast<CXXRecordDecl>(FD->getParent()); + if (!RD || !RD->isPFPType()) + return false; + return FD->getType()->isPointerType() && + !FD->hasAttr<NoFieldProtectionAttr>(); +} + +void ASTContext::recordMemberDataPointerEvaluation(const ValueDecl *VD) { + if (!getLangOpts().PointerFieldProtection) + return; + auto *FD = dyn_cast<FieldDecl>(VD); + if (!FD) + FD = cast<FieldDecl>(cast<IndirectFieldDecl>(VD)->chain().back()); + if (!isPFPField(FD)) + return; + PFPFieldsWithEvaluatedOffset.insert(FD); +} + +void ASTContext::recordOffsetOfEvaluation(const OffsetOfExpr *E) { + if (!getLangOpts().PointerFieldProtection || E->getNumComponents() == 0) + return; + OffsetOfNode Comp = E->getComponent(E->getNumComponents() - 1); + if (Comp.getKind() != OffsetOfNode::Field) + return; + FieldDecl *FD = Comp.getField(); + if (!isPFPField(FD)) + return; + PFPFieldsWithEvaluatedOffset.insert(FD); +} diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index aa1f5a1..d08a807 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -109,9 +109,9 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) ImplicitCopyAssignmentHasConstParam(true), HasDeclaredCopyConstructorWithConstParam(false), HasDeclaredCopyAssignmentWithConstParam(false), - IsAnyDestructorNoReturn(false), IsHLSLIntangible(false), IsLambda(false), - IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false), - HasODRHash(false), Definition(D) {} + IsAnyDestructorNoReturn(false), IsHLSLIntangible(false), IsPFPType(false), + IsLambda(false), IsParsingBaseSpecifiers(false), + ComputedVisibleConversions(false), HasODRHash(false), Definition(D) {} CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const { return Bases.get(Definition->getASTContext().getExternalSource()); @@ -456,6 +456,9 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, if (!BaseClassDecl->allowConstDefaultInit()) data().HasUninitializedFields = true; + if (BaseClassDecl->isPFPType()) + data().IsPFPType = true; + addedClassSubobject(BaseClassDecl); } @@ -1408,6 +1411,9 @@ void CXXRecordDecl::addedMember(Decl *D) { if (FieldRec->hasVariantMembers() && Field->isAnonymousStructOrUnion()) data().HasVariantMembers = true; + + if (FieldRec->isPFPType()) + data().IsPFPType = true; } } else { // Base element type of field is a non-class type. @@ -2305,6 +2311,14 @@ void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) { } setHasUninitializedExplicitInitFields(false); } + + if (getLangOpts().PointerFieldProtection && !isStandardLayout()) { + data().IsPFPType = true; + } else if (hasAttr<PointerFieldProtectionAttr>()) { + data().IsPFPType = true; + data().IsStandardLayout = false; + data().IsCXX11StandardLayout = false; + } } bool CXXRecordDecl::mayBeAbstract() const { diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index e7dc1d1..bf88c74 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -15410,6 +15410,7 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( } bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) { + Info.Ctx.recordOffsetOfEvaluation(OOE); CharUnits Result; unsigned n = OOE->getNumComponents(); if (n == 0) diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 54ca42d..4ea78d2 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2167,6 +2167,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::CFISalt: OS << "cfi_salt(\"" << cast<CFISaltAttr>(T->getAttr())->getSalt() << "\")"; break; + case attr::NoFieldProtection: + OS << "no_field_protection"; + break; + case attr::PointerFieldProtection: + OS << "pointer_field_protection"; + break; } OS << "))"; } diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 16c0591..6f6fade 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4575,18 +4575,48 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, Address Dest = EmitPointerWithAlignment(E->getArg(0)); Address Src = EmitPointerWithAlignment(E->getArg(1)); Value *SizeVal = EmitScalarExpr(E->getArg(2)); + Value *TypeSize = ConstantInt::get( + SizeVal->getType(), + getContext() + .getTypeSizeInChars(E->getArg(0)->getType()->getPointeeType()) + .getQuantity()); if (BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_trivially_relocate) - SizeVal = Builder.CreateMul( - SizeVal, - ConstantInt::get( - SizeVal->getType(), - getContext() - .getTypeSizeInChars(E->getArg(0)->getType()->getPointeeType()) - .getQuantity())); + SizeVal = Builder.CreateMul(SizeVal, TypeSize); EmitArgCheck(TCK_Store, Dest, E->getArg(0), 0); EmitArgCheck(TCK_Load, Src, E->getArg(1), 1); auto *I = Builder.CreateMemMove(Dest, Src, SizeVal, false); addInstToNewSourceAtom(I, nullptr); + if (BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_trivially_relocate) { + if (getContext().hasPFPFields( + E->getArg(0)->getType()->getPointeeType())) { + BasicBlock *Entry = Builder.GetInsertBlock(); + BasicBlock *Loop = createBasicBlock("loop"); + BasicBlock *LoopEnd = createBasicBlock("loop.end"); + Builder.CreateCondBr( + Builder.CreateICmpEQ(SizeVal, + ConstantInt::get(SizeVal->getType(), 0)), + LoopEnd, Loop); + + EmitBlock(Loop); + PHINode *Offset = Builder.CreatePHI(SizeVal->getType(), 2); + Offset->addIncoming(ConstantInt::get(SizeVal->getType(), 0), Entry); + Address DestRec = Dest.withPointer( + Builder.CreateInBoundsGEP(Int8Ty, Dest.getBasePointer(), {Offset}), + KnownNonNull); + Address SrcRec = Src.withPointer( + Builder.CreateInBoundsGEP(Int8Ty, Src.getBasePointer(), {Offset}), + KnownNonNull); + emitPFPTrivialRelocation(DestRec, SrcRec, + E->getArg(0)->getType()->getPointeeType()); + + Value *NextOffset = Builder.CreateAdd(Offset, TypeSize); + Offset->addIncoming(NextOffset, Loop); + Builder.CreateCondBr(Builder.CreateICmpEQ(NextOffset, SizeVal), LoopEnd, + Loop); + + EmitBlock(LoopEnd); + } + } return RValue::get(Dest, *this); } case Builtin::BImemset: diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 5b0dd23..77a93e6 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -1310,6 +1310,61 @@ static llvm::Value *CoerceIntOrPtrToIntOrPtr(llvm::Value *Val, llvm::Type *Ty, return Val; } +static llvm::Value *CreatePFPCoercedLoad(Address Src, QualType SrcFETy, + llvm::Type *Ty, CodeGenFunction &CGF) { + // Coercion directly through memory does not work if the structure has pointer + // field protection because the struct in registers has a different bit + // pattern to the struct in memory, so we must read the elements one by one + // and use them to form the coerced structure. + std::vector<PFPField> PFPFields; + CGF.getContext().findPFPFields(SrcFETy, CharUnits::Zero(), PFPFields, true); + if (PFPFields.empty()) + return nullptr; + + auto LoadCoercedField = [&](CharUnits Offset, + llvm::Type *FieldType) -> llvm::Value * { + if (!PFPFields.empty() && PFPFields[0].offset == Offset) { + auto fieldAddr = CGF.EmitAddressOfPFPField(Src, PFPFields[0]); + llvm::Value *FieldVal = CGF.Builder.CreateLoad(fieldAddr); + if (isa<llvm::IntegerType>(FieldType)) + FieldVal = CGF.Builder.CreatePtrToInt(FieldVal, FieldType); + PFPFields.erase(PFPFields.begin()); + return FieldVal; + } + auto FieldAddr = + CGF.Builder + .CreateConstInBoundsByteGEP(Src.withElementType(CGF.Int8Ty), Offset) + .withElementType(FieldType); + return CGF.Builder.CreateLoad(FieldAddr); + }; + if (isa<llvm::IntegerType>(Ty) || isa<llvm::PointerType>(Ty)) { + auto Addr = CGF.EmitAddressOfPFPField(Src, PFPFields[0]); + llvm::Value *Val = CGF.Builder.CreateLoad(Addr); + if (isa<llvm::IntegerType>(Ty)) + Val = CGF.Builder.CreatePtrToInt(Val, Ty); + return Val; + } + if (auto *AT = dyn_cast<llvm::ArrayType>(Ty)) { + auto *ET = AT->getElementType(); + CharUnits wordSize = CGF.getContext().toCharUnitsFromBits( + CGF.CGM.getDataLayout().getTypeSizeInBits(ET)); + CharUnits Offset = CharUnits::Zero(); + llvm::Value *Val = llvm::PoisonValue::get(AT); + for (unsigned i = 0; i != AT->getNumElements(); ++i, Offset += wordSize) + Val = CGF.Builder.CreateInsertValue(Val, LoadCoercedField(Offset, ET), i); + return Val; + } + auto *ST = cast<llvm::StructType>(Ty); + llvm::Value *Val = llvm::PoisonValue::get(ST); + auto *SL = CGF.CGM.getDataLayout().getStructLayout(ST); + for (unsigned i = 0; i != ST->getNumElements(); ++i) { + CharUnits Offset = CharUnits::fromQuantity(SL->getElementOffset(i)); + Val = CGF.Builder.CreateInsertValue( + Val, LoadCoercedField(Offset, ST->getElementType(i)), i); + } + return Val; +} + /// CreateCoercedLoad - Create a load from \arg SrcPtr interpreted as /// a pointer to an object of type \arg Ty, known to be aligned to /// \arg SrcAlign bytes. @@ -1317,7 +1372,8 @@ static llvm::Value *CoerceIntOrPtrToIntOrPtr(llvm::Value *Val, llvm::Type *Ty, /// This safely handles the case when the src type is smaller than the /// destination type; in this situation the values of bits which not /// present in the src are undefined. -static llvm::Value *CreateCoercedLoad(Address Src, llvm::Type *Ty, +static llvm::Value *CreateCoercedLoad(Address Src, QualType SrcFETy, + llvm::Type *Ty, CodeGenFunction &CGF) { llvm::Type *SrcTy = Src.getElementType(); @@ -1325,6 +1381,9 @@ static llvm::Value *CreateCoercedLoad(Address Src, llvm::Type *Ty, if (SrcTy == Ty) return CGF.Builder.CreateLoad(Src); + if (llvm::Value *V = CreatePFPCoercedLoad(Src, SrcFETy, Ty, CGF)) + return V; + llvm::TypeSize DstSize = CGF.CGM.getDataLayout().getTypeAllocSize(Ty); if (llvm::StructType *SrcSTy = dyn_cast<llvm::StructType>(SrcTy)) { @@ -1396,7 +1455,60 @@ static llvm::Value *CreateCoercedLoad(Address Src, llvm::Type *Ty, return CGF.Builder.CreateLoad(Tmp); } -void CodeGenFunction::CreateCoercedStore(llvm::Value *Src, Address Dst, +static bool CreatePFPCoercedStore(llvm::Value *Src, QualType SrcFETy, + Address Dst, CodeGenFunction &CGF) { + // Coercion directly through memory does not work if the structure has pointer + // field protection because the struct passed by value has a different bit + // pattern to the struct in memory, so we must read the elements one by one + // and use them to form the coerced structure. + std::vector<PFPField> PFPFields; + CGF.getContext().findPFPFields(SrcFETy, CharUnits::Zero(), PFPFields, true); + if (PFPFields.empty()) + return false; + + llvm::Type *SrcTy = Src->getType(); + auto StoreCoercedField = [&](CharUnits Offset, llvm::Value *FieldVal) { + if (!PFPFields.empty() && PFPFields[0].offset == Offset) { + auto fieldAddr = CGF.EmitAddressOfPFPField(Dst, PFPFields[0]); + if (isa<llvm::IntegerType>(FieldVal->getType())) + FieldVal = CGF.Builder.CreateIntToPtr(FieldVal, CGF.VoidPtrTy); + CGF.Builder.CreateStore(FieldVal, fieldAddr); + PFPFields.erase(PFPFields.begin()); + } else { + auto fieldAddr = + CGF.Builder + .CreateConstInBoundsByteGEP(Dst.withElementType(CGF.Int8Ty), Offset) + .withElementType(FieldVal->getType()); + CGF.Builder.CreateStore(FieldVal, fieldAddr); + } + }; + + if (isa<llvm::IntegerType>(SrcTy) || isa<llvm::PointerType>(SrcTy)) { + if (isa<llvm::IntegerType>(SrcTy)) + Src = CGF.Builder.CreateIntToPtr(Src, CGF.VoidPtrTy); + auto Addr = CGF.EmitAddressOfPFPField(Dst, PFPFields[0]); + CGF.Builder.CreateStore(Src, Addr); + } else if (auto *AT = dyn_cast<llvm::ArrayType>(SrcTy)) { + auto *ET = AT->getElementType(); + CharUnits WordSize = CGF.getContext().toCharUnitsFromBits( + CGF.CGM.getDataLayout().getTypeSizeInBits(ET)); + CharUnits Offset = CharUnits::Zero(); + for (unsigned i = 0; i != AT->getNumElements(); ++i, Offset += WordSize) + StoreCoercedField(Offset, CGF.Builder.CreateExtractValue(Src, i)); + } else { + auto *ST = cast<llvm::StructType>(SrcTy); + auto *SL = CGF.CGM.getDataLayout().getStructLayout(ST); + for (unsigned i = 0; i != ST->getNumElements(); ++i) { + CharUnits Offset = CharUnits::fromQuantity(SL->getElementOffset(i)); + StoreCoercedField(Offset, CGF.Builder.CreateExtractValue(Src, i)); + } + } + return true; +} + +void CodeGenFunction::CreateCoercedStore(llvm::Value *Src, + QualType SrcFETy, + Address Dst, llvm::TypeSize DstSize, bool DstIsVolatile) { if (!DstSize) @@ -1417,6 +1529,9 @@ void CodeGenFunction::CreateCoercedStore(llvm::Value *Src, Address Dst, } } + if (CreatePFPCoercedStore(Src, SrcFETy, Dst, *this)) + return; + if (SrcSize.isScalable() || SrcSize <= DstSize) { if (SrcTy->isIntegerTy() && Dst.getElementType()->isPointerTy() && SrcSize == CGM.getDataLayout().getTypeAllocSize(Dst.getElementType())) { @@ -3414,6 +3529,13 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, if (SrcSize > DstSize) { Builder.CreateMemCpy(Ptr, AddrToStoreInto, DstSize); } + + // Structures with PFP fields require a coerced store to add any + // pointer signatures. + if (getContext().hasPFPFields(Ty)) { + llvm::Value *Struct = Builder.CreateLoad(Ptr); + CreatePFPCoercedStore(Struct, Ty, Ptr, *this); + } } } else { // Simple case, just do a coerced store of the argument into the alloca. @@ -3421,7 +3543,7 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, auto AI = Fn->getArg(FirstIRArg); AI->setName(Arg->getName() + ".coerce"); CreateCoercedStore( - AI, Ptr, + AI, Ty, Ptr, llvm::TypeSize::getFixed( getContext().getTypeSizeInChars(Ty).getQuantity() - ArgI.getDirectOffset()), @@ -4073,7 +4195,7 @@ void CodeGenFunction::EmitFunctionEpilog( // If the value is offset in memory, apply the offset now. Address V = emitAddressAtOffset(*this, ReturnValue, RetAI); - RV = CreateCoercedLoad(V, RetAI.getCoerceToType(), *this); + RV = CreateCoercedLoad(V, RetTy, RetAI.getCoerceToType(), *this); } // In ARC, end functions that return a retainable type with a call @@ -4123,6 +4245,7 @@ void CodeGenFunction::EmitFunctionEpilog( auto eltAddr = Builder.CreateStructGEP(addr, i); llvm::Value *elt = CreateCoercedLoad( eltAddr, + RetTy, unpaddedStruct ? unpaddedStruct->getElementType(unpaddedIndex++) : unpaddedCoercionType, *this); @@ -5608,15 +5731,25 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } else { uint64_t SrcSize = SrcTypeSize.getFixedValue(); uint64_t DstSize = DstTypeSize.getFixedValue(); + bool HasPFPFields = getContext().hasPFPFields(I->Ty); // If the source type is smaller than the destination type of the // coerce-to logic, copy the source value into a temp alloca the size // of the destination type to allow loading all of it. The bits past // the source value are left undef. - if (SrcSize < DstSize) { + if (HasPFPFields || SrcSize < DstSize) { Address TempAlloca = CreateTempAlloca(STy, Src.getAlignment(), Src.getName() + ".coerce"); - Builder.CreateMemCpy(TempAlloca, Src, SrcSize); + if (HasPFPFields) { + // Structures with PFP fields require a coerced load to remove any + // pointer signatures. + Builder.CreateStore( + CreatePFPCoercedLoad(Src, I->Ty, ArgInfo.getCoerceToType(), + *this), + TempAlloca); + } else { + Builder.CreateMemCpy(TempAlloca, Src, SrcSize); + } Src = TempAlloca; } else { Src = Src.withElementType(STy); @@ -5635,7 +5768,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, // In the simple case, just pass the coerced loaded value. assert(NumIRArgs == 1); llvm::Value *Load = - CreateCoercedLoad(Src, ArgInfo.getCoerceToType(), *this); + CreateCoercedLoad(Src, I->Ty, ArgInfo.getCoerceToType(), *this); if (CallInfo.isCmseNSCall()) { // For certain parameter types, clear padding bits, as they may reveal @@ -5696,6 +5829,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, Address eltAddr = Builder.CreateStructGEP(addr, i); llvm::Value *elt = CreateCoercedLoad( eltAddr, + I->Ty, unpaddedStruct ? unpaddedStruct->getElementType(unpaddedIndex++) : unpaddedCoercionType, *this); @@ -6211,7 +6345,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, // If the value is offset in memory, apply the offset now. Address StorePtr = emitAddressAtOffset(*this, DestPtr, RetAI); CreateCoercedStore( - CI, StorePtr, + CI, RetTy, StorePtr, llvm::TypeSize::getFixed(DestSize - RetAI.getDirectOffset()), DestIsVolatile); } diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index bae55aa..fd99202 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -585,12 +585,20 @@ static void EmitBaseInitializer(CodeGenFunction &CGF, isBaseVirtual); } -static bool isMemcpyEquivalentSpecialMember(const CXXMethodDecl *D) { +static bool isMemcpyEquivalentSpecialMember(CodeGenModule &CGM, + const CXXMethodDecl *D) { auto *CD = dyn_cast<CXXConstructorDecl>(D); if (!(CD && CD->isCopyOrMoveConstructor()) && !D->isCopyAssignmentOperator() && !D->isMoveAssignmentOperator()) return false; + // Non-trivially-copyable fields with pointer field protection need to be + // copied one by one. + if (!CGM.getContext().arePFPFieldsTriviallyCopyable(D->getParent()) && + CGM.getContext().hasPFPFields( + CGM.getContext().getCanonicalTagType(D->getParent()))) + return false; + // We can emit a memcpy for a trivial copy or move constructor/assignment. if (D->isTrivial() && !D->getParent()->mayInsertExtraPadding()) return true; @@ -656,7 +664,7 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, QualType BaseElementTy = CGF.getContext().getBaseElementType(Array); CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(MemberInit->getInit()); if (BaseElementTy.isPODType(CGF.getContext()) || - (CE && isMemcpyEquivalentSpecialMember(CE->getConstructor()))) { + (CE && isMemcpyEquivalentSpecialMember(CGF.CGM, CE->getConstructor()))) { unsigned SrcArgIndex = CGF.CGM.getCXXABI().getSrcArgforCopyCtor(Constructor, Args); llvm::Value *SrcPtr @@ -923,6 +931,11 @@ namespace { if (PointerAuthQualifier Q = F->getType().getPointerAuth(); Q && Q.isAddressDiscriminated()) return false; + // Non-trivially-copyable fields with pointer field protection need to be + // copied one by one. + if (!CGF.getContext().arePFPFieldsTriviallyCopyable(ClassDecl) && + CGF.getContext().isPFPField(F)) + return false; return true; } @@ -1060,7 +1073,8 @@ namespace { CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(MemberInit->getInit()); // Bail out on non-memcpyable, not-trivially-copyable members. - if (!(CE && isMemcpyEquivalentSpecialMember(CE->getConstructor())) && + if (!(CE && + isMemcpyEquivalentSpecialMember(CGF.CGM, CE->getConstructor())) && !(FieldType.isTriviallyCopyableType(CGF.getContext()) || FieldType->isReferenceType())) return false; @@ -1171,7 +1185,7 @@ namespace { return nullptr; } else if (CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(S)) { CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(MCE->getCalleeDecl()); - if (!(MD && isMemcpyEquivalentSpecialMember(MD))) + if (!(MD && isMemcpyEquivalentSpecialMember(CGF.CGM, MD))) return nullptr; MemberExpr *IOA = dyn_cast<MemberExpr>(MCE->getImplicitObjectArgument()); if (!IOA) @@ -2138,7 +2152,7 @@ void CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, // If this is a trivial constructor, emit a memcpy now before we lose // the alignment information on the argument. // FIXME: It would be better to preserve alignment information into CallArg. - if (isMemcpyEquivalentSpecialMember(D)) { + if (isMemcpyEquivalentSpecialMember(CGM, D)) { assert(E->getNumArgs() == 1 && "unexpected argcount for trivial ctor"); const Expr *Arg = E->getArg(0); @@ -2206,7 +2220,7 @@ void CodeGenFunction::EmitCXXConstructorCall( // If this is a trivial constructor, just emit what's needed. If this is a // union copy constructor, we must emit a memcpy, because the AST does not // model that copy. - if (isMemcpyEquivalentSpecialMember(D)) { + if (isMemcpyEquivalentSpecialMember(CGM, D)) { assert(Args.size() == 2 && "unexpected argcount for trivial ctor"); QualType SrcTy = D->getParamDecl(0)->getType().getNonReferenceType(); Address Src = makeNaturalAddressForPointer( diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 844b445..70eae95 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5191,12 +5191,13 @@ static Address emitAddrOfZeroSizeField(CodeGenFunction &CGF, Address Base, return CGF.Builder.CreateConstInBoundsByteGEP(Base, Offset); } -/// Drill down to the storage of a field without walking into -/// reference types. +/// Drill down to the storage of a field without walking into reference types, +/// and without respect for pointer field protection. /// /// The resulting address doesn't necessarily have the right type. -static Address emitAddrOfFieldStorage(CodeGenFunction &CGF, Address base, - const FieldDecl *field, bool IsInBounds) { +static Address emitRawAddrOfFieldStorage(CodeGenFunction &CGF, Address base, + const FieldDecl *field, + bool IsInBounds) { if (isEmptyFieldForLayout(CGF.getContext(), field)) return emitAddrOfZeroSizeField(CGF, base, field, IsInBounds); @@ -5211,6 +5212,16 @@ static Address emitAddrOfFieldStorage(CodeGenFunction &CGF, Address base, return CGF.Builder.CreateStructGEP(base, idx, field->getName()); } +static Address emitAddrOfFieldStorage(CodeGenFunction &CGF, Address base, + const FieldDecl *field, bool IsInBounds) { + Address Addr = emitRawAddrOfFieldStorage(CGF, base, field, IsInBounds); + + if (!CGF.getContext().isPFPField(field)) + return Addr; + + return CGF.EmitAddressOfPFPField(base, Addr, field); +} + static Address emitPreserveStructAccess(CodeGenFunction &CGF, LValue base, Address addr, const FieldDecl *field) { const RecordDecl *rec = field->getParent(); diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index b8150a2..ad38a763 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -138,7 +138,7 @@ public: if (llvm::Value *Result = ConstantEmitter(CGF).tryEmitConstantExpr(E)) { CGF.CreateCoercedStore( - Result, Dest.getAddress(), + Result, E->getType(), Dest.getAddress(), llvm::TypeSize::getFixed( Dest.getPreferredSize(CGF.getContext(), E->getType()) .getQuantity()), @@ -2391,6 +2391,7 @@ void CodeGenFunction::EmitAggregateCopy(LValue Dest, LValue Src, QualType Ty, auto *Inst = Builder.CreateMemCpy(DestPtr, SrcPtr, SizeVal, isVolatile); addInstToCurrentSourceAtom(Inst, nullptr); + emitPFPTrivialRelocation(DestPtr, SrcPtr, Ty); // Determine the metadata to describe the position of any padding in this // memcpy, as well as the TBAA tags for the members of the struct, in case diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index b44dd9e..c135b99 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -31,6 +31,7 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalVariable.h" +#include "llvm/Support/SipHash.h" #include <optional> using namespace clang; using namespace CodeGen; @@ -905,6 +906,29 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD, if (!EltInit) return false; + if (CGM.getContext().isPFPField(*Field)) { + llvm::ConstantInt *Disc; + llvm::Constant *AddrDisc; + if (CGM.getContext().arePFPFieldsTriviallyCopyable(RD)) { + uint64_t FieldSignature = + llvm::getPointerAuthStableSipHash(CGM.getPFPFieldName(*Field)); + Disc = llvm::ConstantInt::get(CGM.Int64Ty, FieldSignature); + AddrDisc = llvm::ConstantPointerNull::get(CGM.VoidPtrTy); + } else if (Emitter.isAbstract()) { + return false; + } else { + Disc = llvm::ConstantInt::get( + CGM.Int64Ty, (-(Layout.getFieldOffset(FieldNo) / 8)) & 0xffff); + AddrDisc = Emitter.getCurrentAddrPrivate(); + } + EltInit = llvm::ConstantPtrAuth::get( + EltInit, llvm::ConstantInt::get(CGM.Int32Ty, 2), Disc, AddrDisc, + CGM.getPFPDeactivationSymbol(*Field)); + if (!CGM.getContext().arePFPFieldsTriviallyCopyable(RD)) + Emitter.registerCurrentAddrPrivate(EltInit, + cast<llvm::GlobalValue>(AddrDisc)); + } + if (ZeroInitPadding) { if (!DoZeroInitPadding(Layout, FieldNo, **Field, AllowOverwrite, SizeSoFar, ZeroFieldSize)) @@ -1655,7 +1679,20 @@ ConstantEmitter::emitAbstract(SourceLocation loc, const APValue &value, llvm::Constant *ConstantEmitter::tryEmitForInitializer(const VarDecl &D) { initializeNonAbstract(D.getType().getAddressSpace()); - return markIfFailed(tryEmitPrivateForVarInit(D)); + llvm::Constant *Init = tryEmitPrivateForVarInit(D); + + // If a placeholder address was needed for a TLS variable, implying that the + // initializer's value depends on its address, then the object may not be + // initialized in .tdata because the initializer will be memcpy'd to the + // thread's TLS. Instead the initialization must be done in code. + if (!PlaceholderAddresses.empty() && D.getTLSKind() != VarDecl::TLS_None) { + for (auto &entry : PlaceholderAddresses) + entry.second->eraseFromParent(); + PlaceholderAddresses.clear(); + Init = nullptr; + } + + return markIfFailed(Init); } llvm::Constant *ConstantEmitter::tryEmitForInitializer(const Expr *E, @@ -2610,6 +2647,7 @@ CodeGenModule::getMemberPointerConstant(const UnaryOperator *uo) { return getCXXABI().EmitMemberFunctionPointer(method); // Otherwise, a member data pointer. + getContext().recordMemberDataPointerEvaluation(decl); uint64_t fieldOffset = getContext().getFieldOffset(decl); CharUnits chars = getContext().toCharUnitsFromBits((int64_t) fieldOffset); return getCXXABI().EmitMemberDataPointer(type, chars); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index b2fe917..14f31a4 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -46,6 +46,7 @@ #include "llvm/IR/Intrinsics.h" #include "llvm/IR/MDBuilder.h" #include "llvm/Support/CRC.h" +#include "llvm/Support/SipHash.h" #include "llvm/Support/xxhash.h" #include "llvm/Transforms/Scalar/LowerExpectIntrinsic.h" #include "llvm/Transforms/Utils/PromoteMemToReg.h" @@ -2218,6 +2219,45 @@ static void emitNonZeroVLAInit(CodeGenFunction &CGF, QualType baseType, CGF.EmitBlock(contBB); } +Address CodeGenFunction::EmitAddressOfPFPField(Address RecordPtr, + const PFPField &Field) { + return EmitAddressOfPFPField( + RecordPtr, + Builder.CreateConstInBoundsByteGEP(RecordPtr.withElementType(Int8Ty), + Field.offset), + Field.field); +} + +Address CodeGenFunction::EmitAddressOfPFPField(Address RecordPtr, + Address PtrPtr, + const FieldDecl *Field) { + llvm::Value *Disc; + bool IsPAuthSupported = getContext().getTargetInfo().getTriple().getArch() == + llvm::Triple::aarch64; + if (!IsPAuthSupported || + CGM.getContext().arePFPFieldsTriviallyCopyable(Field->getParent())) { + uint64_t FieldSignature = + llvm::getPointerAuthStableSipHash(CGM.getPFPFieldName(Field)); + if (!IsPAuthSupported) + FieldSignature &= 0xff; + Disc = llvm::ConstantInt::get(CGM.Int64Ty, FieldSignature); + } else { + Disc = Builder.CreatePtrToInt(RecordPtr.getBasePointer(), CGM.Int64Ty); + } + + llvm::GlobalValue *DS = CGM.getPFPDeactivationSymbol(Field); + llvm::OperandBundleDef DSBundle("deactivation-symbol", DS); + + return Address( + Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::protected_field_ptr, + PtrPtr.getType()), + {PtrPtr.getBasePointer(), Disc, + IsPAuthSupported ? Builder.getTrue() : Builder.getFalse()}, + DSBundle), + VoidPtrTy, PtrPtr.getAlignment()); +} + void CodeGenFunction::EmitNullInitialization(Address DestPtr, QualType Ty) { // Ignore empty classes in C++. @@ -2277,13 +2317,22 @@ CodeGenFunction::EmitNullInitialization(Address DestPtr, QualType Ty) { // Get and call the appropriate llvm.memcpy overload. Builder.CreateMemCpy(DestPtr, SrcPtr, SizeVal, false); - return; + } else { + // Otherwise, just memset the whole thing to zero. This is legal + // because in LLVM, all default initializers (other than the ones we just + // handled above, and the case handled below) are guaranteed to have a bit + // pattern of all zeros. + Builder.CreateMemSet(DestPtr, Builder.getInt8(0), SizeVal, false); } - // Otherwise, just memset the whole thing to zero. This is legal - // because in LLVM, all default initializers (other than the ones we just - // handled above) are guaranteed to have a bit pattern of all zeros. - Builder.CreateMemSet(DestPtr, Builder.getInt8(0), SizeVal, false); + // With the pointer field protection feature, null pointers do not have a bit + // pattern of zero in memory, so we must initialize them separately. + std::vector<PFPField> PFPFields; + getContext().findPFPFields(Ty, CharUnits::Zero(), PFPFields, true); + for (auto &Field : PFPFields) { + auto addr = EmitAddressOfPFPField(DestPtr, Field); + Builder.CreateStore(llvm::ConstantPointerNull::get(VoidPtrTy), addr); + } } llvm::BlockAddress *CodeGenFunction::GetAddrOfLabel(const LabelDecl *L) { @@ -3386,3 +3435,16 @@ void CodeGenFunction::addInstToNewSourceAtom(llvm::Instruction *KeyInstruction, DI->addInstToCurrentSourceAtom(KeyInstruction, Backup); } } + +void CodeGenFunction::emitPFPTrivialRelocation(Address DestPtr, Address SrcPtr, + QualType Ty) { + std::vector<PFPField> PFPFields; + getContext().findPFPFields(Ty, CharUnits::Zero(), PFPFields, true); + for (auto &Field : PFPFields) { + if (getContext().arePFPFieldsTriviallyCopyable(Field.field->getParent())) + continue; + auto DestFieldPtr = EmitAddressOfPFPField(DestPtr, Field); + auto SrcFieldPtr = EmitAddressOfPFPField(SrcPtr, Field); + Builder.CreateStore(Builder.CreateLoad(SrcFieldPtr), DestFieldPtr); + } +} diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index c02ac18..0e7d410 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -1708,6 +1708,10 @@ public: void addInstToNewSourceAtom(llvm::Instruction *KeyInstruction, llvm::Value *Backup); + /// Trivially relocate all PFP fields from SrcPtr (of type Ty) to DestPtr, + /// assuming that DestPtr was already memcpy'd from SrcPtr. + void emitPFPTrivialRelocation(Address DestPtr, Address SrcPtr, QualType Ty); + private: /// SwitchInsn - This is nearest current switch instruction. It is null if /// current context is not in a switch. @@ -5018,8 +5022,8 @@ public: /// Create a store to \arg DstPtr from \arg Src, truncating the stored value /// to at most \arg DstSize bytes. - void CreateCoercedStore(llvm::Value *Src, Address Dst, llvm::TypeSize DstSize, - bool DstIsVolatile); + void CreateCoercedStore(llvm::Value *Src, QualType SrcFETy, Address Dst, + llvm::TypeSize DstSize, bool DstIsVolatile); /// EmitExtendGCLifetime - Given a pointer to an Objective-C object, /// make sure it survives garbage collection until this point. @@ -5514,6 +5518,10 @@ public: void EmitRISCVMultiVersionResolver(llvm::Function *Resolver, ArrayRef<FMVResolverOption> Options); + Address EmitAddressOfPFPField(Address RecordPtr, const PFPField &Field); + Address EmitAddressOfPFPField(Address RecordPtr, Address FieldPtr, + const FieldDecl *Field); + private: QualType getVarArgType(const Expr *Arg); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 323823c..c28537d 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -960,6 +960,7 @@ void CodeGenModule::Release() { applyGlobalValReplacements(); applyReplacements(); emitMultiVersionFunctions(); + emitPFPFieldsWithEvaluatedOffset(); if (Context.getLangOpts().IncrementalExtensions && GlobalTopLevelStmtBlockInFlight.first) { @@ -4610,6 +4611,35 @@ void CodeGenModule::emitMultiVersionFunctions() { emitMultiVersionFunctions(); } +llvm::GlobalValue *CodeGenModule::getPFPDeactivationSymbol(const FieldDecl *FD) { + std::string DSName = "__pfp_ds_" + getPFPFieldName(FD); + llvm::GlobalValue *DS = TheModule.getNamedValue(DSName); + if (!DS) { + DS = new llvm::GlobalVariable(TheModule, Int8Ty, false, + llvm::GlobalVariable::ExternalWeakLinkage, + nullptr, DSName); + DS->setVisibility(llvm::GlobalValue::HiddenVisibility); + } + return DS; +} + +void CodeGenModule::emitPFPFieldsWithEvaluatedOffset() { + llvm::Constant *Nop = llvm::ConstantExpr::getIntToPtr( + llvm::ConstantInt::get(Int64Ty, 0xd503201f), VoidPtrTy); + for (auto *FD : getContext().PFPFieldsWithEvaluatedOffset) { + std::string DSName = "__pfp_ds_" + getPFPFieldName(FD); + llvm::GlobalValue *OldDS = TheModule.getNamedValue(DSName); + llvm::GlobalValue *DS = llvm::GlobalAlias::create( + Int8Ty, 0, llvm::GlobalValue::ExternalLinkage, DSName, Nop, &TheModule); + DS->setVisibility(llvm::GlobalValue::HiddenVisibility); + if (OldDS) { + DS->takeName(OldDS); + OldDS->replaceAllUsesWith(DS); + OldDS->eraseFromParent(); + } + } +} + static void replaceDeclarationWith(llvm::GlobalValue *Old, llvm::Constant *New) { assert(cast<llvm::Function>(Old)->isDeclaration() && "Not a declaration"); @@ -8147,3 +8177,12 @@ void CodeGenModule::moveLazyEmissionStates(CodeGenModule *NewBuilder) { NewBuilder->ABI->MangleCtx = std::move(ABI->MangleCtx); } + +std::string CodeGenModule::getPFPFieldName(const FieldDecl *FD) { + std::string OutName; + llvm::raw_string_ostream Out(OutName); + getCXXABI().getMangleContext().mangleCanonicalTypeName( + getContext().getCanonicalTagType(FD->getParent()), Out, false); + Out << "." << FD->getName(); + return OutName; +} diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index b4b3a17..db73f23 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1830,6 +1830,9 @@ public: return TrapReasonBuilder(&getDiags(), DiagID, TR); } + std::string getPFPFieldName(const FieldDecl *FD); + llvm::GlobalValue *getPFPDeactivationSymbol(const FieldDecl *FD); + private: bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const; @@ -2024,6 +2027,10 @@ private: llvm::Metadata *CreateMetadataIdentifierImpl(QualType T, MetadataTypeMap &Map, StringRef Suffix); + + /// Emit deactivation symbols for any PFP fields whose offset is taken with + /// offsetof. + void emitPFPFieldsWithEvaluatedOffset(); }; } // end namespace CodeGen diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 885b700..dfe8c69 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -1245,6 +1245,7 @@ llvm::Constant *ItaniumCXXABI::EmitMemberPointer(const APValue &MP, return pointerAuthResignMemberFunctionPointer(Src, MPType, SrcType, CGM); } + getContext().recordMemberDataPointerEvaluation(MPD); CharUnits FieldOffset = getContext().toCharUnitsFromBits(getContext().getFieldOffset(MPD)); return EmitMemberDataPointer(MPT, ThisAdjustment + FieldOffset); diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index 88f0648..8ffcfe4 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -2920,6 +2920,7 @@ llvm::Constant *MicrosoftCXXABI::EmitMemberPointer(const APValue &MP, // the class in which it was declared, and convert from there if necessary. // For indirect field decls, get the outermost anonymous field and use the // parent class. + Ctx.recordMemberDataPointerEvaluation(MPD); CharUnits FieldOffset = Ctx.toCharUnitsFromBits(Ctx.getFieldOffset(MPD)); const FieldDecl *FD = dyn_cast<FieldDecl>(MPD); if (!FD) diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index d22feae..2a5170a 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7624,6 +7624,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Twine("-funique-source-file-identifier=") + Input.getBaseInput())); } + if (!IsCudaDevice) { + Args.addOptInFlag(CmdArgs, + options::OPT_fexperimental_pointer_field_protection, + options::OPT_fno_experimental_pointer_field_protection); + Args.addOptInFlag( + CmdArgs, options::OPT_fexperimental_pointer_field_protection_tagged, + options::OPT_fno_experimental_pointer_field_protection_tagged); + } + // Setup statistics file output. SmallString<128> StatsFile = getStatsFileName(Args, Output, Input, D); if (!StatsFile.empty()) { diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 4865c0b..76bea60 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -1526,6 +1526,11 @@ static void InitializePredefinedMacros(const TargetInfo &TI, if (LangOpts.Sanitize.has(SanitizerKind::Thread)) Builder.defineMacro("__SANITIZE_THREAD__"); + if (LangOpts.PointerFieldProtection) + Builder.defineMacro("__POINTER_FIELD_PROTECTION__"); + if (LangOpts.PointerFieldProtectionTagged) + Builder.defineMacro("__POINTER_FIELD_PROTECTION_TAGGED__"); + // Target OS macro definitions. if (PPOpts.DefineTargetOSMacros) { const llvm::Triple &Triple = TI.getTriple(); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 3ded60c..b54a8ef 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6386,6 +6386,10 @@ static void handleZeroCallUsedRegsAttr(Sema &S, Decl *D, const ParsedAttr &AL) { D->addAttr(ZeroCallUsedRegsAttr::Create(S.Context, Kind, AL)); } +static void handleNoPFPAttrField(Sema &S, Decl *D, const ParsedAttr &AL) { + D->addAttr(NoFieldProtectionAttr::Create(S.Context, AL)); +} + static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) { auto *FD = dyn_cast<FieldDecl>(D); assert(FD); @@ -7411,6 +7415,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, handleCountedByAttrField(S, D, AL); break; + case ParsedAttr::AT_NoFieldProtection: + handleNoPFPAttrField(S, D, AL); + break; + // Microsoft attributes: case ParsedAttr::AT_LayoutVersion: handleLayoutVersion(S, D, AL); diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 3755233..ede411d 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -234,6 +234,22 @@ static bool IsEligibleForReplacement(Sema &SemaRef, const CXXRecordDecl *D) { return !D->hasDeletedDestructor(); } +static bool IsImplementationDefinedNonRelocatable(Sema &SemaRef, + const CXXRecordDecl *D) { + // The implementation-defined carveout only exists for polymorphic types. + if (!D->isPolymorphic()) + return false; + + std::vector<PFPField> pfpFields; + SemaRef.Context.findPFPFields(SemaRef.Context.getCanonicalTagType(D), + CharUnits::Zero(), pfpFields, true); + for (PFPField f : pfpFields) + if (f.isWithinUnion) + return true; + + return false; +} + ASTContext::CXXRecordDeclRelocationInfo Sema::CheckCXX2CRelocatableAndReplaceable(const CXXRecordDecl *D) { ASTContext::CXXRecordDeclRelocationInfo Info{false, false}; @@ -275,6 +291,11 @@ Sema::CheckCXX2CRelocatableAndReplaceable(const CXXRecordDecl *D) { if (!IsEligibleForTrivialRelocation(*this, D)) return false; + // if it does not meet implementation-defined criteria for inhibiting + // trivially-relocatable, + if (IsImplementationDefinedNonRelocatable(*this, D)) + return false; + // has the trivially_relocatable_if_eligible class-property-specifier, if (D->hasAttr<TriviallyRelocatableAttr>()) return true; diff --git a/clang/test/CodeGenCXX/pfp-attribute-disable.cpp b/clang/test/CodeGenCXX/pfp-attribute-disable.cpp new file mode 100644 index 0000000..bb0011a --- /dev/null +++ b/clang/test/CodeGenCXX/pfp-attribute-disable.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck %s + + +struct S { + int* ptr1; + __attribute__((no_field_protection)) int* ptr2; +private: + int private_data; +}; // Not Standard-layout, mixed access + +// CHECK-LABEL: load_pointers_without_no_field_protection +int* load_pointers_without_no_field_protection(S *t) { + return t->ptr1; +} +// CHECK: call {{.*}} @llvm.protected.field.ptr.p0{{.*}} + +// CHECK-LABEL: load_pointers_with_no_field_protection +int* load_pointers_with_no_field_protection(S *t) { + return t->ptr2; +} +// CHECK-NOT: call {{.*}} @llvm.protected.field.ptr.p0{{.*}} + +// CHECK-LABEL: store_pointers_without_no_field_protection +void store_pointers_without_no_field_protection(S *t, int *input) { + t->ptr1 = input; +} +// CHECK: call {{.*}} @llvm.protected.field.ptr.p0{{.*}} + +// CHECK-LABEL: store_pointers_with_no_field_protection +void store_pointers_with_no_field_protection(S *t, int *input) { + t->ptr2 = input; +} +// CHECK-NOT: call {{.*}} @llvm.protected.field.ptr.p0{{.*}} diff --git a/clang/test/CodeGenCXX/pfp-coerce.cpp b/clang/test/CodeGenCXX/pfp-coerce.cpp new file mode 100644 index 0000000..770d749 --- /dev/null +++ b/clang/test/CodeGenCXX/pfp-coerce.cpp @@ -0,0 +1,222 @@ +// RUN: %clang_cc1 -triple aarch64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,AARCH64 %s +// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,X86_64 %s + +// Non-standard layout. Pointer fields are signed and discriminated by type. +struct Pointer { + int* ptr; +private: + int private_data; +}; + +void pass_pointer_callee(Pointer p); + +// CHECK: define dso_local void @_Z12pass_pointerP7Pointer( +void pass_pointer(Pointer *pp) { + // CHECK: %0 = load ptr, ptr %pp.addr, align 8 + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp, ptr align 8 %0, i64 16, i1 false) + // CHECK: %1 = getelementptr inbounds i8, ptr %agg.tmp, i64 0 + + // AARCH64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 36403, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ] + // X86_64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 51, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ] + + // CHECK: %3 = load ptr, ptr %2, align 8 + + // AARCH64: %4 = ptrtoint ptr %3 to i64 + // AARCH64: %5 = insertvalue [2 x i64] poison, i64 %4, 0 + // AARCH64: %6 = getelementptr inbounds i8, ptr %agg.tmp, i64 8 + // AARCH64: %7 = load i64, ptr %6, align 8 + // AARCH64: %8 = insertvalue [2 x i64] %5, i64 %7, 1 + // AARCH64: call void @_Z19pass_pointer_callee7Pointer([2 x i64] %8) + + // X86_64: %4 = insertvalue { ptr, i32 } poison, ptr %3, 0 + // X86_64: %5 = getelementptr inbounds i8, ptr %agg.tmp, i64 8 + // X86_64: %6 = load i32, ptr %5, align 8 + // X86_64: %7 = insertvalue { ptr, i32 } %4, i32 %6, 1 + // X86_64: store { ptr, i32 } %7, ptr %agg.tmp.coerce, align 8 + // X86_64: %8 = getelementptr inbounds nuw { ptr, i32 }, ptr %agg.tmp.coerce, i32 0, i32 0 + // X86_64: %9 = load ptr, ptr %8, align 8 + // X86_64: %10 = getelementptr inbounds nuw { ptr, i32 }, ptr %agg.tmp.coerce, i32 0, i32 1 + // X86_64: %11 = load i32, ptr %10, align 8 + // X86_64: call void @_Z19pass_pointer_callee7Pointer(ptr %9, i32 %11) + pass_pointer_callee(*pp); +} + +// AARCH64: define dso_local void @_Z14passed_pointer7PointerPS_([2 x i64] %p.coerce, ptr noundef %pp) +// X86_64: define dso_local void @_Z14passed_pointer7PointerPS_(ptr %p.coerce0, i32 %p.coerce1, ptr noundef %pp) +void passed_pointer(Pointer p, Pointer *pp) { + // AARCH64: %p = alloca %struct.Pointer, align 8 + // AARCH64: %pp.addr = alloca ptr, align 8 + // AARCH64: %0 = extractvalue [2 x i64] %p.coerce, 0 + // AARCH64: %1 = getelementptr inbounds i8, ptr %p, i64 0 + // AARCH64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 36403, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ] + // AARCH64: %3 = inttoptr i64 %0 to ptr + // AARCH64: store ptr %3, ptr %2, align 8 + // AARCH64: %4 = extractvalue [2 x i64] %p.coerce, 1 + // AARCH64: %5 = getelementptr inbounds i8, ptr %p, i64 8 + // AARCH64: store i64 %4, ptr %5, align 8 + // AARCH64: store ptr %pp, ptr %pp.addr, align 8 + // AARCH64: %6 = load ptr, ptr %pp.addr, align 8 + // AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %6, ptr align 8 %p, i64 12, i1 false) + + // X86_64: %p = alloca %struct.Pointer, align 8 + // X86_64: %pp.addr = alloca ptr, align 8 + // X86_64: %0 = getelementptr inbounds nuw { ptr, i32 }, ptr %p, i32 0, i32 0 + // X86_64: store ptr %p.coerce0, ptr %0, align 8 + // X86_64: %1 = getelementptr inbounds nuw { ptr, i32 }, ptr %p, i32 0, i32 1 + // X86_64: store i32 %p.coerce1, ptr %1, align 8 + // X86_64: %2 = load %struct.Pointer, ptr %p, align 8 + // X86_64: %3 = extractvalue %struct.Pointer %2, 0 + // X86_64: %4 = getelementptr inbounds i8, ptr %p, i64 0 + // X86_64: %5 = call ptr @llvm.protected.field.ptr.p0(ptr %4, i64 51, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ] + // X86_64: store ptr %3, ptr %5, align 8 + // X86_64: %6 = extractvalue %struct.Pointer %2, 1 + // X86_64: %7 = getelementptr inbounds i8, ptr %p, i64 8 + // X86_64: store i32 %6, ptr %7, align 8 + // X86_64: %8 = extractvalue %struct.Pointer %2, 2 + // X86_64: %9 = getelementptr inbounds i8, ptr %p, i64 12 + // X86_64: store [4 x i8] %8, ptr %9, align 4 + // X86_64: store ptr %pp, ptr %pp.addr, align 8 + // X86_64: %10 = load ptr, ptr %pp.addr, align 8 + // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %10, ptr align 8 %p, i64 12, i1 false) + *pp = p; +} + +// AARCH64: define dso_local [2 x i64] @_Z14return_pointerP7Pointer(ptr noundef %pp) +// X86_64: define dso_local { ptr, i32 } @_Z14return_pointerP7Pointer(ptr noundef %pp) +Pointer return_pointer(Pointer *pp) { + // AARCH64: %retval = alloca %struct.Pointer, align 8 + // AARCH64: %pp.addr = alloca ptr, align 8 + // AARCH64: store ptr %pp, ptr %pp.addr, align 8 + // AARCH64: %0 = load ptr, ptr %pp.addr, align 8 + // AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %retval, ptr align 8 %0, i64 16, i1 false) + // AARCH64: %1 = getelementptr inbounds i8, ptr %retval, i64 0 + // AARCH64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 36403, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ] + // AARCH64: %3 = load ptr, ptr %2, align 8 + // AARCH64: %4 = ptrtoint ptr %3 to i64 + // AARCH64: %5 = insertvalue [2 x i64] poison, i64 %4, 0 + // AARCH64: %6 = getelementptr inbounds i8, ptr %retval, i64 8 + // AARCH64: %7 = load i64, ptr %6, align 8 + // AARCH64: %8 = insertvalue [2 x i64] %5, i64 %7, 1 + // AARCH64: ret [2 x i64] %8 + + // X86_64: %retval = alloca %struct.Pointer, align 8 + // X86_64: %pp.addr = alloca ptr, align 8 + // X86_64: store ptr %pp, ptr %pp.addr, align 8 + // X86_64: %0 = load ptr, ptr %pp.addr, align 8 + // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %retval, ptr align 8 %0, i64 16, i1 false) + // X86_64: %1 = getelementptr inbounds i8, ptr %retval, i64 0 + // X86_64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 51, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ] + // X86_64: %3 = load ptr, ptr %2, align 8 + // X86_64: %4 = insertvalue { ptr, i32 } poison, ptr %3, 0 + // X86_64: %5 = getelementptr inbounds i8, ptr %retval, i64 8 + // X86_64: %6 = load i32, ptr %5, align 8 + // X86_64: %7 = insertvalue { ptr, i32 } %4, i32 %6, 1 + // X86_64: ret { ptr, i32 } %7 + return *pp; +} + +Pointer returned_pointer_callee(); + +// CHECK: define dso_local void @_Z16returned_pointerP7Pointer(ptr noundef %pp) +void returned_pointer(Pointer *pp) { + // AARCH64: %pp.addr = alloca ptr, align 8 + // AARCH64: %ref.tmp = alloca %struct.Pointer, align 8 + // AARCH64: store ptr %pp, ptr %pp.addr, align 8 + // AARCH64: %call = call [2 x i64] @_Z23returned_pointer_calleev() + // AARCH64: %0 = extractvalue [2 x i64] %call, 0 + // AARCH64: %1 = getelementptr inbounds i8, ptr %ref.tmp, i64 0 + // AARCH64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 36403, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ] + // AARCH64: %3 = inttoptr i64 %0 to ptr + // AARCH64: store ptr %3, ptr %2, align 8 + // AARCH64: %4 = extractvalue [2 x i64] %call, 1 + // AARCH64: %5 = getelementptr inbounds i8, ptr %ref.tmp, i64 8 + // AARCH64: store i64 %4, ptr %5, align 8 + // AARCH64: %6 = load ptr, ptr %pp.addr, align 8 + // AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %6, ptr align 8 %ref.tmp, i64 12, i1 false) + + // X86_64: %pp.addr = alloca ptr, align 8 + // X86_64: %ref.tmp = alloca %struct.Pointer, align 8 + // X86_64: store ptr %pp, ptr %pp.addr, align 8 + // X86_64: %call = call { ptr, i32 } @_Z23returned_pointer_calleev() + // X86_64: %0 = extractvalue { ptr, i32 } %call, 0 + // X86_64: %1 = getelementptr inbounds i8, ptr %ref.tmp, i64 0 + // X86_64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 51, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ] + // X86_64: store ptr %0, ptr %2, align 8 + // X86_64: %3 = extractvalue { ptr, i32 } %call, 1 + // X86_64: %4 = getelementptr inbounds i8, ptr %ref.tmp, i64 8 + // X86_64: store i32 %3, ptr %4, align 8 + // X86_64: %5 = load ptr, ptr %pp.addr, align 8 + // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %5, ptr align 8 %ref.tmp, i64 12, i1 false) + *pp = returned_pointer_callee(); +} + +// Manual opt into PFP, non-trivially destructible. +// Pointer fields are signed and discriminated by address. +// Trivial ABI: passed and returned by value despite being non-trivial. +struct [[clang::trivial_abi]] [[clang::pointer_field_protection]] TrivialAbiPointer { + int *ptr; + ~TrivialAbiPointer(); +}; + +// CHECK: define dso_local void @_Z24pass_trivial_abi_pointer17TrivialAbiPointerPS_(ptr %p.coerce, ptr noundef %pp) +void pass_trivial_abi_pointer(TrivialAbiPointer p, TrivialAbiPointer *pp) { + // AARCH64: %p = alloca %struct.TrivialAbiPointer, align 8 + // AARCH64: %pp.addr = alloca ptr, align 8 + // AARCH64: %coerce.dive = getelementptr inbounds nuw %struct.TrivialAbiPointer, ptr %p, i32 0, i32 0 + // AARCH64: %0 = getelementptr inbounds i8, ptr %coerce.dive, i64 0 + // AARCH64: %1 = ptrtoint ptr %coerce.dive to i64 + // AARCH64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %0, i64 %1, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ] + // AARCH64: store ptr %p.coerce, ptr %2, align 8 + // AARCH64: store ptr %pp, ptr %pp.addr, align 8 + // AARCH64: %3 = load ptr, ptr %pp.addr, align 8 + // AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %3, ptr align 8 %p, i64 8, i1 false) + // AARCH64: %4 = getelementptr inbounds i8, ptr %3, i64 0 + // AARCH64: %5 = ptrtoint ptr %3 to i64 + // AARCH64: %6 = call ptr @llvm.protected.field.ptr.p0(ptr %4, i64 %5, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ] + // AARCH64: %7 = getelementptr inbounds i8, ptr %p, i64 0 + // AARCH64: %8 = ptrtoint ptr %p to i64 + // AARCH64: %9 = call ptr @llvm.protected.field.ptr.p0(ptr %7, i64 %8, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ] + // AARCH64: %10 = load ptr, ptr %9, align 8 + // AARCH64: store ptr %10, ptr %6, align 8 + // AARCH64: call void @_ZN17TrivialAbiPointerD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %p) + + // X86_64: %p = alloca %struct.TrivialAbiPointer, align 8 + // X86_64: %pp.addr = alloca ptr, align 8 + // X86_64: %coerce.dive = getelementptr inbounds nuw %struct.TrivialAbiPointer, ptr %p, i32 0, i32 0 + // X86_64: %0 = getelementptr inbounds i8, ptr %coerce.dive, i64 0 + // X86_64: %1 = call ptr @llvm.protected.field.ptr.p0(ptr %0, i64 33, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ] + // X86_64: store ptr %p.coerce, ptr %1, align 8 + // X86_64: store ptr %pp, ptr %pp.addr, align 8 + // X86_64: %2 = load ptr, ptr %pp.addr, align 8 + // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %2, ptr align 8 %p, i64 8, i1 false) + // X86_64: call void @_ZN17TrivialAbiPointerD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %p) + *pp = p; +} + +// AARCH64: define dso_local i64 @_Z26return_trivial_abi_pointerP17TrivialAbiPointer(ptr noundef %pp) +// X86_64: define dso_local ptr @_Z26return_trivial_abi_pointerP17TrivialAbiPointer(ptr noundef %pp) +TrivialAbiPointer return_trivial_abi_pointer(TrivialAbiPointer *pp) { + // AARCH64: %retval = alloca %struct.TrivialAbiPointer, align 8 + // AARCH64: %pp.addr = alloca ptr, align 8 + // AARCH64: store ptr %pp, ptr %pp.addr, align 8 + // AARCH64: %0 = load ptr, ptr %pp.addr, align 8 + // AARCH64: call void @_ZN17TrivialAbiPointerC1ERKS_(ptr noundef nonnull align 8 dereferenceable(8) %retval, ptr noundef nonnull align 8 dereferenceable(8) %0) + // AARCH64: %1 = getelementptr inbounds i8, ptr %retval, i64 0 + // AARCH64: %2 = ptrtoint ptr %retval to i64 + // AARCH64: %3 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 %2, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ] + // AARCH64: %4 = load ptr, ptr %3, align 8 + // AARCH64: %5 = ptrtoint ptr %4 to i64 + // AARCH64: ret i64 %5 + + // X86_64: %retval = alloca %struct.TrivialAbiPointer, align 8 + // X86_64: %pp.addr = alloca ptr, align 8 + // X86_64: store ptr %pp, ptr %pp.addr, align 8 + // X86_64: %0 = load ptr, ptr %pp.addr, align 8 + // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %retval, ptr align 8 %0, i64 8, i1 false) + // X86_64: %1 = getelementptr inbounds i8, ptr %retval, i64 0 + // X86_64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 33, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ] + // X86_64: %3 = load ptr, ptr %2, align 8 + // X86_64: ret ptr %3 + return *pp; +} + diff --git a/clang/test/CodeGenCXX/pfp-load-store.cpp b/clang/test/CodeGenCXX/pfp-load-store.cpp new file mode 100644 index 0000000..86dd328 --- /dev/null +++ b/clang/test/CodeGenCXX/pfp-load-store.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -O1 -o - %s | FileCheck %s + +int val; + +struct Pointer { + int* ptr; +private: + int private_data; +}; + +struct ArrayType { + int* array[3]; +private: + int private_data; +}; + +struct Array { + ArrayType array; +private: + int private_data; +}; + +struct Struct { + Pointer ptr; +}; + +// CHECK-LABEL: test_pointer +Pointer test_pointer(Pointer t) { + t.ptr = &val; + return t; +} +// CHECK: call {{.*}} @llvm.protected.field.ptr.p0{{.*}} + + + +// CHECK-LABEL: test_struct +int* test_struct(Struct *t) { + return (t->ptr).ptr; +} +// CHECK: call {{.*}} @llvm.protected.field.ptr.p0{{.*}} diff --git a/clang/test/CodeGenCXX/pfp-member-pointer-offsetof.cpp b/clang/test/CodeGenCXX/pfp-member-pointer-offsetof.cpp new file mode 100644 index 0000000..34009c8 --- /dev/null +++ b/clang/test/CodeGenCXX/pfp-member-pointer-offsetof.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -fexperimental-pointer-field-protection -o - %s | FileCheck %s + +// CHECK: @__pfp_ds__ZTS1S.ptr1 = hidden alias i8, inttoptr (i64 3573751839 to ptr) +// CHECK: @__pfp_ds__ZTS1S.ptr2 = hidden alias i8, inttoptr (i64 3573751839 to ptr) + +struct [[clang::pointer_field_protection]] S { + int *ptr1; + int *ptr2; +}; + +void f() { + &S::ptr1; + __builtin_offsetof(S, ptr2); +} diff --git a/clang/test/CodeGenCXX/pfp-memcpy.cpp b/clang/test/CodeGenCXX/pfp-memcpy.cpp new file mode 100644 index 0000000..53e96a1 --- /dev/null +++ b/clang/test/CodeGenCXX/pfp-memcpy.cpp @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -triple aarch64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck %s + +struct ClassWithTrivialCopy { + ClassWithTrivialCopy(); + ~ClassWithTrivialCopy(); + void *a; +private: + void *c; +}; + +// Make sure that trivial assignments and copies include protected field copies. +// CHECK-LABEL: define dso_local void @_Z14trivial_assignP20ClassWithTrivialCopyS0_ +void trivial_assign(ClassWithTrivialCopy *s1, ClassWithTrivialCopy *s2) { + // CHECK: %0 = load ptr, ptr %s2.addr, align 8 + // CHECK-NEXT: %1 = load ptr, ptr %s1.addr, align 8 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %1, ptr align 8 %0, i64 16, i1 false) + // CHECK-NEXT: %2 = getelementptr inbounds i8, ptr %1, i64 0 + // CHECK-NEXT: %3 = ptrtoint ptr %1 to i64 + // CHECK-NEXT: %4 = call ptr @llvm.protected.field.ptr.p0(ptr %2, i64 %3, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.a) ] + // CHECK-NEXT: %5 = getelementptr inbounds i8, ptr %0, i64 0 + // CHECK-NEXT: %6 = ptrtoint ptr %0 to i64 + // CHECK-NEXT: %7 = call ptr @llvm.protected.field.ptr.p0(ptr %5, i64 %6, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.a) ] + // CHECK-NEXT: %8 = load ptr, ptr %7, align 8 + // CHECK-NEXT: store ptr %8, ptr %4, align 8 + // CHECK-NEXT: %9 = getelementptr inbounds i8, ptr %1, i64 8 + // CHECK-NEXT: %10 = ptrtoint ptr %1 to i64 + // CHECK-NEXT: %11 = call ptr @llvm.protected.field.ptr.p0(ptr %9, i64 %10, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.c) ] + // CHECK-NEXT: %12 = getelementptr inbounds i8, ptr %0, i64 8 + // CHECK-NEXT: %13 = ptrtoint ptr %0 to i64 + // CHECK-NEXT: %14 = call ptr @llvm.protected.field.ptr.p0(ptr %12, i64 %13, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.c) ] + // CHECK-NEXT: %15 = load ptr, ptr %14, align 8 + // CHECK-NEXT: store ptr %15, ptr %11, align 8 + *s1 = *s2; +} + +void trivial_copy(ClassWithTrivialCopy *s1) { + ClassWithTrivialCopy s2(*s1); +} + +// CHECK-LABEL: define linkonce_odr void @_ZN20ClassWithTrivialCopyC2ERKS_ +// CHECK: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK-NEXT: %a = getelementptr inbounds nuw %struct.ClassWithTrivialCopy, ptr %this1, i32 0, i32 0 +// CHECK-NEXT: %1 = ptrtoint ptr %this1 to i64 +// CHECK-NEXT: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %a, i64 %1, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.a) ] +// CHECK-NEXT: %3 = load ptr, ptr %.addr, align 8, !nonnull !2, !align !3 +// CHECK-NEXT: %a2 = getelementptr inbounds nuw %struct.ClassWithTrivialCopy, ptr %3, i32 0, i32 0 +// CHECK-NEXT: %4 = ptrtoint ptr %3 to i64 +// CHECK-NEXT: %5 = call ptr @llvm.protected.field.ptr.p0(ptr %a2, i64 %4, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.a) ] +// CHECK-NEXT: %6 = load ptr, ptr %5, align 8 +// CHECK-NEXT: store ptr %6, ptr %2, align 8 +// CHECK-NEXT: %c = getelementptr inbounds nuw %struct.ClassWithTrivialCopy, ptr %this1, i32 0, i32 1 +// CHECK-NEXT: %7 = ptrtoint ptr %this1 to i64 +// CHECK-NEXT: %8 = call ptr @llvm.protected.field.ptr.p0(ptr %c, i64 %7, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.c) ] +// CHECK-NEXT: %9 = load ptr, ptr %.addr, align 8, !nonnull !2, !align !3 +// CHECK-NEXT: %c3 = getelementptr inbounds nuw %struct.ClassWithTrivialCopy, ptr %9, i32 0, i32 1 +// CHECK-NEXT: %10 = ptrtoint ptr %9 to i64 +// CHECK-NEXT: %11 = call ptr @llvm.protected.field.ptr.p0(ptr %c3, i64 %10, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.c) ] +// CHECK-NEXT: %12 = load ptr, ptr %11, align 8 +// CHECK-NEXT: store ptr %12, ptr %8, align 8 diff --git a/clang/test/CodeGenCXX/pfp-null-init.cpp b/clang/test/CodeGenCXX/pfp-null-init.cpp new file mode 100644 index 0000000..f08bedf --- /dev/null +++ b/clang/test/CodeGenCXX/pfp-null-init.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -triple aarch64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,AARCH64 %s +// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,X86_64 %s + +struct S { + void *p; +private: + int private_data; +}; + +// CHECK-LABEL: null_init +void null_init() { + // Check that null initialization was correctly applied to the pointer field. + // CHECK: %s = alloca %struct.S, align 8 + // CHECK: call void @llvm.memset.p0.i64(ptr align 8 %s, i8 0, i64 16, i1 false) + // CHECK: %0 = getelementptr inbounds i8, ptr %s, i64 0 + // AARCH64: %1 = call ptr @llvm.protected.field.ptr.p0(ptr %0, i64 29832, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.p) ] + // X86_64: %1 = call ptr @llvm.protected.field.ptr.p0(ptr %0, i64 136, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.p) ] + // CHECK: store ptr null, ptr %1, align 8 + S s{}; +} + diff --git a/clang/test/CodeGenCXX/pfp-struct-gep.cpp b/clang/test/CodeGenCXX/pfp-struct-gep.cpp new file mode 100644 index 0000000..be4e7eb --- /dev/null +++ b/clang/test/CodeGenCXX/pfp-struct-gep.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -triple aarch64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,AARCH64 +// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,X86_64 + +struct S { + int* ptr; +private: + int private_data; +}; // Not Standard-layout, mixed access + +// CHECK-LABEL: load_pointers +int* load_pointers(S *t) { + // CHECK: %t.addr = alloca ptr, align 8 + // CHECK: store ptr %t, ptr %t.addr, align 8 + // CHECK: %0 = load ptr, ptr %t.addr, align 8 + // CHECK: %ptr = getelementptr inbounds nuw %struct.S, ptr %0, i32 0, i32 0 + // AARCH64: %1 = call ptr @llvm.protected.field.ptr.p0(ptr %ptr, i64 63261, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.ptr) ] + // X86_64: %1 = call ptr @llvm.protected.field.ptr.p0(ptr %ptr, i64 29, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.ptr) ] + // CHECK: %2 = load ptr, ptr %1, align 8 + // CHECK: ret ptr %2 + return t->ptr; +} + +// CHECK-LABEL: store_pointers +void store_pointers(S* t, int* p) { + // CHECK: %t.addr = alloca ptr, align 8 + // CHECK: %p.addr = alloca ptr, align 8 + // CHECK: store ptr %t, ptr %t.addr, align 8 + // CHECK: store ptr %p, ptr %p.addr, align 8 + // CHECK: %0 = load ptr, ptr %p.addr, align 8 + // CHECK: %1 = load ptr, ptr %t.addr, align 8 + // CHECK: %ptr = getelementptr inbounds nuw %struct.S, ptr %1, i32 0, i32 0 + // AARCH64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %ptr, i64 63261, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.ptr) ] + // X86_64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %ptr, i64 29, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.ptr) ] + // CHECK: store ptr %0, ptr %2, align 8 + t->ptr = p; +} diff --git a/clang/test/CodeGenCXX/pfp-trivially-relocatable.cpp b/clang/test/CodeGenCXX/pfp-trivially-relocatable.cpp new file mode 100644 index 0000000..b14cb11 --- /dev/null +++ b/clang/test/CodeGenCXX/pfp-trivially-relocatable.cpp @@ -0,0 +1,101 @@ +// RUN: %clang_cc1 -std=c++26 -triple x86_64-linux-gnu -emit-llvm -fexperimental-pointer-field-protection -o - %s | FileCheck --check-prefix=RELOC %s +// RUN: %clang_cc1 -std=c++26 -triple x86_64-linux-gnu -emit-llvm -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -o - %s | FileCheck --check-prefix=RELOC %s +// RUN: %clang_cc1 -std=c++26 -triple aarch64-linux-gnu -emit-llvm -fexperimental-pointer-field-protection -o - %s | FileCheck --check-prefix=RELOC %s +// RUN: %clang_cc1 -std=c++26 -triple aarch64-linux-gnu -emit-llvm -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -o - %s | FileCheck --check-prefix=NONRELOC %s + +typedef __SIZE_TYPE__ size_t; + +struct S trivially_relocatable_if_eligible { + S(const S&); + ~S(); + int* a; +private: + int* b; +}; + +// CHECK: define dso_local void @_Z5test1P1SS0_( +void test1(S* source, S* dest) { + // RELOC: %0 = load ptr, ptr %dest.addr, align 8 + // RELOC-NEXT: %1 = load ptr, ptr %source.addr, align 8 + // RELOC-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 8 %0, ptr align 8 %1, i64 16, i1 false) + // RELOC-NOT: @llvm.protected.field.ptr.p0 + + // NONRELOC: %0 = load ptr, ptr %dest.addr, align 8 + // NONRELOC-NEXT: %1 = load ptr, ptr %source.addr, align 8 + // NONRELOC-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 8 %0, ptr align 8 %1, i64 16, i1 false) + // NONRELOC-NEXT: br i1 false, label %loop.end, label %loop + + // NONRELOC: loop: + // NONRELOC-NEXT: %2 = phi i64 [ 0, %entry ], [ %19, %loop ] + // NONRELOC-NEXT: %3 = getelementptr inbounds i8, ptr %0, i64 %2 + // NONRELOC-NEXT: %4 = getelementptr inbounds i8, ptr %1, i64 %2 + // NONRELOC-NEXT: %5 = getelementptr inbounds i8, ptr %3, i64 0 + // NONRELOC-NEXT: %6 = ptrtoint ptr %3 to i64 + // NONRELOC-NEXT: %7 = call ptr @llvm.protected.field.ptr.p0(ptr %5, i64 %6, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.a) ] + // NONRELOC-NEXT: %8 = getelementptr inbounds i8, ptr %4, i64 0 + // NONRELOC-NEXT: %9 = ptrtoint ptr %4 to i64 + // NONRELOC-NEXT: %10 = call ptr @llvm.protected.field.ptr.p0(ptr %8, i64 %9, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.a) ] + // NONRELOC-NEXT: %11 = load ptr, ptr %10, align 8 + // NONRELOC-NEXT: store ptr %11, ptr %7, align 8 + // NONRELOC-NEXT: %12 = getelementptr inbounds i8, ptr %3, i64 8 + // NONRELOC-NEXT: %13 = ptrtoint ptr %3 to i64 + // NONRELOC-NEXT: %14 = call ptr @llvm.protected.field.ptr.p0(ptr %12, i64 %13, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.b) ] + // NONRELOC-NEXT: %15 = getelementptr inbounds i8, ptr %4, i64 8 + // NONRELOC-NEXT: %16 = ptrtoint ptr %4 to i64 + // NONRELOC-NEXT: %17 = call ptr @llvm.protected.field.ptr.p0(ptr %15, i64 %16, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.b) ] + // NONRELOC-NEXT: %18 = load ptr, ptr %17, align 8 + // NONRELOC-NEXT: store ptr %18, ptr %14, align 8 + // NONRELOC-NEXT: %19 = add i64 %2, 16 + // NONRELOC-NEXT: %20 = icmp eq i64 %19, 16 + // NONRELOC-NEXT: br i1 %20, label %loop.end, label %loop + + // NONRELOC: loop.end: + // NONRELOC-NEXT: ret void + __builtin_trivially_relocate(dest, source, 1); +} + +// CHECK: define dso_local void @_Z5testNP1SS0_m( +void testN(S* source, S* dest, size_t count) { + // RELOC: %0 = load ptr, ptr %dest.addr, align 8 + // RELOC-NEXT: %1 = load ptr, ptr %source.addr, align 8 + // RELOC-NEXT: %2 = load i64, ptr %count.addr, align 8 + // RELOC-NEXT: %3 = mul i64 %2, 16 + // RELOC-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 8 %0, ptr align 8 %1, i64 %3, i1 false) + // RELOC-NOT: @llvm.protected.field.ptr.p0 + + // NONRELOC: %0 = load ptr, ptr %dest.addr, align 8 + // NONRELOC-NEXT: %1 = load ptr, ptr %source.addr, align 8 + // NONRELOC-NEXT: %2 = load i64, ptr %count.addr, align 8 + // NONRELOC-NEXT: %3 = mul i64 %2, 16 + // NONRELOC-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 8 %0, ptr align 8 %1, i64 %3, i1 false) + // NONRELOC-NEXT: %4 = icmp eq i64 %3, 0 + // NONRELOC-NEXT: br i1 %4, label %loop.end, label %loop + + // NONRELOC: loop: + // NONRELOC-NEXT: %5 = phi i64 [ 0, %entry ], [ %22, %loop ] + // NONRELOC-NEXT: %6 = getelementptr inbounds i8, ptr %0, i64 %5 + // NONRELOC-NEXT: %7 = getelementptr inbounds i8, ptr %1, i64 %5 + // NONRELOC-NEXT: %8 = getelementptr inbounds i8, ptr %6, i64 0 + // NONRELOC-NEXT: %9 = ptrtoint ptr %6 to i64 + // NONRELOC-NEXT: %10 = call ptr @llvm.protected.field.ptr.p0(ptr %8, i64 %9, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.a) ] + // NONRELOC-NEXT: %11 = getelementptr inbounds i8, ptr %7, i64 0 + // NONRELOC-NEXT: %12 = ptrtoint ptr %7 to i64 + // NONRELOC-NEXT: %13 = call ptr @llvm.protected.field.ptr.p0(ptr %11, i64 %12, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.a) ] + // NONRELOC-NEXT: %14 = load ptr, ptr %13, align 8 + // NONRELOC-NEXT: store ptr %14, ptr %10, align 8 + // NONRELOC-NEXT: %15 = getelementptr inbounds i8, ptr %6, i64 8 + // NONRELOC-NEXT: %16 = ptrtoint ptr %6 to i64 + // NONRELOC-NEXT: %17 = call ptr @llvm.protected.field.ptr.p0(ptr %15, i64 %16, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.b) ] + // NONRELOC-NEXT: %18 = getelementptr inbounds i8, ptr %7, i64 8 + // NONRELOC-NEXT: %19 = ptrtoint ptr %7 to i64 + // NONRELOC-NEXT: %20 = call ptr @llvm.protected.field.ptr.p0(ptr %18, i64 %19, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.b) ] + // NONRELOC-NEXT: %21 = load ptr, ptr %20, align 8 + // NONRELOC-NEXT: store ptr %21, ptr %17, align 8 + // NONRELOC-NEXT: %22 = add i64 %5, 16 + // NONRELOC-NEXT: %23 = icmp eq i64 %22, %3 + // NONRELOC-NEXT: br i1 %23, label %loop.end, label %loop + + // NONRELOC: loop.end: + // NONRELOC-NEXT: ret void + __builtin_trivially_relocate(dest, source, count); +}; diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 37ff33e..bf27201 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -118,6 +118,7 @@ // CHECK-NEXT: NoDestroy (SubjectMatchRule_variable) // CHECK-NEXT: NoDuplicate (SubjectMatchRule_function) // CHECK-NEXT: NoEscape (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: NoFieldProtection (SubjectMatchRule_field) // CHECK-NEXT: NoInline (SubjectMatchRule_function) // CHECK-NEXT: NoInstrumentFunction (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: NoMerge (SubjectMatchRule_function, SubjectMatchRule_variable) @@ -172,6 +173,7 @@ // CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: PatchableFunctionEntry (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union) +// CHECK-NEXT: PointerFieldProtection (SubjectMatchRule_record) // CHECK-NEXT: PreserveNone (SubjectMatchRule_hasType_functionType) // CHECK-NEXT: RandomizeLayout (SubjectMatchRule_record) // CHECK-NEXT: ReadOnlyPlacement (SubjectMatchRule_record) diff --git a/clang/test/Preprocessor/pfp-predefines.c b/clang/test/Preprocessor/pfp-predefines.c new file mode 100644 index 0000000..f2069a67 --- /dev/null +++ b/clang/test/Preprocessor/pfp-predefines.c @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -E -dM -triple aarch64-unknown-linux -fexperimental-pointer-field-protection %s | FileCheck %s --check-prefix=PFP +// RUN: %clang_cc1 -E -dM -triple aarch64-unknown-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged %s | FileCheck %s --check-prefixes=PFP,PFP-TAGGED + +// PFP-TAGGED: #define __POINTER_FIELD_PROTECTION_TAGGED__ 1 +// PFP: #define __POINTER_FIELD_PROTECTION__ 1 |