From: Peter Collingbourne Date: Fri, 20 Feb 2015 20:30:56 +0000 (+0000) Subject: Implement Control Flow Integrity for virtual calls. X-Git-Tag: llvmorg-3.7.0-rc1~11317 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a4ccff32818c05c9f2d7a2a6503866d13636b664;p=platform%2Fupstream%2Fllvm.git Implement Control Flow Integrity for virtual calls. This patch introduces the -fsanitize=cfi-vptr flag, which enables a control flow integrity scheme that checks that virtual calls take place using a vptr of the correct dynamic type. More details in the new docs/ControlFlowIntegrity.rst file. It also introduces the -fsanitize=cfi flag, which is currently a synonym for -fsanitize=cfi-vptr, but will eventually cover all CFI checks implemented in Clang. Differential Revision: http://reviews.llvm.org/D7424 llvm-svn: 230055 --- diff --git a/clang/docs/ControlFlowIntegrity.rst b/clang/docs/ControlFlowIntegrity.rst new file mode 100644 index 0000000..a4c60b3 --- /dev/null +++ b/clang/docs/ControlFlowIntegrity.rst @@ -0,0 +1,74 @@ +====================== +Control Flow Integrity +====================== + +.. toctree:: + :hidden: + + ControlFlowIntegrityDesign + +.. contents:: + :local: + +Introduction +============ + +Clang includes an implementation of a number of control flow integrity (CFI) +schemes, which are designed to abort the program upon detecting certain forms +of undefined behavior that can potentially allow attackers to subvert the +program's control flow. These schemes have been optimized for performance, +allowing developers to enable them in release builds. + +To enable Clang's available CFI schemes, use the flag ``-fsanitize=cfi``. +As currently implemented, CFI relies on link-time optimization (LTO); the CFI +schemes imply ``-flto``, and the linker used must support LTO, for example +via the `gold plugin`_. To allow the checks to be implemented efficiently, +the program must be structured such that certain object files are compiled +with CFI enabled, and are statically linked into the program. This may +preclude the use of shared libraries in some cases. + +Clang currently implements forward-edge CFI for virtual calls. More schemes +are under development. + +.. _gold plugin: http://llvm.org/docs/GoldPlugin.html + +Forward-Edge CFI for Virtual Calls +---------------------------------- + +This scheme checks that virtual calls take place using a vptr of the correct +dynamic type; that is, the dynamic type of the called object must be a +derived class of the static type of the object used to make the call. +This CFI scheme can be enabled on its own using ``-fsanitize=cfi-vptr``. + +For this scheme to work, all translation units containing the definition +of a virtual member function (whether inline or not) must be compiled +with ``-fsanitize=cfi-vptr`` enabled and be statically linked into the +program. Classes in the C++ standard library (under namespace ``std``) are +exempted from checking, and therefore programs may be linked against a +pre-built standard library, but this may change in the future. + +Performance +~~~~~~~~~~~ + +A performance overhead of less than 1% has been measured by running the +Dromaeo benchmark suite against an instrumented version of the Chromium +web browser. Another good performance benchmark for this mechanism is the +virtual-call-heavy SPEC 2006 xalancbmk. + +Note that this scheme has not yet been optimized for binary size; an increase +of up to 15% has been observed for Chromium. + +Design +------ + +Please refer to the :doc:`design document`. + +Publications +------------ + +`Control-Flow Integrity: Principles, Implementations, and Applications `_. +Martin Abadi, Mihai Budiu, Úlfar Erlingsson, Jay Ligatti. + +`Enforcing Forward-Edge Control-Flow Integrity in GCC & LLVM `_. +Caroline Tice, Tom Roeder, Peter Collingbourne, Stephen Checkoway, +Úlfar Erlingsson, Luis Lozano, Geoff Pike. diff --git a/clang/docs/ControlFlowIntegrityDesign.rst b/clang/docs/ControlFlowIntegrityDesign.rst new file mode 100644 index 0000000..2dcb6165 --- /dev/null +++ b/clang/docs/ControlFlowIntegrityDesign.rst @@ -0,0 +1,59 @@ +=========================================== +Control Flow Integrity Design Documentation +=========================================== + +This page documents the design of the :doc:`ControlFlowIntegrity` schemes +supported by Clang. + +Forward-Edge CFI for Virtual Calls +---------------------------------- + +This scheme works by allocating, for each static type used to make a virtual +call, a region of read-only storage in the object file holding a bit vector +that maps onto to the region of storage used for those virtual tables. Each +set bit in the bit vector corresponds to the `address point`_ for a virtual +table compatible with the static type for which the bit vector is being built. + +For example, consider the following three C++ classes: + +.. code-block:: c++ + + struct A { + virtual void f(); + }; + + struct B : A { + virtual void f(); + }; + + struct C : A { + virtual void f(); + }; + +The scheme will cause the virtual tables for A, B and C to be laid out +consecutively: + +.. csv-table:: Virtual Table Layout for A, B, C + :header: 0, 1, 2, 3, 4, 5, 6, 7, 8 + + A::offset-to-top, &A::rtti, &A::f, B::offset-to-top, &B::rtti, &B::f, C::offset-to-top, &C::rtti, &C::f + +The bit vector for static types A, B and C will look like this: + +.. csv-table:: Bit Vectors for A, B, C + :header: Class, 0, 1, 2, 3, 4, 5, 6, 7, 8 + + A, 0, 0, 1, 0, 0, 1, 0, 0, 1 + B, 0, 0, 0, 0, 0, 1, 0, 0, 0 + C, 0, 0, 0, 0, 0, 0, 0, 0, 1 + +To emit a virtual call, the compiler will assemble code that checks that +the object's virtual table pointer is in-bounds and aligned and that the +relevant bit is set in the bit vector. + +The compiler relies on co-operation from the linker in order to assemble +the bit vector for the whole program. It currently does this using LLVM's +`bit sets`_ mechanism together with link-time optimization. + +.. _address point: https://mentorembedded.github.io/cxx-abi/abi.html#vtable-general +.. _bit sets: http://llvm.org/docs/BitSets.html diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 8d5804b..d4a7b3e 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -957,6 +957,8 @@ are listed below. ``unsigned-integer-overflow`` and ``vptr``. - ``-fsanitize=dataflow``: :doc:`DataFlowSanitizer`, a general data flow analysis. + - ``-fsanitize=cfi``: :doc:`control flow integrity ` + checks. Implies ``-flto``. The following more fine-grained checks are also available: @@ -966,6 +968,8 @@ are listed below. ``true`` nor ``false``. - ``-fsanitize=bounds``: Out of bounds array indexing, in cases where the array bound can be statically determined. + - ``-fsanitize=cfi-vptr``: Use of an object whose vptr is of the + wrong dynamic type. Implies ``-flto``. - ``-fsanitize=enum``: Load of a value of an enumerated type which is not in the range of representable values for that enumerated type. diff --git a/clang/docs/index.rst b/clang/docs/index.rst index bf2de7e..67bdf68 100644 --- a/clang/docs/index.rst +++ b/clang/docs/index.rst @@ -27,6 +27,7 @@ Using Clang as a Compiler DataFlowSanitizer LeakSanitizer SanitizerSpecialCaseList + ControlFlowIntegrity Modules MSVCCompatibility FAQ diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h index f6bb12a..20ce32e 100644 --- a/clang/include/clang/AST/Mangle.h +++ b/clang/include/clang/AST/Mangle.h @@ -141,6 +141,9 @@ public: /// across translation units so it can be used with LTO. virtual void mangleTypeName(QualType T, raw_ostream &) = 0; + virtual void mangleCXXVTableBitSet(const CXXRecordDecl *RD, + raw_ostream &) = 0; + /// @} }; diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def index 91a1ef4..a0acce9 100644 --- a/clang/include/clang/Basic/Sanitizers.def +++ b/clang/include/clang/Basic/Sanitizers.def @@ -76,6 +76,10 @@ SANITIZER("unsigned-integer-overflow", UnsignedIntegerOverflow) // DataFlowSanitizer SANITIZER("dataflow", DataFlow) +// Control Flow Integrity +SANITIZER("cfi-vptr", CFIVptr) +SANITIZER_GROUP("cfi", CFI, CFIVptr) + // -fsanitize=undefined includes all the sanitizers which have low overhead, no // ABI or address space layout implications, and only catch undefined behavior. SANITIZER_GROUP("undefined", Undefined, diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h index 18186fd..09521c2 100644 --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -357,8 +357,8 @@ public: /// \p Phase on the \p Input, taking in to account arguments /// like -fsyntax-only or --analyze. std::unique_ptr - ConstructPhaseAction(const llvm::opt::ArgList &Args, phases::ID Phase, - std::unique_ptr Input) const; + ConstructPhaseAction(const ToolChain &TC, const llvm::opt::ArgList &Args, + phases::ID Phase, std::unique_ptr Input) const; /// BuildJobsForAction - Construct the jobs to perform for the /// action \p A. @@ -402,7 +402,7 @@ public: /// handle this action. bool ShouldUseClangCompiler(const JobAction &JA) const; - bool IsUsingLTO(const llvm::opt::ArgList &Args) const; + bool IsUsingLTO(const ToolChain &TC, const llvm::opt::ArgList &Args) const; private: /// \brief Retrieves a ToolChain for a particular target triple. diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h index c2b1b4d..5874c26 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -51,6 +51,7 @@ class SanitizerArgs { bool requiresPIE() const; bool needsUnwindTables() const; + bool needsLTO() const; bool linkCXXRuntimes() const { return LinkCXXRuntimes; } void addArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 95fca00..b2a1b24 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -172,6 +172,8 @@ public: void mangleStringLiteral(const StringLiteral *, raw_ostream &) override; + void mangleCXXVTableBitSet(const CXXRecordDecl *RD, raw_ostream &) override; + bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) { // Lambda closure types are already numbered. if (isLambda(ND)) @@ -4040,6 +4042,22 @@ void ItaniumMangleContextImpl::mangleTypeName(QualType Ty, raw_ostream &Out) { mangleCXXRTTIName(Ty, Out); } +void ItaniumMangleContextImpl::mangleCXXVTableBitSet(const CXXRecordDecl *RD, + raw_ostream &Out) { + Linkage L = RD->getLinkageInternal(); + if (L == InternalLinkage || L == UniqueExternalLinkage) { + // This part of the identifier needs to be unique across all translation + // units in the linked program. The scheme fails if multiple translation + // units are compiled using the same relative source file path, or if + // multiple translation units are built from the same source file. + SourceManager &SM = getASTContext().getSourceManager(); + Out << "[" << SM.getFileEntryForID(SM.getMainFileID())->getName() << "]"; + } + + CXXNameMangler Mangler(*this, Out); + Mangler.mangleType(QualType(RD->getTypeForDecl(), 0)); +} + void ItaniumMangleContextImpl::mangleStringLiteral(const StringLiteral *, raw_ostream &) { llvm_unreachable("Can't mangle string literals"); } diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 4b23886..9dbbe5b 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -138,6 +138,8 @@ public: void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl, raw_ostream &Out) override; void mangleStringLiteral(const StringLiteral *SL, raw_ostream &Out) override; + void mangleCXXVTableBitSet(const CXXRecordDecl *RD, + raw_ostream &Out) override; bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) { // Lambda closure types are already numbered. if (isLambda(ND)) @@ -2567,6 +2569,11 @@ void MicrosoftMangleContextImpl::mangleStringLiteral(const StringLiteral *SL, Mangler.getStream() << '@'; } +void MicrosoftMangleContextImpl::mangleCXXVTableBitSet(const CXXRecordDecl *RD, + raw_ostream &Out) { + llvm::report_fatal_error("Cannot mangle bitsets yet"); +} + MicrosoftMangleContext * MicrosoftMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags) { return new MicrosoftMangleContextImpl(Context, Diags); diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 524697c..66ab615 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -24,6 +24,7 @@ #include "clang/Basic/TargetBuiltins.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/Frontend/CodeGenOptions.h" +#include "llvm/IR/Intrinsics.h" using namespace clang; using namespace CodeGen; @@ -2087,6 +2088,38 @@ llvm::Value *CodeGenFunction::GetVTablePtr(llvm::Value *This, return VTable; } +void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, + llvm::Value *VTable) { + if (!SanOpts.has(SanitizerKind::CFIVptr)) + return; + + const CXXRecordDecl *RD = MD->getParent(); + // FIXME: Add blacklisting scheme. + if (RD->isInStdNamespace()) + return; + + std::string OutName; + llvm::raw_string_ostream Out(OutName); + CGM.getCXXABI().getMangleContext().mangleCXXVTableBitSet(RD, Out); + + llvm::Value *BitSetName = llvm::MetadataAsValue::get( + getLLVMContext(), llvm::MDString::get(getLLVMContext(), Out.str())); + + llvm::Value *BitSetTest = Builder.CreateCall2( + CGM.getIntrinsic(llvm::Intrinsic::bitset_test), + Builder.CreateBitCast(VTable, CGM.Int8PtrTy), BitSetName); + + llvm::BasicBlock *ContBlock = createBasicBlock("vtable.check.cont"); + llvm::BasicBlock *TrapBlock = createBasicBlock("vtable.check.trap"); + + Builder.CreateCondBr(BitSetTest, ContBlock, TrapBlock); + + EmitBlock(TrapBlock); + Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::trap)); + Builder.CreateUnreachable(); + + EmitBlock(ContBlock); +} // FIXME: Ideally Expr::IgnoreParenNoopCasts should do this, but it doesn't do // quite what we want. diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp index 7724467..14116e4 100644 --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -669,6 +669,8 @@ CodeGenVTables::GenerateConstructionVTable(const CXXRecordDecl *RD, VTLayout->getNumVTableThunks(), RTTI); VTable->setInitializer(Init); + CGM.EmitVTableBitSetEntries(VTable, *VTLayout.get()); + return VTable; } @@ -837,3 +839,62 @@ void CodeGenModule::EmitDeferredVTables() { "deferred extra v-tables during v-table emission?"); DeferredVTables.clear(); } + +void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout) { + if (!LangOpts.Sanitize.has(SanitizerKind::CFIVptr)) + return; + + llvm::Metadata *VTableMD = llvm::ConstantAsMetadata::get(VTable); + + std::vector BitsetEntries; + // Create a bit set entry for each address point. + for (auto &&AP : VTLayout.getAddressPoints()) { + // FIXME: Add blacklisting scheme. + if (AP.first.getBase()->isInStdNamespace()) + continue; + + std::string OutName; + llvm::raw_string_ostream Out(OutName); + getCXXABI().getMangleContext().mangleCXXVTableBitSet(AP.first.getBase(), + Out); + + CharUnits PointerWidth = + Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); + uint64_t AddrPointOffset = AP.second * PointerWidth.getQuantity(); + + llvm::Metadata *BitsetOps[] = { + llvm::MDString::get(getLLVMContext(), Out.str()), + VTableMD, + llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(Int64Ty, AddrPointOffset))}; + llvm::MDTuple *BitsetEntry = + llvm::MDTuple::get(getLLVMContext(), BitsetOps); + BitsetEntries.push_back(BitsetEntry); + } + + // Sort the bit set entries for determinism. + std::sort(BitsetEntries.begin(), BitsetEntries.end(), [](llvm::MDTuple *T1, + llvm::MDTuple *T2) { + StringRef S1 = cast(T1->getOperand(0))->getString(); + StringRef S2 = cast(T2->getOperand(0))->getString(); + if (S1 < S2) + return true; + if (S1 != S2) + return false; + + uint64_t Offset1 = cast( + cast(T1->getOperand(2)) + ->getValue())->getZExtValue(); + uint64_t Offset2 = cast( + cast(T2->getOperand(2)) + ->getValue())->getZExtValue(); + assert(Offset1 != Offset2); + return Offset1 < Offset2; + }); + + llvm::NamedMDNode *BitsetsMD = + getModule().getOrInsertNamedMetadata("llvm.bitsets"); + for (auto BitsetEntry : BitsetEntries) + BitsetsMD->addOperand(BitsetEntry); +} diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index b52ba8a..7571332 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -1346,6 +1346,9 @@ public: /// to by This. llvm::Value *GetVTablePtr(llvm::Value *This, llvm::Type *Ty); + /// EmitVTablePtrCheckForCall - Virtual method MD is being called via VTable. + /// If vptr CFI is enabled, emit a check that VTable is valid. + void EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, llvm::Value *VTable); /// CanDevirtualizeMemberFunctionCalls - Checks whether virtual calls on given /// expr can be devirtualized. diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 7daa093..6902d19 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1103,6 +1103,11 @@ public: /// \param D Threadprivate declaration. void EmitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D); + /// Emit bit set entries for the given vtable using the given layout if + /// vptr CFI is enabled. + void EmitVTableBitSetEntries(llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout); + private: llvm::Constant * GetOrCreateLLVMFunction(StringRef MangledName, llvm::Type *Ty, GlobalDecl D, diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 14a2890..e580969 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -1281,6 +1281,8 @@ void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT, cast(DC)->getIdentifier()->isStr("__cxxabiv1") && DC->getParent()->isTranslationUnit()) EmitFundamentalRTTIDescriptors(); + + CGM.EmitVTableBitSetEntries(VTable, VTLayout); } llvm::Value *ItaniumCXXABI::getVTableAddressPointInStructor( @@ -1372,6 +1374,8 @@ llvm::Value *ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, Ty = Ty->getPointerTo()->getPointerTo(); llvm::Value *VTable = CGF.GetVTablePtr(This, Ty); + CGF.EmitVTablePtrCheckForCall(cast(GD.getDecl()), VTable); + uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD); llvm::Value *VFuncPtr = CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn"); diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 0424c4b..61aaa97 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -17,6 +17,7 @@ #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Job.h" #include "clang/Driver/Options.h" +#include "clang/Driver/SanitizerArgs.h" #include "clang/Driver/Tool.h" #include "clang/Driver/ToolChain.h" #include "llvm/ADT/ArrayRef.h" @@ -1269,7 +1270,7 @@ void Driver::BuildActions(const ToolChain &TC, DerivedArgList &Args, continue; // Otherwise construct the appropriate action. - Current = ConstructPhaseAction(Args, Phase, std::move(Current)); + Current = ConstructPhaseAction(TC, Args, Phase, std::move(Current)); if (Current->getType() == types::TY_Nothing) break; } @@ -1295,7 +1296,8 @@ void Driver::BuildActions(const ToolChain &TC, DerivedArgList &Args, } std::unique_ptr -Driver::ConstructPhaseAction(const ArgList &Args, phases::ID Phase, +Driver::ConstructPhaseAction(const ToolChain &TC, const ArgList &Args, + phases::ID Phase, std::unique_ptr Input) const { llvm::PrettyStackTraceString CrashInfo("Constructing phase actions"); // Build the appropriate action. @@ -1354,7 +1356,7 @@ Driver::ConstructPhaseAction(const ArgList &Args, phases::ID Phase, types::TY_LLVM_BC); } case phases::Backend: { - if (IsUsingLTO(Args)) { + if (IsUsingLTO(TC, Args)) { types::ID Output = Args.hasArg(options::OPT_S) ? types::TY_LTO_IR : types::TY_LTO_BC; return llvm::make_unique(std::move(Input), Output); @@ -1375,7 +1377,10 @@ Driver::ConstructPhaseAction(const ArgList &Args, phases::ID Phase, llvm_unreachable("invalid phase in ConstructPhaseAction"); } -bool Driver::IsUsingLTO(const ArgList &Args) const { +bool Driver::IsUsingLTO(const ToolChain &TC, const ArgList &Args) const { + if (TC.getSanitizerArgs().needsLTO()) + return true; + if (Args.hasFlag(options::OPT_flto, options::OPT_fno_lto, false)) return true; diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 7bec383..d09378c 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -47,7 +47,8 @@ ID = ALIAS, ID##Group = 1 << SO_##ID##Group, SupportsCoverage = Address | Memory | Leak | Undefined | Integer, RecoverableByDefault = Undefined | Integer, Unrecoverable = Address | Unreachable | Return, - LegacyFsanitizeRecoverMask = Undefined | Integer + LegacyFsanitizeRecoverMask = Undefined | Integer, + NeedsLTO = CFIVptr, }; } @@ -148,6 +149,10 @@ bool SanitizerArgs::needsUnwindTables() const { return hasOneOf(Sanitizers, NeedsUnwindTables); } +bool SanitizerArgs::needsLTO() const { + return hasOneOf(Sanitizers, CFIVptr); +} + void SanitizerArgs::clear() { Sanitizers.clear(); RecoverableSanitizers.clear(); diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index dd878d0..ff58fcb 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -5817,7 +5817,8 @@ void darwin::Link::AddLinkArgs(Compilation &C, // If we are using LTO, then automatically create a temporary file path for // the linker to use, so that it's lifetime will extend past a possible // dsymutil step. - if (Version[0] >= 116 && D.IsUsingLTO(Args) && NeedsTempPath(Inputs)) { + if (Version[0] >= 116 && D.IsUsingLTO(getToolChain(), Args) && + NeedsTempPath(Inputs)) { const char *TmpPath = C.getArgs().MakeArgString( D.GetTemporaryPath("cc", types::getTypeTempSuffix(types::TY_Object))); C.addTempFile(TmpPath); @@ -6840,7 +6841,7 @@ void freebsd::Link::ConstructJob(Compilation &C, const JobAction &JA, Args.AddAllArgs(CmdArgs, options::OPT_Z_Flag); Args.AddAllArgs(CmdArgs, options::OPT_r); - if (D.IsUsingLTO(Args)) + if (D.IsUsingLTO(getToolChain(), Args)) AddGoldPlugin(ToolChain, Args, CmdArgs); bool NeedsSanitizerDeps = addSanitizerRuntimes(ToolChain, Args, CmdArgs); @@ -7676,7 +7677,7 @@ void gnutools::Link::ConstructJob(Compilation &C, const JobAction &JA, for (const auto &Path : Paths) CmdArgs.push_back(Args.MakeArgString(StringRef("-L") + Path)); - if (D.IsUsingLTO(Args)) + if (D.IsUsingLTO(getToolChain(), Args)) AddGoldPlugin(ToolChain, Args, CmdArgs); if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle)) diff --git a/clang/test/CodeGenCXX/cfi-vptr.cpp b/clang/test/CodeGenCXX/cfi-vptr.cpp new file mode 100644 index 0000000..545f22c --- /dev/null +++ b/clang/test/CodeGenCXX/cfi-vptr.cpp @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vptr -emit-llvm -o - %s | FileCheck %s + +struct A { + A(); + virtual void f(); +}; + +struct B : virtual A { + B(); +}; + +struct C : virtual A { + C(); +}; + +namespace { + +struct D : B, C { + D(); + virtual void f(); +}; + +} + +A::A() {} +B::B() {} +C::C() {} +D::D() {} + +void A::f() { +} + +void D::f() { +} + +// CHECK: define void @_Z2afP1A +void af(A *a) { + // CHECK: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A") + // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]] + + // CHECK: [[TRAPBB]] + // CHECK-NEXT: call void @llvm.trap() + // CHECK-NEXT: unreachable + + // CHECK: [[CONTBB]] + // CHECK: call void % + a->f(); +} + +// CHECK: define internal void @_Z2dfPN12_GLOBAL__N_11DE +void df(D *d) { + // CHECK: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vptr.cpp]N12_GLOBAL__N_11DE") + d->f(); +} + +D d; + +void foo() { + df(&d); +} + +// CHECK-DAG: !{!"1A", [3 x i8*]* @_ZTV1A, i64 16} +// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// CHECK-DAG: !{!"1B", [5 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// CHECK-DAG: !{!"1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} +// CHECK-DAG: !{!"1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32} +// CHECK-DAG: !{!"1A", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// CHECK-DAG: !{!"1B", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// CHECK-DAG: !{!"1C", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 72} +// CHECK-DAG: !{!"[{{.*}}cfi-vptr.cpp]N12_GLOBAL__N_11DE", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTV1B, i64 32} +// CHECK-DAG: !{!"1B", [5 x i8*]* @_ZTV1B, i64 32} +// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTV1C, i64 32} +// CHECK-DAG: !{!"1C", [5 x i8*]* @_ZTV1C, i64 32} diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index 87ac078..8f37b02 100644 --- a/clang/test/Driver/fsanitize.c +++ b/clang/test/Driver/fsanitize.c @@ -190,6 +190,10 @@ // RUN: %clang -target x86_64-apple-darwin10 -fsanitize=function -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FSAN-UBSAN-DARWIN // CHECK-FSAN-UBSAN-DARWIN: unsupported option '-fsanitize=function' for target 'x86_64-apple-darwin10' +// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI +// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-vptr -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI +// CHECK-CFI: -emit-llvm-bc{{.*}}-fsanitize=cfi-vptr + // RUN: %clang_cl -fsanitize=address -c -MDd -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-DEBUGRTL // RUN: %clang_cl -fsanitize=address -c -MTd -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-DEBUGRTL // RUN: %clang_cl -fsanitize=address -c -LDd -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-DEBUGRTL