llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-llvm-ir Author: Alexander Potapenko (ramosian-glider) <details> <summary>Changes</summary> Some code must not be instrumented as a whole but still has parts where instrumentation is allowed. The Linux kernel marks such functions `noinstr` and delimits the parts that may call instrumented code with `instrumentation_begin()` and `instrumentation_end()`. Sanitizers currently cannot see these parts: they either skip the whole function or instrument all of it. Under KMSAN this causes false positives on the kernel entry paths, because `noinstr` code never sets up parameter shadow for the instrumented functions it calls (e.g. https://lore.kernel.org/all/69e7ee1f.a00a0220.17a17.001d.GAE@<!-- -->google.com/T/#u). Other kernel sanitizers simply miss bugs in these regions. This series adds region markers and implements them in four sanitizers. Each commit is self-contained and can be reviewed separately: 1. **[Sanitizers] Add sanitizer instrumentation region intrinsics and builtins** `llvm.sanitizer.instrumentation.begin`/`end`, `__builtin_sanitizer_instrumentation_begin()`/`end()`, and `computeSanitizerInstrumentationRegions()`, a must-analysis over the region nesting depth shared by the passes (an instruction is in a region only if every path to it entered one, so it is always dominated by a `begin`). The intrinsics keep the default memory effects so that accesses to escaping memory do not move across region boundaries; codegen drops them. Clang only emits them for sanitizers that consume them. 2. **[MSan] Support sanitizer instrumentation regions in KMSAN** In `disable_sanitizer_instrumentation` functions, fetch the context state at `begin` and propagate shadow / set up parameter shadow inside regions. 3. **[ASan] Support sanitizer instrumentation regions** Check memory accesses and memory intrinsics inside regions of functions without `sanitize_address` or with `disable_sanitizer_instrumentation`. 4. **[SanitizerCoverage] Support sanitizer instrumentation regions** Add coverage points for in-region code of `nosanitize_coverage` / `disable_sanitizer_instrumentation` functions (KCOV). 5. **[TSan] Support sanitizer instrumentation regions** Instrument in-region accesses of `disable_sanitizer_instrumentation` functions and functions without `sanitize_thread` (KCSAN). The semantics are documented in LangRef and the per-sanitizer docs. Tests: IR tests for each pass, a PhaseOrdering test that the markers survive the `-O2` pipeline, clang CodeGen tests for the builtins and for the full `-O2` pipelines (KMSAN, KASAN, KCOV, KCSAN, KMSAN+KCOV), CodeGen tests for SelectionDAG/FastISel/GlobalISel, and unit tests for the region analysis. The kernel side (KMSAN first, then KASAN/KCOV/KCSAN) will use the builtins from `instrumentation_begin()`/`instrumentation_end()`; with KASAN, about 170 checks are added in `noinstr` code of a typical x86 config. This supersedes #<!-- -->202603. --- Patch is 117.25 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/231374.diff 32 Files Affected: - (modified) clang/docs/AddressSanitizer.md (+21) - (modified) clang/docs/MemorySanitizer.md (+8) - (modified) clang/docs/SanitizerCoverage.md (+25) - (modified) clang/docs/ThreadSanitizer.md (+23) - (modified) clang/include/clang/Basic/Builtins.td (+18) - (modified) clang/lib/CodeGen/CGBuiltin.cpp (+17) - (added) clang/test/CodeGen/sanitizer-instrumentation-builtins.c (+53) - (added) clang/test/CodeGen/sanitizer-instrumentation-regions-pipeline.c (+58) - (modified) llvm/docs/LangRef.md (+29) - (modified) llvm/include/llvm/IR/Intrinsics.td (+18) - (modified) llvm/include/llvm/Transforms/Utils/Instrumentation.h (+19) - (modified) llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp (+5) - (modified) llvm/lib/CodeGen/SelectionDAG/FastISel.cpp (+2) - (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (+2) - (modified) llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp (+40-9) - (modified) llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp (+248-44) - (modified) llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp (+59-7) - (modified) llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp (+36-10) - (modified) llvm/lib/Transforms/Utils/Instrumentation.cpp (+79) - (added) llvm/test/CodeGen/Generic/sanitizer-instrumentation-intrinsics.ll (+19) - (added) llvm/test/CodeGen/X86/sanitizer-instrumentation-intrinsics.ll (+20) - (added) llvm/test/Instrumentation/AddressSanitizer/sanitizer-instrumentation-regions.ll (+183) - (added) llvm/test/Instrumentation/MemorySanitizer/kmsan-intrinsics.ll (+138) - (added) llvm/test/Instrumentation/MemorySanitizer/kmsan-region-dataflow.ll (+124) - (added) llvm/test/Instrumentation/MemorySanitizer/kmsan-region-mem-intrinsics.ll (+41) - (added) llvm/test/Instrumentation/MemorySanitizer/kmsan-region-userspace.ll (+26) - (added) llvm/test/Instrumentation/MemorySanitizer/kmsan-region-varargs.ll (+36) - (added) llvm/test/Instrumentation/SanitizerCoverage/sanitizer-instrumentation-regions.ll (+205) - (added) llvm/test/Instrumentation/ThreadSanitizer/sanitizer-instrumentation-regions.ll (+178) - (added) llvm/test/Transforms/PhaseOrdering/sanitizer-instrumentation-markers.ll (+86) - (modified) llvm/unittests/Transforms/Utils/CMakeLists.txt (+1) - (added) llvm/unittests/Transforms/Utils/SanitizerRegionsTest.cpp (+192) ``````````diff diff --git a/clang/docs/AddressSanitizer.md b/clang/docs/AddressSanitizer.md index 160c63dce2d97..1f502584b1de9 100644 --- a/clang/docs/AddressSanitizer.md +++ b/clang/docs/AddressSanitizer.md @@ -243,6 +243,27 @@ AddressSanitizer also supports works similarly to `__attribute__((no_sanitize("address")))`, but it also prevents instrumentation performed by other sanitizers. +### Re-enabling Instrumentation in Regions + +Inside a function that AddressSanitizer does not instrument, a region +delimited by `__builtin_sanitizer_instrumentation_begin()` and +`__builtin_sanitizer_instrumentation_end()` is instrumented again: memory +accesses and memory intrinsics in it are checked. The function's stack frame +is still not instrumented, and accesses to its own local variables are not +checked. Regions nest, and code reachable without passing through a +`__builtin_sanitizer_instrumentation_begin()` is not considered to be in a +region. + +```c +__attribute__((no_sanitize("address"))) +void mostly_uninstrumented(int *p) { + ... // not checked + __builtin_sanitizer_instrumentation_begin(); + *p = 0; // checked + __builtin_sanitizer_instrumentation_end(); +} +``` + ### Interaction of Inlining with Disabling Sanitizer Instrumentation - A `no_sanitize` function will not be inlined heuristically by the compiler into a sanitized function. diff --git a/clang/docs/MemorySanitizer.md b/clang/docs/MemorySanitizer.md index c6d5d337952de..88f8811ab6117 100644 --- a/clang/docs/MemorySanitizer.md +++ b/clang/docs/MemorySanitizer.md @@ -94,6 +94,14 @@ positives and therefore should be used with care, and only if absolutely required; for example for certain code that cannot tolerate any instrumentation and resulting side-effects. This attribute overrides `no_sanitize("memory")`. +In the context of KernelMemorySanitizer (KMSAN), this attribute skips the standard +function-level ABI prologue and MSan instrumentation. However, +`__builtin_sanitizer_instrumentation_begin()` and +`__builtin_sanitizer_instrumentation_end()` can be used within a function marked +with this attribute to selectively enable instrumentation for a specific region +of code. Values computed outside such a region are treated as initialized when +used inside it, and memory written outside a region keeps its previous shadow. + ### Interaction of Inlining with Disabling Sanitizer Instrumentation - A `no_sanitize` function will not be inlined heuristically by the compiler into a sanitized function. diff --git a/clang/docs/SanitizerCoverage.md b/clang/docs/SanitizerCoverage.md index f859838616b49..0e5015466a1a0 100644 --- a/clang/docs/SanitizerCoverage.md +++ b/clang/docs/SanitizerCoverage.md @@ -449,6 +449,31 @@ function attribute `__attribute__((no_sanitize("coverage")))`. Because this attribute may not be supported by other compilers, it is recommended to use it together with `__has_feature(coverage_sanitizer)`. +### Re-enabling instrumentation in regions + +Inside a function that has `__attribute__((no_sanitize("coverage")))` or +`__attribute__((disable_sanitizer_instrumentation))`, a region delimited by +`__builtin_sanitizer_instrumentation_begin()` and +`__builtin_sanitizer_instrumentation_end()` is instrumented again: every basic +block that enters a region gets a coverage point right after it does, and +comparisons, divisions, GEPs, loads, stores and indirect calls in regions are +traced as requested. Coverage points are not pruned in such functions, and the +function entry and exit callbacks, stack depth tracing and control flow tables +are not emitted. Regions nest, and code reachable without passing through a +`__builtin_sanitizer_instrumentation_begin()` is not considered to be in a +region. + +```c +__attribute__((no_sanitize("coverage"))) +void mostly_uninstrumented(int x) { + ... // not covered + __builtin_sanitizer_instrumentation_begin(); + if (x) // covered, and traced with -fsanitize-coverage=trace-cmp + foo(); + __builtin_sanitizer_instrumentation_end(); +} +``` + ## Disabling instrumentation without source modification It is sometimes useful to tell SanitizerCoverage to instrument only a subset of the diff --git a/clang/docs/ThreadSanitizer.md b/clang/docs/ThreadSanitizer.md index 244f2ffee0a19..e251f306cb9dc 100644 --- a/clang/docs/ThreadSanitizer.md +++ b/clang/docs/ThreadSanitizer.md @@ -102,6 +102,29 @@ and only if absolutely required; for example for certain code that cannot tolerate any instrumentation and resulting side-effects. This attribute overrides `no_sanitize("thread")`. +## Re-enabling Instrumentation in Regions + +Inside a function with `__attribute__((disable_sanitizer_instrumentation))` or +`__attribute__((no_sanitize("thread")))`, a region delimited by +`__builtin_sanitizer_instrumentation_begin()` and +`__builtin_sanitizer_instrumentation_end()` is instrumented again: plain +memory accesses and memory intrinsics in it are instrumented. A +`disable_sanitizer_instrumentation` function also gets its atomic accesses +instrumented only inside regions, and no function entry/exit hooks. Regions +nest, and code reachable without passing through a +`__builtin_sanitizer_instrumentation_begin()` is not considered to be in a +region. + +```c +__attribute__((disable_sanitizer_instrumentation)) +void mostly_uninstrumented(int *p) { + ... // not instrumented + __builtin_sanitizer_instrumentation_begin(); + *p = 0; // instrumented + __builtin_sanitizer_instrumentation_end(); +} +``` + ## Interaction of Inlining with Disabling Sanitizer Instrumentation - A `no_sanitize` function will not be inlined heuristically by the compiler into a sanitized function. diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index ea8dbb96fab56..b1f84575d9dec 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -1469,6 +1469,24 @@ def BuiltinAllowSanitizeCheck : Builtin { let Prototype = "bool(char const*)"; } +// Sanitizer instrumentation region markers. These delimit a region of an +// otherwise uninstrumented function (e.g. one marked with +// __attribute__((disable_sanitizer_instrumentation))) in which sanitizer +// instrumentation is enabled. They must not be Const or Pure: they are +// optimization barriers, and moving code across them changes which +// instructions belong to the region. +def SanitizerInstrumentationBegin : Builtin { + let Spellings = ["__builtin_sanitizer_instrumentation_begin"]; + let Attributes = [NoThrow]; + let Prototype = "void()"; +} + +def SanitizerInstrumentationEnd : Builtin { + let Spellings = ["__builtin_sanitizer_instrumentation_end"]; + let Attributes = [NoThrow]; + let Prototype = "void()"; +} + def ShuffleVector : Builtin { let Spellings = ["__builtin_shufflevector"]; let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr]; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 4c1318f6543c1..6aabc993a3217 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3908,6 +3908,23 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, llvm::MetadataAsValue::get(Ctx, llvm::MDString::get(Ctx, Kind))); return RValue::get(Allow); } + case Builtin::BI__builtin_sanitizer_instrumentation_begin: + case Builtin::BI__builtin_sanitizer_instrumentation_end: { + // Only emit the region markers for sanitizers that honour them. Elsewhere + // they would just be optimization barriers that no pass ever removes. This + // lets code guard its uses on __has_builtin alone. + SanitizerMask Consumers = + SanitizerKind::KernelMemory | SanitizerKind::Address | + SanitizerKind::KernelAddress | SanitizerKind::Thread; + if (getLangOpts().Sanitize.hasOneOf(Consumers) || + CGM.getCodeGenOpts().hasSanitizeCoverage()) + Builder.CreateCall(CGM.getIntrinsic( + BuiltinIDIfNoAsmLabel == + Builtin::BI__builtin_sanitizer_instrumentation_begin + ? Intrinsic::sanitizer_instrumentation_begin + : Intrinsic::sanitizer_instrumentation_end)); + return RValue::get(nullptr); + } case Builtin::BI__builtin_allow_sanitize_check: { Intrinsic::ID IntrID = Intrinsic::not_intrinsic; StringRef Name = diff --git a/clang/test/CodeGen/sanitizer-instrumentation-builtins.c b/clang/test/CodeGen/sanitizer-instrumentation-builtins.c new file mode 100644 index 0000000000000..5fe7083ac668f --- /dev/null +++ b/clang/test/CodeGen/sanitizer-instrumentation-builtins.c @@ -0,0 +1,53 @@ +// Check that the sanitizer region markers lower to their intrinsics under +// -fsanitize=kernel-memory, kernel-address, address and thread, and with +// -fsanitize-coverage, and to nothing without a sanitizer that consumes them. + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=kernel-memory \ +// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -disable-llvm-passes \ +// RUN: -o - %s | FileCheck %s --check-prefix=NONE +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=kernel-address \ +// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=MARKERS +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=address \ +// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=MARKERS +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize-coverage-type=3 \ +// RUN: -fsanitize-coverage-trace-pc -emit-llvm -disable-llvm-passes -o - %s \ +// RUN: | FileCheck %s --check-prefix=MARKERS +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=thread \ +// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=MARKERS +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=null \ +// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=NONE + +#if !__has_builtin(__builtin_sanitizer_instrumentation_begin) +#error "__builtin_sanitizer_instrumentation_begin not available" +#endif +#if !__has_builtin(__builtin_sanitizer_instrumentation_end) +#error "__builtin_sanitizer_instrumentation_end not available" +#endif + +extern void instrumented_callee(int); + +__attribute__((disable_sanitizer_instrumentation)) +void noinstr_function(int x) { + __builtin_sanitizer_instrumentation_begin(); + instrumented_callee(x); + __builtin_sanitizer_instrumentation_end(); +} + +// CHECK-LABEL: define {{.*}}void @noinstr_function( +// CHECK: call void @llvm.sanitizer.instrumentation.begin() +// CHECK: call void @instrumented_callee( +// CHECK: call void @llvm.sanitizer.instrumentation.end() + +// The markers are optimization barriers, so they must not be marked readnone +// or speculatable; only the default attributes plus nomerge are expected. +// CHECK: declare void @llvm.sanitizer.instrumentation.begin() #[[ATTR:[0-9]+]] +// CHECK: attributes #[[ATTR]] = { nocallback nofree nomerge nosync nounwind willreturn } + +// MARKERS-LABEL: define {{.*}}void @noinstr_function( +// MARKERS: call void @llvm.sanitizer.instrumentation.begin() +// MARKERS: call void @llvm.sanitizer.instrumentation.end() + +// NONE-LABEL: define {{.*}}void @noinstr_function( +// NONE-NOT: llvm.sanitizer.instrumentation +// NONE: } diff --git a/clang/test/CodeGen/sanitizer-instrumentation-regions-pipeline.c b/clang/test/CodeGen/sanitizer-instrumentation-regions-pipeline.c new file mode 100644 index 0000000000000..69ecd6166d6e0 --- /dev/null +++ b/clang/test/CodeGen/sanitizer-instrumentation-regions-pipeline.c @@ -0,0 +1,58 @@ +// Check that, after the full -O2 pipeline, each sanitizer instruments the code +// inside a region of a function it otherwise does not instrument, and nothing +// else. The region markers come from an always_inline helper, as in the Linux +// kernel. Debug info is enabled so that the verifier checks that the +// instrumentation calls get debug locations. + +// RUN: %clang_cc1 -triple x86_64-linux-gnu -O2 -debug-info-kind=line-tables-only \ +// RUN: -fsanitize=kernel-memory -emit-llvm -o - %s \ +// RUN: | FileCheck %s --check-prefixes=CHECK,KMSAN +// RUN: %clang_cc1 -triple x86_64-linux-gnu -O2 -debug-info-kind=line-tables-only \ +// RUN: -fsanitize=kernel-address -emit-llvm -o - %s \ +// RUN: | FileCheck %s --check-prefixes=CHECK,KASAN +// RUN: %clang_cc1 -triple x86_64-linux-gnu -O2 -debug-info-kind=line-tables-only \ +// RUN: -fsanitize-coverage-type=3 -fsanitize-coverage-trace-pc \ +// RUN: -fsanitize-coverage-trace-cmp -emit-llvm -o - %s \ +// RUN: | FileCheck %s --check-prefixes=CHECK,KCOV +// RUN: %clang_cc1 -triple x86_64-linux-gnu -O2 -debug-info-kind=line-tables-only \ +// RUN: -fsanitize=kernel-memory -fsanitize-coverage-type=3 \ +// RUN: -fsanitize-coverage-trace-pc -fsanitize-coverage-trace-cmp \ +// RUN: -emit-llvm -o - %s \ +// RUN: | FileCheck %s --check-prefixes=CHECK,KMSAN,KCOV +// RUN: %clang_cc1 -triple x86_64-linux-gnu -O2 -debug-info-kind=line-tables-only \ +// RUN: -fsanitize=thread -mllvm -tsan-instrument-func-entry-exit=0 \ +// RUN: -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,KCSAN + +static inline __attribute__((always_inline)) void instrumentation_begin(void) { + __builtin_sanitizer_instrumentation_begin(); +} + +static inline __attribute__((always_inline)) void instrumentation_end(void) { + __builtin_sanitizer_instrumentation_end(); +} + +void instrumented_callee(int); + +__attribute__((noinline, disable_sanitizer_instrumentation)) +void noinstr_function(int *p, int x) { + int v = *p + (x == 42); + instrumentation_begin(); + if (x == 7) + instrumented_callee(*p + v); + instrumentation_end(); + *p = v; +} + +// CHECK-LABEL: define {{.*}}void @noinstr_function( +// CHECK-NOT: call {{.*}}@__{{msan|asan|tsan|sanitizer_cov}} +// CHECK: call void @llvm.sanitizer.instrumentation.begin() +// KMSAN: call ptr @__msan_get_context_state() +// KCOV: call void @__sanitizer_cov_trace_pc() +// KCOV: call void @__sanitizer_cov_trace_const_cmp4(i32 7, +// KMSAN: call { ptr, ptr } @__msan_metadata_ptr_for_load_4( +// KASAN: call void @__asan_report_load4( +// KCSAN: call void @__tsan_read4( +// CHECK: call void @instrumented_callee( +// CHECK: call void @llvm.sanitizer.instrumentation.end() +// CHECK-NOT: call {{.*}}@__{{msan|asan|tsan|sanitizer_cov}} +// CHECK: ret void diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md index 6a3194271b838..8173b697fefc2 100644 --- a/llvm/docs/LangRef.md +++ b/llvm/docs/LangRef.md @@ -30489,3 +30489,32 @@ taken. A late optimization pass will convert this intrinsic to either `llvm.cond.loop(true)` or `llvm.cond.loop(pred)`, where `pred` is a predicate for a conditional branch leading to the intrinsic call, if possible. + +#### '`llvm.sanitizer.instrumentation.begin`' and '`llvm.sanitizer.instrumentation.end`' Intrinsics + +##### Syntax: + +```llvm +declare void @llvm.sanitizer.instrumentation.begin() +declare void @llvm.sanitizer.instrumentation.end() +``` + +##### Overview: + +The '`llvm.sanitizer.instrumentation.begin`' and '`llvm.sanitizer.instrumentation.end`' intrinsics delimit a region of a function that a sanitizer does not otherwise instrument, in which that sanitizer's instrumentation is enabled again. + +##### Semantics: + +Inside a region, a sanitizer instruments the code as if the function were instrumented, to the extent it supports instrumenting part of a function; code outside regions stays uninstrumented. Sanitizers that do not support regions ignore the intrinsics, as do all sanitizers in functions that they instrument anyway. Which functions count as uninstrumented, and what is instrumented inside regions, depends on the sanitizer. + +Regions nest: `llvm.sanitizer.instrumentation.begin` increments a region nesting depth and `llvm.sanitizer.instrumentation.end` decrements it, so an inner `end` does not terminate an enclosing region. An unmatched `end` is ignored rather than making the depth negative. + +An instruction is considered to be inside a region only if *every* path from the function entry to it is inside a region, i.e. region membership is an intersection over predecessors. This makes an in-region instruction always dominated by a `llvm.sanitizer.instrumentation.begin`, so any per-function sanitizer state set up there is available. A region that is entered on some paths but not others is therefore instrumented only where it is unambiguous. + +MemorySanitizer supports regions in kernel mode (KMSAN), in functions marked with `disable_sanitizer_instrumentation`. The function-level ABI prologue is skipped, the MSan context state is fetched at `llvm.sanitizer.instrumentation.begin`, and shadow propagation and parameter shadow setup are only performed inside regions. Memory written outside a region keeps whatever shadow it had, and values computed outside a region are treated as fully initialized when they are used inside one. + +AddressSanitizer supports regions in functions that lack the `sanitize_address` attribute or are marked with `disable_sanitizer_instrumentation`. Inside regions, memory accesses and memory intrinsics are instrumented. The stack frame of such a function is not instrumented, accesses to its own `alloca`s are not checked, and no `__asan_handle_no_return` calls are inserted. + +SanitizerCoverage supports regions in functions marked with `nosanitize_coverage` or `disable_sanitizer_instrumentation`. Each basic block that enters a region gets a coverage point right after the point where it does (splitting the block if necessary), without pruning, and the requested tracing of comparisons, divisions, GEPs, loads, stores and indirect calls is performed inside regions. Function entry/exit callbacks, stack depth tracing and control flow tables are not emitted for such functions. + +ThreadSanitizer supports regions in functions marked with `disable_sanitizer_instrumentation`, and in functions that lack the `sanitize_thread` attribute. In the former, plain and atomic memory accesses and memory intrinsics are instrumented only inside regions, and no function entry/exit hooks are inserted. In the latter, which already get their atomic accesses and function entry/exit instrumented, plain memory accesses and memory intrinsics inside regions are instrumented as well. diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index 37c9c783465d6..f3b4cb0a9e2b8 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -2828,6 +2828,24 @@ def int_load_relative: DefaultAttrsIntrinsic<[llvm_ptr_ty], def int_asan_check_memaccess : Intrinsic<[],[llvm_ptr_ty, llvm_i32_ty], [ImmArg<ArgIndex<1>>]>; +// Sanitizer instrumentation regions in functions that are otherwise not +// instrumented. +// +// These are markers consumed by sanitizer passes, but they deliberately +// keep the default (read/write any memory) effects and so act as memory +// barriers. Relaxing them, e.g. to IntrInaccessibleMemOnly, lets the optimizer +// move memory operations across a region boundary: a load can be CSEd with an +// identical load from before begin() and lose its shadow check, and a store can +// sink past end() and leave a stale shadow behind. Both defeat the purpose of +// the region. What the explicit attribute list buys us is the rest of the +// modelling - these calls do not throw, do not free, do not call back into the +// module and always return - plus IntrNoMerge, which stops identical markers in +// sibling blocks from being merged and thereby moving code out of a region. +def int_sanitizer_instrumentation_begin + : DefaultAttrsIntrinsic<[], [], [IntrNoMerge]>; +def int_sanitizer_instrumentation_end + : DefaultAttrsIntrinsic<[], [], [IntrNoMerge]>; + // Spin in an infinite loop (using instructions specified by the target) iff the // argument is true. Used to implement efficient conditional traps. def int_cond_loop : Intrinsic<[], [llvm_i1_ty], diff --git a/llvm/include/llvm/Transforms/Utils/Instrumentation.h b/llvm/include/llvm/Transforms/Utils/Instrumentation.h index 95a985ba3f0c4..dd143af599e25 100644 --- a/llvm/include/llvm/Transforms/Utils/Instrumentation.h +++ b/llvm/include/llvm/Transforms/Utils/Instrumentation.h @@ -13,6 +... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/231374 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
