Skip to content

Commit

Permalink
Use-after-return sanitizer binary metadata
Browse files Browse the repository at this point in the history
Currently per-function metadata consists of:
(start-pc, size, features)

This adds a new UAR feature and if it's set an additional element:
(start-pc, size, features, stack-args-size)

Reviewed By: melver

Differential Revision: https://reviews.llvm.org/D136078
  • Loading branch information
dvyukov committed Nov 29, 2022
1 parent dca1a3f commit a1255dc
Show file tree
Hide file tree
Showing 20 changed files with 375 additions and 20 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.def
Expand Up @@ -288,6 +288,8 @@ CODEGENOPT(SanitizeCoverageTraceLoads, 1, 0) ///< Enable tracing of loads.
CODEGENOPT(SanitizeCoverageTraceStores, 1, 0) ///< Enable tracing of stores.
CODEGENOPT(SanitizeBinaryMetadataCovered, 1, 0) ///< Emit PCs for covered functions.
CODEGENOPT(SanitizeBinaryMetadataAtomics, 1, 0) ///< Emit PCs for atomic operations.
CODEGENOPT(SanitizeBinaryMetadataUAR, 1, 0) ///< Emit PCs for start of functions
///< that are subject for use-after-return checking.
CODEGENOPT(SanitizeStats , 1, 0) ///< Collect statistics for sanitizers.
CODEGENOPT(SimplifyLibCalls , 1, 1) ///< Set when -fbuiltin is enabled.
CODEGENOPT(SoftFloat , 1, 0) ///< -soft-float.
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/CodeGenOptions.h
Expand Up @@ -497,7 +497,8 @@ class CodeGenOptions : public CodeGenOptionsBase {

// Check if any one of SanitizeBinaryMetadata* is enabled.
bool hasSanitizeBinaryMetadata() const {
return SanitizeBinaryMetadataCovered || SanitizeBinaryMetadataAtomics;
return SanitizeBinaryMetadataCovered || SanitizeBinaryMetadataAtomics ||
SanitizeBinaryMetadataUAR;
}
};

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Expand Up @@ -5575,6 +5575,10 @@ def fexperimental_sanitize_metadata_EQ_atomics
: Flag<["-"], "fexperimental-sanitize-metadata=atomics">,
HelpText<"Emit PCs for atomic operations used by binary analysis sanitizers">,
MarshallingInfoFlag<CodeGenOpts<"SanitizeBinaryMetadataAtomics">>;
def fexperimental_sanitize_metadata_EQ_uar
: Flag<["-"], "fexperimental-sanitize-metadata=uar">,
HelpText<"Emit PCs for start of functions that are subject for use-after-return checking.">,
MarshallingInfoFlag<CodeGenOpts<"SanitizeBinaryMetadataUAR">>;
def fpatchable_function_entry_offset_EQ
: Joined<["-"], "fpatchable-function-entry-offset=">, MetaVarName<"<M>">,
HelpText<"Generate M NOPs before function entry">,
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/BackendUtil.cpp
Expand Up @@ -234,6 +234,7 @@ getSanitizerBinaryMetadataOptions(const CodeGenOptions &CGOpts) {
SanitizerBinaryMetadataOptions Opts;
Opts.Covered = CGOpts.SanitizeBinaryMetadataCovered;
Opts.Atomics = CGOpts.SanitizeBinaryMetadataAtomics;
Opts.UAR = CGOpts.SanitizeBinaryMetadataUAR;
return Opts;
}

Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Driver/SanitizerArgs.cpp
Expand Up @@ -104,6 +104,7 @@ enum CoverageFeature {
enum BinaryMetadataFeature {
BinaryMetadataCovered = 1 << 0,
BinaryMetadataAtomics = 1 << 1,
BinaryMetadataUAR = 1 << 2,
};

/// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
Expand Down Expand Up @@ -1133,7 +1134,8 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
// flags. Does not depend on any other sanitizers.
const std::pair<int, std::string> BinaryMetadataFlags[] = {
std::make_pair(BinaryMetadataCovered, "covered"),
std::make_pair(BinaryMetadataAtomics, "atomics")};
std::make_pair(BinaryMetadataAtomics, "atomics"),
std::make_pair(BinaryMetadataUAR, "uar")};
for (const auto &F : BinaryMetadataFlags) {
if (BinaryMetadataFeatures & F.first)
CmdArgs.push_back(
Expand Down Expand Up @@ -1399,6 +1401,7 @@ int parseBinaryMetadataFeatures(const Driver &D, const llvm::opt::Arg *A,
int F = llvm::StringSwitch<int>(Value)
.Case("covered", BinaryMetadataCovered)
.Case("atomics", BinaryMetadataAtomics)
.Case("uar", BinaryMetadataUAR)
.Case("all", ~0)
.Default(0);
if (F == 0 && DiagnoseErrors)
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/CodeGen/CodeGenPassBuilder.h
Expand Up @@ -926,6 +926,7 @@ Error CodeGenPassBuilder<Derived>::addMachinePasses(

addPass(StackMapLivenessPass());
addPass(LiveDebugValuesPass());
addPass(MachineSanitizerBinaryMetadata());

if (TM.Options.EnableMachineOutliner && getOptLevel() != CodeGenOpt::None &&
Opt.EnableMachineOutliner != RunOutliner::NeverOutline) {
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/CodeGen/MachinePassRegistry.def
Expand Up @@ -202,4 +202,5 @@ DUMMY_MACHINE_FUNCTION_PASS("instruction-select", InstructionSelectPass, ())
DUMMY_MACHINE_FUNCTION_PASS("reset-machine-function", ResetMachineFunctionPass, ())
DUMMY_MACHINE_FUNCTION_PASS("machineverifier", MachineVerifierPass, ())
DUMMY_MACHINE_FUNCTION_PASS("print-machine-cycles", MachineCycleInfoPrinterPass, ())
DUMMY_MACHINE_FUNCTION_PASS("machine-sanmd", MachineSanitizerBinaryMetadata, ())
#undef DUMMY_MACHINE_FUNCTION_PASS
4 changes: 4 additions & 0 deletions llvm/include/llvm/CodeGen/Passes.h
Expand Up @@ -409,6 +409,10 @@ namespace llvm {
/// the intrinsic for later emission to the StackMap.
extern char &StackMapLivenessID;

// MachineSanitizerBinaryMetadata - appends/finalizes sanitizer binary
// metadata after llvm SanitizerBinaryMetadata pass.
extern char &MachineSanitizerBinaryMetadataID;

/// RemoveRedundantDebugValues pass.
extern char &RemoveRedundantDebugValuesID;

Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/InitializePasses.h
Expand Up @@ -285,6 +285,7 @@ void initializeMachineOutlinerPass(PassRegistry&);
void initializeMachinePipelinerPass(PassRegistry&);
void initializeMachinePostDominatorTreePass(PassRegistry&);
void initializeMachineRegionInfoPassPass(PassRegistry&);
void initializeMachineSanitizerBinaryMetadataPass(PassRegistry &);
void initializeMachineSchedulerPass(PassRegistry&);
void initializeMachineSinkingPass(PassRegistry&);
void initializeMachineTraceMetricsPass(PassRegistry&);
Expand Down
7 changes: 0 additions & 7 deletions llvm/include/llvm/Transforms/Instrumentation.h
Expand Up @@ -150,13 +150,6 @@ struct SanitizerCoverageOptions {
SanitizerCoverageOptions() = default;
};

/// Options for SanitizerBinaryMetadata.
struct SanitizerBinaryMetadataOptions {
bool Covered = false;
bool Atomics = false;
SanitizerBinaryMetadataOptions() = default;
};

/// Calculate what to divide by to scale counts.
///
/// Given the maximum count, calculate a divisor that will scale all the
Expand Down
Expand Up @@ -19,6 +19,27 @@

namespace llvm {

struct SanitizerBinaryMetadataOptions {
bool Covered = false;
bool Atomics = false;
bool UAR = false;
SanitizerBinaryMetadataOptions() = default;
};

inline constexpr int kSanitizerBinaryMetadataAtomicsBit = 0;
inline constexpr int kSanitizerBinaryMetadataUARBit = 1;

inline constexpr uint32_t kSanitizerBinaryMetadataNone = 0;
inline constexpr uint32_t kSanitizerBinaryMetadataAtomics =
1 << kSanitizerBinaryMetadataAtomicsBit;
inline constexpr uint32_t kSanitizerBinaryMetadataUAR =
1 << kSanitizerBinaryMetadataUARBit;

inline constexpr char kSanitizerBinaryMetadataCoveredSection[] =
"sanmd_covered";
inline constexpr char kSanitizerBinaryMetadataAtomicsSection[] =
"sanmd_atomics";

/// Public interface to the SanitizerBinaryMetadata module pass for emitting
/// metadata for binary analysis sanitizers.
//
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/CMakeLists.txt
Expand Up @@ -196,6 +196,7 @@ add_llvm_component_library(LLVMCodeGen
RegisterBankInfo.cpp
SafeStack.cpp
SafeStackLayout.cpp
SanitizerBinaryMetadata.cpp
ScheduleDAG.cpp
ScheduleDAGInstrs.cpp
ScheduleDAGPrinter.cpp
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/CodeGen.cpp
Expand Up @@ -83,6 +83,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) {
initializeMachineOptimizationRemarkEmitterPassPass(Registry);
initializeMachineOutlinerPass(Registry);
initializeMachinePipelinerPass(Registry);
initializeMachineSanitizerBinaryMetadataPass(Registry);
initializeModuloScheduleTestPass(Registry);
initializeMachinePostDominatorTreePass(Registry);
initializeMachineRegionInfoPassPass(Registry);
Expand Down
78 changes: 78 additions & 0 deletions llvm/lib/CodeGen/SanitizerBinaryMetadata.cpp
@@ -0,0 +1,78 @@
//===- SanitizerBinaryMetadata.cpp
//----------------------------------------------===//
//
// 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 file is a part of SanitizerBinaryMetadata.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include <algorithm>

using namespace llvm;

class MachineSanitizerBinaryMetadata : public MachineFunctionPass {
public:
static char ID;

MachineSanitizerBinaryMetadata();
bool runOnMachineFunction(MachineFunction &F) override;
};

INITIALIZE_PASS(MachineSanitizerBinaryMetadata, "machine-sanmd",
"Machine Sanitizer Binary Metadata", false, false)

char MachineSanitizerBinaryMetadata::ID = 0;
char &llvm::MachineSanitizerBinaryMetadataID =
MachineSanitizerBinaryMetadata::ID;

MachineSanitizerBinaryMetadata::MachineSanitizerBinaryMetadata()
: MachineFunctionPass(ID) {
initializeMachineSanitizerBinaryMetadataPass(
*PassRegistry::getPassRegistry());
}

bool MachineSanitizerBinaryMetadata::runOnMachineFunction(MachineFunction &MF) {
MDNode *MD = MF.getFunction().getMetadata(LLVMContext::MD_pcsections);
if (!MD)
return false;
const auto &Section = *cast<MDString>(MD->getOperand(0));
if (!Section.getString().equals(kSanitizerBinaryMetadataCoveredSection))
return false;
auto &AuxMDs = *cast<MDTuple>(MD->getOperand(1));
// Assume it currently only has features.
assert(AuxMDs.size() == 1);
auto *Features = cast<ConstantAsMetadata>(AuxMDs.getOperand(0))->getValue();
if (!Features->getUniqueInteger()[kSanitizerBinaryMetadataUARBit])
return false;
// Calculate size of stack args for the function.
int64_t Size = 0;
uint64_t Align = 0;
const MachineFrameInfo &MFI = MF.getFrameInfo();
for (int i = -1; i >= (int)-MFI.getNumFixedObjects(); --i) {
Size = std::max(Size, MFI.getObjectOffset(i) + MFI.getObjectSize(i));
Align = std::max(Align, MFI.getObjectAlign(i).value());
}
Size = (Size + Align - 1) & ~(Align - 1);
auto &F = MF.getFunction();
IRBuilder<> IRB(F.getContext());
MDBuilder MDB(F.getContext());
// Keep the features and append size of stack args to the metadata.
const auto *NewMD = MDB.createPCSections(
{{Section.getString(), {Features, IRB.getInt32(Size)}}});
MD->replaceOperandWith(1, NewMD->getOperand(1));
return false;
}
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/TargetPassConfig.cpp
Expand Up @@ -1269,6 +1269,7 @@ void TargetPassConfig::addMachinePasses() {

addPass(&StackMapLivenessID);
addPass(&LiveDebugValuesID);
addPass(&MachineSanitizerBinaryMetadataID);

if (TM->Options.EnableMachineOutliner && getOptLevel() != CodeGenOpt::None &&
EnableMachineOutliner != RunOutliner::NeverOutline) {
Expand Down
53 changes: 42 additions & 11 deletions llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp
Expand Up @@ -67,14 +67,16 @@ class MetadataInfo {
private:
// Forbid construction elsewhere.
explicit constexpr MetadataInfo(StringRef FunctionPrefix,
StringRef SectionSuffix, int Feature)
StringRef SectionSuffix, uint32_t Feature)
: FunctionPrefix(FunctionPrefix), SectionSuffix(SectionSuffix),
FeatureMask(Feature != -1 ? (1u << Feature) : 0) {}
FeatureMask(Feature) {}
};
const MetadataInfo MetadataInfo::Covered{"__sanitizer_metadata_covered",
"sanmd_covered", -1};
kSanitizerBinaryMetadataCoveredSection,
kSanitizerBinaryMetadataNone};
const MetadataInfo MetadataInfo::Atomics{"__sanitizer_metadata_atomics",
"sanmd_atomics", 0};
kSanitizerBinaryMetadataAtomicsSection,
kSanitizerBinaryMetadataAtomics};

// The only instances of MetadataInfo are the constants above, so a set of
// them may simply store pointers to them. To deterministically generate code,
Expand All @@ -89,11 +91,16 @@ cl::opt<bool> ClEmitCovered("sanitizer-metadata-covered",
cl::opt<bool> ClEmitAtomics("sanitizer-metadata-atomics",
cl::desc("Emit PCs for atomic operations."),
cl::Hidden, cl::init(false));
cl::opt<bool> ClEmitUAR("sanitizer-metadata-uar",
cl::desc("Emit PCs for start of functions that are "
"subject for use-after-return checking"),
cl::Hidden, cl::init(false));

//===--- Statistics -------------------------------------------------------===//

STATISTIC(NumMetadataCovered, "Metadata attached to covered functions");
STATISTIC(NumMetadataAtomics, "Metadata attached to atomics");
STATISTIC(NumMetadataUAR, "Metadata attached to UAR functions");

//===----------------------------------------------------------------------===//

Expand All @@ -102,6 +109,7 @@ SanitizerBinaryMetadataOptions &&
transformOptionsFromCl(SanitizerBinaryMetadataOptions &&Opts) {
Opts.Covered |= ClEmitCovered;
Opts.Atomics |= ClEmitAtomics;
Opts.UAR |= ClEmitUAR;
return std::move(Opts);
}

Expand Down Expand Up @@ -142,7 +150,8 @@ class SanitizerBinaryMetadata {
// function with memory operations (atomic or not) requires covered metadata
// to determine if a memory operation is atomic or not in modules compiled
// with SanitizerBinaryMetadata.
bool runOn(Instruction &I, MetadataInfoSet &MIS, MDBuilder &MDB);
bool runOn(Instruction &I, MetadataInfoSet &MIS, MDBuilder &MDB,
uint32_t &FeatureMask);

// Get start/end section marker pointer.
GlobalVariable *getSectionMarker(const Twine &MarkerName, Type *Ty);
Expand Down Expand Up @@ -232,36 +241,58 @@ void SanitizerBinaryMetadata::runOn(Function &F, MetadataInfoSet &MIS) {

// The metadata features enabled for this function, stored along covered
// metadata (if enabled).
uint32_t PerInstrFeatureMask = getEnabledPerInstructionFeature();
uint32_t FeatureMask = getEnabledPerInstructionFeature();
// Don't emit unnecessary covered metadata for all functions to save space.
bool RequiresCovered = false;
if (PerInstrFeatureMask) {
// We can only understand if we need to set UAR feature after looking
// at the instructions. So we need to check instructions even if FeatureMask
// is empty.
if (FeatureMask || Options.UAR) {
for (BasicBlock &BB : F)
for (Instruction &I : BB)
RequiresCovered |= runOn(I, MIS, MDB);
RequiresCovered |= runOn(I, MIS, MDB, FeatureMask);
}

if (F.isVarArg())
FeatureMask &= ~kSanitizerBinaryMetadataUAR;
if (FeatureMask & kSanitizerBinaryMetadataUAR)
NumMetadataUAR++;

// Covered metadata is always emitted if explicitly requested, otherwise only
// if some other metadata requires it to unambiguously interpret it for
// modules compiled with SanitizerBinaryMetadata.
if (Options.Covered || RequiresCovered) {
if (Options.Covered || (FeatureMask && RequiresCovered)) {
NumMetadataCovered++;
const auto *MI = &MetadataInfo::Covered;
MIS.insert(MI);
const StringRef Section = getSectionName(MI->SectionSuffix);
// The feature mask will be placed after the size (32 bit) of the function,
// so in total one covered entry will use `sizeof(void*) + 4 + 4`.
Constant *CFM = IRB.getInt32(PerInstrFeatureMask);
Constant *CFM = IRB.getInt32(FeatureMask);
F.setMetadata(LLVMContext::MD_pcsections,
MDB.createPCSections({{Section, {CFM}}}));
}
}

bool SanitizerBinaryMetadata::runOn(Instruction &I, MetadataInfoSet &MIS,
MDBuilder &MDB) {
MDBuilder &MDB, uint32_t &FeatureMask) {
SmallVector<const MetadataInfo *, 1> InstMetadata;
bool RequiresCovered = false;

if (Options.UAR) {
for (unsigned i = 0; i < I.getNumOperands(); ++i) {
const Value *V = I.getOperand(i);
// TODO(dvyukov): check if V is an address of alloca/function arg.
// See isSafeAndProfitableToSinkLoad for addr-taken allocas
// and DeadArgumentEliminationPass::removeDeadStuffFromFunction
// for iteration over function args.
if (V) {
RequiresCovered = true;
FeatureMask |= kSanitizerBinaryMetadataUAR;
}
}
}

if (Options.Atomics && I.mayReadOrWriteMemory()) {
auto SSID = getAtomicSyncScopeID(&I);
if (SSID.has_value() && SSID.value() != SyncScope::SingleThread) {
Expand Down

1 comment on commit a1255dc

@kazutakahirata
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've checked in dbb1130 to rever this commit due to a build error. Would you mind looking into the assertion? Thanks!

Please sign in to comment.