-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[analysis] Software Bill of Mitigations #130103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[analysis] Software Bill of Mitigations #130103
Conversation
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)
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/pr-subscribers-llvm-analysis @llvm/pr-subscribers-clang-driver Author: Matthew Levy (matthewlevy97) ChangesThe goal of this stack is to provide a metric to audit the deployed mitigations in a binary and where they are enabled at function level granularity. This will enable tracking of where we do and don't have mitigations, versus the current approach of tracking where flags are passed. Two flags are added to control this behavior:
Patch is 30.77 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/130103.diff 18 Files Affected:
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,
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_nv...
[truncated]
|
|
@llvm/pr-subscribers-clang Author: Matthew Levy (matthewlevy97) ChangesThe goal of this stack is to provide a metric to audit the deployed mitigations in a binary and where they are enabled at function level granularity. This will enable tracking of where we do and don't have mitigations, versus the current approach of tracking where flags are passed. Two flags are added to control this behavior:
Patch is 30.77 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/130103.diff 18 Files Affected:
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,
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_nv...
[truncated]
|
|
This needs a higher-level description of the overall goal, and why you've picked the specific instrumentation points you've chosen. |
And tests! Thanks |
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
- Add tests for verifying metadata added correctly - Added new parameter to only add mitigation metadata for functions when MitigationAnalysis CGO is enabled
f0baa6f to
df88ae3
Compare
oskarwirga
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some comments for other reviewers to highlight some of the tradeoffs we had to make + if they could be improved upon. CC: @devincoughlin
| else if (isStackProtectorOn(LangOpts, getTriple(), LangOptions::SSPReq)) | ||
| B.addAttribute(llvm::Attribute::StackProtectReq); | ||
|
|
||
| bool noStackProtectionAttr = D && D->hasAttr<NoStackProtectorAttr>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should either refactor our approach to stack protectors or add comment explaining the caveat here that our mitigation analysis pass runs before the stack protectors are lowered so this is more of a heuristic rather than actual hard signal.
| /// 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; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to explain here how we modified the headers to emit this as a heuristic, otherwise LibCPPHardeningMode will not work out of the box.
What we currently do is change the libcpp assert macro to a function call like this:
// Define a function with an annotation for the no-op assume case
static constexpr void __attribute__((optnone, noinline)) _libcpp_hardening_mode_disabled() { }
#define _LIBCPP_ASSUME_TRACKED(expression) _libcpp_hardening_mode_disabled()
This lets us track functions which have checks emitted using a function name match. The benefit is high signal, but the drawback is modifying a build which otherwise could have been used in production, meaning we are disabling libcpphardening mode to measure where it is.
Or if anyone else has suggestions for our approach, would be appreciated.
| // 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")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We pass in link unit name information through a flag, we weren't sure if there was another way available.
|
So, this isn't checking whether a given mitigation is enabled for a given bit of code; it's checking where we emit mitigation-specific code. So for example, if you have cfi-icall enabled, it points out all functions that contain indirect calls? That seems like a pretty weird way to approach mitigations. This isn't at all helpful for most kinds of audits; usually people care about whether the mitigations they want are enabled, not how many times a particular mitigation triggered. And it seems like there are more direct ways to measure the cost of a mitigation, like comparing codesize with it enabled vs. disabled. So I'm really not sure how you'd use this. |
|
@efriedma-quic That is correct about this just tracking where mitigations are emitted. I believe the emissions points should largely correlate with where they would get instrumented, but if this analysis runs super late (after all optimizations, etc.) to determine exactly if/where mitigations actually get instrumented, it might become more of a binary analysis task and greatly increase complexity. I am open to suggestions on different approaches. The use case isn't really to verify that mitigations are enabled, but provide a way of tracking incremental deployments of mitigations (e.g., large binary where a given mitigation being deployed causes to large of a regression so only certain sub-units have a given mitigation). I have a follow-up diff that outputs a summary instead of a per-function tracking to show overall coverage on mitigations in the binary which I assume will be the more used option. |
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
03f1de1 to
7ae4ec3
Compare
cf54c6e to
43ba7ef
Compare
RFC: https://discourse.llvm.org/t/rfc-implement-analysis-for-exploit-mitigation-coverage/84326
The goal of this stack is to provide a metric to audit the deployed mitigations in a binary and where they are enabled at function level granularity. This will enable tracking of where we do and don't have mitigations, versus the current approach of tracking where flags are passed. This is useful for several reasons including:
Providing assertions on mitigation deployment. For example, tools like
checkseccan alert of stack protector usage but cannot state which functions are enabled and why ("normal", "strong", or "all" option). The alternative to this method is binary analysis which has its own pros/cons.Tracking of partial mitigation enablement. Some large projects might not want a boolean enablement of mitigations for multiple reasons (e.g., performance, size, etc), and so partial enablement is desired. This should provide accurate tracking over which functions have a given mitigation to better support incremental approaches.
Two flags are added to control this behavior:
-fmitigation-analysisto generate the metadata pertaining to mitigation enablement/disablement-enable-mitigation-analysisto generate the output JSON when building with LTO