Author: Dmitry Vyukov Date: 2022-12-05T14:40:31+01:00 New Revision: dbe8c2c316c40b25a0a37b91f1a1a02a55182378
URL: https://github.com/llvm/llvm-project/commit/dbe8c2c316c40b25a0a37b91f1a1a02a55182378 DIFF: https://github.com/llvm/llvm-project/commit/dbe8c2c316c40b25a0a37b91f1a1a02a55182378.diff LOG: Use-after-return sanitizer binary metadata 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 Added: compiler-rt/test/metadata/CMakeLists.txt compiler-rt/test/metadata/common.h compiler-rt/test/metadata/covered.cpp compiler-rt/test/metadata/lit.cfg.py compiler-rt/test/metadata/lit.site.cfg.py.in compiler-rt/test/metadata/uar.cpp llvm/lib/CodeGen/SanitizerBinaryMetadata.cpp Modified: clang/include/clang/Basic/CodeGenOptions.def clang/include/clang/Basic/CodeGenOptions.h clang/include/clang/Driver/Options.td clang/lib/CodeGen/BackendUtil.cpp clang/lib/Driver/SanitizerArgs.cpp compiler-rt/test/CMakeLists.txt llvm/include/llvm/CodeGen/CodeGenPassBuilder.h llvm/include/llvm/CodeGen/MachinePassRegistry.def llvm/include/llvm/CodeGen/Passes.h llvm/include/llvm/InitializePasses.h llvm/include/llvm/Transforms/Instrumentation.h llvm/include/llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h llvm/lib/CodeGen/CMakeLists.txt llvm/lib/CodeGen/CodeGen.cpp llvm/lib/CodeGen/TargetPassConfig.cpp llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp llvm/test/CodeGen/AArch64/O0-pipeline.ll llvm/test/CodeGen/AArch64/O3-pipeline.ll llvm/test/CodeGen/AMDGPU/llc-pipeline.ll llvm/test/CodeGen/ARM/O3-pipeline.ll llvm/test/CodeGen/M68k/pipeline.ll llvm/test/CodeGen/PowerPC/O3-pipeline.ll llvm/test/CodeGen/RISCV/O0-pipeline.ll llvm/test/CodeGen/RISCV/O3-pipeline.ll llvm/test/CodeGen/X86/O0-pipeline.ll llvm/test/CodeGen/X86/opt-pipeline.ll Removed: ################################################################################ diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 43521b76652db..81d5ccd4856d4 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -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. diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index 24cc56c8e05df..d34ed1d6d4919 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -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; } }; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 11f6253db7445..c888d4f6194ab 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -5582,6 +5582,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">, diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index b18ead066c6d2..16b48c6dc1d18 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -235,6 +235,7 @@ getSanitizerBinaryMetadataOptions(const CodeGenOptions &CGOpts) { SanitizerBinaryMetadataOptions Opts; Opts.Covered = CGOpts.SanitizeBinaryMetadataCovered; Opts.Atomics = CGOpts.SanitizeBinaryMetadataAtomics; + Opts.UAR = CGOpts.SanitizeBinaryMetadataUAR; return Opts; } diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index af27dbc92c8e6..96a701366e37b 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -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 @@ -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( @@ -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) diff --git a/compiler-rt/test/CMakeLists.txt b/compiler-rt/test/CMakeLists.txt index 4fc51e0b41e6e..04ff819c6e5af 100644 --- a/compiler-rt/test/CMakeLists.txt +++ b/compiler-rt/test/CMakeLists.txt @@ -105,6 +105,8 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS) # ShadowCallStack does not yet provide a runtime with compiler-rt, the tests # include their own minimal runtime add_subdirectory(shadowcallstack) + # These tests are self-contained and don't need an additional runtime. + add_subdirectory(metadata) endif() if(COMPILER_RT_STANDALONE_BUILD) diff --git a/compiler-rt/test/metadata/CMakeLists.txt b/compiler-rt/test/metadata/CMakeLists.txt new file mode 100644 index 0000000000000..b3765d7dd0885 --- /dev/null +++ b/compiler-rt/test/metadata/CMakeLists.txt @@ -0,0 +1,15 @@ +set(TEST_ARCH ${X86_64}) + +set(METADATA_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(METADATA_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +set(SANITIZER_COMMON_TEST_TARGET_ARCH ${X86_64}) +get_test_cc_for_arch(${X86_64} METADATA_TEST_TARGET_CC METADATA_TEST_TARGET_CFLAGS) +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py) + +add_lit_testsuite(check-sanmd "Running the SanitizerBinaryMetadata tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +set_target_properties(check-sanmd PROPERTIES FOLDER "Compiler-RT Misc") diff --git a/compiler-rt/test/metadata/common.h b/compiler-rt/test/metadata/common.h new file mode 100644 index 0000000000000..b121e871718e9 --- /dev/null +++ b/compiler-rt/test/metadata/common.h @@ -0,0 +1,75 @@ +#include <assert.h> +#include <stdint.h> +#include <stdio.h> + +int main() { printf("main\n"); } + +typedef unsigned long uptr; + +#define FN(X) \ + if (pc == reinterpret_cast<uptr>(X)) \ + return #X + +const char *symbolize(uptr pc) { + FUNCTIONS; + return nullptr; +} + +template <typename T> T consume(const char *&pos, const char *end) { + T v = *reinterpret_cast<const T *>(pos); + pos += sizeof(T); + assert(pos <= end); + return v; +} + +uint32_t meta_version; +const char *meta_start; +const char *meta_end; + +extern "C" { +void __sanitizer_metadata_covered_add(uint32_t version, const char *start, + const char *end) { + printf("metadata add version %u\n", version); + for (const char *pos = start; pos < end;) { + const uptr base = reinterpret_cast<uptr>(pos); + const long offset = (version & (1 << 16)) ? consume<long>(pos, end) + : consume<int>(pos, end); + const uint32_t size = consume<uint32_t>(pos, end); + const uint32_t features = consume<uint32_t>(pos, end); + uint32_t stack_args = 0; + if (features & (1 << 1)) + stack_args = consume<uint32_t>(pos, end); + if (const char *name = symbolize(base + offset)) + printf("%s: features=%x stack_args=%u\n", name, features, stack_args); + } + meta_version = version; + meta_start = start; + meta_end = end; +} + +void __sanitizer_metadata_covered_del(uint32_t version, const char *start, + const char *end) { + assert(version == meta_version); + assert(start == meta_start); + assert(end == meta_end); +} + +const char *atomics_start; +const char *atomics_end; + +void __sanitizer_metadata_atomics_add(uint32_t version, const char *start, + const char *end) { + assert(version == meta_version); + assert(start); + assert(end >= end); + atomics_start = start; + atomics_end = end; +} + +void __sanitizer_metadata_atomics_del(uint32_t version, const char *start, + const char *end) { + assert(version == meta_version); + assert(atomics_start == start); + assert(atomics_end == end); +} +} diff --git a/compiler-rt/test/metadata/covered.cpp b/compiler-rt/test/metadata/covered.cpp new file mode 100644 index 0000000000000..d00f970c795c9 --- /dev/null +++ b/compiler-rt/test/metadata/covered.cpp @@ -0,0 +1,75 @@ +// RUN: %clangxx %s -o %t && %t | FileCheck %s +// RUN: %clangxx %s -o %t -fexperimental-sanitize-metadata=covered && %t | FileCheck -check-prefix=CHECK-C %s +// RUN: %clangxx %s -o %t -fexperimental-sanitize-metadata=atomics && %t | FileCheck -check-prefix=CHECK-A %s +// RUN: %clangxx %s -o %t -fexperimental-sanitize-metadata=uar && %t | FileCheck -check-prefix=CHECK-U %s +// RUN: %clangxx %s -o %t -fexperimental-sanitize-metadata=covered,atomics && %t | FileCheck -check-prefix=CHECK-CA %s +// RUN: %clangxx %s -o %t -fexperimental-sanitize-metadata=covered,uar && %t | FileCheck -check-prefix=CHECK-CU %s +// RUN: %clangxx %s -o %t -fexperimental-sanitize-metadata=atomics,uar && %t | FileCheck -check-prefix=CHECK-AU %s +// RUN: %clangxx %s -o %t -fexperimental-sanitize-metadata=covered,atomics,uar && %t | FileCheck -check-prefix=CHECK-CAU %s + +// CHECK-NOT: metadata add +// CHECK: main +// CHECK-NOT: metadata del + +// CHECK-C: empty: features=0 +// CHECK-A-NOT: empty: +// CHECK-U-NOT: empty: +// CHECK-CA: empty: features=1 +// CHECK-CU: empty: features=0 +// CHECK-AU-NOT: empty: +// CHECK-CAU: empty: features=1 +void empty() {} + +// CHECK-C: normal: features=0 +// CHECK-A: normal: features=1 +// CHECK-U: normal: features=2 +// CHECK-CA: normal: features=1 +// CHECK-CU: normal: features=2 +// CHECK-AU: normal: features=3 +// CHECK-CAU:normal: features=3 +void normal() { + volatile int x; + x = 0; +} + +// CHECK-C: with_atomic: features=0 +// CHECK-A: with_atomic: features=1 +// CHECK-U: with_atomic: features=2 +// CHECK-CA: with_atomic: features=1 +// CHECK-CU: with_atomic: features=2 +// CHECK-AU: with_atomic: features=3 +// CHECK-CAU: with_atomic: features=3 +int with_atomic(int *p) { return __atomic_load_n(p, __ATOMIC_RELAXED); } + +// CHECK-C: ellipsis: features=0 +// CHECK-A: ellipsis: features=1 +// CHECK-U-NOT: ellipsis: +// CHECK-CA: ellipsis: features=1 +// CHECK-CU: ellipsis: features=0 +// CHECK-AU: ellipsis: features=1 +// CHECK-CAU: ellipsis: features=1 +void ellipsis(int *p, ...) { + volatile int x; + x = 0; +} + +// CHECK-C: ellipsis_with_atomic: features=0 +// CHECK-A: ellipsis_with_atomic: features=1 +// CHECK-U-NOT: ellipsis_with_atomic: +// CHECK-CA: ellipsis_with_atomic: features=1 +// CHECK-CU: ellipsis_with_atomic: features=0 +// CHECK-AU: ellipsis_with_atomic: features=1 +// CHECK-CAU: ellipsis_with_atomic: features=1 +int ellipsis_with_atomic(int *p, ...) { + return __atomic_load_n(p, __ATOMIC_RELAXED); +} + +#define FUNCTIONS \ + FN(empty); \ + FN(normal); \ + FN(with_atomic); \ + FN(ellipsis); \ + FN(ellipsis_with_atomic); \ + /**/ + +#include "common.h" diff --git a/compiler-rt/test/metadata/lit.cfg.py b/compiler-rt/test/metadata/lit.cfg.py new file mode 100644 index 0000000000000..aefc97f09ed92 --- /dev/null +++ b/compiler-rt/test/metadata/lit.cfg.py @@ -0,0 +1,9 @@ +import os + +config.name = 'SanitizerBinaryMetadata' +config.test_source_root = os.path.dirname(__file__) +config.suffixes = ['.cpp'] +# Binary metadata is currently emited only for ELF binaries +# and sizes of stack arguments depend on the arch. +if config.host_os not in ['Linux'] or config.target_arch not in ['x86_64']: + config.unsupported = True diff --git a/compiler-rt/test/metadata/lit.site.cfg.py.in b/compiler-rt/test/metadata/lit.site.cfg.py.in new file mode 100644 index 0000000000000..7b95b4b0fb73d --- /dev/null +++ b/compiler-rt/test/metadata/lit.site.cfg.py.in @@ -0,0 +1,14 @@ +@LIT_SITE_CFG_IN_HEADER@ + +# Tool-specific config options. +config.clang = "@METADATA_TEST_TARGET_CC@" +config.target_cflags = "@METADATA_TEST_TARGET_CFLAGS@" +config.target_arch = "x86_64" + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@METADATA_LIT_SOURCE_DIR@/lit.cfg.py") + +config.substitutions.append(("%clangxx ", " " + config.clang + " " + config.target_cflags + " ")) diff --git a/compiler-rt/test/metadata/uar.cpp b/compiler-rt/test/metadata/uar.cpp new file mode 100644 index 0000000000000..dc6abdf63ab45 --- /dev/null +++ b/compiler-rt/test/metadata/uar.cpp @@ -0,0 +1,59 @@ +// RUN: %clangxx %s -o %t -fexperimental-sanitize-metadata=covered,uar && %t | FileCheck %s + +// CHECK: metadata add version 1 + +// CHECK: empty: features=0 stack_args=0 +void empty() {} + +// CHECK: ellipsis: features=0 stack_args=0 +void ellipsis(const char *fmt, ...) { + volatile int x; + x = 1; +} + +// CHECK: non_empty_function: features=2 stack_args=0 +void non_empty_function() { + // Completely empty functions don't get uar metadata. + volatile int x; + x = 1; +} + +// CHECK: no_stack_args: features=2 stack_args=0 +void no_stack_args(long a0, long a1, long a2, long a3, long a4, long a5) { + volatile int x; + x = 1; +} + +// CHECK: stack_args: features=2 stack_args=16 +void stack_args(long a0, long a1, long a2, long a3, long a4, long a5, long a6) { + volatile int x; + x = 1; +} + +// CHECK: more_stack_args: features=2 stack_args=32 +void more_stack_args(long a0, long a1, long a2, long a3, long a4, long a5, + long a6, long a7, long a8) { + volatile int x; + x = 1; +} + +// CHECK: struct_stack_args: features=2 stack_args=144 +struct large { + char x[131]; +}; +void struct_stack_args(large a) { + volatile int x; + x = 1; +} + +#define FUNCTIONS \ + FN(empty); \ + FN(ellipsis); \ + FN(non_empty_function); \ + FN(no_stack_args); \ + FN(stack_args); \ + FN(more_stack_args); \ + FN(struct_stack_args); \ + /**/ + +#include "common.h" diff --git a/llvm/include/llvm/CodeGen/CodeGenPassBuilder.h b/llvm/include/llvm/CodeGen/CodeGenPassBuilder.h index 48aa4b034fee0..fbaa1b229f55a 100644 --- a/llvm/include/llvm/CodeGen/CodeGenPassBuilder.h +++ b/llvm/include/llvm/CodeGen/CodeGenPassBuilder.h @@ -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) { diff --git a/llvm/include/llvm/CodeGen/MachinePassRegistry.def b/llvm/include/llvm/CodeGen/MachinePassRegistry.def index c1ceff9680d67..e5799a12a6dfe 100644 --- a/llvm/include/llvm/CodeGen/MachinePassRegistry.def +++ b/llvm/include/llvm/CodeGen/MachinePassRegistry.def @@ -203,4 +203,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 diff --git a/llvm/include/llvm/CodeGen/Passes.h b/llvm/include/llvm/CodeGen/Passes.h index 5701dd13e152c..06cf550ed12c7 100644 --- a/llvm/include/llvm/CodeGen/Passes.h +++ b/llvm/include/llvm/CodeGen/Passes.h @@ -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; diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h index 0c69ff3166d68..fd52c529bb4e6 100644 --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -276,6 +276,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&); diff --git a/llvm/include/llvm/Transforms/Instrumentation.h b/llvm/include/llvm/Transforms/Instrumentation.h index 2497b3d40d0b0..392983a198444 100644 --- a/llvm/include/llvm/Transforms/Instrumentation.h +++ b/llvm/include/llvm/Transforms/Instrumentation.h @@ -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 diff --git a/llvm/include/llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h b/llvm/include/llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h index a54801309c1d8..67e22d1aa6818 100644 --- a/llvm/include/llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h +++ b/llvm/include/llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h @@ -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. // diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt index db3b6183b5fd2..75b77ee7cd5fe 100644 --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -197,6 +197,7 @@ add_llvm_component_library(LLVMCodeGen RegisterBankInfo.cpp SafeStack.cpp SafeStackLayout.cpp + SanitizerBinaryMetadata.cpp ScheduleDAG.cpp ScheduleDAGInstrs.cpp ScheduleDAGPrinter.cpp diff --git a/llvm/lib/CodeGen/CodeGen.cpp b/llvm/lib/CodeGen/CodeGen.cpp index a8bde3b700970..82581e262f843 100644 --- a/llvm/lib/CodeGen/CodeGen.cpp +++ b/llvm/lib/CodeGen/CodeGen.cpp @@ -84,6 +84,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) { initializeMachineOptimizationRemarkEmitterPassPass(Registry); initializeMachineOutlinerPass(Registry); initializeMachinePipelinerPass(Registry); + initializeMachineSanitizerBinaryMetadataPass(Registry); initializeModuloScheduleTestPass(Registry); initializeMachinePostDominatorTreePass(Registry); initializeMachineRegionInfoPassPass(Registry); diff --git a/llvm/lib/CodeGen/SanitizerBinaryMetadata.cpp b/llvm/lib/CodeGen/SanitizerBinaryMetadata.cpp new file mode 100644 index 0000000000000..f9e608fe0f107 --- /dev/null +++ b/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.getNumOperands() == 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. + F.setMetadata(LLVMContext::MD_pcsections, + MDB.createPCSections( + {{Section.getString(), {Features, IRB.getInt32(Size)}}})); + return false; +} diff --git a/llvm/lib/CodeGen/TargetPassConfig.cpp b/llvm/lib/CodeGen/TargetPassConfig.cpp index 59e714c4f2801..78195bc3d7a4c 100644 --- a/llvm/lib/CodeGen/TargetPassConfig.cpp +++ b/llvm/lib/CodeGen/TargetPassConfig.cpp @@ -1270,6 +1270,7 @@ void TargetPassConfig::addMachinePasses() { addPass(&StackMapLivenessID); addPass(&LiveDebugValuesID); + addPass(&MachineSanitizerBinaryMetadataID); if (TM->Options.EnableMachineOutliner && getOptLevel() != CodeGenOpt::None && EnableMachineOutliner != RunOutliner::NeverOutline) { diff --git a/llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp b/llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp index 169e87625c254..e8eaa9439c474 100644 --- a/llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp +++ b/llvm/lib/Transforms/Instrumentation/SanitizerBinaryMetadata.cpp @@ -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, @@ -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"); //===----------------------------------------------------------------------===// @@ -102,6 +109,7 @@ SanitizerBinaryMetadataOptions && transformOptionsFromCl(SanitizerBinaryMetadataOptions &&Opts) { Opts.Covered |= ClEmitCovered; Opts.Atomics |= ClEmitAtomics; + Opts.UAR |= ClEmitUAR; return std::move(Opts); } @@ -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); @@ -227,36 +236,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) { diff --git a/llvm/test/CodeGen/AArch64/O0-pipeline.ll b/llvm/test/CodeGen/AArch64/O0-pipeline.ll index 595343979f9b7..8d4556c6bda1c 100644 --- a/llvm/test/CodeGen/AArch64/O0-pipeline.ll +++ b/llvm/test/CodeGen/AArch64/O0-pipeline.ll @@ -71,6 +71,7 @@ ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis +; CHECK-NEXT: Machine Sanitizer Binary Metadata ; CHECK-NEXT: Insert CFI remember/restore state instructions ; CHECK-NEXT: Unpack machine instruction bundles ; CHECK-NEXT: Lazy Machine Block Frequency Analysis diff --git a/llvm/test/CodeGen/AArch64/O3-pipeline.ll b/llvm/test/CodeGen/AArch64/O3-pipeline.ll index 90cf49e8ed8fc..8d697c8b647db 100644 --- a/llvm/test/CodeGen/AArch64/O3-pipeline.ll +++ b/llvm/test/CodeGen/AArch64/O3-pipeline.ll @@ -219,6 +219,7 @@ ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis +; CHECK-NEXT: Machine Sanitizer Binary Metadata ; CHECK-NEXT: Machine Outliner ; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Insert CFI remember/restore state instructions diff --git a/llvm/test/CodeGen/AMDGPU/llc-pipeline.ll b/llvm/test/CodeGen/AMDGPU/llc-pipeline.ll index 8ceb5ec1ed8ab..c4e4f36df5980 100644 --- a/llvm/test/CodeGen/AMDGPU/llc-pipeline.ll +++ b/llvm/test/CodeGen/AMDGPU/llc-pipeline.ll @@ -140,6 +140,7 @@ ; GCN-O0-NEXT: Branch relaxation pass ; GCN-O0-NEXT: Register Usage Information Collector Pass ; GCN-O0-NEXT: Live DEBUG_VALUE analysis +; GCN-O0-NEXT: Machine Sanitizer Binary Metadata ; GCN-O0-NEXT: Function register usage analysis ; GCN-O0-NEXT: FunctionPass Manager ; GCN-O0-NEXT: Lazy Machine Block Frequency Analysis @@ -404,6 +405,7 @@ ; GCN-O1-NEXT: Branch relaxation pass ; GCN-O1-NEXT: Register Usage Information Collector Pass ; GCN-O1-NEXT: Live DEBUG_VALUE analysis +; GCN-O1-NEXT: Machine Sanitizer Binary Metadata ; GCN-O1-NEXT: Function register usage analysis ; GCN-O1-NEXT: FunctionPass Manager ; GCN-O1-NEXT: Lazy Machine Block Frequency Analysis @@ -700,6 +702,7 @@ ; GCN-O1-OPTS-NEXT: Branch relaxation pass ; GCN-O1-OPTS-NEXT: Register Usage Information Collector Pass ; GCN-O1-OPTS-NEXT: Live DEBUG_VALUE analysis +; GCN-O1-OPTS-NEXT: Machine Sanitizer Binary Metadata ; GCN-O1-OPTS-NEXT: Function register usage analysis ; GCN-O1-OPTS-NEXT: FunctionPass Manager ; GCN-O1-OPTS-NEXT: Lazy Machine Block Frequency Analysis @@ -999,6 +1002,7 @@ ; GCN-O2-NEXT: Branch relaxation pass ; GCN-O2-NEXT: Register Usage Information Collector Pass ; GCN-O2-NEXT: Live DEBUG_VALUE analysis +; GCN-O2-NEXT: Machine Sanitizer Binary Metadata ; GCN-O2-NEXT: Function register usage analysis ; GCN-O2-NEXT: FunctionPass Manager ; GCN-O2-NEXT: Lazy Machine Block Frequency Analysis @@ -1310,6 +1314,7 @@ ; GCN-O3-NEXT: Branch relaxation pass ; GCN-O3-NEXT: Register Usage Information Collector Pass ; GCN-O3-NEXT: Live DEBUG_VALUE analysis +; GCN-O3-NEXT: Machine Sanitizer Binary Metadata ; GCN-O3-NEXT: Function register usage analysis ; GCN-O3-NEXT: FunctionPass Manager ; GCN-O3-NEXT: Lazy Machine Block Frequency Analysis diff --git a/llvm/test/CodeGen/ARM/O3-pipeline.ll b/llvm/test/CodeGen/ARM/O3-pipeline.ll index 253e17cdd303e..853aab572d58e 100644 --- a/llvm/test/CodeGen/ARM/O3-pipeline.ll +++ b/llvm/test/CodeGen/ARM/O3-pipeline.ll @@ -190,6 +190,7 @@ ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis +; CHECK-NEXT: Machine Sanitizer Binary Metadata ; CHECK-NEXT: Machine Outliner ; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: ReachingDefAnalysis diff --git a/llvm/test/CodeGen/M68k/pipeline.ll b/llvm/test/CodeGen/M68k/pipeline.ll index f1dba29cf4cf7..93c1e102ebfaf 100644 --- a/llvm/test/CodeGen/M68k/pipeline.ll +++ b/llvm/test/CodeGen/M68k/pipeline.ll @@ -131,6 +131,7 @@ ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis +; CHECK-NEXT: Machine Sanitizer Binary Metadata ; CHECK-NEXT: Lazy Machine Block Frequency Analysis ; CHECK-NEXT: Machine Optimization Remark Emitter ; CHECK-NEXT: M68k Assembly Printer diff --git a/llvm/test/CodeGen/PowerPC/O3-pipeline.ll b/llvm/test/CodeGen/PowerPC/O3-pipeline.ll index 6b87f605dd8b7..9f1703e238eb6 100644 --- a/llvm/test/CodeGen/PowerPC/O3-pipeline.ll +++ b/llvm/test/CodeGen/PowerPC/O3-pipeline.ll @@ -207,6 +207,7 @@ ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis +; CHECK-NEXT: Machine Sanitizer Binary Metadata ; CHECK-NEXT: PowerPC Expand Atomic ; CHECK-NEXT: PowerPC Branch Selector ; CHECK-NEXT: Lazy Machine Block Frequency Analysis diff --git a/llvm/test/CodeGen/RISCV/O0-pipeline.ll b/llvm/test/CodeGen/RISCV/O0-pipeline.ll index ea5bfb0bdc86b..beb2ace4b809e 100644 --- a/llvm/test/CodeGen/RISCV/O0-pipeline.ll +++ b/llvm/test/CodeGen/RISCV/O0-pipeline.ll @@ -57,6 +57,7 @@ ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis +; CHECK-NEXT: Machine Sanitizer Binary Metadata ; CHECK-NEXT: RISCV pseudo instruction expansion pass ; CHECK-NEXT: RISCV atomic pseudo instruction expansion pass ; CHECK-NEXT: Lazy Machine Block Frequency Analysis diff --git a/llvm/test/CodeGen/RISCV/O3-pipeline.ll b/llvm/test/CodeGen/RISCV/O3-pipeline.ll index 73853d614fcea..64ffdb75b0eb3 100644 --- a/llvm/test/CodeGen/RISCV/O3-pipeline.ll +++ b/llvm/test/CodeGen/RISCV/O3-pipeline.ll @@ -164,6 +164,7 @@ ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis +; CHECK-NEXT: Machine Sanitizer Binary Metadata ; CHECK-NEXT: Machine Outliner ; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: RISCV pseudo instruction expansion pass diff --git a/llvm/test/CodeGen/X86/O0-pipeline.ll b/llvm/test/CodeGen/X86/O0-pipeline.ll index 8ef0202a16536..959add648245f 100644 --- a/llvm/test/CodeGen/X86/O0-pipeline.ll +++ b/llvm/test/CodeGen/X86/O0-pipeline.ll @@ -72,6 +72,7 @@ ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis +; CHECK-NEXT: Machine Sanitizer Binary Metadata ; CHECK-NEXT: X86 Speculative Execution Side Effect Suppression ; CHECK-NEXT: X86 Indirect Thunks ; CHECK-NEXT: X86 Return Thunks diff --git a/llvm/test/CodeGen/X86/opt-pipeline.ll b/llvm/test/CodeGen/X86/opt-pipeline.ll index 9de77d2f4c3c8..d3c65f5dffa40 100644 --- a/llvm/test/CodeGen/X86/opt-pipeline.ll +++ b/llvm/test/CodeGen/X86/opt-pipeline.ll @@ -206,6 +206,7 @@ ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis +; CHECK-NEXT: Machine Sanitizer Binary Metadata ; CHECK-NEXT: X86 Speculative Execution Side Effect Suppression ; CHECK-NEXT: X86 Indirect Thunks ; CHECK-NEXT: X86 Return Thunks _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits