diff --git a/clang/docs/AddressSanitizer.rst b/clang/docs/AddressSanitizer.rst index 21e1a3652192e..766733b9a9371 100644 --- a/clang/docs/AddressSanitizer.rst +++ b/clang/docs/AddressSanitizer.rst @@ -18,6 +18,7 @@ following types of bugs: * Enable with: ``ASAN_OPTIONS=detect_stack_use_after_return=1`` (already enabled on Linux). * Disable with: ``ASAN_OPTIONS=detect_stack_use_after_return=0``. * Use-after-scope (clang flag ``-fsanitize-address-use-after-scope``) +* Container overflow detection (clang flag ``-fsanitize-address-disable-container-overflow`` to disable (experimental)) * Double-free, invalid free * Memory leaks (experimental) @@ -164,6 +165,23 @@ To summarize: ``-fsanitize-address-use-after-return=`` * ``always``: Enables detection of UAR errors in all cases. (reduces code size, but not as much as ``never``). +Container Overflow Detection +---------------------------- + +AddressSanitizer can detect overflows in containers with custom allocators +(such as std::vector) where the Library developers have added calls into the +AddressSanitizer runtime to indicate which memory is poisoned etc. + +In environments where not all the process binaries can be recompiled with +AddressSanitizer enabled, these checks can cause false positives. + +These checks can be disabled at runtime using +``ASAN_OPTIONS=detect_container_overflow=0`` + +``-fsanitize-address-disable-container-overflow`` can be used at compile time +to disable container overflow checks if both the container and compiler support +the flag (experimental). + Memory leak detection --------------------- @@ -242,6 +260,32 @@ AddressSanitizer also supports works similar to ``__attribute__((no_sanitize("address")))``, but it also prevents instrumentation performed by other sanitizers. +Disabling container overflow checks with ``__has_feature(sanitize_address_disable_container_overflow)`` +------------------------------------------------------------------------------------------------------- + +Library developers may use this feature test in conjunction with the +AddressSanitizer feature test to conditionally include container overflow +related code compiled into user code: + +The recommended form is + +.. code-block:: c + + #if __has_feature(address_sanitizer) && !__has_feature(sanitize_address_disable_container_overflow) + // Container overflow detection enabled - include annotations + __sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid); + #endif + +This pattern ensures that: + +* Container overflow annotations are only included when AddressSanitizer is + enabled +* Container overflow detection can be disabled by + ``-fsanitize-address-disable-container-overflow`` +* Code compiles correctly with older compilers that don't support the + ``sanitize_address_disable_container_overflow`` feature test (defaulting to + enabled container overflow checks) + Suppressing Errors in Recompiled Code (Ignorelist) -------------------------------------------------- diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index a8bbf146431ea..a7f9df14e04c1 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -5125,6 +5125,8 @@ Execute ``clang-cl /?`` to see a list of supported options: Select the mode of detecting stack use-after-return in AddressSanitizer: never | runtime (default) | always -fsanitize-address-use-after-scope Enable use-after-scope detection in AddressSanitizer + -fsanitize-address-disable-container-overflow + Disable container overflow detection at compile time in AddressSanitizer (experimental) -fsanitize-address-use-odr-indicator Enable ODR indicator globals to avoid false ODR violation reports in partially sanitized programs at the cost of an increase in binary size -fsanitize-ignorelist= diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 872f73ebf3810..7cba0917c9a4a 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -245,6 +245,8 @@ CODEGENOPT(NewStructPathTBAA , 1, 0, Benign) ///< Whether or not to use enhanced CODEGENOPT(SaveTempLabels , 1, 0, Benign) ///< Save temporary labels. CODEGENOPT(SanitizeAddressUseAfterScope , 1, 0, Benign) ///< Enable use-after-scope detection ///< in AddressSanitizer +CODEGENOPT(SanitizeAddressDisableContainerOverflow, 1, 0, Benign) ///< Disable container overflow checks + ///< in AddressSanitizer ENUM_CODEGENOPT(SanitizeAddressUseAfterReturn, AsanDetectStackUseAfterReturnMode, 2, AsanDetectStackUseAfterReturnMode::Runtime, diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 0e91b42a132c1..57d4aea79bd4f 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -40,6 +40,8 @@ FEATURE(speculative_load_hardening, LangOpts.SpeculativeLoadHardening) FEATURE(address_sanitizer, LangOpts.Sanitize.hasOneOf(SanitizerKind::Address | SanitizerKind::KernelAddress)) +FEATURE(sanitize_address_disable_container_overflow, + LangOpts.SanitizeAddressDisableContainerOverflow) FEATURE(leak_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Leak)) FEATURE(hwaddress_sanitizer, diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 84f5ab3443a59..4e5d0ff0aa37c 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -499,6 +499,9 @@ LANGOPT(EnableLifetimeSafety, 1, 0, NotCompatible, "Experimental lifetime safety LANGOPT(PreserveVec3Type, 1, 0, NotCompatible, "Preserve 3-component vector type") +LANGOPT(SanitizeAddressDisableContainerOverflow, 1, 0, NotCompatible, + "Disable container overflow checks in AddressSanitizer (experimental)") + #undef LANGOPT #undef ENUM_LANGOPT #undef VALUE_LANGOPT diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index ef6665a87d2cc..76598d33711f4 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2512,6 +2512,12 @@ An operator new\[\] is "custom" if it is not one of the allocation functions provided by the C++ standard library. Array cookies from non-custom allocation functions are always poisoned.}]>, Group; +defm sanitize_address_disable_container_overflow : BoolOption<"f", "sanitize-address-disable-container-overflow", + CodeGenOpts<"SanitizeAddressDisableContainerOverflow">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption], " container overflow checks in AddressSanitizer (experimental)">>, + Group; defm sanitize_address_globals_dead_stripping : BoolOption<"f", "sanitize-address-globals-dead-stripping", CodeGenOpts<"SanitizeAddressGlobalsDeadStripping">, DefaultFalse, PosFlag, diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h index 2b72268c8606c..1620583a47c92 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -50,6 +50,7 @@ class SanitizerArgs { bool SharedRuntime = false; bool StableABI = false; bool AsanUseAfterScope = true; + bool AsanDisableContainerOverflow = false; bool AsanPoisonCustomArrayCookie = false; bool AsanGlobalsDeadStripping = false; bool AsanUseOdrIndicator = false; diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 7ce1afe6f2e6a..192a9649ea7f9 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -1131,8 +1131,13 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, AsanUseAfterScope = Args.hasFlag( options::OPT_fsanitize_address_use_after_scope, options::OPT_fno_sanitize_address_use_after_scope, AsanUseAfterScope); + AsanDisableContainerOverflow = Args.hasFlag( + options::OPT_fsanitize_address_disable_container_overflow, + options::OPT_fno_sanitize_address_disable_container_overflow, + AsanDisableContainerOverflow); } else { AsanUseAfterScope = false; + AsanDisableContainerOverflow = false; } if (AllAddedKinds & SanitizerKind::HWAddress) { @@ -1459,6 +1464,9 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, if (AsanUseAfterScope) CmdArgs.push_back("-fsanitize-address-use-after-scope"); + if (AsanDisableContainerOverflow) + CmdArgs.push_back("-fsanitize-address-disable-container-overflow"); + if (AsanPoisonCustomArrayCookie) CmdArgs.push_back("-fsanitize-address-poison-custom-array-cookie"); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 422375240bab6..8b1ae1e2ec95c 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -586,6 +586,8 @@ static bool FixupInvocation(CompilerInvocation &Invocation, LangOpts.ForceEmitVTables = CodeGenOpts.ForceEmitVTables; LangOpts.SpeculativeLoadHardening = CodeGenOpts.SpeculativeLoadHardening; LangOpts.CurrentModule = LangOpts.ModuleName; + LangOpts.SanitizeAddressDisableContainerOverflow = + CodeGenOpts.SanitizeAddressDisableContainerOverflow; llvm::Triple T(TargetOpts.Triple); llvm::Triple::ArchType Arch = T.getArch(); diff --git a/clang/test/Driver/fsanitize-address-disable-container-overflow.c b/clang/test/Driver/fsanitize-address-disable-container-overflow.c new file mode 100644 index 0000000000000..98a8a8fb7882c --- /dev/null +++ b/clang/test/Driver/fsanitize-address-disable-container-overflow.c @@ -0,0 +1,34 @@ +// Test the -fsanitize-address-disable-container-overflow option + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=address %s \ +// RUN: -### 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-DEFAULT +// CHECK-DEFAULT-NOT: -fsanitize-address-disable-container-overflow + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=address \ +// RUN: -fsanitize-address-disable-container-overflow %s -### 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-ENABLE %s +// CHECK-ENABLE: "-fsanitize-address-disable-container-overflow" + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=address \ +// RUN: -fno-sanitize-address-disable-container-overflow %s -### 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-DISABLE %s +// CHECK-DISABLE-NOT: -fsanitize-address-disable-container-overflow + +// RUN: %clang -target x86_64-linux-gnu -fsanitize=address \ +// RUN: -fsanitize-address-disable-container-overflow \ +// RUN: -fno-sanitize-address-disable-container-overflow %s -### 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-OVERRIDE %s +// CHECK-OVERRIDE-NOT: -fsanitize-address-disable-container-overflow + +// Test that the flag generates unused warning without address sanitizer +// RUN: %clang -target x86_64-linux-gnu -fsanitize-address-disable-container-overflow %s \ +// RUN: -### 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-NO-ASAN %s +// CHECK-NO-ASAN: warning: argument unused during compilation: '-fsanitize-address-disable-container-overflow' + +// Test with kernel address sanitizer +// RUN: %clang -target x86_64-linux-gnu -fsanitize=kernel-address \ +// RUN: -fsanitize-address-disable-container-overflow %s -### 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-KERNEL-ASAN %s +// CHECK-KERNEL-ASAN: "-fsanitize-address-disable-container-overflow" \ No newline at end of file diff --git a/clang/test/Lexer/has_feature_sanitize_address_disable_container_overflow.cpp b/clang/test/Lexer/has_feature_sanitize_address_disable_container_overflow.cpp new file mode 100644 index 0000000000000..aa6e56aa4ce91 --- /dev/null +++ b/clang/test/Lexer/has_feature_sanitize_address_disable_container_overflow.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -E -fsanitize=address %s -o - | FileCheck --check-prefix=CHECK-ASAN %s +// RUN: %clang_cc1 -E -fsanitize=address -fsanitize-address-disable-container-overflow %s -o - | FileCheck --check-prefix=CHECK-ASAN-DISABLE-CONTAINER %s +// RUN: %clang_cc1 -E -fsanitize=kernel-address -fsanitize-address-disable-container-overflow %s -o - | FileCheck --check-prefix=CHECK-ASAN-DISABLE-CONTAINER %s +// RUN: %clang_cc1 -E -fsanitize=address -fno-sanitize-address-disable-container-overflow %s -o - | FileCheck --check-prefix=CHECK-ASAN %s +// RUN: %clang_cc1 -E %s -o - | FileCheck --check-prefix=CHECK-NO-ASAN %s + +#if __has_feature(address_sanitizer) +int AddressSanitizerEnabled(); +#else +int AddressSanitizerDisabled(); +#endif + +#if __has_feature(sanitize_address_disable_container_overflow) +int AddressSanitizerContainerOverflowDisabled(); +#else +int AddressSanitizerContainerOverflowEnabled(); +#endif + +// CHECK-ASAN: AddressSanitizerEnabled +// CHECK-ASAN: AddressSanitizerContainerOverflowEnabled + +// CHECK-ASAN-DISABLE-CONTAINER: AddressSanitizerEnabled +// CHECK-ASAN-DISABLE-CONTAINER: AddressSanitizerContainerOverflowDisabled + +// CHECK-NO-ASAN: AddressSanitizerDisabled +// CHECK-NO-ASAN: AddressSanitizerContainerOverflowEnabled \ No newline at end of file diff --git a/compiler-rt/test/asan/TestCases/contiguous_container_crash.cpp b/compiler-rt/test/asan/TestCases/contiguous_container_crash.cpp index b88e02b84ad67..3c0880589288e 100644 --- a/compiler-rt/test/asan/TestCases/contiguous_container_crash.cpp +++ b/compiler-rt/test/asan/TestCases/contiguous_container_crash.cpp @@ -6,24 +6,36 @@ // RUN: not %run %t odd-alignment-end 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s // RUN: %env_asan_opts=detect_container_overflow=0 %run %t crash // +// Test with -fsanitize-address-disable-container-overflow flag - should not crash +// RUN: %clangxx_asan -fsanitize-address-disable-container-overflow -O %s -o %t-no-overflow +// RUN: %run %t-no-overflow crash +// RUN: %run %t-no-overflow bad-bounds +// RUN: %run %t-no-overflow unaligned-bad-bounds +// RUN: %run %t-no-overflow odd-alignment +// RUN: %run %t-no-overflow odd-alignment-end +// // Test crash due to __sanitizer_annotate_contiguous_container. #include #include +#if __has_feature(address_sanitizer) && !__has_feature(sanitize_address_disable_container_overflow) extern "C" { void __sanitizer_annotate_contiguous_container(const void *beg, const void *end, const void *old_mid, const void *new_mid); } // extern "C" +#endif static volatile int one = 1; int TestCrash() { long t[100]; t[60] = 0; +#if __has_feature(address_sanitizer) && !__has_feature(sanitize_address_disable_container_overflow) __sanitizer_annotate_contiguous_container(&t[0], &t[0] + 100, &t[0] + 100, &t[0] + 50); +#endif // CHECK-CRASH: AddressSanitizer: container-overflow // CHECK-CRASH: if you don't care about these errors you may set ASAN_OPTIONS=detect_container_overflow=0 return (int)t[60 * one]; // Touches the poisoned memory. @@ -32,30 +44,38 @@ int TestCrash() { void BadBounds() { long t[100]; // CHECK-BAD-BOUNDS: ERROR: AddressSanitizer: bad parameters to __sanitizer_annotate_contiguous_container +#if __has_feature(address_sanitizer) && !__has_feature(sanitize_address_disable_container_overflow) __sanitizer_annotate_contiguous_container(&t[0], &t[0] + 100, &t[0] + 101, &t[0] + 50); +#endif } void UnalignedBadBounds() { char t[100]; // CHECK-UNALIGNED-BAD-BOUNDS: ERROR: AddressSanitizer: bad parameters to __sanitizer_annotate_contiguous_container +#if __has_feature(address_sanitizer) && !__has_feature(sanitize_address_disable_container_overflow) __sanitizer_annotate_contiguous_container(&t[1], &t[0] + 100, &t[0] + 101, &t[0] + 50); +#endif } int OddAlignment() { int t[100]; t[60] = 0; +#if __has_feature(address_sanitizer) && !__has_feature(sanitize_address_disable_container_overflow) __sanitizer_annotate_contiguous_container(&t[1], &t[0] + 100, &t[0] + 100, &t[1] + 50); +#endif return (int)t[60 * one]; // Touches the poisoned memory. } int OddAlignmentEnd() { int t[99]; t[60] = 0; +#if __has_feature(address_sanitizer) && !__has_feature(sanitize_address_disable_container_overflow) __sanitizer_annotate_contiguous_container(&t[0], &t[0] + 98, &t[0] + 98, &t[0] + 50); +#endif return (int)t[60 * one]; // Touches the poisoned memory. }