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=`` * ``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 +#include +#include + +// 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; +} 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 +#include +#include + +template 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 &stack) { stack.push(42); } +#else // SHARED_LIB + +# include +# include + +typedef void (*push_func_t)(Stack &); + +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 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 +#include + +// 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 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 + +#endif + +// Mimic template based container library header +template 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 &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 &stack); + +extern "C" void do_push_value_to_stack(Stack &stack) { + push_value_to_stack(stack); +} + +#else + +extern "C" void do_push_value_to_stack(Stack &stack); + +# include + +int main(int argc, char *argv[]) { + + Stack 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