summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/scudo/scudo_allocator.cpp18
-rw-r--r--lib/scudo/scudo_flags.cpp9
-rw-r--r--lib/scudo/scudo_flags.inc6
-rw-r--r--test/scudo/alignment.cpp3
-rw-r--r--test/scudo/double-free.cpp12
-rw-r--r--test/scudo/interface.cpp45
-rw-r--r--test/scudo/lit.cfg6
-rw-r--r--test/scudo/malloc.cpp7
-rw-r--r--test/scudo/memalign.cpp12
-rw-r--r--test/scudo/mismatch.cpp11
-rw-r--r--test/scudo/options.cpp6
-rw-r--r--test/scudo/overflow.cpp10
-rw-r--r--test/scudo/preinit.cpp5
-rw-r--r--test/scudo/quarantine.cpp62
-rw-r--r--test/scudo/realloc.cpp25
-rw-r--r--test/scudo/secondary.cpp7
-rw-r--r--test/scudo/sized-delete.cpp3
-rw-r--r--test/scudo/sizes.cpp21
18 files changed, 143 insertions, 125 deletions
diff --git a/lib/scudo/scudo_allocator.cpp b/lib/scudo/scudo_allocator.cpp
index a6d6aafe3..dab6abedc 100644
--- a/lib/scudo/scudo_allocator.cpp
+++ b/lib/scudo/scudo_allocator.cpp
@@ -354,6 +354,8 @@ struct Allocator {
// Helper function that checks for a valid Scudo chunk.
bool isValidPointer(const void *UserPtr) {
+ if (UNLIKELY(!ThreadInited))
+ initThread();
uptr ChunkBeg = reinterpret_cast<uptr>(UserPtr);
if (!IsAligned(ChunkBeg, MinAlignment)) {
return false;
@@ -580,6 +582,14 @@ struct Allocator {
AllocatorQuarantine.Drain(&ThreadQuarantineCache,
QuarantineCallback(&Cache));
}
+
+ uptr getStats(AllocatorStat StatType) {
+ if (UNLIKELY(!ThreadInited))
+ initThread();
+ uptr stats[AllocatorStatCount];
+ BackendAllocator.GetStats(stats);
+ return stats[StatType];
+ }
};
static Allocator Instance(LINKER_INITIALIZED);
@@ -664,15 +674,11 @@ using namespace __scudo;
// MallocExtension helper functions
uptr __sanitizer_get_current_allocated_bytes() {
- uptr stats[AllocatorStatCount];
- getAllocator().GetStats(stats);
- return stats[AllocatorStatAllocated];
+ return Instance.getStats(AllocatorStatAllocated);
}
uptr __sanitizer_get_heap_size() {
- uptr stats[AllocatorStatCount];
- getAllocator().GetStats(stats);
- return stats[AllocatorStatMapped];
+ return Instance.getStats(AllocatorStatMapped);
}
uptr __sanitizer_get_free_bytes() {
diff --git a/lib/scudo/scudo_flags.cpp b/lib/scudo/scudo_flags.cpp
index b9c838107..64da1d9d8 100644
--- a/lib/scudo/scudo_flags.cpp
+++ b/lib/scudo/scudo_flags.cpp
@@ -68,7 +68,7 @@ void initFlags() {
// Sanity checks and default settings for the Quarantine parameters.
if (f->QuarantineSizeMb < 0) {
- const int DefaultQuarantineSizeMb = 64;
+ const int DefaultQuarantineSizeMb = FIRST_32_SECOND_64(16, 64);
f->QuarantineSizeMb = DefaultQuarantineSizeMb;
}
// We enforce an upper limit for the quarantine size of 4Gb.
@@ -76,7 +76,8 @@ void initFlags() {
dieWithMessage("ERROR: the quarantine size is too large\n");
}
if (f->ThreadLocalQuarantineSizeKb < 0) {
- const int DefaultThreadLocalQuarantineSizeKb = 1024;
+ const int DefaultThreadLocalQuarantineSizeKb =
+ FIRST_32_SECOND_64(256, 1024);
f->ThreadLocalQuarantineSizeKb = DefaultThreadLocalQuarantineSizeKb;
}
// And an upper limit of 128Mb for the thread quarantine cache.
@@ -84,6 +85,10 @@ void initFlags() {
dieWithMessage("ERROR: the per thread quarantine cache size is too "
"large\n");
}
+ if (f->ThreadLocalQuarantineSizeKb == 0 && f->QuarantineSizeMb > 0) {
+ dieWithMessage("ERROR: ThreadLocalQuarantineSizeKb can be set to 0 only "
+ "when QuarantineSizeMb is set to 0\n");
+ }
}
Flags *getFlags() {
diff --git a/lib/scudo/scudo_flags.inc b/lib/scudo/scudo_flags.inc
index c7a2acf14..45f9ea846 100644
--- a/lib/scudo/scudo_flags.inc
+++ b/lib/scudo/scudo_flags.inc
@@ -15,12 +15,14 @@
# error "Define SCUDO_FLAG prior to including this file!"
#endif
-SCUDO_FLAG(int, QuarantineSizeMb, 64,
+// Default value is set in scudo_flags.cpp based on architecture.
+SCUDO_FLAG(int, QuarantineSizeMb, -1,
"Size (in Mb) of quarantine used to delay the actual deallocation "
"of chunks. Lower value may reduce memory usage but decrease the "
"effectiveness of the mitigation.")
-SCUDO_FLAG(int, ThreadLocalQuarantineSizeKb, 1024,
+// Default value is set in scudo_flags.cpp based on architecture.
+SCUDO_FLAG(int, ThreadLocalQuarantineSizeKb, -1,
"Size (in Kb) of per-thread cache used to offload the global "
"quarantine. Lower value may reduce memory usage but might increase "
"the contention on the global quarantine.")
diff --git a/test/scudo/alignment.cpp b/test/scudo/alignment.cpp
index a6eca87a8..125ad8cbe 100644
--- a/test/scudo/alignment.cpp
+++ b/test/scudo/alignment.cpp
@@ -14,8 +14,7 @@ int main(int argc, char **argv)
assert(argc == 2);
if (!strcmp(argv[1], "pointers")) {
void *p = malloc(1U << 16);
- if (!p)
- return 1;
+ assert(p);
free(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(p) | 1));
}
return 0;
diff --git a/test/scudo/double-free.cpp b/test/scudo/double-free.cpp
index 75919f0c4..ddc520505 100644
--- a/test/scudo/double-free.cpp
+++ b/test/scudo/double-free.cpp
@@ -16,30 +16,26 @@ int main(int argc, char **argv)
assert(argc == 2);
if (!strcmp(argv[1], "malloc")) {
void *p = malloc(sizeof(int));
- if (!p)
- return 1;
+ assert(p);
free(p);
free(p);
}
if (!strcmp(argv[1], "new")) {
int *p = new int;
- if (!p)
- return 1;
+ assert(p);
delete p;
delete p;
}
if (!strcmp(argv[1], "newarray")) {
int *p = new int[8];
- if (!p)
- return 1;
+ assert(p);
delete[] p;
delete[] p;
}
if (!strcmp(argv[1], "memalign")) {
void *p = nullptr;
posix_memalign(&p, 0x100, sizeof(int));
- if (!p)
- return 1;
+ assert(p);
free(p);
free(p);
}
diff --git a/test/scudo/interface.cpp b/test/scudo/interface.cpp
index 55de17432..c5a887780 100644
--- a/test/scudo/interface.cpp
+++ b/test/scudo/interface.cpp
@@ -1,9 +1,13 @@
// RUN: %clang_scudo %s -lstdc++ -o %t
-// RUN: %run %t 2>&1
+// RUN: %run %t ownership 2>&1
+// RUN: %run %t ownership-and-size 2>&1
+// RUN: %run %t heap-size 2>&1
// Tests that the sanitizer interface functions behave appropriately.
#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
#include <vector>
@@ -11,18 +15,33 @@
int main(int argc, char **argv)
{
- void *p;
- std::vector<ssize_t> sizes{1, 8, 16, 32, 1024, 32768,
- 1 << 16, 1 << 17, 1 << 20, 1 << 24};
- for (size_t size : sizes) {
- p = malloc(size);
- if (!p)
- return 1;
- if (!__sanitizer_get_ownership(p))
- return 1;
- if (__sanitizer_get_allocated_size(p) < size)
- return 1;
- free(p);
+ assert(argc == 2);
+
+ if (!strcmp(argv[1], "ownership")) {
+ // Ensures that __sanitizer_get_ownership can be called before any other
+ // allocator function, and that it behaves properly on a pointer not owned
+ // by us.
+ assert(!__sanitizer_get_ownership(argv));
+ }
+ if (!strcmp(argv[1], "ownership-and-size")) {
+ // Tests that __sanitizer_get_ownership and __sanitizer_get_allocated_size
+ // behave properly on chunks allocated by the Primary and Secondary.
+ void *p;
+ std::vector<ssize_t> sizes{1, 8, 16, 32, 1024, 32768,
+ 1 << 16, 1 << 17, 1 << 20, 1 << 24};
+ for (size_t size : sizes) {
+ p = malloc(size);
+ assert(p);
+ assert(__sanitizer_get_ownership(p));
+ assert(__sanitizer_get_allocated_size(p) >= size);
+ free(p);
+ }
}
+ if (!strcmp(argv[1], "heap-size")) {
+ // Ensures that __sanitizer_get_heap_size can be called before any other
+ // allocator function. At this point, this heap size should be 0.
+ assert(__sanitizer_get_heap_size() == 0);
+ }
+
return 0;
}
diff --git a/test/scudo/lit.cfg b/test/scudo/lit.cfg
index b0476303c..0fa44087e 100644
--- a/test/scudo/lit.cfg
+++ b/test/scudo/lit.cfg
@@ -19,12 +19,12 @@ config.suffixes = ['.c', '.cc', '.cpp']
# C flags.
c_flags = ([config.target_cflags] +
["-std=c++11",
- "-lrt",
- "-ldl",
"-pthread",
"-fPIE",
"-pie",
- "-O0"])
+ "-O0",
+ "-UNDEBUG",
+ "-Wl,--gc-sections"])
def build_invocation(compile_flags):
return " " + " ".join([config.clang] + compile_flags) + " "
diff --git a/test/scudo/malloc.cpp b/test/scudo/malloc.cpp
index fbe57a8cb..50e52590f 100644
--- a/test/scudo/malloc.cpp
+++ b/test/scudo/malloc.cpp
@@ -5,6 +5,7 @@
// intended. Tests various sizes serviced by the primary and secondary
// allocators.
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -18,8 +19,7 @@ int main(int argc, char **argv)
std::vector<int> offsets{1, 0, -1, -7, -8, -15, -16, -31, -32};
p = malloc(0);
- if (!p)
- return 1;
+ assert(p);
free(p);
for (ssize_t size : sizes) {
for (int offset: offsets) {
@@ -27,8 +27,7 @@ int main(int argc, char **argv)
if (actual_size <= 0)
continue;
p = malloc(actual_size);
- if (!p)
- return 1;
+ assert(p);
memset(p, 0xff, actual_size);
free(p);
}
diff --git a/test/scudo/memalign.cpp b/test/scudo/memalign.cpp
index b407ec574..c263da75e 100644
--- a/test/scudo/memalign.cpp
+++ b/test/scudo/memalign.cpp
@@ -29,12 +29,10 @@ int main(int argc, char **argv)
if (!strcmp(argv[1], "valid")) {
posix_memalign(&p, alignment, size);
- if (!p)
- return 1;
+ assert(p);
free(p);
p = aligned_alloc(alignment, size);
- if (!p)
- return 1;
+ assert(p);
free(p);
// Tests various combinations of alignment and sizes
for (int i = (sizeof(void *) == 4) ? 3 : 4; i < 19; i++) {
@@ -43,8 +41,7 @@ int main(int argc, char **argv)
size = 0x800 * j;
for (int k = 0; k < 3; k++) {
p = memalign(alignment, size - (2 * sizeof(void *) * k));
- if (!p)
- return 1;
+ assert(p);
free(p);
}
}
@@ -54,8 +51,7 @@ int main(int argc, char **argv)
for (int i = 19; i <= 24; i++) {
for (int k = 0; k < 3; k++) {
p = memalign(alignment, 0x1000 - (2 * sizeof(void *) * k));
- if (!p)
- return 1;
+ assert(p);
free(p);
}
}
diff --git a/test/scudo/mismatch.cpp b/test/scudo/mismatch.cpp
index 54cdafc86..15dce83ce 100644
--- a/test/scudo/mismatch.cpp
+++ b/test/scudo/mismatch.cpp
@@ -10,29 +10,26 @@
// caught when the related option is set.
#include <assert.h>
+#include <malloc.h>
#include <stdlib.h>
#include <string.h>
-#include <malloc.h>
int main(int argc, char **argv)
{
assert(argc == 2);
if (!strcmp(argv[1], "mallocdel")) {
int *p = (int *)malloc(16);
- if (!p)
- return 1;
+ assert(p);
delete p;
}
if (!strcmp(argv[1], "newfree")) {
int *p = new int;
- if (!p)
- return 1;
+ assert(p);
free((void *)p);
}
if (!strcmp(argv[1], "memaligndel")) {
int *p = (int *)memalign(16, 16);
- if (!p)
- return 1;
+ assert(p);
delete p;
}
return 0;
diff --git a/test/scudo/options.cpp b/test/scudo/options.cpp
index bccf7c8fb..f4afe7d79 100644
--- a/test/scudo/options.cpp
+++ b/test/scudo/options.cpp
@@ -6,8 +6,9 @@
// Tests that the options can be passed using getScudoDefaultOptions, and that
// the environment ones take precedence over them.
-#include <stdlib.h>
+#include <assert.h>
#include <malloc.h>
+#include <stdlib.h>
extern "C" const char* __scudo_default_options() {
return "DeallocationTypeMismatch=0"; // Defaults to true in scudo_flags.inc.
@@ -16,8 +17,7 @@ extern "C" const char* __scudo_default_options() {
int main(int argc, char **argv)
{
int *p = (int *)malloc(16);
- if (!p)
- return 1;
+ assert(p);
delete p;
return 0;
}
diff --git a/test/scudo/overflow.cpp b/test/scudo/overflow.cpp
index c93a544ea..d12824578 100644
--- a/test/scudo/overflow.cpp
+++ b/test/scudo/overflow.cpp
@@ -10,20 +10,20 @@
int main(int argc, char **argv)
{
- assert(argc == 2);
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);
- if (!p)
- return 1;
+ assert(p);
((char *)p)[-(offset + 1)] ^= 1;
free(p);
}
if (!strcmp(argv[1], "quarantine")) {
void *p = malloc(1U << 4);
- if (!p)
- return 1;
+ assert(p);
free(p);
// Simulate a header corruption of a quarantined chunk
((char *)p)[-(offset + 2)] ^= 1;
diff --git a/test/scudo/preinit.cpp b/test/scudo/preinit.cpp
index 34f61c9dd..b8c01a401 100644
--- a/test/scudo/preinit.cpp
+++ b/test/scudo/preinit.cpp
@@ -4,6 +4,7 @@
// Verifies that calling malloc in a preinit_array function succeeds, and that
// the resulting pointer can be freed at program termination.
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -23,8 +24,7 @@ void __fini(void) {
int main(int argc, char **argv)
{
void *p = malloc(1);
- if (!p)
- return 1;
+ assert(p);
free(p);
return 0;
@@ -34,4 +34,3 @@ __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/quarantine.cpp b/test/scudo/quarantine.cpp
index 4ce0197ac..39ce1bd91 100644
--- a/test/scudo/quarantine.cpp
+++ b/test/scudo/quarantine.cpp
@@ -1,43 +1,57 @@
// RUN: %clang_scudo %s -o %t
-// RUN: SCUDO_OPTIONS=QuarantineSizeMb=1 %run %t 2>&1
+// 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 size = 1U << 16;
-
- // The delayed freelist will prevent a chunk from being available right away
- p = malloc(size);
- if (!p)
- return 1;
- old_p = p;
- free(p);
- p = malloc(size);
- if (!p)
- return 1;
- if (old_p == p)
- return 1;
- free(p);
-
- // Eventually the chunk should become available again
- bool found = false;
- for (int i = 0; i < 0x100 && found == false; i++) {
+ 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);
- if (!p)
- return 1;
- found = (p == old_p);
+ 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);
}
- if (found == false)
- return 1;
return 0;
}
diff --git a/test/scudo/realloc.cpp b/test/scudo/realloc.cpp
index d34e356fb..da377205f 100644
--- a/test/scudo/realloc.cpp
+++ b/test/scudo/realloc.cpp
@@ -23,48 +23,41 @@ int main(int argc, char **argv)
std::vector<size_t> sizes{1, 16, 1024, 32768, 1 << 16, 1 << 17, 1 << 20};
assert(argc == 2);
+
for (size_t size : sizes) {
if (!strcmp(argv[1], "pointers")) {
old_p = p = realloc(nullptr, size);
- if (!p)
- return 1;
+ 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);
- if (p != old_p)
- return 1;
+ assert(p == old_p);
p = realloc(p, size);
- if (p != old_p)
- return 1;
+ assert(p == old_p);
// And a new one if the size is greater.
p = realloc(p, size + 1);
- if (p == old_p)
- return 1;
+ assert(p != old_p);
// A size of 0 will free the chunk and return nullptr.
p = realloc(p, 0);
- if (p)
- return 1;
+ assert(!p);
old_p = nullptr;
}
if (!strcmp(argv[1], "contents")) {
p = realloc(nullptr, size);
- if (!p)
- return 1;
+ 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++)
- if (reinterpret_cast<char *>(p)[i] != 'A')
- return 1;
+ assert(reinterpret_cast<char *>(p)[i] == 'A');
}
if (!strcmp(argv[1], "memalign")) {
// A chunk coming from memalign cannot be reallocated.
p = memalign(16, size);
- if (!p)
- return 1;
+ assert(p);
p = realloc(p, size);
free(p);
}
diff --git a/test/scudo/secondary.cpp b/test/scudo/secondary.cpp
index 7a634a81e..dc14f8ca8 100644
--- a/test/scudo/secondary.cpp
+++ b/test/scudo/secondary.cpp
@@ -5,12 +5,12 @@
// 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 <assert.h>
#include <malloc.h>
+#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <signal.h>
-#include <assert.h>
void handler(int signo, siginfo_t *info, void *uctx) {
if (info->si_code == SEGV_ACCERR) {
@@ -33,8 +33,7 @@ int main(int argc, char **argv)
a.sa_flags = SA_SIGINFO;
char *p = (char *)malloc(size);
- if (!p)
- return 1;
+ 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);
diff --git a/test/scudo/sized-delete.cpp b/test/scudo/sized-delete.cpp
index 5b1bf5fd4..e467f5565 100644
--- a/test/scudo/sized-delete.cpp
+++ b/test/scudo/sized-delete.cpp
@@ -10,11 +10,12 @@
// option is passed and the sizes do not match between allocation and
// deallocation functions.
-#include <new>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
+#include <new>
+
int main(int argc, char **argv)
{
assert(argc == 2);
diff --git a/test/scudo/sizes.cpp b/test/scudo/sizes.cpp
index 7190cb64f..981b859a8 100644
--- a/test/scudo/sizes.cpp
+++ b/test/scudo/sizes.cpp
@@ -23,35 +23,28 @@ int main(int argc, char **argv)
// Currently the maximum size the allocator can allocate is 1ULL<<40 bytes.
size_t size = std::numeric_limits<size_t>::max();
void *p = malloc(size);
- if (p)
- return 1;
+ assert(!p);
size = (1ULL << 40) - 16;
p = malloc(size);
- if (p)
- return 1;
+ assert(!p);
}
if (!strcmp(argv[1], "calloc")) {
// Trigger an overflow in calloc.
size_t size = std::numeric_limits<size_t>::max();
void *p = calloc((size / 0x1000) + 1, 0x1000);
- if (p)
- return 1;
+ assert(!p);
}
if (!strcmp(argv[1], "usable")) {
// Playing with the actual usable size of a chunk.
void *p = malloc(1007);
- if (!p)
- return 1;
+ assert(p);
size_t size = malloc_usable_size(p);
- if (size < 1007)
- return 1;
+ assert(size >= 1007);
memset(p, 'A', size);
p = realloc(p, 2014);
- if (!p)
- return 1;
+ assert(p);
size = malloc_usable_size(p);
- if (size < 2014)
- return 1;
+ assert(size >= 2014);
memset(p, 'B', size);
free(p);
}