summaryrefslogtreecommitdiff
path: root/test/scudo
diff options
context:
space:
mode:
Diffstat (limited to 'test/scudo')
-rw-r--r--test/scudo/CMakeLists.txt10
-rw-r--r--test/scudo/alignment.c (renamed from test/scudo/alignment.cpp)2
-rw-r--r--test/scudo/double-free.cpp2
-rw-r--r--test/scudo/interface.cpp48
-rw-r--r--test/scudo/lit.cfg43
-rw-r--r--test/scudo/malloc.cpp2
-rw-r--r--test/scudo/memalign.c (renamed from test/scudo/memalign.cpp)21
-rw-r--r--test/scudo/mismatch.cpp25
-rw-r--r--test/scudo/options.cpp10
-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.cpp20
-rw-r--r--test/scudo/quarantine.c124
-rw-r--r--test/scudo/quarantine.cpp57
-rw-r--r--test/scudo/random_shuffle.cpp3
-rw-r--r--test/scudo/realloc.cpp104
-rw-r--r--test/scudo/rss.c56
-rw-r--r--test/scudo/secondary.c (renamed from test/scudo/secondary.cpp)2
-rw-r--r--test/scudo/sized-delete.cpp14
-rw-r--r--test/scudo/sizes.cpp20
-rw-r--r--test/scudo/threads.c (renamed from test/scudo/threads.cpp)8
-rw-r--r--test/scudo/tsd_destruction.c42
-rw-r--r--test/scudo/valloc.c65
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;
+}