https://github.com/padriff updated https://github.com/llvm/llvm-project/pull/163468
>From 01743d4797e307b68ef0ff65bb49d35712025529 Mon Sep 17 00:00:00 2001 From: Paddy McDonald <[email protected]> Date: Tue, 14 Oct 2025 15:41:13 -0700 Subject: [PATCH 1/2] Document a define to allow library developers to support disabling AddressSanitizer's container overflow detection in template code at compile time. The primary motivation is to reduce false positives in environments where libraries and frameworks that cannot be recompiled with sanitizers enabled are called from application code. This supports disabling checks when the runtime environment cannot be reliably controlled to use ASAN_OPTIONS. Key changes: - Update documentation in AddressSanitizer.rst to suggest and illustrate use of the define - Add details of the define in PrintContainerOverflowHint() - Add test disable_container_overflow_checks to verify new hints on the error and fill the testing gap that ASAN_OPTIONS=detect_container_overflow=0 works This requires no compiler changes and should be supportable cross compiler toolchains. An RFC has been opened to discuss: https://discourse.llvm.org/t/rfc-add-fsanitize-address-disable-container-overflow-flag-to-addresssanitizer/88349 --- clang/docs/AddressSanitizer.rst | 49 ++++++++++++++++++ compiler-rt/lib/asan/asan_errors.cpp | 14 ++++-- .../disable_container_overflow_checks.cpp | 50 +++++++++++++++++++ 3 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 compiler-rt/test/asan/TestCases/disable_container_overflow_checks.cpp diff --git a/clang/docs/AddressSanitizer.rst b/clang/docs/AddressSanitizer.rst index 21e1a3652192e..40e876a02d4ea 100644 --- a/clang/docs/AddressSanitizer.rst +++ b/clang/docs/AddressSanitizer.rst @@ -164,6 +164,18 @@ To summarize: ``-fsanitize-address-use-after-return=<mode>`` * ``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. + +See `Disabling container overflow checks`_ for details on suppressing checks. + Memory leak detection --------------------- @@ -242,6 +254,43 @@ AddressSanitizer also supports works similar to ``__attribute__((no_sanitize("address")))``, but it also prevents instrumentation performed by other sanitizers. +Disabling container overflow checks +----------------------------------- + +Runtime suppression +^^^^^^^^^^^^^^^^^^^ + +Container overflow checks can be disabled at runtime using the +``ASAN_OPTIONS=detect_container_overflow=0`` environment variable. + +Compile time suppression +^^^^^^^^^^^^^^^^^^^^^^^^ + +``-D__ASAN_DISABLE_CONTAINER_OVERFLOW__`` can be used at compile time to +disable container overflow checks if the container library has added support +for this define. + +To support a standard way to disable container overflow checks at compile time, +library developers should use this definition 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) && !__ASAN_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 passing + ``-D__ASAN_DISABLE_CONTAINER_OVERFLOW__`` to the compiler + Suppressing Errors in Recompiled Code (Ignorelist) -------------------------------------------------- diff --git a/compiler-rt/lib/asan/asan_errors.cpp b/compiler-rt/lib/asan/asan_errors.cpp index 2a207cd06ccac..567a295d861ac 100644 --- a/compiler-rt/lib/asan/asan_errors.cpp +++ b/compiler-rt/lib/asan/asan_errors.cpp @@ -514,11 +514,15 @@ ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr, } static void PrintContainerOverflowHint() { - Printf("HINT: if you don't care about these errors you may set " - "ASAN_OPTIONS=detect_container_overflow=0.\n" - "If you suspect a false positive see also: " - "https://github.com/google/sanitizers/wiki/" - "AddressSanitizerContainerOverflow.\n"); + Printf( + "HINT: if you don't care about these errors you may set " + "ASAN_OPTIONS=detect_container_overflow=0.\n" + "Or if supported by the container library, pass " + "-D__ASAN_DISABLE_CONTAINER_OVERFLOW__ to the compiler to disable " + " instrumentation.\n" + "If you suspect a false positive see also: " + "https://github.com/google/sanitizers/wiki/" + "AddressSanitizerContainerOverflow.\n"); } static void PrintShadowByte(InternalScopedString *str, const char *before, diff --git a/compiler-rt/test/asan/TestCases/disable_container_overflow_checks.cpp b/compiler-rt/test/asan/TestCases/disable_container_overflow_checks.cpp new file mode 100644 index 0000000000000..294ade20c0dd3 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/disable_container_overflow_checks.cpp @@ -0,0 +1,50 @@ +// Test crash gives guidance on -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ and +// ASAN_OPTIONS=detect_container_overflow=0 +// RUN: %clangxx_asan -O %s -o %t +// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s +// +// Test overflow checks can be disabled at runtime with +// ASAN_OPTIONS=detect_container_overflow=0 +// RUN: %env_asan_opts=detect_container_overflow=0 %run %t 2>&1 | FileCheck --check-prefix=CHECK-NOCRASH %s +// +// Illustrate use of -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ flag to suppress +// overflow checks at compile time. +// RUN: %clangxx_asan -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -O %s -o %t-no-overflow +// RUN: %run %t-no-overflow 2>&1 | FileCheck --check-prefix=CHECK-NOCRASH %s +// + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +// public definition of __sanitizer_annotate_contiguous_container +#include "sanitizer/common_interface_defs.h" + +static volatile int one = 1; + +int TestCrash() { + long t[100]; + t[60] = 0; +#if __has_feature(address_sanitizer) && !__ASAN_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: ASAN_OPTIONS=detect_container_overflow=0 + // CHECK-CRASH: __ASAN_DISABLE_CONTAINER_OVERFLOW__ + // CHECK-NOCRASH-NOT: AddressSanitizer: container-overflow + // CHECK-NOCRASH-NOT: ASAN_OPTIONS=detect_container_overflow=0 + // CHECK-NOCRASH-NOT: __ASAN_DISABLE_CONTAINER_OVERFLOW__ + return (int)t[60 * one]; // Touches the poisoned memory. +} + +int main(int argc, char **argv) { + + int retval = 0; + + retval = TestCrash(); + + printf("Exiting main\n"); + + return retval; +} >From 45d0d77051c200d5c2cccc4762282202d0703b36 Mon Sep 17 00:00:00 2001 From: Paddy McDonald <[email protected]> Date: Thu, 23 Oct 2025 12:06:11 -0700 Subject: [PATCH 2/2] Add tests demonstrating the issue around closed source libraries and instrumented apps that both modify containers and the proposed solutions to this. stack_container_dynamic_lib.cpp uses the approach that library providers do the work to remove the container overflow instrumentation stack_container_dynamic_lib_redef.cpp shows three alternate approaches to providing the solution in the santizer headers when __ASAN_DISABLE_CONTAINER_OVERFLOW__ is defined: - provide an alternate empty body that is force inlined - provide an alternate empty body that is declared with static linkage - use a #define to remove the calls from the library sources These rely on the library providers including the sanitizer interface headers rather than declaring the functions themselves. --- .../TestCases/stack_container_dynamic_lib.cpp | 93 +++++++++++ .../stack_container_dynamic_lib_redef.cpp | 158 ++++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 compiler-rt/test/asan/TestCases/stack_container_dynamic_lib.cpp create mode 100644 compiler-rt/test/asan/TestCases/stack_container_dynamic_lib_redef.cpp diff --git a/compiler-rt/test/asan/TestCases/stack_container_dynamic_lib.cpp b/compiler-rt/test/asan/TestCases/stack_container_dynamic_lib.cpp new file mode 100644 index 0000000000000..3e5b7de808eb0 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/stack_container_dynamic_lib.cpp @@ -0,0 +1,93 @@ +// Test to demonstrate compile-time disabling of container-overflow checks +// in order to handle uninstrumented libraries + +// Mimic a closed-source library compiled without ASan +// RUN: %clangxx_asan -fno-sanitize=address -DSHARED_LIB %s -fPIC -shared -o %t-so.so + +// RUN: %clangxx_asan %s %libdl -o %t +// RUN: not %run %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan %s %libdl -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -o %t +// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-NO-CONTAINER-OVERFLOW %s + +#include <assert.h> +#include <sanitizer/common_interface_defs.h> +#include <stdio.h> + +template <typename T> class Stack { +private: + T data[5]; + size_t size; + +public: + Stack() : size(0) { +#if __has_feature(address_sanitizer) && !__ASAN_DISABLE_CONTAINER_OVERFLOW__ + // Mark entire storage as unaddressable initially + __sanitizer_annotate_contiguous_container(data, data + 5, data + 5, data); +#endif + } + + ~Stack() { +#if __has_feature(address_sanitizer) && !__ASAN_DISABLE_CONTAINER_OVERFLOW__ + __sanitizer_annotate_contiguous_container(data, data + 5, data + size, + data + 5); +#endif + } + + void push(const T &value) { + assert(size < 5 && "Stack overflow"); +#if __has_feature(address_sanitizer) && !__ASAN_DISABLE_CONTAINER_OVERFLOW__ + __sanitizer_annotate_contiguous_container(data, data + 5, data + size, + data + size + 1); +#endif + data[size++] = value; + } + + T pop() { + assert(size > 0 && "Cannot pop from empty stack"); + T result = data[--size]; +#if __has_feature(address_sanitizer) && !__ASAN_DISABLE_CONTAINER_OVERFLOW__ + __sanitizer_annotate_contiguous_container(data, data + 5, data + size + 1, + data + size); +#endif + return result; + } +}; + +#ifdef SHARED_LIB +// Mimics a closed-source library compiled without ASan + +extern "C" void push_value_to_stack(Stack<int> &stack) { stack.push(42); } +#else // SHARED_LIB + +# include <dlfcn.h> +# include <string> + +typedef void (*push_func_t)(Stack<int> &); + +int main(int argc, char *argv[]) { + std::string path = std::string(argv[0]) + "-so.so"; + printf("Loading library: %s\n", path.c_str()); + + void *lib = dlopen(path.c_str(), RTLD_NOW); + assert(lib); + + push_func_t push_value = (push_func_t)dlsym(lib, "push_value_to_stack"); + assert(push_value); + + Stack<int> stack; + push_value(stack); + + // BOOM! uninstrumented library didn't update container bounds + int value = stack.pop(); + // CHECK: AddressSanitizer: container-overflow + printf("Popped value: %d\n", value); + assert(value == 42 && "Expected value 42"); + + dlclose(lib); + printf("SUCCESS\n"); + // CHECK-NO-CONTAINER-OVERFLOW: SUCCESS + return 0; +} + +#endif // SHARED_LIB diff --git a/compiler-rt/test/asan/TestCases/stack_container_dynamic_lib_redef.cpp b/compiler-rt/test/asan/TestCases/stack_container_dynamic_lib_redef.cpp new file mode 100644 index 0000000000000..f7bb38dca8efa --- /dev/null +++ b/compiler-rt/test/asan/TestCases/stack_container_dynamic_lib_redef.cpp @@ -0,0 +1,158 @@ +// Test to demonstrate compile-time disabling of container-overflow checks +// in order to handle uninstrumented libraries +// +// Explore three options discussed as implementable in the santizer headers to reduce the need +// for changes in library code if they include the sanitizer interface header sanitizer/common_interface_defs.h +// instead of having their own forward declaration +// +// - force inlined alternative body - minimizes the need for optimizations to reduce bloat +// - static declaration of alternate body - potential bloat if optimizer is not run +// - use of #define to remove calls completely + +// Mimic a closed-source library compiled without ASan +// RUN: %clangxx_asan -fno-sanitize=address -DSHARED_LIB %s -dynamiclib -o %t-closedsource.dylib + +// Mimic multiple files being linked into a single executable, +// %t-object.o and %t-main compiled seperately and then linked together +// +// -fsanitize=address with container overflow turned on (default) +// RUN: %clangxx_asan -DMULTI_SOURCE %s -c -o %t-object.o +// RUN: %clangxx_asan %s -c -o %t-main.o +// RUN: %clangxx_asan -o %t %t-main.o %t-object.o -framework %t-closedsource.dylib +// RUN: not %run %t 2>&1 | FileCheck %s +// +// -fsanitize=address with container overflow turned off using ALWAYS_INLINE +// RUN: %clangxx_asan -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -DMULTI_SOURCE %s -c -o %t-object.o +// RUN: %clangxx_asan -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ %s -c -o %t-main.o +// RUN: %clangxx_asan -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -o %t %t-main.o %t-object.o -framework %t-closedsource.dylib +// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-NO-CONTAINER-OVERFLOW %s +// +// -fsanitize=address with container overflow turned off using static linkage +// RUN: %clangxx_asan -DUSE_STATIC -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -DMULTI_SOURCE %s -c -o %t-object.o +// RUN: %clangxx_asan -DUSE_STATIC -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ %s -c -o %t-main.o +// RUN: %clangxx_asan -DUSE_STATIC -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -o %t %t-main.o %t-object.o -framework %t-closedsource.dylib +// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-NO-CONTAINER-OVERFLOW %s +// +// -fsanitize=address with container overflow turned off using #define to remove the function calls +// RUN: %clangxx_asan -DUSE_DEFINE -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -DMULTI_SOURCE %s -c -o %t-object.o +// RUN: %clangxx_asan -DUSE_DEFINE -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ %s -c -o %t-main.o +// RUN: %clangxx_asan -DUSE_DEFINE -D__ASAN_DISABLE_CONTAINER_OVERFLOW__ -o %t %t-main.o %t-object.o -framework %t-closedsource.dylib +// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-NO-CONTAINER-OVERFLOW %s + +#include <assert.h> +#include <stdio.h> + +// Mimic sanitizer/common_interface_defs disabling the container overflow calls +#if !__has_feature(address_sanitizer) || __ASAN_DISABLE_CONTAINER_OVERFLOW__ + +# if USE_DEFINE +# define __sanitizer_annotate_contiguous_container(...) +# else + +// in this test match the extern "C" declaration as <string.h> pulls in copies of the +// declarations +extern "C" { +# if USE_STATIC +static +# else + +// clone of the ALWAYS_INLINE macro +# if defined(_MSC_VER) +# define ALWAYS_INLINE __forceinline +# else // _MSC_VER +# define ALWAYS_INLINE inline __attribute__((always_inline)) +# endif // _MSC_VER + +ALWAYS_INLINE +# endif + void __sanitizer_annotate_contiguous_container(const void *beg, + const void *end, + const void *old_mid, + const void *new_mid) {}; +} +# endif + +#else + +# include <sanitizer/common_interface_defs.h> + +#endif + +// Mimic template based container library header +template <typename T> class Stack { +private: + T data[5]; + size_t size; + +public: + Stack() : size(0) { + // Mark entire storage as unaddressable initially +#if __has_feature(address_sanitizer) + __sanitizer_annotate_contiguous_container(data, data + 5, data + 5, data); +#endif + } + + ~Stack() { +#if __has_feature(address_sanitizer) + __sanitizer_annotate_contiguous_container(data, data + 5, data + size, + data + 5); +#endif + } + + void push(const T &value) { + assert(size < 5 && "Stack overflow"); +#if __has_feature(address_sanitizer) + __sanitizer_annotate_contiguous_container(data, data + 5, data + size, + data + size + 1); +#endif + data[size++] = value; + } + + T pop() { + assert(size > 0 && "Cannot pop from empty stack"); + T result = data[--size]; +#if __has_feature(address_sanitizer) + __sanitizer_annotate_contiguous_container(data, data + 5, data + size + 1, + data + size); +#endif + return result; + } +}; + +#if defined(SHARED_LIB) +// Mimics a closed-source library compiled without ASan + +extern "C" void push_value_to_stack(Stack<int> &stack) { stack.push(42); } + +#elif defined(MULTI_SOURCE) + +// Mimic multiple source files in a single project compiled seperately +extern "C" void push_value_to_stack(Stack<int> &stack); + +extern "C" void do_push_value_to_stack(Stack<int> &stack) { + push_value_to_stack(stack); +} + +#else + +extern "C" void do_push_value_to_stack(Stack<int> &stack); + +# include <string> + +int main(int argc, char *argv[]) { + + Stack<int> stack; + do_push_value_to_stack(stack); + + // BOOM! uninstrumented library didn't update container bounds + int value = stack.pop(); + // CHECK: AddressSanitizer: container-overflow + printf("Popped value: %d\n", value); + assert(value == 42 && "Expected value 42"); + + printf("SUCCESS\n"); + // CHECK-NO-CONTAINER-OVERFLOW: SUCCESS + return 0; +} + +#endif // SHARED_LIB _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
