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

Reply via email to