https://github.com/matthewlevy97 updated https://github.com/llvm/llvm-project/pull/130103
>From fb04b7bf5f2b668bf354632fc53e7521f44880c9 Mon Sep 17 00:00:00 2001 From: Matt Levy <levyma...@gmail.com> Date: Wed, 5 Mar 2025 12:36:02 -0500 Subject: [PATCH 1/5] [clang][CodeGen] Software Bill of Mitigations Metadata The goal of this stack is to create a high fidelity mapping of mitigations to their possible insertion points and their actual insertion points. This would let us track where we do and don't have mitigations rather than the current approach of tracking where we have the flag. There are some challenges posed by this like: - Some mitigations are not emitted by the compiler, but the preprocessor - Some mitigations are lowered later during IR -> MIR (stack cookies) --- clang/include/clang/Basic/CodeGenOptions.def | 1 + clang/include/clang/Driver/Options.td | 6 ++ clang/lib/CodeGen/CGBuiltin.cpp | 10 +++ clang/lib/CodeGen/CGClass.cpp | 3 + clang/lib/CodeGen/CGDecl.cpp | 4 + clang/lib/CodeGen/CGExpr.cpp | 5 ++ clang/lib/CodeGen/CGExprCXX.cpp | 6 ++ clang/lib/CodeGen/CMakeLists.txt | 1 + clang/lib/CodeGen/CodeGenModule.cpp | 22 +++++ clang/lib/CodeGen/MitigationTagging.cpp | 84 ++++++++++++++++++++ clang/lib/CodeGen/MitigationTagging.h | 45 +++++++++++ clang/lib/Driver/ToolChains/Clang.cpp | 3 + 12 files changed, 190 insertions(+) create mode 100644 clang/lib/CodeGen/MitigationTagging.cpp create mode 100644 clang/lib/CodeGen/MitigationTagging.h diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index a7f5f1abbb825..76a46ac3e592b 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -191,6 +191,7 @@ CODEGENOPT(NoTypeCheck , 1, 0) ///< Set when -Wa,--no-type-check is enable CODEGENOPT(MisExpect , 1, 0) ///< Set when -Wmisexpect is enabled CODEGENOPT(EnableSegmentedStacks , 1, 0) ///< Set when -fsplit-stack is enabled. CODEGENOPT(StackClashProtector, 1, 0) ///< Set when -fstack-clash-protection is enabled. +CODEGENOPT(MitigationAnalysis, 1, 0) ///< Set when -fmitigation-analysis is enabled. CODEGENOPT(NoImplicitFloat , 1, 0) ///< Set when -mno-implicit-float is enabled. CODEGENOPT(NullPointerIsValid , 1, 0) ///< Assume Null pointer deference is defined. CODEGENOPT(OpenCLCorrectlyRoundedDivSqrt, 1, 0) ///< -cl-fp32-correctly-rounded-divide-sqrt diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index d0414aba35209..e50bb5c1c2cb4 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3891,6 +3891,12 @@ defm split_stack : BoolFOption<"split-stack", CodeGenOpts<"EnableSegmentedStacks">, DefaultFalse, NegFlag<SetFalse, [], [ClangOption], "Wouldn't use segmented stack">, PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use segmented stack">>; +defm mitigation_analysis : BoolFOption<"mitigation-analysis", + CodeGenOpts<"MitigationAnalysis">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">, + NegFlag<SetFalse, [], [ClangOption], "Disable">, + BothFlags<[], [ClangOption], " mitigation analysis">>, + DocBrief<"Instrument mitigations (CFI, Stack Protectors, Auto-Var-Init, StackClashProtection) to analyze their coverage">; def fstack_protector_all : Flag<["-"], "fstack-protector-all">, Group<f_Group>, HelpText<"Enable stack protectors for all functions">; defm stack_clash_protection : BoolFOption<"stack-clash-protection", diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index ab8f19b25fa66..4e180bb1a87cf 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -21,6 +21,7 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" +#include "MitigationTagging.h" #include "PatternInit.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" @@ -83,6 +84,8 @@ static void initializeAlloca(CodeGenFunction &CGF, AllocaInst *AI, Value *Size, switch (CGF.getLangOpts().getTrivialAutoVarInit()) { case LangOptions::TrivialAutoVarInitKind::Uninitialized: // Nothing to initialize. + AttachMitigationMetadataToFunction(CGF, MitigationKey::AUTO_VAR_INIT, + false); return; case LangOptions::TrivialAutoVarInitKind::Zero: Byte = CGF.Builder.getInt8(0x00); @@ -94,6 +97,7 @@ static void initializeAlloca(CodeGenFunction &CGF, AllocaInst *AI, Value *Size, break; } } + AttachMitigationMetadataToFunction(CGF, MitigationKey::AUTO_VAR_INIT, true); if (CGF.CGM.stopAutoInit()) return; auto *I = CGF.Builder.CreateMemSet(AI, Byte, Size, AlignmentInBytes); @@ -4642,6 +4646,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, AI->setAlignment(SuitableAlignmentInBytes); if (BuiltinID != Builtin::BI__builtin_alloca_uninitialized) initializeAlloca(*this, AI, Size, SuitableAlignmentInBytes); + else + AttachMitigationMetadataToFunction(*this, MitigationKey::AUTO_VAR_INIT, + false); LangAS AAS = getASTAllocaAddressSpace(); LangAS EAS = E->getType()->getPointeeType().getAddressSpace(); if (AAS != EAS) { @@ -4664,6 +4671,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, AI->setAlignment(AlignmentInBytes); if (BuiltinID != Builtin::BI__builtin_alloca_with_align_uninitialized) initializeAlloca(*this, AI, Size, AlignmentInBytes); + else + AttachMitigationMetadataToFunction(*this, MitigationKey::AUTO_VAR_INIT, + false); LangAS AAS = getASTAllocaAddressSpace(); LangAS EAS = E->getType()->getPointeeType().getAddressSpace(); if (AAS != EAS) { diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index e54fd543f217b..6858cd35615d3 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -16,6 +16,7 @@ #include "CGDebugInfo.h" #include "CGRecordLayout.h" #include "CodeGenFunction.h" +#include "MitigationTagging.h" #include "TargetInfo.h" #include "clang/AST/Attr.h" #include "clang/AST/CXXInheritance.h" @@ -2847,6 +2848,8 @@ void CodeGenFunction::EmitTypeMetadataCodeForVCall(const CXXRecordDecl *RD, Builder.CreateCall(CGM.getIntrinsic(IID), {VTable, TypeId}); Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::assume), TypeTest); } + + AttachMitigationMetadataToFunction(*this, MitigationKey::CFI_VCALL, false); } void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXRecordDecl *RD, diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 9cd5885aaae51..39f94b093b23c 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -20,6 +20,7 @@ #include "CodeGenModule.h" #include "ConstantEmitter.h" #include "EHScopeStack.h" +#include "MitigationTagging.h" #include "PatternInit.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" @@ -1974,6 +1975,9 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { ? LangOptions::TrivialAutoVarInitKind::Uninitialized : getContext().getLangOpts().getTrivialAutoVarInit()); + AttachMitigationMetadataToFunction( + *this, MitigationKey::AUTO_VAR_INIT, + trivialAutoVarInit != LangOptions::TrivialAutoVarInitKind::Uninitialized); auto initializeWhatIsTechnicallyUninitialized = [&](Address Loc) { if (trivialAutoVarInit == LangOptions::TrivialAutoVarInitKind::Uninitialized) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 191912ca7d800..dda5ea63b93d1 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -22,6 +22,7 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" +#include "MitigationTagging.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTLambda.h" @@ -6091,6 +6092,10 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, // function pointer is a member of the bit set for the function type. if (SanOpts.has(SanitizerKind::CFIICall) && (!TargetDecl || !isa<FunctionDecl>(TargetDecl))) { + AttachMitigationMetadataToFunction( + *this, MitigationKey::CFI_ICALL, + SanOpts.has(clang::SanitizerKind::CFIICall)); + SanitizerScope SanScope(this); EmitSanitizerStatReport(llvm::SanStat_CFI_ICall); diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index d4e14f4574b87..9ae192d60ca91 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -16,6 +16,7 @@ #include "CGObjCRuntime.h" #include "CodeGenFunction.h" #include "ConstantEmitter.h" +#include "MitigationTagging.h" #include "TargetInfo.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/CodeGen/CGFunctionInfo.h" @@ -416,6 +417,11 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr( std::tie(VTable, RD) = CGM.getCXXABI().LoadVTablePtr( *this, This.getAddress(), CalleeDecl->getParent()); EmitVTablePtrCheckForCall(RD, VTable, CFITCK_NVCall, CE->getBeginLoc()); + AttachMitigationMetadataToFunction(*this, MitigationKey::CFI_NVCALL, + true); + } else if (MD->getParent()->isDynamicClass()) { + AttachMitigationMetadataToFunction(*this, MitigationKey::CFI_NVCALL, + false); } if (getLangOpts().AppleKext && MD->isVirtual() && HasQualifier) diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index 05ab6671453f8..56b98742d35af 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -110,6 +110,7 @@ add_clang_library(clangCodeGen LinkInModulesPass.cpp MacroPPCallbacks.cpp MicrosoftCXXABI.cpp + MitigationTagging.cpp ModuleBuilder.cpp ObjectFilePCHContainerWriter.cpp PatternInit.cpp diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index cec8f1233b663..f8c65a21b0783 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -26,6 +26,7 @@ #include "CodeGenPGO.h" #include "ConstantEmitter.h" #include "CoverageMappingGen.h" +#include "MitigationTagging.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTLambda.h" @@ -2490,6 +2491,8 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, if ((!D || !D->hasAttr<NoUwtableAttr>()) && CodeGenOpts.UnwindTables) B.addUWTableAttr(llvm::UWTableKind(CodeGenOpts.UnwindTables)); + AttachMitigationMetadataToFunction(*F, MitigationKey::STACK_CLASH_PROTECTION, + CodeGenOpts.StackClashProtector); if (CodeGenOpts.StackClashProtector) B.addAttribute("probe-stack", "inline-asm"); @@ -2512,6 +2515,25 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, else if (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq)) B.addAttribute(llvm::Attribute::StackProtectReq); + bool noStackProtectionAttr = D && D->hasAttr<NoStackProtectorAttr>(); + AttachMitigationMetadataToFunction( + *F, MitigationKey::STACK_PROTECTOR, + !noStackProtectionAttr && + (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPOn) || + isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPStrong) || + isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq))); + + AttachMitigationMetadataToFunction( + *F, MitigationKey::STACK_PROTECTOR_STRONG, + !noStackProtectionAttr && + (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPStrong) || + isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq))); + + AttachMitigationMetadataToFunction( + *F, MitigationKey::STACK_PROTECTOR_ALL, + !noStackProtectionAttr && + isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq)); + if (!D) { // Non-entry HLSL functions must always be inlined. if (getLangOpts().HLSL && !F->hasFnAttribute(llvm::Attribute::NoInline)) diff --git a/clang/lib/CodeGen/MitigationTagging.cpp b/clang/lib/CodeGen/MitigationTagging.cpp new file mode 100644 index 0000000000000..d247dcab84059 --- /dev/null +++ b/clang/lib/CodeGen/MitigationTagging.cpp @@ -0,0 +1,84 @@ +//===--- MitigationTagging.cpp - Emit LLVM Code from ASTs for a Module ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This enables tagging functions with metadata to indicate mitigations are +// applied to them. +// +//===----------------------------------------------------------------------===// + +#include "MitigationTagging.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Metadata.h" + +#include <string> +#include <vector> + +namespace clang { +namespace CodeGen { + +inline static std::string +MitigationKeyToString(enum MitigationKey key) noexcept { + switch (key) { + case MitigationKey::AUTO_VAR_INIT: + return "auto-var-init"; + case MitigationKey::STACK_CLASH_PROTECTION: + return "stack-clash-protection"; + case MitigationKey::STACK_PROTECTOR: + return "stack-protector"; + case MitigationKey::STACK_PROTECTOR_STRONG: + return "stack-protector-strong"; + case MitigationKey::STACK_PROTECTOR_ALL: + return "stack-protector-all"; + case MitigationKey::CFI_VCALL: + return "cfi-vcall"; + case MitigationKey::CFI_ICALL: + return "cfi-icall"; + case MitigationKey::CFI_NVCALL: + return "cfi-nvcall"; + } +} + +void AttachMitigationMetadataToFunction(llvm::Function &F, + enum MitigationKey key, bool enabled) { + llvm::LLVMContext &Context = F.getContext(); + + unsigned kindID = Context.getMDKindID("security_mitigations"); + + llvm::Metadata *ValueMD = llvm::ConstantAsMetadata::get( + llvm::ConstantInt::get(llvm::Type::getInt1Ty(Context), enabled)); + llvm::MDString *KeyMD = + llvm::MDString::get(Context, MitigationKeyToString(key)); + + llvm::MDNode *NewMD = llvm::MDNode::get(Context, {KeyMD, ValueMD}); + llvm::MDNode *ExistingMD = F.getMetadata(kindID); + + if (ExistingMD) { + std::vector<llvm::Metadata *> MDs; + for (unsigned i = 0, e = ExistingMD->getNumOperands(); i != e; ++i) { + MDs.push_back(ExistingMD->getOperand(i)); + } + MDs.push_back(NewMD); + + llvm::MDNode *CombinedMD = llvm::MDNode::get(Context, MDs); + F.setMetadata(kindID, CombinedMD); + } else { + F.setMetadata(kindID, NewMD); + } +} + +void AttachMitigationMetadataToFunction(CodeGenFunction &CGF, + enum MitigationKey key, bool enabled) { + if (!CGF.CGM.getCodeGenOpts().MitigationAnalysis) { + return; + } + AttachMitigationMetadataToFunction(*(CGF.CurFn), key, enabled); +} + +} // namespace CodeGen +} // namespace clang diff --git a/clang/lib/CodeGen/MitigationTagging.h b/clang/lib/CodeGen/MitigationTagging.h new file mode 100644 index 0000000000000..fa80f63439734 --- /dev/null +++ b/clang/lib/CodeGen/MitigationTagging.h @@ -0,0 +1,45 @@ +//===--- MitigationTagging.h - Emit LLVM Code from ASTs for a Module ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This enables tagging functions with metadata to indicate mitigations are +// applied to them. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_CODEGEN_MITIGATIONTAGGING_H +#define LLVM_CLANG_LIB_CODEGEN_MITIGATIONTAGGING_H + +#include "CodeGenFunction.h" +#include "llvm/IR/Function.h" + +namespace clang { +namespace CodeGen { + +enum class MitigationKey { + AUTO_VAR_INIT, + + STACK_CLASH_PROTECTION, + + STACK_PROTECTOR, + STACK_PROTECTOR_STRONG, + STACK_PROTECTOR_ALL, + + CFI_VCALL, + CFI_ICALL, + CFI_NVCALL, +}; + +void AttachMitigationMetadataToFunction(llvm::Function &F, + enum MitigationKey key, bool enabled); +void AttachMitigationMetadataToFunction(CodeGenFunction &CGF, + enum MitigationKey key, bool enabled); + +} // namespace CodeGen +} // namespace clang + +#endif diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 4ebbd241d2f0b..b1d960c41a274 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7033,6 +7033,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, RenderSCPOptions(TC, Args, CmdArgs); RenderTrivialAutoVarInitOptions(D, TC, Args, CmdArgs); + Args.addOptInFlag(CmdArgs, options::OPT_fmitigation_analysis, + options::OPT_fno_mitigation_analysis); + Args.AddLastArg(CmdArgs, options::OPT_fswift_async_fp_EQ); Args.addOptInFlag(CmdArgs, options::OPT_mstackrealign, >From a4b4ee141193f13cffa42308392b246414573375 Mon Sep 17 00:00:00 2001 From: Matt Levy <levyma...@gmail.com> Date: Wed, 5 Mar 2025 13:27:47 -0500 Subject: [PATCH 2/5] [pass] Software Bill of Mitigations Output The goal of this stack is to create a high fidelity mapping of mitigations to their possible insertion points and their actual insertion points. This would let us track where we do and don't have mitigations rather than the current approach of tracking where we have the flag. This diff outputs what mitigations are enabled for all functions during the default LTO pipeline pass using the metadata generated in the previous diff. --- .../llvm/Analysis/MitigationAnalysis.h | 27 ++ llvm/lib/Analysis/CMakeLists.txt | 1 + llvm/lib/Analysis/MitigationAnalysis.cpp | 304 ++++++++++++++++++ llvm/lib/Passes/PassBuilder.cpp | 1 + llvm/lib/Passes/PassBuilderPipelines.cpp | 9 + llvm/lib/Passes/PassRegistry.def | 1 + 6 files changed, 343 insertions(+) create mode 100644 llvm/include/llvm/Analysis/MitigationAnalysis.h create mode 100644 llvm/lib/Analysis/MitigationAnalysis.cpp diff --git a/llvm/include/llvm/Analysis/MitigationAnalysis.h b/llvm/include/llvm/Analysis/MitigationAnalysis.h new file mode 100644 index 0000000000000..c32f55f79fdb9 --- /dev/null +++ b/llvm/include/llvm/Analysis/MitigationAnalysis.h @@ -0,0 +1,27 @@ +#ifndef LLVM_ANALYSIS_MITIGATIONANALYSIS_H +#define LLVM_ANALYSIS_MITIGATIONANALYSIS_H + +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/IR/ValueHandle.h" +#include "llvm/Pass.h" + +namespace llvm { + +class MitigationAnalysis : public AnalysisInfoMixin<MitigationAnalysis> { + friend AnalysisInfoMixin<MitigationAnalysis>; + static AnalysisKey Key; + + static constexpr const char *kMitigationAnalysisDebugType = + "mitigation_analysis"; + +public: + using Result = PreservedAnalyses; + Result run(Module &M, ModuleAnalysisManager &AM); +}; + +} // end namespace llvm + +#endif // LLVM_ANALYSIS_MITIGATIONANALYSIS_H diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt index a44f6c6a135ef..662e170c3c247 100644 --- a/llvm/lib/Analysis/CMakeLists.txt +++ b/llvm/lib/Analysis/CMakeLists.txt @@ -103,6 +103,7 @@ add_llvm_component_library(LLVMAnalysis MemoryProfileInfo.cpp MemorySSA.cpp MemorySSAUpdater.cpp + MitigationAnalysis.cpp ModelUnderTrainingRunner.cpp ModuleDebugInfoPrinter.cpp ModuleSummaryAnalysis.cpp diff --git a/llvm/lib/Analysis/MitigationAnalysis.cpp b/llvm/lib/Analysis/MitigationAnalysis.cpp new file mode 100644 index 0000000000000..08cbd3ae9ce48 --- /dev/null +++ b/llvm/lib/Analysis/MitigationAnalysis.cpp @@ -0,0 +1,304 @@ +#include "llvm/Analysis/MitigationAnalysis.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Metadata.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" +#include <string> +#include <unordered_map> + +using namespace llvm; + +AnalysisKey MitigationAnalysis::Key; + +// Add a command line flag for the module name +static cl::opt<std::string> + ClOutputModuleName("mitigation-analysis-dso-name", cl::Optional, + cl::desc("DSO name for the module"), + cl::init("unknown")); + +enum class MitigationState { Ineligible, EligibleDisabled, EligibleEnabled }; + +static const std::unordered_map<MitigationState, std::string> mapStateToString = + { + {MitigationState::Ineligible, "N/A"}, + {MitigationState::EligibleDisabled, "Disabled"}, + {MitigationState::EligibleEnabled, "Enabled"}, +}; + +struct MitigationInfo { + MitigationState auto_var_init = MitigationState::Ineligible; + MitigationState cfi_icall = MitigationState::Ineligible; + MitigationState cfi_vcall = MitigationState::Ineligible; + MitigationState cfi_nvcall = MitigationState::Ineligible; + MitigationState stack_clash_protection = MitigationState::Ineligible; + MitigationState stack_protector = MitigationState::Ineligible; + MitigationState stack_protector_strong = MitigationState::Ineligible; + MitigationState stack_protector_all = MitigationState::Ineligible; + MitigationState libcpp_hardening_mode = MitigationState::Ineligible; + std::string source_mapping = "(unknown)"; + std::string type_signature = "??"; + uint64_t type_id = 0; + std::string function; + std::string gmodule; +}; + +/// Convert an integer value (0 or 1) to the appropriate MitigationState. +static inline MitigationState valToState(int value) { + switch (value) { + case 0: + return MitigationState::EligibleDisabled; + case 1: + return MitigationState::EligibleEnabled; + default: + return MitigationState::Ineligible; + } +} + +/// Print out fields in MitigationInfo for debugging/verification purposes. +#ifndef NDEBUG +static void printInfo(const MitigationInfo &info) { + dbgs() << "module: " << info.gmodule << "\n"; + dbgs() << "function: " << info.function << "\n"; + dbgs() << "source_location: " << info.source_mapping << "\n"; + dbgs() << "auto-var-init: " << mapStateToString.at(info.auto_var_init) + << "\n"; + dbgs() << "cfi-icall: " << mapStateToString.at(info.cfi_icall) << "\n"; + dbgs() << "cfi-vcall: " << mapStateToString.at(info.cfi_vcall) << "\n"; + dbgs() << "cfi-nvcall: " << mapStateToString.at(info.cfi_nvcall) << "\n"; + dbgs() << "stack-clash-protection: " + << mapStateToString.at(info.stack_clash_protection) << "\n"; + dbgs() << "stack-protector: " << mapStateToString.at(info.stack_protector) + << "\n"; + dbgs() << "stack-protector-strong: " + << mapStateToString.at(info.stack_protector_strong) << "\n"; + dbgs() << "stack-protector-all: " + << mapStateToString.at(info.stack_protector_all) << "\n"; + dbgs() << "libcpp-hardening-mode: " + << mapStateToString.at(info.libcpp_hardening_mode) << "\n"; + dbgs() << "type_signature: " << info.type_signature << "\n"; + dbgs() << "type_id: " << info.type_id << "\n\n"; +} +#endif + +/// Convert a mitigation key + integer value into the appropriate field +/// of MitigationInfo. This replaces a long chain of if/else statements. +static void keyAndValueToInfo(MitigationInfo &info, StringRef key, int value) { + static constexpr struct { + const StringRef Key; + MitigationState MitigationInfo::*Field; + } Mappings[] = { + {StringRef("auto-var-init"), &MitigationInfo::auto_var_init}, + {StringRef("cfi-icall"), &MitigationInfo::cfi_icall}, + {StringRef("cfi-vcall"), &MitigationInfo::cfi_vcall}, + {StringRef("cfi-nvcall"), &MitigationInfo::cfi_nvcall}, + {StringRef("stack-clash-protection"), + &MitigationInfo::stack_clash_protection}, + {StringRef("stack-protector"), &MitigationInfo::stack_protector}, + {StringRef("stack-protector-strong"), + &MitigationInfo::stack_protector_strong}, + {StringRef("stack-protector-all"), &MitigationInfo::stack_protector_all}, + {StringRef("libcpp-hardening-mode"), + &MitigationInfo::libcpp_hardening_mode}, + }; + + for (const auto &Mapping : Mappings) { + if (key == Mapping.Key) { + info.*(Mapping.Field) = valToState(value); + break; + } + } +} + +/// Retrieve the first valid source path for the given function. +static std::string getFunctionSourcePath(const Function &F) { + if (const DISubprogram *SP = F.getSubprogram()) { + std::string Dir = SP->getDirectory().str(); + std::string File = SP->getFilename().str(); + unsigned Line = SP->getLine(); + if (!Dir.empty() && !File.empty()) + return Dir + "/" + File + ":" + std::to_string(Line); + } + return "(unknown)"; +} + +/// Write the given JSON string to file with a lock. On error, prints to stderr. +static void writeJsonToFile(const std::string &jsonString, + const std::string &fileName, + const std::string &errorMsg) { + std::error_code errCode; + raw_fd_ostream OutputStream(fileName, errCode, sys::fs::CD_CreateAlways, + sys::fs::FA_Read | sys::fs::FA_Write, + sys::fs::OF_Text | sys::fs::OF_UpdateAtime); + if (errCode) { + errs() << errorMsg << "\n"; + errs() << errCode.message() << "\n"; + return; + } + + if (auto lock = OutputStream.lock()) { + OutputStream << jsonString << "\n"; + if (OutputStream.has_error()) { + errs() << errorMsg << "\n"; + errs() << jsonString << "\n"; + } + } else { + errs() << errorMsg << "\n"; + errs() << "Couldn't acquire lock for " << fileName << "\n"; + } +} + +/// Convert a MitigationInfo struct to a JSON object. +static json::Object infoToJson(const MitigationInfo &info) { + json::Object object; + object["auto_var_init"] = mapStateToString.at(info.auto_var_init); + object["cfi_icall"] = mapStateToString.at(info.cfi_icall); + object["cfi_vcall"] = mapStateToString.at(info.cfi_vcall); + object["cfi_nvcall"] = mapStateToString.at(info.cfi_nvcall); + object["stack_clash_protection"] = + mapStateToString.at(info.stack_clash_protection); + object["stack_protector"] = mapStateToString.at(info.stack_protector); + object["stack_protector_strong"] = + mapStateToString.at(info.stack_protector_strong); + object["stack_protector_all"] = mapStateToString.at(info.stack_protector_all); + object["libcpp_hardening_mode"] = + mapStateToString.at(info.libcpp_hardening_mode); + object["source_mapping"] = info.source_mapping; + object["function"] = info.function; + object["type_signature"] = info.type_signature; + object["type_id"] = (uint64_t)info.type_id; + object["module"] = info.gmodule; + return object; +} + +/// Return true if function F calls a function whose name contains +/// targetFunctionName. +static bool functionCallsFunctionWithName(Function &F, + StringRef targetFunctionName) { + for (Instruction &I : instructions(F)) { + auto *callInst = dyn_cast<CallInst>(&I); + if (!callInst) + continue; + + Function *calledFunction = callInst->getCalledFunction(); + if (calledFunction && + calledFunction->getName().contains(targetFunctionName)) + return true; + } + return false; +} + +/// Extract the first function type signature (that doesn't end with +/// .generalized) from metadata in Function F. +static std::string getFirstFunctionTypeSignature(Function &F) { + SmallVector<std::pair<unsigned, MDNode *>, 4> MDs; + F.getAllMetadata(MDs); + + for (const auto &MD : MDs) { + if (MD.first != LLVMContext::MD_type) + continue; + if (MDNode *Node = MD.second) { + if (Node->getNumOperands() <= 1) + continue; + auto *str = dyn_cast<MDString>(Node->getOperand(1)); + if (!str) + continue; + + std::string signature = str->getString().str(); + if (!StringRef(signature).ends_with(".generalized")) + return signature; + } + } + return ""; +} + +/// Extract a type ID from MD_type metadata in Function F (0 if not found). +static uint64_t getFunctionTypeId(Function &F) { + SmallVector<std::pair<unsigned, MDNode *>, 4> MDs; + F.getAllMetadata(MDs); + + for (const auto &MD : MDs) { + if (MD.first != LLVMContext::MD_type) + continue; + + MDNode *Node = MD.second; + if (!Node || Node->getNumOperands() <= 1) + continue; + + auto *MDInt = dyn_cast<ConstantAsMetadata>(Node->getOperand(1)); + if (!MDInt) + continue; + + auto *CI = dyn_cast<ConstantInt>(MDInt->getValue()); + if (CI) { + return CI->getZExtValue(); + } + } + return 0; +} + +/// Detect the libcpp hardening mode from calls in the given function. +static MitigationState detectLibcppHardeningMode(Function &F) { + if (functionCallsFunctionWithName(F, "_libcpp_hardening_mode_enabled")) + return MitigationState::EligibleEnabled; + if (functionCallsFunctionWithName(F, "_libcpp_hardening_mode_disabled")) + return MitigationState::EligibleDisabled; + return MitigationState::Ineligible; +} + +PreservedAnalyses MitigationAnalysis::run(Module &M, + ModuleAnalysisManager &AM) { + json::Array jsonArray; + + for (Function &F : M) { + LLVMContext &Context = F.getContext(); + unsigned kindID = Context.getMDKindID("security_mitigations"); + MDNode *ExistingMD = F.getMetadata(kindID); + if (!ExistingMD) + continue; + + MitigationInfo info; + info.gmodule = ClOutputModuleName; + info.function = F.getName(); + + for (unsigned i = 0; i < ExistingMD->getNumOperands(); ++i) { + auto *node = dyn_cast<MDNode>(ExistingMD->getOperand(i)); + if (!node || node->getNumOperands() != 2) + continue; + + auto *mds = dyn_cast<MDString>(node->getOperand(0)); + auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); + if (!mds || !cam) + continue; + + if (auto *ci = cam->getValue()) { + int value = ci->isOneValue() ? 1 : 0; + keyAndValueToInfo(info, mds->getString(), value); + } + } + + info.libcpp_hardening_mode = detectLibcppHardeningMode(F); + + info.source_mapping = getFunctionSourcePath(F); + info.type_signature = getFirstFunctionTypeSignature(F); + info.type_id = getFunctionTypeId(F); + + DEBUG_WITH_TYPE(kMitigationAnalysisDebugType, printInfo(info)); + jsonArray.push_back(infoToJson(info)); + } + + if (!jsonArray.empty()) { + std::string jsonString = + formatv("{0}", json::Value(std::move(jsonArray))).str(); + if (!jsonString.empty()) { + writeJsonToFile(jsonString, "mitigation_info.json", + "Couldn't write to mitigation_info.json!"); + } + } + + return PreservedAnalyses::all(); +} diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 8080059f0bb03..a4dcafd3b5ab4 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -58,6 +58,7 @@ #include "llvm/Analysis/MemDerefPrinter.h" #include "llvm/Analysis/MemoryDependenceAnalysis.h" #include "llvm/Analysis/MemorySSA.h" +#include "llvm/Analysis/MitigationAnalysis.h" #include "llvm/Analysis/ModuleDebugInfoPrinter.h" #include "llvm/Analysis/ModuleSummaryAnalysis.h" #include "llvm/Analysis/MustExecute.h" diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index 546a5eb1ec283..e2c6ebc7017e5 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -21,6 +21,7 @@ #include "llvm/Analysis/CtxProfAnalysis.h" #include "llvm/Analysis/GlobalsModRef.h" #include "llvm/Analysis/InlineAdvisor.h" +#include "llvm/Analysis/MitigationAnalysis.h" #include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/Analysis/ScopedNoAliasAA.h" #include "llvm/Analysis/TypeBasedAliasAnalysis.h" @@ -305,6 +306,11 @@ static cl::opt<std::string> InstrumentColdFuncOnlyPath( "with --pgo-instrument-cold-function-only)"), cl::Hidden); +static cl::opt<bool> + EnableMitigationAnalysis("enable-mitigation-analysis", cl::init(false), + cl::Hidden, + cl::desc("Enable the MitigationAnalysis Pass")); + extern cl::opt<std::string> UseCtxProfile; extern cl::opt<bool> PGOInstrumentColdFunctionOnly; @@ -1852,6 +1858,9 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, // in the current module. MPM.addPass(CrossDSOCFIPass()); + if (EnableMitigationAnalysis) + MPM.addPass(MitigationAnalysis()); + if (Level == OptimizationLevel::O0) { // The WPD and LowerTypeTest passes need to run at -O0 to lower type // metadata and intrinsics. diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index bfd952df25e98..0a4732ab1e7ff 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -105,6 +105,7 @@ MODULE_PASS("memprof-context-disambiguation", MemProfContextDisambiguation()) MODULE_PASS("memprof-module", ModuleMemProfilerPass()) MODULE_PASS("mergefunc", MergeFunctionsPass()) MODULE_PASS("metarenamer", MetaRenamerPass()) +MODULE_PASS("mitigation-analysis", MitigationAnalysis()) MODULE_PASS("module-inline", ModuleInlinerPass()) MODULE_PASS("name-anon-globals", NameAnonGlobalPass()) MODULE_PASS("no-op-module", NoOpModulePass()) >From df88ae361869f1dcc269129e33272dbadcb87d24 Mon Sep 17 00:00:00 2001 From: Matt Levy <levyma...@gmail.com> Date: Mon, 10 Mar 2025 09:26:41 -0400 Subject: [PATCH 3/5] Software Bill of Mitigations Tests - Add tests for verifying metadata added correctly - Added new parameter to only add mitigation metadata for functions when MitigationAnalysis CGO is enabled --- clang/lib/CodeGen/CodeGenModule.cpp | 12 +- clang/lib/CodeGen/MitigationTagging.cpp | 20 +- clang/lib/CodeGen/MitigationTagging.h | 3 +- clang/unittests/CodeGen/CMakeLists.txt | 1 + .../CodeGen/MitigationTaggingTest.cpp | 414 ++++++++++++++++++ 5 files changed, 439 insertions(+), 11 deletions(-) create mode 100644 clang/unittests/CodeGen/MitigationTaggingTest.cpp diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index f8c65a21b0783..9b40cee0edb37 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2492,7 +2492,8 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, B.addUWTableAttr(llvm::UWTableKind(CodeGenOpts.UnwindTables)); AttachMitigationMetadataToFunction(*F, MitigationKey::STACK_CLASH_PROTECTION, - CodeGenOpts.StackClashProtector); + CodeGenOpts.StackClashProtector, + CodeGenOpts.MitigationAnalysis); if (CodeGenOpts.StackClashProtector) B.addAttribute("probe-stack", "inline-asm"); @@ -2521,18 +2522,21 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, !noStackProtectionAttr && (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPOn) || isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPStrong) || - isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq))); + isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq)), + CodeGenOpts.MitigationAnalysis); AttachMitigationMetadataToFunction( *F, MitigationKey::STACK_PROTECTOR_STRONG, !noStackProtectionAttr && (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPStrong) || - isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq))); + isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq)), + CodeGenOpts.MitigationAnalysis); AttachMitigationMetadataToFunction( *F, MitigationKey::STACK_PROTECTOR_ALL, !noStackProtectionAttr && - isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq)); + isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq), + CodeGenOpts.MitigationAnalysis); if (!D) { // Non-entry HLSL functions must always be inlined. diff --git a/clang/lib/CodeGen/MitigationTagging.cpp b/clang/lib/CodeGen/MitigationTagging.cpp index d247dcab84059..2f6f4c4a0dce1 100644 --- a/clang/lib/CodeGen/MitigationTagging.cpp +++ b/clang/lib/CodeGen/MitigationTagging.cpp @@ -44,8 +44,17 @@ MitigationKeyToString(enum MitigationKey key) noexcept { } } +/// +/// Store metadata (tied to the function) related to enablement of mitigations. +/// @param mitigationAnalysisEnable - if false, do not attach metadata. +/// void AttachMitigationMetadataToFunction(llvm::Function &F, - enum MitigationKey key, bool enabled) { + enum MitigationKey key, bool enabled, + bool mitigationAnalysisEnable) { + if (!mitigationAnalysisEnable) { + return; + } + llvm::LLVMContext &Context = F.getContext(); unsigned kindID = Context.getMDKindID("security_mitigations"); @@ -68,16 +77,15 @@ void AttachMitigationMetadataToFunction(llvm::Function &F, llvm::MDNode *CombinedMD = llvm::MDNode::get(Context, MDs); F.setMetadata(kindID, CombinedMD); } else { - F.setMetadata(kindID, NewMD); + F.setMetadata(kindID, llvm::MDNode::get( + Context, std::vector<llvm::Metadata *>{NewMD})); } } void AttachMitigationMetadataToFunction(CodeGenFunction &CGF, enum MitigationKey key, bool enabled) { - if (!CGF.CGM.getCodeGenOpts().MitigationAnalysis) { - return; - } - AttachMitigationMetadataToFunction(*(CGF.CurFn), key, enabled); + AttachMitigationMetadataToFunction( + *(CGF.CurFn), key, enabled, CGF.CGM.getCodeGenOpts().MitigationAnalysis); } } // namespace CodeGen diff --git a/clang/lib/CodeGen/MitigationTagging.h b/clang/lib/CodeGen/MitigationTagging.h index fa80f63439734..bbf5e735b9d06 100644 --- a/clang/lib/CodeGen/MitigationTagging.h +++ b/clang/lib/CodeGen/MitigationTagging.h @@ -35,7 +35,8 @@ enum class MitigationKey { }; void AttachMitigationMetadataToFunction(llvm::Function &F, - enum MitigationKey key, bool enabled); + enum MitigationKey key, bool enabled, + bool mitigationAnalysisEnable); void AttachMitigationMetadataToFunction(CodeGenFunction &CGF, enum MitigationKey key, bool enabled); diff --git a/clang/unittests/CodeGen/CMakeLists.txt b/clang/unittests/CodeGen/CMakeLists.txt index a437f441568f2..845188844f9e9 100644 --- a/clang/unittests/CodeGen/CMakeLists.txt +++ b/clang/unittests/CodeGen/CMakeLists.txt @@ -9,6 +9,7 @@ add_clang_unittest(ClangCodeGenTests CodeGenExternalTest.cpp TBAAMetadataTest.cpp CheckTargetFeaturesTest.cpp + MitigationAnalysisTest.cpp ) clang_target_link_libraries(ClangCodeGenTests diff --git a/clang/unittests/CodeGen/MitigationTaggingTest.cpp b/clang/unittests/CodeGen/MitigationTaggingTest.cpp new file mode 100644 index 0000000000000..7325cc0d649f3 --- /dev/null +++ b/clang/unittests/CodeGen/MitigationTaggingTest.cpp @@ -0,0 +1,414 @@ +#include "TestCompiler.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Sema/Sema.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/TargetParser/Host.h" +#include "llvm/TargetParser/Triple.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { + +TEST(MitigationTaggingTest, FlagDisabled) { + const char TestProgram[] = "void HasStackProtector(int x, int y) " + "{ " + " char buf[x]; " + " while(y) " + " buf[y--] = 0; " + "} "; + + clang::LangOptions LO; + LO.CPlusPlus = 1; + LO.CPlusPlus11 = 1; + + clang::CodeGenOptions CGO; + + TestCompiler Compiler(LO, CGO); + Compiler.init(TestProgram); + + clang::ParseAST(Compiler.compiler.getSema(), false, false); + auto M = + static_cast<clang::CodeGenerator &>(Compiler.compiler.getASTConsumer()) + .GetModule(); + auto FuncPtr = M->begin(); + + auto *MD = FuncPtr->getMetadata("security_mitigations"); + ASSERT_TRUE(MD == nullptr); +} + +TEST(MitigationTaggingTest, MetadataEnabledOnly) { + const char TestProgram[] = "void HasStackProtector(int x, int y) " + "{ " + " char buf[x]; " + " while(y) " + " buf[y--] = 0; " + "} "; + + clang::CodeGenOptions CGO; + CGO.MitigationAnalysis = true; + + clang::LangOptions LO; + LO.CPlusPlus = 1; + LO.CPlusPlus11 = 1; + + TestCompiler Compiler(LO, CGO); + Compiler.init(TestProgram); + + clang::ParseAST(Compiler.compiler.getSema(), false, false); + auto M = + static_cast<clang::CodeGenerator &>(Compiler.compiler.getASTConsumer()) + .GetModule(); + auto FuncPtr = M->begin(); + + auto *MD = FuncPtr->getMetadata("security_mitigations"); + ASSERT_TRUE(MD != nullptr); + + // Get All Enabled Mitigations + std::unordered_map<std::string, bool> MDs; + for (unsigned i = 0, n = MD->getNumOperands(); i != n; ++i) { + if (MD->getOperand(i) == nullptr) + continue; + + auto *node = dyn_cast<MDNode>(MD->getOperand(i)); + if (node == nullptr) + continue; + + ASSERT_NE(node, nullptr); + EXPECT_EQ(node->getNumOperands(), 2); + + auto *mds = dyn_cast<MDString>(node->getOperand(0)); + auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); + EXPECT_FALSE(!mds || !cam); + + auto *ci = cam->getValue(); + ASSERT_NE(ci, nullptr); + + auto mitigationName = std::string(mds->getString()); + ASSERT_TRUE(MDs.find(mitigationName) == MDs.end()); + MDs[mitigationName] = ci->isOneValue(); + } + + // Check that the correct mitigations are enabled + EXPECT_FALSE(MDs["auto-var-init"]); + EXPECT_FALSE(MDs["stack-clash-protection"]); + EXPECT_FALSE(MDs["stack-protector"]); + EXPECT_FALSE(MDs["stack-protector-strong"]); + EXPECT_FALSE(MDs["stack-protector-all"]); +} + +TEST(MitigationTaggingTest, AutoVarInitZeroEnabled) { + const char TestProgram[] = "void HasStackProtector(int x, int y) " + "{ " + " char buf[x]; " + " while(y) " + " buf[y--] = 0; " + "} "; + + clang::CodeGenOptions CGO; + CGO.MitigationAnalysis = true; + + clang::LangOptions LO; + LO.CPlusPlus = 1; + LO.CPlusPlus11 = 1; + LO.setTrivialAutoVarInit(LangOptions::TrivialAutoVarInitKind::Zero); + + TestCompiler Compiler(LO, CGO); + Compiler.init(TestProgram); + + clang::ParseAST(Compiler.compiler.getSema(), false, false); + auto M = + static_cast<clang::CodeGenerator &>(Compiler.compiler.getASTConsumer()) + .GetModule(); + auto FuncPtr = M->begin(); + + auto *MD = FuncPtr->getMetadata("security_mitigations"); + ASSERT_TRUE(MD != nullptr); + + // Get All Enabled Mitigations + std::unordered_map<std::string, bool> MDs; + for (unsigned i = 0, n = MD->getNumOperands(); i != n; ++i) { + if (MD->getOperand(i) == nullptr) + continue; + + auto *node = dyn_cast<MDNode>(MD->getOperand(i)); + if (node == nullptr) + continue; + + ASSERT_NE(node, nullptr); + EXPECT_EQ(node->getNumOperands(), 2); + + auto *mds = dyn_cast<MDString>(node->getOperand(0)); + auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); + EXPECT_FALSE(!mds || !cam); + + auto *ci = cam->getValue(); + ASSERT_NE(ci, nullptr); + + auto mitigationName = std::string(mds->getString()); + ASSERT_TRUE(MDs.find(mitigationName) == MDs.end()); + MDs[mitigationName] = ci->isOneValue(); + } + + // Check that the correct mitigations are enabled + EXPECT_TRUE(MDs["auto-var-init"]); + EXPECT_FALSE(MDs["stack-clash-protection"]); + EXPECT_FALSE(MDs["stack-protector"]); + EXPECT_FALSE(MDs["stack-protector-strong"]); + EXPECT_FALSE(MDs["stack-protector-all"]); +} + +TEST(MitigationTaggingTest, StackClashEnabled) { + const char TestProgram[] = "void HasStackProtector(int x, int y) " + "{ " + " char buf[x]; " + " while(y) " + " buf[y--] = 0; " + "} "; + + clang::CodeGenOptions CGO; + CGO.MitigationAnalysis = true; + CGO.StackClashProtector = true; + + clang::LangOptions LO; + LO.CPlusPlus = 1; + LO.CPlusPlus11 = 1; + + TestCompiler Compiler(LO, CGO); + Compiler.init(TestProgram); + + clang::ParseAST(Compiler.compiler.getSema(), false, false); + auto M = + static_cast<clang::CodeGenerator &>(Compiler.compiler.getASTConsumer()) + .GetModule(); + auto FuncPtr = M->begin(); + + auto *MD = FuncPtr->getMetadata("security_mitigations"); + ASSERT_TRUE(MD != nullptr); + + // Get All Enabled Mitigations + std::unordered_map<std::string, bool> MDs; + for (unsigned i = 0, n = MD->getNumOperands(); i != n; ++i) { + if (MD->getOperand(i) == nullptr) + continue; + + auto *node = dyn_cast<MDNode>(MD->getOperand(i)); + if (node == nullptr) + continue; + + ASSERT_NE(node, nullptr); + EXPECT_EQ(node->getNumOperands(), 2); + + auto *mds = dyn_cast<MDString>(node->getOperand(0)); + auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); + EXPECT_FALSE(!mds || !cam); + + auto *ci = cam->getValue(); + ASSERT_NE(ci, nullptr); + + auto mitigationName = std::string(mds->getString()); + ASSERT_TRUE(MDs.find(mitigationName) == MDs.end()); + MDs[mitigationName] = ci->isOneValue(); + } + + // Check that the correct mitigations are enabled + EXPECT_FALSE(MDs["auto-var-init"]); + EXPECT_TRUE(MDs["stack-clash-protection"]); + EXPECT_FALSE(MDs["stack-protector"]); + EXPECT_FALSE(MDs["stack-protector-strong"]); + EXPECT_FALSE(MDs["stack-protector-all"]); +} + +TEST(MitigationTaggingTest, StackProtectorEnabled) { + const char TestProgram[] = "void HasStackProtector(int x, int y) " + "{ " + " char buf[x]; " + " while(y) " + " buf[y--] = 0; " + "} "; + + clang::CodeGenOptions CGO; + CGO.MitigationAnalysis = true; + + clang::LangOptions LO; + LO.CPlusPlus = 1; + LO.CPlusPlus11 = 1; + LO.setStackProtector(LangOptions::SSPOn); + + TestCompiler Compiler(LO, CGO); + Compiler.init(TestProgram); + + clang::ParseAST(Compiler.compiler.getSema(), false, false); + auto M = + static_cast<clang::CodeGenerator &>(Compiler.compiler.getASTConsumer()) + .GetModule(); + auto FuncPtr = M->begin(); + + auto *MD = FuncPtr->getMetadata("security_mitigations"); + ASSERT_TRUE(MD != nullptr); + + // Get All Enabled Mitigations + std::unordered_map<std::string, bool> MDs; + for (unsigned i = 0, n = MD->getNumOperands(); i != n; ++i) { + if (MD->getOperand(i) == nullptr) + continue; + + auto *node = dyn_cast<MDNode>(MD->getOperand(i)); + if (node == nullptr) + continue; + + ASSERT_NE(node, nullptr); + EXPECT_EQ(node->getNumOperands(), 2); + + auto *mds = dyn_cast<MDString>(node->getOperand(0)); + auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); + EXPECT_FALSE(!mds || !cam); + + auto *ci = cam->getValue(); + ASSERT_NE(ci, nullptr); + + auto mitigationName = std::string(mds->getString()); + ASSERT_TRUE(MDs.find(mitigationName) == MDs.end()); + MDs[mitigationName] = ci->isOneValue(); + } + + // Check that the correct mitigations are enabled + EXPECT_FALSE(MDs["auto-var-init"]); + EXPECT_FALSE(MDs["stack-clash-protection"]); + EXPECT_TRUE(MDs["stack-protector"]); + EXPECT_FALSE(MDs["stack-protector-strong"]); + EXPECT_FALSE(MDs["stack-protector-all"]); +} + +TEST(MitigationTaggingTest, StackProtectorStrongEnabled) { + const char TestProgram[] = "void HasStackProtector(int x, int y) " + "{ " + " char buf[x]; " + " while(y) " + " buf[y--] = 0; " + "} "; + + clang::CodeGenOptions CGO; + CGO.MitigationAnalysis = true; + + clang::LangOptions LO; + LO.CPlusPlus = 1; + LO.CPlusPlus11 = 1; + LO.setStackProtector(LangOptions::SSPStrong); + + TestCompiler Compiler(LO, CGO); + Compiler.init(TestProgram); + + clang::ParseAST(Compiler.compiler.getSema(), false, false); + auto M = + static_cast<clang::CodeGenerator &>(Compiler.compiler.getASTConsumer()) + .GetModule(); + auto FuncPtr = M->begin(); + + auto *MD = FuncPtr->getMetadata("security_mitigations"); + ASSERT_TRUE(MD != nullptr); + + // Get All Enabled Mitigations + std::unordered_map<std::string, bool> MDs; + for (unsigned i = 0, n = MD->getNumOperands(); i != n; ++i) { + if (MD->getOperand(i) == nullptr) + continue; + + auto *node = dyn_cast<MDNode>(MD->getOperand(i)); + if (node == nullptr) + continue; + + ASSERT_NE(node, nullptr); + EXPECT_EQ(node->getNumOperands(), 2); + + auto *mds = dyn_cast<MDString>(node->getOperand(0)); + auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); + EXPECT_FALSE(!mds || !cam); + + auto *ci = cam->getValue(); + ASSERT_NE(ci, nullptr); + + auto mitigationName = std::string(mds->getString()); + ASSERT_TRUE(MDs.find(mitigationName) == MDs.end()); + MDs[mitigationName] = ci->isOneValue(); + } + + // Check that the correct mitigations are enabled + EXPECT_FALSE(MDs["auto-var-init"]); + EXPECT_FALSE(MDs["stack-clash-protection"]); + EXPECT_TRUE(MDs["stack-protector"]); + EXPECT_TRUE(MDs["stack-protector-strong"]); + EXPECT_FALSE(MDs["stack-protector-all"]); +} + +TEST(MitigationTaggingTest, StackProtectorAllEnabled) { + const char TestProgram[] = "void HasStackProtector(int x, int y) " + "{ " + " char buf[x]; " + " while(y) " + " buf[y--] = 0; " + "} "; + + clang::CodeGenOptions CGO; + CGO.MitigationAnalysis = true; + + clang::LangOptions LO; + LO.CPlusPlus = 1; + LO.CPlusPlus11 = 1; + LO.setStackProtector(LangOptions::SSPReq); + + TestCompiler Compiler(LO, CGO); + Compiler.init(TestProgram); + + clang::ParseAST(Compiler.compiler.getSema(), false, false); + auto M = + static_cast<clang::CodeGenerator &>(Compiler.compiler.getASTConsumer()) + .GetModule(); + auto FuncPtr = M->begin(); + + auto *MD = FuncPtr->getMetadata("security_mitigations"); + ASSERT_TRUE(MD != nullptr); + + // Get All Enabled Mitigations + std::unordered_map<std::string, bool> MDs; + for (unsigned i = 0, n = MD->getNumOperands(); i != n; ++i) { + if (MD->getOperand(i) == nullptr) + continue; + + auto *node = dyn_cast<MDNode>(MD->getOperand(i)); + if (node == nullptr) + continue; + + ASSERT_NE(node, nullptr); + EXPECT_EQ(node->getNumOperands(), 2); + + auto *mds = dyn_cast<MDString>(node->getOperand(0)); + auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); + EXPECT_FALSE(!mds || !cam); + + auto *ci = cam->getValue(); + ASSERT_NE(ci, nullptr); + + auto mitigationName = std::string(mds->getString()); + ASSERT_TRUE(MDs.find(mitigationName) == MDs.end()); + MDs[mitigationName] = ci->isOneValue(); + } + + // Check that the correct mitigations are enabled + EXPECT_FALSE(MDs["auto-var-init"]); + EXPECT_FALSE(MDs["stack-clash-protection"]); + EXPECT_TRUE(MDs["stack-protector"]); + EXPECT_TRUE(MDs["stack-protector-strong"]); + EXPECT_TRUE(MDs["stack-protector-all"]); +} + +} // end anonymous namespace >From 1adb8974c4001df382cf0feec09450aef6805063 Mon Sep 17 00:00:00 2001 From: Matt Levy <levyma...@gmail.com> Date: Mon, 10 Mar 2025 12:28:51 -0400 Subject: [PATCH 4/5] Incorrect test name in CMakeLists --- clang/unittests/CodeGen/CMakeLists.txt | 2 +- .../CodeGen/MitigationTaggingTest.cpp | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/clang/unittests/CodeGen/CMakeLists.txt b/clang/unittests/CodeGen/CMakeLists.txt index 845188844f9e9..ac0814bc331e4 100644 --- a/clang/unittests/CodeGen/CMakeLists.txt +++ b/clang/unittests/CodeGen/CMakeLists.txt @@ -9,7 +9,7 @@ add_clang_unittest(ClangCodeGenTests CodeGenExternalTest.cpp TBAAMetadataTest.cpp CheckTargetFeaturesTest.cpp - MitigationAnalysisTest.cpp + MitigationTaggingTest.cpp ) clang_target_link_libraries(ClangCodeGenTests diff --git a/clang/unittests/CodeGen/MitigationTaggingTest.cpp b/clang/unittests/CodeGen/MitigationTaggingTest.cpp index 7325cc0d649f3..96ccbb67cdbd1 100644 --- a/clang/unittests/CodeGen/MitigationTaggingTest.cpp +++ b/clang/unittests/CodeGen/MitigationTaggingTest.cpp @@ -84,11 +84,11 @@ TEST(MitigationTaggingTest, MetadataEnabledOnly) { continue; ASSERT_NE(node, nullptr); - EXPECT_EQ(node->getNumOperands(), 2); + ASSERT_EQ(node->getNumOperands(), (unsigned int)2); auto *mds = dyn_cast<MDString>(node->getOperand(0)); auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); - EXPECT_FALSE(!mds || !cam); + ASSERT_FALSE(!mds || !cam); auto *ci = cam->getValue(); ASSERT_NE(ci, nullptr); @@ -145,11 +145,11 @@ TEST(MitigationTaggingTest, AutoVarInitZeroEnabled) { continue; ASSERT_NE(node, nullptr); - EXPECT_EQ(node->getNumOperands(), 2); + ASSERT_EQ(node->getNumOperands(), (unsigned int)2); auto *mds = dyn_cast<MDString>(node->getOperand(0)); auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); - EXPECT_FALSE(!mds || !cam); + ASSERT_FALSE(!mds || !cam); auto *ci = cam->getValue(); ASSERT_NE(ci, nullptr); @@ -206,11 +206,11 @@ TEST(MitigationTaggingTest, StackClashEnabled) { continue; ASSERT_NE(node, nullptr); - EXPECT_EQ(node->getNumOperands(), 2); + ASSERT_EQ(node->getNumOperands(), (unsigned int)2); auto *mds = dyn_cast<MDString>(node->getOperand(0)); auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); - EXPECT_FALSE(!mds || !cam); + ASSERT_FALSE(!mds || !cam); auto *ci = cam->getValue(); ASSERT_NE(ci, nullptr); @@ -267,11 +267,11 @@ TEST(MitigationTaggingTest, StackProtectorEnabled) { continue; ASSERT_NE(node, nullptr); - EXPECT_EQ(node->getNumOperands(), 2); + ASSERT_EQ(node->getNumOperands(), (unsigned int)2); auto *mds = dyn_cast<MDString>(node->getOperand(0)); auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); - EXPECT_FALSE(!mds || !cam); + ASSERT_FALSE(!mds || !cam); auto *ci = cam->getValue(); ASSERT_NE(ci, nullptr); @@ -328,11 +328,11 @@ TEST(MitigationTaggingTest, StackProtectorStrongEnabled) { continue; ASSERT_NE(node, nullptr); - EXPECT_EQ(node->getNumOperands(), 2); + ASSERT_EQ(node->getNumOperands(), (unsigned int)2); auto *mds = dyn_cast<MDString>(node->getOperand(0)); auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); - EXPECT_FALSE(!mds || !cam); + ASSERT_FALSE(!mds || !cam); auto *ci = cam->getValue(); ASSERT_NE(ci, nullptr); @@ -389,11 +389,11 @@ TEST(MitigationTaggingTest, StackProtectorAllEnabled) { continue; ASSERT_NE(node, nullptr); - EXPECT_EQ(node->getNumOperands(), 2); + ASSERT_EQ(node->getNumOperands(), (unsigned int)2); auto *mds = dyn_cast<MDString>(node->getOperand(0)); auto *cam = dyn_cast<ConstantAsMetadata>(node->getOperand(1)); - EXPECT_FALSE(!mds || !cam); + ASSERT_FALSE(!mds || !cam); auto *ci = cam->getValue(); ASSERT_NE(ci, nullptr); >From 03f1de1b9552baa0ecb5ca84147cdf35571837b4 Mon Sep 17 00:00:00 2001 From: Matt Levy <levyma...@gmail.com> Date: Fri, 14 Mar 2025 09:19:12 -0400 Subject: [PATCH 5/5] [SBOM] Add option to switch output information Updated Linker Flag: - Previous linker flag only enabled toggling analysis pass to output per-function mitigation information. - New version of flag can output a summary over the entire linkage unit or a per-function output. General: - Fixed incorrect CFI_ICALL mitigation metadata assignment - Changed how output file is created/named --- clang/lib/CodeGen/CGClass.cpp | 3 +- .../llvm/Analysis/MitigationAnalysis.h | 12 ++ llvm/lib/Analysis/MitigationAnalysis.cpp | 163 ++++++++++++++++-- llvm/lib/Passes/PassBuilderPipelines.cpp | 20 ++- 4 files changed, 177 insertions(+), 21 deletions(-) diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 6858cd35615d3..4a634be6cc963 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -2849,7 +2849,8 @@ void CodeGenFunction::EmitTypeMetadataCodeForVCall(const CXXRecordDecl *RD, Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::assume), TypeTest); } - AttachMitigationMetadataToFunction(*this, MitigationKey::CFI_VCALL, false); + AttachMitigationMetadataToFunction(*this, MitigationKey::CFI_VCALL, + SanOpts.has(SanitizerKind::CFIVCall)); } void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXRecordDecl *RD, diff --git a/llvm/include/llvm/Analysis/MitigationAnalysis.h b/llvm/include/llvm/Analysis/MitigationAnalysis.h index c32f55f79fdb9..25dff46386c58 100644 --- a/llvm/include/llvm/Analysis/MitigationAnalysis.h +++ b/llvm/include/llvm/Analysis/MitigationAnalysis.h @@ -10,6 +10,12 @@ namespace llvm { +enum class MitigationAnalysisSummary { + NONE = 0, + SHORT = 1 << 0, + FUNCTION = 1 << 1, +}; + class MitigationAnalysis : public AnalysisInfoMixin<MitigationAnalysis> { friend AnalysisInfoMixin<MitigationAnalysis>; static AnalysisKey Key; @@ -18,8 +24,14 @@ class MitigationAnalysis : public AnalysisInfoMixin<MitigationAnalysis> { "mitigation_analysis"; public: + MitigationAnalysis( + MitigationAnalysisSummary Summary = MitigationAnalysisSummary::NONE); + using Result = PreservedAnalyses; Result run(Module &M, ModuleAnalysisManager &AM); + +private: + MitigationAnalysisSummary summary_; }; } // end namespace llvm diff --git a/llvm/lib/Analysis/MitigationAnalysis.cpp b/llvm/lib/Analysis/MitigationAnalysis.cpp index 08cbd3ae9ce48..6dce86034369a 100644 --- a/llvm/lib/Analysis/MitigationAnalysis.cpp +++ b/llvm/lib/Analysis/MitigationAnalysis.cpp @@ -8,8 +8,6 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/JSON.h" #include "llvm/Support/raw_ostream.h" -#include <string> -#include <unordered_map> using namespace llvm; @@ -18,8 +16,7 @@ AnalysisKey MitigationAnalysis::Key; // Add a command line flag for the module name static cl::opt<std::string> ClOutputModuleName("mitigation-analysis-dso-name", cl::Optional, - cl::desc("DSO name for the module"), - cl::init("unknown")); + cl::desc("DSO name for the module"), cl::init("")); enum class MitigationState { Ineligible, EligibleDisabled, EligibleEnabled }; @@ -30,6 +27,37 @@ static const std::unordered_map<MitigationState, std::string> mapStateToString = {MitigationState::EligibleEnabled, "Enabled"}, }; +struct ModuleMitigationInfo { + std::size_t eligable_auto_var_init = 0; + std::size_t enabled_auto_var_init = 0; + + std::size_t eligable_cfi_icall = 0; + std::size_t enabled_cfi_icall = 0; + + std::size_t eligable_cfi_vcall = 0; + std::size_t enabled_cfi_vcall = 0; + + std::size_t eligable_cfi_nvcall = 0; + std::size_t enabled_cfi_nvcall = 0; + + std::size_t eligable_stack_clash_protection = 0; + std::size_t enabled_stack_clash_protection = 0; + + std::size_t eligable_stack_protector = 0; + std::size_t enabled_stack_protector = 0; + + std::size_t eligable_stack_protector_strong = 0; + std::size_t enabled_stack_protector_strong = 0; + + std::size_t eligable_stack_protector_all = 0; + std::size_t enabled_stack_protector_all = 0; + + std::size_t eligable_libcpp_hardening_mode = 0; + std::size_t enabled_libcpp_hardening_mode = 0; + + std::size_t total_functions = 0; +}; + struct MitigationInfo { MitigationState auto_var_init = MitigationState::Ineligible; MitigationState cfi_icall = MitigationState::Ineligible; @@ -59,6 +87,56 @@ static inline MitigationState valToState(int value) { } } +static void updateModuleInfo(ModuleMitigationInfo &moduleInfo, + const MitigationInfo &info) { + moduleInfo.total_functions++; + + moduleInfo.eligable_auto_var_init += + (info.auto_var_init != MitigationState::Ineligible); + moduleInfo.enabled_auto_var_init += + (info.auto_var_init == MitigationState::EligibleEnabled); + + moduleInfo.eligable_cfi_icall += + (info.cfi_icall != MitigationState::Ineligible); + moduleInfo.enabled_cfi_icall += + (info.cfi_icall == MitigationState::EligibleEnabled); + + moduleInfo.eligable_cfi_vcall += + (info.cfi_vcall != MitigationState::Ineligible); + moduleInfo.enabled_cfi_vcall += + (info.cfi_vcall == MitigationState::EligibleEnabled); + + moduleInfo.eligable_cfi_nvcall += + (info.cfi_nvcall != MitigationState::Ineligible); + moduleInfo.enabled_cfi_nvcall += + (info.cfi_nvcall == MitigationState::EligibleEnabled); + + moduleInfo.eligable_stack_clash_protection += + (info.stack_clash_protection != MitigationState::Ineligible); + moduleInfo.enabled_stack_clash_protection += + (info.stack_clash_protection == MitigationState::EligibleEnabled); + + moduleInfo.eligable_stack_protector += + (info.stack_protector != MitigationState::Ineligible); + moduleInfo.enabled_stack_protector += + (info.stack_protector == MitigationState::EligibleEnabled); + + moduleInfo.eligable_stack_protector_strong += + (info.stack_protector_strong != MitigationState::Ineligible); + moduleInfo.enabled_stack_protector_strong += + (info.stack_protector_strong == MitigationState::EligibleEnabled); + + moduleInfo.eligable_stack_protector_all += + (info.stack_protector_all != MitigationState::Ineligible); + moduleInfo.enabled_stack_protector_all += + (info.stack_protector_all == MitigationState::EligibleEnabled); + + moduleInfo.eligable_libcpp_hardening_mode += + (info.libcpp_hardening_mode != MitigationState::Ineligible); + moduleInfo.enabled_libcpp_hardening_mode += + (info.libcpp_hardening_mode == MitigationState::EligibleEnabled); +} + /// Print out fields in MitigationInfo for debugging/verification purposes. #ifndef NDEBUG static void printInfo(const MitigationInfo &info) { @@ -131,9 +209,10 @@ static void writeJsonToFile(const std::string &jsonString, const std::string &fileName, const std::string &errorMsg) { std::error_code errCode; - raw_fd_ostream OutputStream(fileName, errCode, sys::fs::CD_CreateAlways, + raw_fd_ostream OutputStream(fileName, errCode, sys::fs::CD_OpenAlways, sys::fs::FA_Read | sys::fs::FA_Write, - sys::fs::OF_Text | sys::fs::OF_UpdateAtime); + sys::fs::OF_Text | sys::fs::OF_UpdateAtime | + sys::fs::OF_Append); if (errCode) { errs() << errorMsg << "\n"; errs() << errCode.message() << "\n"; @@ -175,6 +254,42 @@ static json::Object infoToJson(const MitigationInfo &info) { return object; } +/// Convert a ModuleMitigationInfo struct to a JSON object. +static json::Object moduleInfoToJson(const ModuleMitigationInfo &moduleInfo) { + json::Object object; + + object["enabled_auto_var_init"] = moduleInfo.enabled_auto_var_init; + object["eligable_cfi_icall"] = moduleInfo.eligable_cfi_icall; + object["enabled_cfi_icall"] = moduleInfo.enabled_cfi_icall; + object["eligable_cfi_vcall"] = moduleInfo.eligable_cfi_vcall; + object["enabled_cfi_vcall"] = moduleInfo.enabled_cfi_vcall; + object["eligable_cfi_nvcall"] = moduleInfo.eligable_cfi_nvcall; + object["enabled_cfi_nvcall"] = moduleInfo.enabled_cfi_nvcall; + object["eligable_stack_clash_protection"] = + moduleInfo.eligable_stack_clash_protection; + object["enabled_stack_clash_protection"] = + moduleInfo.enabled_stack_clash_protection; + object["eligable_stack_protector"] = moduleInfo.eligable_stack_protector; + object["enabled_stack_protector"] = moduleInfo.enabled_stack_protector; + object["eligable_stack_protector_strong"] = + moduleInfo.eligable_stack_protector_strong; + object["enabled_stack_protector_strong"] = + moduleInfo.enabled_stack_protector_strong; + object["eligable_stack_protector_all"] = + moduleInfo.eligable_stack_protector_all; + object["enabled_stack_protector_all"] = + moduleInfo.enabled_stack_protector_all; + object["eligable_libcpp_hardening_mode"] = + moduleInfo.eligable_libcpp_hardening_mode; + object["enabled_libcpp_hardening_mode"] = + moduleInfo.enabled_libcpp_hardening_mode; + + object["total_functions"] = moduleInfo.total_functions; + object["module"] = + ClOutputModuleName.empty() ? std::string("unknown") : ClOutputModuleName; + return object; +} + /// Return true if function F calls a function whose name contains /// targetFunctionName. static bool functionCallsFunctionWithName(Function &F, @@ -250,9 +365,13 @@ static MitigationState detectLibcppHardeningMode(Function &F) { return MitigationState::Ineligible; } +MitigationAnalysis::MitigationAnalysis(MitigationAnalysisSummary Summary) + : summary_(std::move(Summary)) {} + PreservedAnalyses MitigationAnalysis::run(Module &M, ModuleAnalysisManager &AM) { json::Array jsonArray; + ModuleMitigationInfo moduleInfo; for (Function &F : M) { LLVMContext &Context = F.getContext(); @@ -262,7 +381,7 @@ PreservedAnalyses MitigationAnalysis::run(Module &M, continue; MitigationInfo info; - info.gmodule = ClOutputModuleName; + info.gmodule = ClOutputModuleName.empty() ? std::string("unknown") : ClOutputModuleName; info.function = F.getName(); for (unsigned i = 0; i < ExistingMD->getNumOperands(); ++i) { @@ -283,20 +402,36 @@ PreservedAnalyses MitigationAnalysis::run(Module &M, info.libcpp_hardening_mode = detectLibcppHardeningMode(F); - info.source_mapping = getFunctionSourcePath(F); - info.type_signature = getFirstFunctionTypeSignature(F); - info.type_id = getFunctionTypeId(F); + if (summary_ == MitigationAnalysisSummary::FUNCTION) { + info.source_mapping = getFunctionSourcePath(F); + info.type_signature = getFirstFunctionTypeSignature(F); + info.type_id = getFunctionTypeId(F); - DEBUG_WITH_TYPE(kMitigationAnalysisDebugType, printInfo(info)); - jsonArray.push_back(infoToJson(info)); + DEBUG_WITH_TYPE(kMitigationAnalysisDebugType, printInfo(info)); + jsonArray.push_back(infoToJson(info)); + } else { + // Start aggregating mitigations for entire module + updateModuleInfo(moduleInfo, info); + } } + std::string fileName = + ClOutputModuleName.empty() + ? std::string("mitigation_info.json") + : formatv("mitigation_info-{0}.json", ClOutputModuleName); if (!jsonArray.empty()) { std::string jsonString = formatv("{0}", json::Value(std::move(jsonArray))).str(); if (!jsonString.empty()) { - writeJsonToFile(jsonString, "mitigation_info.json", - "Couldn't write to mitigation_info.json!"); + writeJsonToFile(jsonString, fileName, + formatv("Couldn't write to {0}!", fileName)); + } + } else if (moduleInfo.total_functions > 0) { + std::string jsonString = + formatv("{0}", json::Value(moduleInfoToJson(moduleInfo))).str(); + if (!jsonString.empty()) { + writeJsonToFile(jsonString, fileName, + formatv("Couldn't write to {0}!", fileName)); } } diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index e2c6ebc7017e5..1fa2a49c1484b 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -306,10 +306,18 @@ static cl::opt<std::string> InstrumentColdFuncOnlyPath( "with --pgo-instrument-cold-function-only)"), cl::Hidden); -static cl::opt<bool> - EnableMitigationAnalysis("enable-mitigation-analysis", cl::init(false), - cl::Hidden, - cl::desc("Enable the MitigationAnalysis Pass")); +static cl::opt<MitigationAnalysisSummary> EnableMitigationSummary( + "mitigation-summary", cl::Hidden, + cl::init(MitigationAnalysisSummary::SHORT), + cl::desc("Enable the MitigationAnalysis Pass"), + cl::values( + clEnumValN(MitigationAnalysisSummary::NONE, "none", + "Disable generating mitigation analysis information"), + clEnumValN( + MitigationAnalysisSummary::SHORT, "short", + "Generate summary of mitigation coverage for the current module"), + clEnumValN(MitigationAnalysisSummary::FUNCTION, "function", + "Generate per-function mitigation coverage information"))); extern cl::opt<std::string> UseCtxProfile; extern cl::opt<bool> PGOInstrumentColdFunctionOnly; @@ -1858,8 +1866,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level, // in the current module. MPM.addPass(CrossDSOCFIPass()); - if (EnableMitigationAnalysis) - MPM.addPass(MitigationAnalysis()); + if (EnableMitigationSummary != llvm::MitigationAnalysisSummary::NONE) + MPM.addPass(MitigationAnalysis(EnableMitigationSummary)); if (Level == OptimizationLevel::O0) { // The WPD and LowerTypeTest passes need to run at -O0 to lower type _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits