diff options
Diffstat (limited to 'test/scudo')
-rw-r--r-- | test/scudo/CMakeLists.txt | 10 | ||||
-rw-r--r-- | test/scudo/alignment.c (renamed from test/scudo/alignment.cpp) | 2 | ||||
-rw-r--r-- | test/scudo/double-free.cpp | 2 | ||||
-rw-r--r-- | test/scudo/interface.cpp | 48 | ||||
-rw-r--r-- | test/scudo/lit.cfg | 43 | ||||
-rw-r--r-- | test/scudo/malloc.cpp | 2 | ||||
-rw-r--r-- | test/scudo/memalign.c (renamed from test/scudo/memalign.cpp) | 21 | ||||
-rw-r--r-- | test/scudo/mismatch.cpp | 25 | ||||
-rw-r--r-- | test/scudo/options.cpp | 10 | ||||
-rw-r--r-- | test/scudo/overflow.c (renamed from test/scudo/overflow.cpp) | 6 | ||||
-rw-r--r-- | test/scudo/preinit.c (renamed from test/scudo/preinit.cpp) | 6 | ||||
-rw-r--r-- | test/scudo/preload.cpp | 20 | ||||
-rw-r--r-- | test/scudo/quarantine.c | 124 | ||||
-rw-r--r-- | test/scudo/quarantine.cpp | 57 | ||||
-rw-r--r-- | test/scudo/random_shuffle.cpp | 3 | ||||
-rw-r--r-- | test/scudo/realloc.cpp | 104 | ||||
-rw-r--r-- | test/scudo/rss.c | 56 | ||||
-rw-r--r-- | test/scudo/secondary.c (renamed from test/scudo/secondary.cpp) | 2 | ||||
-rw-r--r-- | test/scudo/sized-delete.cpp | 14 | ||||
-rw-r--r-- | test/scudo/sizes.cpp | 20 | ||||
-rw-r--r-- | test/scudo/threads.c (renamed from test/scudo/threads.cpp) | 8 | ||||
-rw-r--r-- | test/scudo/tsd_destruction.c | 42 | ||||
-rw-r--r-- | test/scudo/valloc.c | 65 |
23 files changed, 522 insertions, 168 deletions
diff --git a/test/scudo/CMakeLists.txt b/test/scudo/CMakeLists.txt index a8990999722e..513168b189d6 100644 --- a/test/scudo/CMakeLists.txt +++ b/test/scudo/CMakeLists.txt @@ -15,7 +15,15 @@ configure_lit_site_cfg( set(SCUDO_TEST_ARCH ${SCUDO_SUPPORTED_ARCH}) foreach(arch ${SCUDO_TEST_ARCH}) - set(SCUDO_TEST_TARGET_ARCH ${arch}) + if(ANDROID) + if (${arch} STREQUAL "i386") + set(SCUDO_TEST_TARGET_ARCH i686-android) + else() + set(SCUDO_TEST_TARGET_ARCH ${arch}-android) + endif() + else() + set(SCUDO_TEST_TARGET_ARCH ${arch}) + endif() string(TOLOWER "-${arch}" SCUDO_TEST_CONFIG_SUFFIX) get_test_cc_for_arch(${arch} SCUDO_TEST_TARGET_CC SCUDO_TEST_TARGET_CFLAGS) string(TOUPPER ${arch} ARCH_UPPER_CASE) diff --git a/test/scudo/alignment.cpp b/test/scudo/alignment.c index 125ad8cbe76f..6235d50608db 100644 --- a/test/scudo/alignment.cpp +++ b/test/scudo/alignment.c @@ -15,7 +15,7 @@ int main(int argc, char **argv) if (!strcmp(argv[1], "pointers")) { void *p = malloc(1U << 16); assert(p); - free(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(p) | 1)); + free((void *)((uintptr_t)p | 1)); } return 0; } diff --git a/test/scudo/double-free.cpp b/test/scudo/double-free.cpp index ddc520505ed1..56118038cf1f 100644 --- a/test/scudo/double-free.cpp +++ b/test/scudo/double-free.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_scudo %s -o %t +// RUN: %clangxx_scudo %s -o %t // RUN: not %run %t malloc 2>&1 | FileCheck %s // RUN: not %run %t new 2>&1 | FileCheck %s // RUN: not %run %t newarray 2>&1 | FileCheck %s diff --git a/test/scudo/interface.cpp b/test/scudo/interface.cpp index e9575adf3100..73ea0a738e43 100644 --- a/test/scudo/interface.cpp +++ b/test/scudo/interface.cpp @@ -1,17 +1,22 @@ -// RUN: %clang_scudo %s -lstdc++ -o %t -// RUN: %run %t ownership 2>&1 -// RUN: %run %t ownership-and-size 2>&1 -// RUN: %run %t heap-size 2>&1 +// RUN: %clangxx_scudo %s -lstdc++ -o %t +// RUN: %run %t ownership 2>&1 +// RUN: %run %t ownership-and-size 2>&1 +// RUN: %run %t heap-size 2>&1 +// RUN: %env_scudo_opts="allocator_may_return_null=1" %run %t soft-limit 2>&1 +// RUN: %env_scudo_opts="allocator_may_return_null=1" not %run %t hard-limit 2>&1 +// UNSUPPORTED: armhf-linux // Tests that the sanitizer interface functions behave appropriately. #include <stdlib.h> #include <assert.h> #include <string.h> +#include <unistd.h> #include <vector> #include <sanitizer/allocator_interface.h> +#include <sanitizer/scudo_interface.h> int main(int argc, char **argv) { @@ -42,6 +47,41 @@ int main(int argc, char **argv) // allocator function. assert(__sanitizer_get_heap_size() >= 0); } + if (!strcmp(argv[1], "soft-limit")) { + // Verifies that setting the soft RSS limit at runtime works as expected. + std::vector<void *> pointers; + size_t size = 1 << 19; // 512Kb + for (int i = 0; i < 5; i++) + pointers.push_back(malloc(size)); + // Set the soft RSS limit to 1Mb. + __scudo_set_rss_limit(1, 0); + usleep(20000); + // The following allocation should return NULL. + void *p = malloc(size); + assert(!p); + // Remove the soft RSS limit. + __scudo_set_rss_limit(0, 0); + // The following allocation should succeed. + p = malloc(size); + assert(p); + free(p); + while (!pointers.empty()) { + free(pointers.back()); + pointers.pop_back(); + } + } + if (!strcmp(argv[1], "hard-limit")) { + // Verifies that setting the hard RSS limit at runtime works as expected. + std::vector<void *> pointers; + size_t size = 1 << 19; // 512Kb + for (int i = 0; i < 5; i++) + pointers.push_back(malloc(size)); + // Set the hard RSS limit to 1Mb + __scudo_set_rss_limit(1, 1); + usleep(20000); + // The following should trigger our death. + void *p = malloc(size); + } return 0; } diff --git a/test/scudo/lit.cfg b/test/scudo/lit.cfg index adf16f57bbbd..028bf721b89e 100644 --- a/test/scudo/lit.cfg +++ b/test/scudo/lit.cfg @@ -8,32 +8,53 @@ config.name = 'Scudo' + config.name_suffix # Setup source root. config.test_source_root = os.path.dirname(__file__) -# Path to the static library -base_lib = os.path.join(config.compiler_rt_libdir, - "libclang_rt.scudo-%s.a" % config.target_arch) -whole_archive = "-Wl,-whole-archive %s -Wl,-no-whole-archive " % base_lib +# Path to the shared & static libraries +shared_libscudo = os.path.join(config.compiler_rt_libdir, "libclang_rt.scudo-%s.so" % config.target_arch) +static_libscudo = os.path.join(config.compiler_rt_libdir, "libclang_rt.scudo-%s.a" % config.target_arch) +static_libscudo_cxx = os.path.join(config.compiler_rt_libdir, "libclang_rt.scudo_cxx-%s.a" % config.target_arch) + +whole_archive = "-Wl,-whole-archive %s -Wl,-no-whole-archive " % static_libscudo +whole_archive_cxx = "-Wl,-whole-archive %s -Wl,-no-whole-archive " % static_libscudo_cxx # Test suffixes. config.suffixes = ['.c', '.cc', '.cpp'] -# C flags. +# C & CXX flags. c_flags = ([config.target_cflags] + - ["-std=c++11", - "-pthread", + ["-pthread", "-fPIE", "-pie", "-O0", "-UNDEBUG", "-ldl", - "-lrt", "-Wl,--gc-sections"]) +# Android doesn't want -lrt. +if not config.android: + c_flags += ["-lrt"] + +cxx_flags = (c_flags + config.cxx_mode_flags + ["-std=c++11"]) + def build_invocation(compile_flags): - return " " + " ".join([config.clang] + compile_flags) + " " + return " " + " ".join([config.clang] + compile_flags) + " " # Add clang substitutions. -config.substitutions.append( ("%clang_scudo ", - build_invocation(c_flags) + whole_archive) ) +config.substitutions.append(("%clang ", build_invocation(c_flags))) +config.substitutions.append(("%clang_scudo ", build_invocation(c_flags) + whole_archive)) +config.substitutions.append(("%clangxx_scudo ", build_invocation(cxx_flags) + whole_archive + whole_archive_cxx)) +config.substitutions.append(("%shared_libscudo", shared_libscudo)) + +# Platform-specific default SCUDO_OPTIONS for lit tests. +default_scudo_opts = '' +if config.android: + # Android defaults to abort_on_error=1, which doesn't work for us. + default_scudo_opts = 'abort_on_error=0' + +if default_scudo_opts: + config.environment['SCUDO_OPTIONS'] = default_scudo_opts + default_scudo_opts += ':' +config.substitutions.append(('%env_scudo_opts=', + 'env SCUDO_OPTIONS=' + default_scudo_opts)) # Hardened Allocator tests are currently supported on Linux only. if config.host_os not in ['Linux']: diff --git a/test/scudo/malloc.cpp b/test/scudo/malloc.cpp index 50e52590f565..6c6a6c464f29 100644 --- a/test/scudo/malloc.cpp +++ b/test/scudo/malloc.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_scudo %s -lstdc++ -o %t +// RUN: %clangxx_scudo %s -lstdc++ -o %t // RUN: %run %t 2>&1 // Tests that a regular workflow of allocation, memory fill and free works as diff --git a/test/scudo/memalign.cpp b/test/scudo/memalign.c index 82c54af8b0e4..1fe6e3ec7eed 100644 --- a/test/scudo/memalign.cpp +++ b/test/scudo/memalign.c @@ -1,6 +1,7 @@ // RUN: %clang_scudo %s -o %t -// RUN: %run %t valid 2>&1 -// RUN: %run %t invalid 2>&1 +// RUN: %run %t valid 2>&1 +// RUN: not %run %t invalid 2>&1 +// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t invalid 2>&1 // Tests that the various aligned allocation functions work as intended. Also // tests for the condition where the alignment is not a power of 2. @@ -8,21 +9,17 @@ #include <assert.h> #include <errno.h> #include <malloc.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> - -// Reduce the size of the quarantine, or the test can run out of aligned memory -// on 32-bit for the larger alignments. -extern "C" const char *__scudo_default_options() { - return "QuarantineSizeMb=1"; -} +#include <unistd.h> // Sometimes the headers may not have this... -extern "C" void *aligned_alloc (size_t alignment, size_t size); +void *aligned_alloc(size_t alignment, size_t size); int main(int argc, char **argv) { - void *p = nullptr; + void *p = NULL; size_t alignment = 1U << 12; size_t size = 1U << 12; int err; @@ -32,9 +29,11 @@ int main(int argc, char **argv) if (!strcmp(argv[1], "valid")) { posix_memalign(&p, alignment, size); assert(p); + assert(((uintptr_t)p & (alignment - 1)) == 0); free(p); p = aligned_alloc(alignment, size); assert(p); + assert(((uintptr_t)p & (alignment - 1)) == 0); free(p); // Tests various combinations of alignment and sizes for (int i = (sizeof(void *) == 4) ? 3 : 4; i < 19; i++) { @@ -44,6 +43,7 @@ int main(int argc, char **argv) for (int k = 0; k < 3; k++) { p = memalign(alignment, size - (2 * sizeof(void *) * k)); assert(p); + assert(((uintptr_t)p & (alignment - 1)) == 0); free(p); } } @@ -54,6 +54,7 @@ int main(int argc, char **argv) for (int k = 0; k < 3; k++) { p = memalign(alignment, 0x1000 - (2 * sizeof(void *) * k)); assert(p); + assert(((uintptr_t)p & (alignment - 1)) == 0); free(p); } } diff --git a/test/scudo/mismatch.cpp b/test/scudo/mismatch.cpp index 15dce83ce18a..b49e0ea46f12 100644 --- a/test/scudo/mismatch.cpp +++ b/test/scudo/mismatch.cpp @@ -1,10 +1,12 @@ -// RUN: %clang_scudo %s -o %t -// RUN: SCUDO_OPTIONS=DeallocationTypeMismatch=1 not %run %t mallocdel 2>&1 | FileCheck %s -// RUN: SCUDO_OPTIONS=DeallocationTypeMismatch=0 %run %t mallocdel 2>&1 -// RUN: SCUDO_OPTIONS=DeallocationTypeMismatch=1 not %run %t newfree 2>&1 | FileCheck %s -// RUN: SCUDO_OPTIONS=DeallocationTypeMismatch=0 %run %t newfree 2>&1 -// RUN: SCUDO_OPTIONS=DeallocationTypeMismatch=1 not %run %t memaligndel 2>&1 | FileCheck %s -// RUN: SCUDO_OPTIONS=DeallocationTypeMismatch=0 %run %t memaligndel 2>&1 +// RUN: %clangxx_scudo %s -o %t +// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t mallocdel 2>&1 | FileCheck --check-prefix=CHECK-dealloc %s +// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t mallocdel 2>&1 +// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t newfree 2>&1 | FileCheck --check-prefix=CHECK-dealloc %s +// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t newfree 2>&1 +// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t memaligndel 2>&1 | FileCheck --check-prefix=CHECK-dealloc %s +// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t memaligndel 2>&1 +// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t memalignrealloc 2>&1 | FileCheck --check-prefix=CHECK-realloc %s +// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t memalignrealloc 2>&1 // Tests that type mismatches between allocation and deallocation functions are // caught when the related option is set. @@ -32,7 +34,14 @@ int main(int argc, char **argv) assert(p); delete p; } + if (!strcmp(argv[1], "memalignrealloc")) { + void *p = memalign(16, 16); + assert(p); + p = realloc(p, 32); + free(p); + } return 0; } -// CHECK: ERROR: allocation type mismatch on address +// CHECK-dealloc: ERROR: allocation type mismatch when deallocating address +// CHECK-realloc: ERROR: allocation type mismatch when reallocating address diff --git a/test/scudo/options.cpp b/test/scudo/options.cpp index f4afe7d79bfc..605b63241337 100644 --- a/test/scudo/options.cpp +++ b/test/scudo/options.cpp @@ -1,7 +1,7 @@ -// RUN: %clang_scudo %s -o %t -// RUN: %run %t 2>&1 -// RUN: SCUDO_OPTIONS=DeallocationTypeMismatch=0 %run %t 2>&1 -// RUN: SCUDO_OPTIONS=DeallocationTypeMismatch=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_scudo %s -o %t +// RUN: %run %t 2>&1 +// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t 2>&1 +// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t 2>&1 | FileCheck %s // Tests that the options can be passed using getScudoDefaultOptions, and that // the environment ones take precedence over them. @@ -22,4 +22,4 @@ int main(int argc, char **argv) return 0; } -// CHECK: ERROR: allocation type mismatch on address +// CHECK: ERROR: allocation type mismatch when deallocating address diff --git a/test/scudo/overflow.cpp b/test/scudo/overflow.c index d12824578524..c5a58f87f305 100644 --- a/test/scudo/overflow.cpp +++ b/test/scudo/overflow.c @@ -1,6 +1,6 @@ // RUN: %clang_scudo %s -o %t -// RUN: not %run %t malloc 2>&1 | FileCheck %s -// RUN: SCUDO_OPTIONS=QuarantineSizeMb=1 not %run %t quarantine 2>&1 | FileCheck %s +// RUN: not %run %t malloc 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=QuarantineSizeKb=64 not %run %t quarantine 2>&1 | FileCheck %s // Tests that header corruption of an allocated or quarantined chunk is caught. @@ -29,7 +29,7 @@ int main(int argc, char **argv) ((char *)p)[-(offset + 2)] ^= 1; // Trigger the quarantine recycle for (int i = 0; i < 0x100; i++) { - p = malloc(1U << 16); + p = malloc(1U << 8); free(p); } } diff --git a/test/scudo/preinit.cpp b/test/scudo/preinit.c index b8c01a401dd7..792b2368e37d 100644 --- a/test/scudo/preinit.cpp +++ b/test/scudo/preinit.c @@ -4,11 +4,15 @@ // Verifies that calling malloc in a preinit_array function succeeds, and that // the resulting pointer can be freed at program termination. +// On some Android versions, calling mmap() from a preinit function segfaults. +// It looks like __mmap2.S ends up calling a NULL function pointer. +// UNSUPPORTED: android + #include <assert.h> #include <stdlib.h> #include <string.h> -static void *global_p = nullptr; +static void *global_p = NULL; void __init(void) { global_p = malloc(1); diff --git a/test/scudo/preload.cpp b/test/scudo/preload.cpp new file mode 100644 index 000000000000..b41a70e472b3 --- /dev/null +++ b/test/scudo/preload.cpp @@ -0,0 +1,20 @@ +// Test that the preloaded runtime works without linking the static library. + +// RUN: %clang %s -lstdc++ -o %t +// RUN: env LD_PRELOAD=%shared_libscudo not %run %t 2>&1 | FileCheck %s + +// This way of setting LD_PRELOAD does not work with Android test runner. +// REQUIRES: !android + +#include <assert.h> + +int main(int argc, char *argv[]) { + int *p = new int; + assert(p); + *p = 0; + delete p; + delete p; + return 0; +} + +// CHECK: ERROR: invalid chunk state diff --git a/test/scudo/quarantine.c b/test/scudo/quarantine.c new file mode 100644 index 000000000000..ddbb92005b84 --- /dev/null +++ b/test/scudo/quarantine.c @@ -0,0 +1,124 @@ +// RUN: %clang_scudo %s -o %t +// RUN: %env_scudo_opts="QuarantineSizeMb=1:QuarantineSizeKb=64" not %run %t unused 2>&1 +// RUN: %env_scudo_opts="QuarantineSizeMb=1:QuarantineChunksUpToSize=256" not %run %t unused 2>&1 +// RUN: %env_scudo_opts="QuarantineSizeKb=0:ThreadLocalQuarantineSizeKb=0" %run %t zeroquarantine 2>&1 +// RUN: %env_scudo_opts=QuarantineSizeKb=64 %run %t smallquarantine 2>&1 +// RUN: %env_scudo_opts=QuarantineChunksUpToSize=256 %run %t threshold 2>&1 +// RUN: %env_scudo_opts="QuarantineSizeMb=1" %run %t oldquarantine 2>&1 + +// Tests that the quarantine prevents a chunk from being reused right away. +// Also tests that a chunk will eventually become available again for +// allocation when the recycling criteria has been met. Finally, tests the +// threshold up to which a chunk is quarantine, and the old quarantine behavior. + +#include <assert.h> +#include <malloc.h> +#include <stdlib.h> +#include <string.h> + +#include <sanitizer/allocator_interface.h> + +int main(int argc, char **argv) +{ + void *p, *old_p; + size_t allocated_bytes, size = 1U << 8, alignment = 1U << 8; + + assert(argc == 2); + // First, warm up the allocator for the classes used. + p = malloc(size); + assert(p); + free(p); + p = malloc(size + 1); + assert(p); + free(p); + assert(posix_memalign(&p, alignment, size) == 0); + assert(p); + free(p); + assert(posix_memalign(&p, alignment, size + 1) == 0); + assert(p); + free(p); + + if (!strcmp(argv[1], "zeroquarantine")) { + // Verifies that a chunk is deallocated right away when the local and + // global quarantine sizes are 0. + allocated_bytes = __sanitizer_get_current_allocated_bytes(); + p = malloc(size); + assert(p); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + free(p); + assert(__sanitizer_get_current_allocated_bytes() == allocated_bytes); + } + if (!strcmp(argv[1], "smallquarantine")) { + // The delayed freelist will prevent a chunk from being available right + // away. + p = malloc(size); + assert(p); + old_p = p; + free(p); + p = malloc(size); + assert(p); + assert(old_p != p); + free(p); + + // Eventually the chunk should become available again. + char found = 0; + for (int i = 0; i < 0x200 && !found; i++) { + p = malloc(size); + assert(p); + found = (p == old_p); + free(p); + } + assert(found); + } + if (!strcmp(argv[1], "threshold")) { + // Verifies that a chunk of size greater than the threshold will be freed + // right away. Alignment has no impact on the threshold. + allocated_bytes = __sanitizer_get_current_allocated_bytes(); + p = malloc(size + 1); + assert(p); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + free(p); + assert(__sanitizer_get_current_allocated_bytes() == allocated_bytes); + assert(posix_memalign(&p, alignment, size + 1) == 0); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + free(p); + assert(__sanitizer_get_current_allocated_bytes() == allocated_bytes); + // Verifies that a chunk of size lower or equal to the threshold will be + // quarantined. + p = malloc(size); + assert(p); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + free(p); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + allocated_bytes = __sanitizer_get_current_allocated_bytes(); + assert(posix_memalign(&p, alignment, size) == 0); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + free(p); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + } + if (!strcmp(argv[1], "oldquarantine")) { + // Verifies that we quarantine everything if the deprecated quarantine + // option is specified. Alignment has no impact on the threshold. + allocated_bytes = __sanitizer_get_current_allocated_bytes(); + p = malloc(size); + assert(p); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + free(p); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + allocated_bytes = __sanitizer_get_current_allocated_bytes(); + assert(posix_memalign(&p, alignment, size) == 0); + assert(p); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + free(p); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + // Secondary backed allocation. + allocated_bytes = __sanitizer_get_current_allocated_bytes(); + p = malloc(1U << 19); + assert(p); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + free(p); + assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); + } + + return 0; +} diff --git a/test/scudo/quarantine.cpp b/test/scudo/quarantine.cpp deleted file mode 100644 index 39ce1bd91d31..000000000000 --- a/test/scudo/quarantine.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// RUN: %clang_scudo %s -o %t -// RUN: SCUDO_OPTIONS="QuarantineSizeMb=0:ThreadLocalQuarantineSizeKb=0" %run %t zeroquarantine 2>&1 -// RUN: SCUDO_OPTIONS=QuarantineSizeMb=1 %run %t smallquarantine 2>&1 - -// Tests that the quarantine prevents a chunk from being reused right away. -// Also tests that a chunk will eventually become available again for -// allocation when the recycling criteria has been met. - -#include <assert.h> -#include <malloc.h> -#include <stdlib.h> -#include <string.h> - -#include <sanitizer/allocator_interface.h> - -int main(int argc, char **argv) -{ - void *p, *old_p; - size_t allocated_bytes, size = 1U << 16; - - assert(argc == 2); - - if (!strcmp(argv[1], "zeroquarantine")) { - // Verifies that a chunk is deallocated right away when the local and - // global quarantine sizes are 0. - allocated_bytes = __sanitizer_get_current_allocated_bytes(); - p = malloc(size); - assert(p); - assert(__sanitizer_get_current_allocated_bytes() > allocated_bytes); - free(p); - assert(__sanitizer_get_current_allocated_bytes() == allocated_bytes); - } - if (!strcmp(argv[1], "smallquarantine")) { - // The delayed freelist will prevent a chunk from being available right - // away. - p = malloc(size); - assert(p); - old_p = p; - free(p); - p = malloc(size); - assert(p); - assert(old_p != p); - free(p); - - // Eventually the chunk should become available again. - bool found = false; - for (int i = 0; i < 0x100 && found == false; i++) { - p = malloc(size); - assert(p); - found = (p == old_p); - free(p); - } - assert(found == true); - } - - return 0; -} diff --git a/test/scudo/random_shuffle.cpp b/test/scudo/random_shuffle.cpp index 05a432615017..f886cb1504e1 100644 --- a/test/scudo/random_shuffle.cpp +++ b/test/scudo/random_shuffle.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_scudo %s -o %t +// RUN: %clangxx_scudo %s -o %t // RUN: rm -rf %T/random_shuffle_tmp_dir // RUN: mkdir %T/random_shuffle_tmp_dir // RUN: %run %t 100 > %T/random_shuffle_tmp_dir/out1 @@ -7,7 +7,6 @@ // RUN: %run %t 10000 > %T/random_shuffle_tmp_dir/out2 // RUN: not diff %T/random_shuffle_tmp_dir/out? // RUN: rm -rf %T/random_shuffle_tmp_dir -// UNSUPPORTED: i386-linux,i686-linux,arm-linux,armhf-linux,aarch64-linux,mips-linux,mipsel-linux,mips64-linux,mips64el-linux // Tests that the allocator shuffles the chunks before returning to the user. diff --git a/test/scudo/realloc.cpp b/test/scudo/realloc.cpp index da377205f10f..254c67a2cca4 100644 --- a/test/scudo/realloc.cpp +++ b/test/scudo/realloc.cpp @@ -1,14 +1,13 @@ -// RUN: %clang_scudo %s -lstdc++ -o %t -// RUN: %run %t pointers 2>&1 -// RUN: %run %t contents 2>&1 -// RUN: not %run %t memalign 2>&1 | FileCheck %s +// RUN: %clangxx_scudo %s -lstdc++ -o %t +// RUN: %run %t pointers 2>&1 +// RUN: %run %t contents 2>&1 +// RUN: %run %t usablesize 2>&1 // Tests that our reallocation function returns the same pointer when the // requested size can fit into the previously allocated chunk. Also tests that // a new chunk is returned if the size is greater, and that the contents of the -// chunk are left unchanged. -// As a final test, make sure that a chunk allocated by memalign cannot be -// reallocated. +// chunk are left unchanged. Finally, checks that realloc copies the usable +// size of the old chunk to the new one (as opposed to the requested size). #include <assert.h> #include <malloc.h> @@ -24,42 +23,65 @@ int main(int argc, char **argv) assert(argc == 2); - for (size_t size : sizes) { - if (!strcmp(argv[1], "pointers")) { - old_p = p = realloc(nullptr, size); - assert(p); - size = malloc_usable_size(p); - // Our realloc implementation will return the same pointer if the size - // requested is lower than or equal to the usable size of the associated - // chunk. - p = realloc(p, size - 1); - assert(p == old_p); + if (!strcmp(argv[1], "usablesize")) { + // This tests a sketchy behavior inherited from poorly written libraries + // that have become somewhat standard. When realloc'ing a chunk, the + // copied contents should span the usable size of the chunk, not the + // requested size. + size_t size = 496, usable_size; + p = nullptr; + // Make sure we get a chunk with a usable size actually larger than size. + do { + if (p) free(p); + size += 16; + p = malloc(size); + usable_size = malloc_usable_size(p); + assert(usable_size >= size); + } while (usable_size == size); + for (int i = 0; i < usable_size; i++) + reinterpret_cast<char *>(p)[i] = 'A'; + old_p = p; + // Make sure we get a different chunk so that the data is actually copied. + do { + size *= 2; p = realloc(p, size); - assert(p == old_p); - // And a new one if the size is greater. - p = realloc(p, size + 1); - assert(p != old_p); - // A size of 0 will free the chunk and return nullptr. - p = realloc(p, 0); - assert(!p); - old_p = nullptr; - } - if (!strcmp(argv[1], "contents")) { - p = realloc(nullptr, size); assert(p); - for (int i = 0; i < size; i++) - reinterpret_cast<char *>(p)[i] = 'A'; - p = realloc(p, size + 1); - // The contents of the reallocated chunk must match the original one. - for (int i = 0; i < size; i++) - assert(reinterpret_cast<char *>(p)[i] == 'A'); - } - if (!strcmp(argv[1], "memalign")) { - // A chunk coming from memalign cannot be reallocated. - p = memalign(16, size); - assert(p); - p = realloc(p, size); - free(p); + } while (p == old_p); + // The contents of the new chunk must match the old one up to usable_size. + for (int i = 0; i < usable_size; i++) + assert(reinterpret_cast<char *>(p)[i] == 'A'); + free(p); + } else { + for (size_t size : sizes) { + if (!strcmp(argv[1], "pointers")) { + old_p = p = realloc(nullptr, size); + assert(p); + size = malloc_usable_size(p); + // Our realloc implementation will return the same pointer if the size + // requested is lower than or equal to the usable size of the associated + // chunk. + p = realloc(p, size - 1); + assert(p == old_p); + p = realloc(p, size); + assert(p == old_p); + // And a new one if the size is greater. + p = realloc(p, size + 1); + assert(p != old_p); + // A size of 0 will free the chunk and return nullptr. + p = realloc(p, 0); + assert(!p); + old_p = nullptr; + } + if (!strcmp(argv[1], "contents")) { + p = realloc(nullptr, size); + assert(p); + for (int i = 0; i < size; i++) + reinterpret_cast<char *>(p)[i] = 'A'; + p = realloc(p, size + 1); + // The contents of the reallocated chunk must match the original one. + for (int i = 0; i < size; i++) + assert(reinterpret_cast<char *>(p)[i] == 'A'); + } } } return 0; diff --git a/test/scudo/rss.c b/test/scudo/rss.c new file mode 100644 index 000000000000..4376290ad0cd --- /dev/null +++ b/test/scudo/rss.c @@ -0,0 +1,56 @@ +// RUN: %clang_scudo %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-nolimit +// RUN: %env_scudo_opts="soft_rss_limit_mb=256" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-nolimit +// RUN: %env_scudo_opts="hard_rss_limit_mb=256" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-nolimit +// RUN: %env_scudo_opts="soft_rss_limit_mb=64:allocator_may_return_null=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-softlimit +// RUN: %env_scudo_opts="soft_rss_limit_mb=64:allocator_may_return_null=1" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-softlimit-returnnull +// RUN: %env_scudo_opts="soft_rss_limit_mb=64:allocator_may_return_null=0:can_use_proc_maps_statm=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-softlimit +// RUN: %env_scudo_opts="soft_rss_limit_mb=64:allocator_may_return_null=1:can_use_proc_maps_statm=0" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-softlimit-returnnull +// RUN: %env_scudo_opts="hard_rss_limit_mb=64:allocator_may_return_null=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-hardlimit +// RUN: %env_scudo_opts="hard_rss_limit_mb=64:allocator_may_return_null=1" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-hardlimit +// RUN: %env_scudo_opts="hard_rss_limit_mb=64:allocator_may_return_null=0:can_use_proc_maps_statm=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-hardlimit +// RUN: %env_scudo_opts="hard_rss_limit_mb=64:allocator_may_return_null=1:can_use_proc_maps_statm=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-hardlimit + +// Tests that the soft and hard RSS limits work as intended. Without limit or +// with a high limit, the test should pass without any malloc returning NULL or +// the program dying. +// If a limit is specified, it should return some NULL or die depending on +// allocator_may_return_null. This should also work without statm. + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static const size_t kNumAllocs = 128; +static const size_t kAllocSize = 1 << 20; // 1MB. + +static void *allocs[kNumAllocs]; + +int main(int argc, char *argv[]) { + int returned_null = 0; + for (int i = 0; i < kNumAllocs; i++) { + if ((i & 0xf) == 0) + usleep(50000); + allocs[i] = malloc(kAllocSize); + if (allocs[i]) + memset(allocs[i], 0xff, kAllocSize); // Dirty the pages. + else + returned_null++; + } + for (int i = 0; i < kNumAllocs; i++) + free(allocs[i]); + if (returned_null == 0) + printf("All malloc calls succeeded\n"); + else + printf("%d malloc calls returned NULL\n", returned_null); + return 0; +} + +// CHECK-nolimit: All malloc calls succeeded +// CHECK-softlimit: soft RSS limit exhausted +// CHECK-softlimit-NOT: malloc calls +// CHECK-softlimit-returnnull: soft RSS limit exhausted +// CHECK-softlimit-returnnull: malloc calls returned NULL +// CHECK-hardlimit: hard RSS limit exhausted +// CHECK-hardlimit-NOT: malloc calls diff --git a/test/scudo/secondary.cpp b/test/scudo/secondary.c index dc14f8ca846e..b770ca076b70 100644 --- a/test/scudo/secondary.cpp +++ b/test/scudo/secondary.c @@ -36,7 +36,7 @@ int main(int argc, char **argv) assert(p); memset(p, 'A', size); // This should not trigger anything. // Set up the SIGSEGV handler now, as the rest should trigger an AV. - sigaction(SIGSEGV, &a, nullptr); + sigaction(SIGSEGV, &a, NULL); if (!strcmp(argv[1], "after")) { for (int i = 0; i < page_size; i++) p[size + i] = 'A'; diff --git a/test/scudo/sized-delete.cpp b/test/scudo/sized-delete.cpp index e467f556520e..85df05e2f809 100644 --- a/test/scudo/sized-delete.cpp +++ b/test/scudo/sized-delete.cpp @@ -1,10 +1,10 @@ -// RUN: %clang_scudo -fsized-deallocation %s -o %t -// RUN: SCUDO_OPTIONS=DeleteSizeMismatch=1 %run %t gooddel 2>&1 -// RUN: SCUDO_OPTIONS=DeleteSizeMismatch=1 not %run %t baddel 2>&1 | FileCheck %s -// RUN: SCUDO_OPTIONS=DeleteSizeMismatch=0 %run %t baddel 2>&1 -// RUN: SCUDO_OPTIONS=DeleteSizeMismatch=1 %run %t gooddelarr 2>&1 -// RUN: SCUDO_OPTIONS=DeleteSizeMismatch=1 not %run %t baddelarr 2>&1 | FileCheck %s -// RUN: SCUDO_OPTIONS=DeleteSizeMismatch=0 %run %t baddelarr 2>&1 +// RUN: %clangxx_scudo -fsized-deallocation %s -o %t +// RUN: %env_scudo_opts=DeleteSizeMismatch=1 %run %t gooddel 2>&1 +// RUN: %env_scudo_opts=DeleteSizeMismatch=1 not %run %t baddel 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=DeleteSizeMismatch=0 %run %t baddel 2>&1 +// RUN: %env_scudo_opts=DeleteSizeMismatch=1 %run %t gooddelarr 2>&1 +// RUN: %env_scudo_opts=DeleteSizeMismatch=1 not %run %t baddelarr 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=DeleteSizeMismatch=0 %run %t baddelarr 2>&1 // Ensures that the sized delete operator errors out when the appropriate // option is passed and the sizes do not match between allocation and diff --git a/test/scudo/sizes.cpp b/test/scudo/sizes.cpp index a0994c2515a2..73fc71f25c54 100644 --- a/test/scudo/sizes.cpp +++ b/test/scudo/sizes.cpp @@ -1,13 +1,13 @@ -// RUN: %clang_scudo %s -lstdc++ -o %t -// RUN: SCUDO_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s -// RUN: SCUDO_OPTIONS=allocator_may_return_null=1 %run %t malloc 2>&1 -// RUN: SCUDO_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s -// RUN: SCUDO_OPTIONS=allocator_may_return_null=1 %run %t calloc 2>&1 -// RUN: SCUDO_OPTIONS=allocator_may_return_null=0 not %run %t new 2>&1 | FileCheck %s -// RUN: SCUDO_OPTIONS=allocator_may_return_null=1 not %run %t new 2>&1 | FileCheck %s -// RUN: SCUDO_OPTIONS=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 | FileCheck %s -// RUN: SCUDO_OPTIONS=allocator_may_return_null=1 %run %t new-nothrow 2>&1 -// RUN: %run %t usable 2>&1 +// RUN: %clangxx_scudo %s -lstdc++ -o %t +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t malloc 2>&1 +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t calloc 2>&1 +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t new 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=allocator_may_return_null=1 not %run %t new 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 +// RUN: %run %t usable 2>&1 // Tests for various edge cases related to sizes, notably the maximum size the // allocator can allocate. Tests that an integer overflow in the parameters of diff --git a/test/scudo/threads.cpp b/test/scudo/threads.c index d9f4a86c1acf..b34e6f0f79a2 100644 --- a/test/scudo/threads.cpp +++ b/test/scudo/threads.c @@ -1,6 +1,6 @@ // RUN: %clang_scudo %s -o %t -// RUN: SCUDO_OPTIONS="QuarantineSizeMb=0:ThreadLocalQuarantineSizeKb=0" %run %t 5 1000000 2>&1 -// RUN: SCUDO_OPTIONS="QuarantineSizeMb=1:ThreadLocalQuarantineSizeKb=64" %run %t 5 1000000 2>&1 +// RUN: %env_scudo_opts="QuarantineSizeKb=0:ThreadLocalQuarantineSizeKb=0" %run %t 5 1000000 2>&1 +// RUN: %env_scudo_opts="QuarantineSizeKb=1024:ThreadLocalQuarantineSizeKb=64" %run %t 5 1000000 2>&1 // Tests parallel allocations and deallocations of memory chunks from a number // of concurrent threads, with and without quarantine. @@ -20,7 +20,7 @@ pthread_t tid[kMaxNumThreads]; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -bool go = false; +char go = 0; void *thread_fun(void *arg) { pthread_mutex_lock(&mutex); @@ -51,7 +51,7 @@ int main(int argc, char** argv) { for (int i = 0; i < num_threads; i++) pthread_create(&tid[i], 0, thread_fun, 0); pthread_mutex_lock(&mutex); - go = true; + go = 1; pthread_cond_broadcast(&cond); pthread_mutex_unlock(&mutex); for (int i = 0; i < num_threads; i++) diff --git a/test/scudo/tsd_destruction.c b/test/scudo/tsd_destruction.c new file mode 100644 index 000000000000..1b0d0eff9f11 --- /dev/null +++ b/test/scudo/tsd_destruction.c @@ -0,0 +1,42 @@ +// RUN: %clang_scudo %s -o %t +// RUN: %run %t 2>&1 + +#include <locale.h> +#include <pthread.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +// Some of glibc's own thread local data is destroyed after a user's thread +// local destructors are called, via __libc_thread_freeres. This might involve +// calling free, as is the case for strerror_thread_freeres. +// If there is no prior heap operation in the thread, this free would end up +// initializing some thread specific data that would never be destroyed +// properly, while still being deallocated when the TLS goes away. As a result, +// a program could SEGV, usually in +// __sanitizer::AllocatorGlobalStats::Unregister, where one of the doubly +// linked list links would refer to a now unmapped memory area. + +// This test reproduces those circumstances. Success means executing without +// a segmentation fault. + +const int kNumThreads = 16; +pthread_t tid[kNumThreads]; + +void *thread_func(void *arg) { + uintptr_t i = (uintptr_t)arg; + if ((i & 1) == 0) free(malloc(16)); + // Calling strerror_l allows for strerror_thread_freeres to be called. + strerror_l(0, LC_GLOBAL_LOCALE); + return 0; +} + +int main(int argc, char** argv) { + for (uintptr_t j = 0; j < 8; j++) { + for (uintptr_t i = 0; i < kNumThreads; i++) + pthread_create(&tid[i], 0, thread_func, (void *)i); + for (uintptr_t i = 0; i < kNumThreads; i++) + pthread_join(tid[i], 0); + } + return 0; +} diff --git a/test/scudo/valloc.c b/test/scudo/valloc.c new file mode 100644 index 000000000000..132c4f280220 --- /dev/null +++ b/test/scudo/valloc.c @@ -0,0 +1,65 @@ +// RUN: %clang_scudo %s -o %t +// RUN: %run %t valid 2>&1 +// RUN: not %run %t invalid 2>&1 +// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t invalid 2>&1 +// UNSUPPORTED: android, armhf-linux + +// Tests that valloc and pvalloc work as intended. + +#include <assert.h> +#include <errno.h> +#include <malloc.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +size_t round_up_to(size_t size, size_t alignment) { + return (size + alignment - 1) & ~(alignment - 1); +} + +int main(int argc, char **argv) +{ + void *p = NULL; + size_t size, page_size; + + assert(argc == 2); + + page_size = sysconf(_SC_PAGESIZE); + // Check that the page size is a power of two. + assert((page_size & (page_size - 1)) == 0); + + if (!strcmp(argv[1], "valid")) { + for (int i = (sizeof(void *) == 4) ? 3 : 4; i < 21; i++) { + size = 1U << i; + p = valloc(size - (2 * sizeof(void *))); + assert(p); + assert(((uintptr_t)p & (page_size - 1)) == 0); + free(p); + p = pvalloc(size - (2 * sizeof(void *))); + assert(p); + assert(((uintptr_t)p & (page_size - 1)) == 0); + assert(malloc_usable_size(p) >= round_up_to(size, page_size)); + free(p); + p = valloc(size); + assert(p); + assert(((uintptr_t)p & (page_size - 1)) == 0); + free(p); + p = pvalloc(size); + assert(p); + assert(((uintptr_t)p & (page_size - 1)) == 0); + assert(malloc_usable_size(p) >= round_up_to(size, page_size)); + free(p); + } + } + if (!strcmp(argv[1], "invalid")) { + // Size passed to pvalloc overflows when rounded up. + p = pvalloc((size_t)-1); + assert(!p); + assert(errno == ENOMEM); + errno = 0; + p = pvalloc((size_t)-page_size); + assert(!p); + assert(errno == ENOMEM); + } + return 0; +} |