From 60ec9d032df6ac22be15ee06e1559c73cffc22ea Mon Sep 17 00:00:00 2001 From: Kostya Kortchinsky Date: Wed, 1 Nov 2017 15:28:20 +0000 Subject: [scudo] Implement stricter separation of C vs C++ Summary: Initially, Scudo had a monolithic design where both C and C++ functions were living in the same library. This was not necessarily ideal, and with the work on -fsanitize=scudo, it became more apparent that this needed to change. We are splitting the new/delete interceptor in their own C++ library. This allows more flexibility, notably with regard to std::bad_alloc when the work is done. This also allows us to not link new & delete when using pure C. Additionally, we add the UBSan runtimes with Scudo, in order to be able to have a -fsanitize=scudo,undefined in Clang (see work in D39334). The changes in this patch: - split the cxx specific code in the scudo cmake file into a new library; (remove the spurious foreach loop, that was not necessary) - add the UBSan runtimes (both C and C++); - change the test cmake file to allow for specific C & C++ tests; - make C tests pure C, rename their extension accordingly. Reviewers: alekseyshl Reviewed By: alekseyshl Subscribers: srhines, mgorny, llvm-commits Differential Revision: https://reviews.llvm.org/D39461 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@317097 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/scudo/CMakeLists.txt | 57 +++++++++++-------- test/scudo/alignment.c | 23 ++++++++ test/scudo/alignment.cpp | 23 -------- test/scudo/double-free.cpp | 2 +- test/scudo/interface.cpp | 2 +- test/scudo/lit.cfg | 10 +++- test/scudo/malloc.cpp | 2 +- test/scudo/memalign.c | 81 +++++++++++++++++++++++++++ test/scudo/memalign.cpp | 81 --------------------------- test/scudo/mismatch.cpp | 2 +- test/scudo/options.cpp | 2 +- test/scudo/overflow.c | 39 +++++++++++++ test/scudo/overflow.cpp | 39 ------------- test/scudo/preinit.c | 40 +++++++++++++ test/scudo/preinit.cpp | 40 ------------- test/scudo/preload.cpp | 10 ++-- test/scudo/quarantine.c | 124 +++++++++++++++++++++++++++++++++++++++++ test/scudo/quarantine.cpp | 124 ----------------------------------------- test/scudo/random_shuffle.cpp | 2 +- test/scudo/realloc.cpp | 2 +- test/scudo/secondary.c | 53 ++++++++++++++++++ test/scudo/secondary.cpp | 53 ------------------ test/scudo/sized-delete.cpp | 2 +- test/scudo/sizes.cpp | 2 +- test/scudo/threads.c | 65 +++++++++++++++++++++ test/scudo/threads.cpp | 65 --------------------- test/scudo/tsd_destruction.c | 42 ++++++++++++++ test/scudo/tsd_destruction.cpp | 42 -------------- test/scudo/valloc.c | 65 +++++++++++++++++++++ test/scudo/valloc.cpp | 65 --------------------- 30 files changed, 587 insertions(+), 572 deletions(-) create mode 100644 test/scudo/alignment.c delete mode 100644 test/scudo/alignment.cpp create mode 100644 test/scudo/memalign.c delete mode 100644 test/scudo/memalign.cpp create mode 100644 test/scudo/overflow.c delete mode 100644 test/scudo/overflow.cpp create mode 100644 test/scudo/preinit.c delete mode 100644 test/scudo/preinit.cpp create mode 100644 test/scudo/quarantine.c delete mode 100644 test/scudo/quarantine.cpp create mode 100644 test/scudo/secondary.c delete mode 100644 test/scudo/secondary.cpp create mode 100644 test/scudo/threads.c delete mode 100644 test/scudo/threads.cpp create mode 100644 test/scudo/tsd_destruction.c delete mode 100644 test/scudo/tsd_destruction.cpp create mode 100644 test/scudo/valloc.c delete mode 100644 test/scudo/valloc.cpp diff --git a/lib/scudo/CMakeLists.txt b/lib/scudo/CMakeLists.txt index 0d6314680..4d26a3477 100644 --- a/lib/scudo/CMakeLists.txt +++ b/lib/scudo/CMakeLists.txt @@ -12,12 +12,14 @@ set(SCUDO_SOURCES scudo_flags.cpp scudo_crc32.cpp scudo_interceptors.cpp - scudo_new_delete.cpp scudo_termination.cpp scudo_tsd_exclusive.cpp scudo_tsd_shared.cpp scudo_utils.cpp) +set(SCUDO_CXX_SOURCES + scudo_new_delete.cpp) + # Enable the SSE 4.2 instruction set for scudo_crc32.cpp, if available. if (COMPILER_RT_HAS_MSSE4_2_FLAG) set_source_files_properties(scudo_crc32.cpp PROPERTIES COMPILE_FLAGS -msse4.2) @@ -36,26 +38,35 @@ if(COMPILER_RT_HAS_SCUDO) append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread SCUDO_DYNAMIC_LIBS) append_list_if(COMPILER_RT_HAS_LIBLOG log SCUDO_DYNAMIC_LIBS) - foreach(arch ${SCUDO_SUPPORTED_ARCH}) - add_compiler_rt_runtime(clang_rt.scudo - STATIC - ARCHS ${arch} - SOURCES ${SCUDO_SOURCES} - OBJECT_LIBS RTSanitizerCommonNoTermination - RTSanitizerCommonLibc - RTInterception - CFLAGS ${SCUDO_CFLAGS} - PARENT_TARGET scudo) - - add_compiler_rt_runtime(clang_rt.scudo - SHARED - ARCHS ${arch} - SOURCES ${SCUDO_SOURCES} - OBJECT_LIBS RTSanitizerCommonNoTermination - RTSanitizerCommonLibc - RTInterception - CFLAGS ${SCUDO_CFLAGS} - LINK_LIBS ${SCUDO_DYNAMIC_LIBS} - PARENT_TARGET scudo) - endforeach() + add_compiler_rt_runtime(clang_rt.scudo + STATIC + ARCHS ${SCUDO_SUPPORTED_ARCH} + SOURCES ${SCUDO_SOURCES} + OBJECT_LIBS RTSanitizerCommonNoTermination + RTSanitizerCommonLibc + RTInterception + RTUbsan + CFLAGS ${SCUDO_CFLAGS} + PARENT_TARGET scudo) + + add_compiler_rt_runtime(clang_rt.scudo_cxx + STATIC + ARCHS ${SCUDO_SUPPORTED_ARCH} + SOURCES ${SCUDO_CXX_SOURCES} + OBJECT_LIBS RTUbsan_cxx + CFLAGS ${SCUDO_CFLAGS} + PARENT_TARGET scudo) + + add_compiler_rt_runtime(clang_rt.scudo + SHARED + ARCHS ${SCUDO_SUPPORTED_ARCH} + SOURCES ${SCUDO_SOURCES} ${SCUDO_CXX_SOURCES} + OBJECT_LIBS RTSanitizerCommonNoTermination + RTSanitizerCommonLibc + RTInterception + RTUbsan + RTUbsan_cxx + CFLAGS ${SCUDO_CFLAGS} + LINK_LIBS ${SCUDO_DYNAMIC_LIBS} + PARENT_TARGET scudo) endif() diff --git a/test/scudo/alignment.c b/test/scudo/alignment.c new file mode 100644 index 000000000..6235d5060 --- /dev/null +++ b/test/scudo/alignment.c @@ -0,0 +1,23 @@ +// RUN: %clang_scudo %s -o %t +// RUN: not %run %t pointers 2>&1 | FileCheck %s + +// Tests that a non MinAlignment aligned pointer will trigger the associated +// error on deallocation. + +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + assert(argc == 2); + if (!strcmp(argv[1], "pointers")) { + void *p = malloc(1U << 16); + assert(p); + free((void *)((uintptr_t)p | 1)); + } + return 0; +} + +// CHECK: ERROR: attempted to deallocate a chunk not properly aligned diff --git a/test/scudo/alignment.cpp b/test/scudo/alignment.cpp deleted file mode 100644 index 125ad8cbe..000000000 --- a/test/scudo/alignment.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// RUN: %clang_scudo %s -o %t -// RUN: not %run %t pointers 2>&1 | FileCheck %s - -// Tests that a non MinAlignment aligned pointer will trigger the associated -// error on deallocation. - -#include -#include -#include -#include - -int main(int argc, char **argv) -{ - assert(argc == 2); - if (!strcmp(argv[1], "pointers")) { - void *p = malloc(1U << 16); - assert(p); - free(reinterpret_cast(reinterpret_cast(p) | 1)); - } - return 0; -} - -// CHECK: ERROR: attempted to deallocate a chunk not properly aligned diff --git a/test/scudo/double-free.cpp b/test/scudo/double-free.cpp index ddc520505..56118038c 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 e9575adf3..16523bfe3 100644 --- a/test/scudo/interface.cpp +++ b/test/scudo/interface.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_scudo %s -lstdc++ -o %t +// 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 diff --git a/test/scudo/lit.cfg b/test/scudo/lit.cfg index f4b864777..0113bf0e7 100644 --- a/test/scudo/lit.cfg +++ b/test/scudo/lit.cfg @@ -11,21 +11,24 @@ config.test_source_root = os.path.dirname(__file__) # 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", "-Wl,--gc-sections"]) +cxx_flags = (c_flags + config.cxx_mode_flags + ["-std=c++11"]) # Android doesn't want -lrt. if not config.android: @@ -37,6 +40,7 @@ def build_invocation(compile_flags): # Add clang substitutions. 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. diff --git a/test/scudo/malloc.cpp b/test/scudo/malloc.cpp index 50e52590f..6c6a6c464 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.c b/test/scudo/memalign.c new file mode 100644 index 000000000..1fe6e3ec7 --- /dev/null +++ b/test/scudo/memalign.c @@ -0,0 +1,81 @@ +// 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 + +// Tests that the various aligned allocation functions work as intended. Also +// tests for the condition where the alignment is not a power of 2. + +#include +#include +#include +#include +#include +#include +#include + +// Sometimes the headers may not have this... +void *aligned_alloc(size_t alignment, size_t size); + +int main(int argc, char **argv) +{ + void *p = NULL; + size_t alignment = 1U << 12; + size_t size = 1U << 12; + int err; + + assert(argc == 2); + + 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++) { + alignment = 1U << i; + for (int j = 1; j < 33; j++) { + size = 0x800 * j; + 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); + } + } + } + // For larger alignment, reduce the number of allocations to avoid running + // out of potential addresses (on 32-bit). + for (int i = 19; i <= 24; i++) { + 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); + } + } + } + if (!strcmp(argv[1], "invalid")) { + // Alignment is not a power of 2. + p = memalign(alignment - 1, size); + assert(!p); + // Size is not a multiple of alignment. + p = aligned_alloc(alignment, size >> 1); + assert(!p); + void *p_unchanged = (void *)0x42UL; + p = p_unchanged; + // Alignment is not a power of 2. + err = posix_memalign(&p, 3, size); + assert(p == p_unchanged); + assert(err == EINVAL); + // Alignment is a power of 2, but not a multiple of size(void *). + err = posix_memalign(&p, 2, size); + assert(p == p_unchanged); + assert(err == EINVAL); + } + return 0; +} diff --git a/test/scudo/memalign.cpp b/test/scudo/memalign.cpp deleted file mode 100644 index fece04dfc..000000000 --- a/test/scudo/memalign.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// 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 - -// Tests that the various aligned allocation functions work as intended. Also -// tests for the condition where the alignment is not a power of 2. - -#include -#include -#include -#include -#include -#include -#include - -// Sometimes the headers may not have this... -extern "C" void *aligned_alloc(size_t alignment, size_t size); - -int main(int argc, char **argv) -{ - void *p = nullptr; - size_t alignment = 1U << 12; - size_t size = 1U << 12; - int err; - - assert(argc == 2); - - 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++) { - alignment = 1U << i; - for (int j = 1; j < 33; j++) { - size = 0x800 * j; - 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); - } - } - } - // For larger alignment, reduce the number of allocations to avoid running - // out of potential addresses (on 32-bit). - for (int i = 19; i <= 24; i++) { - 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); - } - } - } - if (!strcmp(argv[1], "invalid")) { - // Alignment is not a power of 2. - p = memalign(alignment - 1, size); - assert(!p); - // Size is not a multiple of alignment. - p = aligned_alloc(alignment, size >> 1); - assert(!p); - void *p_unchanged = (void *)0x42UL; - p = p_unchanged; - // Alignment is not a power of 2. - err = posix_memalign(&p, 3, size); - assert(p == p_unchanged); - assert(err == EINVAL); - // Alignment is a power of 2, but not a multiple of size(void *). - err = posix_memalign(&p, 2, size); - assert(p == p_unchanged); - assert(err == EINVAL); - } - return 0; -} diff --git a/test/scudo/mismatch.cpp b/test/scudo/mismatch.cpp index 538c8e6de..b49e0ea46 100644 --- a/test/scudo/mismatch.cpp +++ b/test/scudo/mismatch.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_scudo %s -o %t +// 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 diff --git a/test/scudo/options.cpp b/test/scudo/options.cpp index 6464bc65b..605b63241 100644 --- a/test/scudo/options.cpp +++ b/test/scudo/options.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_scudo %s -o %t +// 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 diff --git a/test/scudo/overflow.c b/test/scudo/overflow.c new file mode 100644 index 000000000..c5a58f87f --- /dev/null +++ b/test/scudo/overflow.c @@ -0,0 +1,39 @@ +// RUN: %clang_scudo %s -o %t +// 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. + +#include +#include +#include + +int main(int argc, char **argv) +{ + ssize_t offset = sizeof(void *) == 8 ? 8 : 0; + + assert(argc == 2); + + if (!strcmp(argv[1], "malloc")) { + // Simulate a header corruption of an allocated chunk (1-bit) + void *p = malloc(1U << 4); + assert(p); + ((char *)p)[-(offset + 1)] ^= 1; + free(p); + } + if (!strcmp(argv[1], "quarantine")) { + void *p = malloc(1U << 4); + assert(p); + free(p); + // Simulate a header corruption of a quarantined chunk + ((char *)p)[-(offset + 2)] ^= 1; + // Trigger the quarantine recycle + for (int i = 0; i < 0x100; i++) { + p = malloc(1U << 8); + free(p); + } + } + return 0; +} + +// CHECK: ERROR: corrupted chunk header at address diff --git a/test/scudo/overflow.cpp b/test/scudo/overflow.cpp deleted file mode 100644 index c5a58f87f..000000000 --- a/test/scudo/overflow.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// RUN: %clang_scudo %s -o %t -// 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. - -#include -#include -#include - -int main(int argc, char **argv) -{ - ssize_t offset = sizeof(void *) == 8 ? 8 : 0; - - assert(argc == 2); - - if (!strcmp(argv[1], "malloc")) { - // Simulate a header corruption of an allocated chunk (1-bit) - void *p = malloc(1U << 4); - assert(p); - ((char *)p)[-(offset + 1)] ^= 1; - free(p); - } - if (!strcmp(argv[1], "quarantine")) { - void *p = malloc(1U << 4); - assert(p); - free(p); - // Simulate a header corruption of a quarantined chunk - ((char *)p)[-(offset + 2)] ^= 1; - // Trigger the quarantine recycle - for (int i = 0; i < 0x100; i++) { - p = malloc(1U << 8); - free(p); - } - } - return 0; -} - -// CHECK: ERROR: corrupted chunk header at address diff --git a/test/scudo/preinit.c b/test/scudo/preinit.c new file mode 100644 index 000000000..792b2368e --- /dev/null +++ b/test/scudo/preinit.c @@ -0,0 +1,40 @@ +// RUN: %clang_scudo %s -o %t +// RUN: %run %t 2>&1 + +// 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 +#include +#include + +static void *global_p = NULL; + +void __init(void) { + global_p = malloc(1); + if (!global_p) + exit(1); +} + +void __fini(void) { + if (global_p) + free(global_p); +} + +int main(int argc, char **argv) +{ + void *p = malloc(1); + assert(p); + free(p); + + return 0; +} + +__attribute__((section(".preinit_array"), used)) + void (*__local_preinit)(void) = __init; +__attribute__((section(".fini_array"), used)) + void (*__local_fini)(void) = __fini; diff --git a/test/scudo/preinit.cpp b/test/scudo/preinit.cpp deleted file mode 100644 index f904c6c2d..000000000 --- a/test/scudo/preinit.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// RUN: %clang_scudo %s -o %t -// RUN: %run %t 2>&1 - -// 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 -#include -#include - -static void *global_p = nullptr; - -void __init(void) { - global_p = malloc(1); - if (!global_p) - exit(1); -} - -void __fini(void) { - if (global_p) - free(global_p); -} - -int main(int argc, char **argv) -{ - void *p = malloc(1); - assert(p); - free(p); - - return 0; -} - -__attribute__((section(".preinit_array"), used)) - void (*__local_preinit)(void) = __init; -__attribute__((section(".fini_array"), used)) - void (*__local_fini)(void) = __fini; diff --git a/test/scudo/preload.cpp b/test/scudo/preload.cpp index 0da507b3c..b41a70e47 100644 --- a/test/scudo/preload.cpp +++ b/test/scudo/preload.cpp @@ -1,19 +1,19 @@ // Test that the preloaded runtime works without linking the static library. -// RUN: %clang %s -o %t +// 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 -#include int main(int argc, char *argv[]) { - void *p = malloc(sizeof(int)); + int *p = new int; assert(p); - free(p); - free(p); + *p = 0; + delete p; + delete p; return 0; } diff --git a/test/scudo/quarantine.c b/test/scudo/quarantine.c new file mode 100644 index 000000000..ddbb92005 --- /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 +#include +#include +#include + +#include + +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 8872fe688..000000000 --- a/test/scudo/quarantine.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// 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 -#include -#include -#include - -#include - -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. - bool found = false; - 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/random_shuffle.cpp b/test/scudo/random_shuffle.cpp index 54768a578..f886cb150 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 diff --git a/test/scudo/realloc.cpp b/test/scudo/realloc.cpp index 01149670e..254c67a2c 100644 --- a/test/scudo/realloc.cpp +++ b/test/scudo/realloc.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_scudo %s -lstdc++ -o %t +// 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 diff --git a/test/scudo/secondary.c b/test/scudo/secondary.c new file mode 100644 index 000000000..b770ca076 --- /dev/null +++ b/test/scudo/secondary.c @@ -0,0 +1,53 @@ +// RUN: %clang_scudo %s -o %t +// RUN: %run %t after 2>&1 | FileCheck %s +// RUN: %run %t before 2>&1 | FileCheck %s + +// Test that we hit a guard page when writing past the end of a chunk +// allocated by the Secondary allocator, or writing too far in front of it. + +#include +#include +#include +#include +#include +#include + +void handler(int signo, siginfo_t *info, void *uctx) { + if (info->si_code == SEGV_ACCERR) { + fprintf(stderr, "SCUDO SIGSEGV\n"); + exit(0); + } + exit(1); +} + +int main(int argc, char **argv) +{ + // The size must be large enough to be serviced by the secondary allocator. + long page_size = sysconf(_SC_PAGESIZE); + size_t size = (1U << 17) + page_size; + struct sigaction a; + + assert(argc == 2); + memset(&a, 0, sizeof(a)); + a.sa_sigaction = handler; + a.sa_flags = SA_SIGINFO; + + char *p = (char *)malloc(size); + 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, NULL); + if (!strcmp(argv[1], "after")) { + for (int i = 0; i < page_size; i++) + p[size + i] = 'A'; + } + if (!strcmp(argv[1], "before")) { + for (int i = 1; i < page_size; i++) + p[-i] = 'A'; + } + free(p); + + return 1; // A successful test means we shouldn't reach this. +} + +// CHECK: SCUDO SIGSEGV diff --git a/test/scudo/secondary.cpp b/test/scudo/secondary.cpp deleted file mode 100644 index dc14f8ca8..000000000 --- a/test/scudo/secondary.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// RUN: %clang_scudo %s -o %t -// RUN: %run %t after 2>&1 | FileCheck %s -// RUN: %run %t before 2>&1 | FileCheck %s - -// Test that we hit a guard page when writing past the end of a chunk -// allocated by the Secondary allocator, or writing too far in front of it. - -#include -#include -#include -#include -#include -#include - -void handler(int signo, siginfo_t *info, void *uctx) { - if (info->si_code == SEGV_ACCERR) { - fprintf(stderr, "SCUDO SIGSEGV\n"); - exit(0); - } - exit(1); -} - -int main(int argc, char **argv) -{ - // The size must be large enough to be serviced by the secondary allocator. - long page_size = sysconf(_SC_PAGESIZE); - size_t size = (1U << 17) + page_size; - struct sigaction a; - - assert(argc == 2); - memset(&a, 0, sizeof(a)); - a.sa_sigaction = handler; - a.sa_flags = SA_SIGINFO; - - char *p = (char *)malloc(size); - 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); - if (!strcmp(argv[1], "after")) { - for (int i = 0; i < page_size; i++) - p[size + i] = 'A'; - } - if (!strcmp(argv[1], "before")) { - for (int i = 1; i < page_size; i++) - p[-i] = 'A'; - } - free(p); - - return 1; // A successful test means we shouldn't reach this. -} - -// CHECK: SCUDO SIGSEGV diff --git a/test/scudo/sized-delete.cpp b/test/scudo/sized-delete.cpp index 9c3a2c596..85df05e2f 100644 --- a/test/scudo/sized-delete.cpp +++ b/test/scudo/sized-delete.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_scudo -fsized-deallocation %s -o %t +// 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 diff --git a/test/scudo/sizes.cpp b/test/scudo/sizes.cpp index 8f147b708..73fc71f25 100644 --- a/test/scudo/sizes.cpp +++ b/test/scudo/sizes.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_scudo %s -lstdc++ -o %t +// 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 diff --git a/test/scudo/threads.c b/test/scudo/threads.c new file mode 100644 index 000000000..b34e6f0f7 --- /dev/null +++ b/test/scudo/threads.c @@ -0,0 +1,65 @@ +// RUN: %clang_scudo %s -o %t +// 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. +// This test passes if everything executes properly without crashing. + +#include +#include +#include +#include + +#include + +int num_threads; +int total_num_alloc; +const int kMaxNumThreads = 500; +pthread_t tid[kMaxNumThreads]; + +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +char go = 0; + +void *thread_fun(void *arg) { + pthread_mutex_lock(&mutex); + while (!go) pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + for (int i = 0; i < total_num_alloc / num_threads; i++) { + void *p = malloc(10); + __asm__ __volatile__("" : : "r"(p) : "memory"); + free(p); + } + return 0; +} + +int main(int argc, char** argv) { + assert(argc == 3); + num_threads = atoi(argv[1]); + assert(num_threads > 0); + assert(num_threads <= kMaxNumThreads); + total_num_alloc = atoi(argv[2]); + assert(total_num_alloc > 0); + + printf("%d threads, %d allocations in each\n", num_threads, + total_num_alloc / num_threads); + fprintf(stderr, "Heap size before: %zd\n", __sanitizer_get_heap_size()); + fprintf(stderr, "Allocated bytes before: %zd\n", + __sanitizer_get_current_allocated_bytes()); + + for (int i = 0; i < num_threads; i++) + pthread_create(&tid[i], 0, thread_fun, 0); + pthread_mutex_lock(&mutex); + go = 1; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); + for (int i = 0; i < num_threads; i++) + pthread_join(tid[i], 0); + + fprintf(stderr, "Heap size after: %zd\n", __sanitizer_get_heap_size()); + fprintf(stderr, "Allocated bytes after: %zd\n", + __sanitizer_get_current_allocated_bytes()); + + return 0; +} diff --git a/test/scudo/threads.cpp b/test/scudo/threads.cpp deleted file mode 100644 index 643eda77d..000000000 --- a/test/scudo/threads.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// RUN: %clang_scudo %s -o %t -// 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. -// This test passes if everything executes properly without crashing. - -#include -#include -#include -#include - -#include - -int num_threads; -int total_num_alloc; -const int kMaxNumThreads = 500; -pthread_t tid[kMaxNumThreads]; - -pthread_cond_t cond = PTHREAD_COND_INITIALIZER; -pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -bool go = false; - -void *thread_fun(void *arg) { - pthread_mutex_lock(&mutex); - while (!go) pthread_cond_wait(&cond, &mutex); - pthread_mutex_unlock(&mutex); - for (int i = 0; i < total_num_alloc / num_threads; i++) { - void *p = malloc(10); - __asm__ __volatile__("" : : "r"(p) : "memory"); - free(p); - } - return 0; -} - -int main(int argc, char** argv) { - assert(argc == 3); - num_threads = atoi(argv[1]); - assert(num_threads > 0); - assert(num_threads <= kMaxNumThreads); - total_num_alloc = atoi(argv[2]); - assert(total_num_alloc > 0); - - printf("%d threads, %d allocations in each\n", num_threads, - total_num_alloc / num_threads); - fprintf(stderr, "Heap size before: %zd\n", __sanitizer_get_heap_size()); - fprintf(stderr, "Allocated bytes before: %zd\n", - __sanitizer_get_current_allocated_bytes()); - - for (int i = 0; i < num_threads; i++) - pthread_create(&tid[i], 0, thread_fun, 0); - pthread_mutex_lock(&mutex); - go = true; - pthread_cond_broadcast(&cond); - pthread_mutex_unlock(&mutex); - for (int i = 0; i < num_threads; i++) - pthread_join(tid[i], 0); - - fprintf(stderr, "Heap size after: %zd\n", __sanitizer_get_heap_size()); - fprintf(stderr, "Allocated bytes after: %zd\n", - __sanitizer_get_current_allocated_bytes()); - - return 0; -} diff --git a/test/scudo/tsd_destruction.c b/test/scudo/tsd_destruction.c new file mode 100644 index 000000000..1b0d0eff9 --- /dev/null +++ b/test/scudo/tsd_destruction.c @@ -0,0 +1,42 @@ +// RUN: %clang_scudo %s -o %t +// RUN: %run %t 2>&1 + +#include +#include +#include +#include +#include + +// 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/tsd_destruction.cpp b/test/scudo/tsd_destruction.cpp deleted file mode 100644 index 1b0d0eff9..000000000 --- a/test/scudo/tsd_destruction.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// RUN: %clang_scudo %s -o %t -// RUN: %run %t 2>&1 - -#include -#include -#include -#include -#include - -// 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 000000000..132c4f280 --- /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 +#include +#include +#include +#include +#include + +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; +} diff --git a/test/scudo/valloc.cpp b/test/scudo/valloc.cpp deleted file mode 100644 index 514a88449..000000000 --- a/test/scudo/valloc.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// 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 -#include -#include -#include -#include -#include - -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 = nullptr; - 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; -} -- cgit v1.2.1