Index: lib/scudo/scudo_allocator.cpp =================================================================== --- lib/scudo/scudo_allocator.cpp +++ lib/scudo/scudo_allocator.cpp @@ -354,6 +354,8 @@ // Helper function that checks for a valid Scudo chunk. bool isValidPointer(const void *UserPtr) { + if (UNLIKELY(!ThreadInited)) + initThread(); uptr ChunkBeg = reinterpret_cast(UserPtr); if (!IsAligned(ChunkBeg, MinAlignment)) { return false; @@ -580,6 +582,14 @@ 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 @@ // 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() { Index: lib/scudo/scudo_flags.cpp =================================================================== --- lib/scudo/scudo_flags.cpp +++ lib/scudo/scudo_flags.cpp @@ -68,7 +68,7 @@ // 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 @@ 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 @@ 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() { Index: lib/scudo/scudo_flags.inc =================================================================== --- lib/scudo/scudo_flags.inc +++ 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.") Index: test/scudo/interface.cpp =================================================================== --- test/scudo/interface.cpp +++ 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 +#include +#include #include @@ -11,18 +15,38 @@ int main(int argc, char **argv) { - void *p; - std::vector 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)) + 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. + if (__sanitizer_get_ownership(argv)) return 1; - if (__sanitizer_get_allocated_size(p) < size) + } + 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 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); + } + } + 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. + if (__sanitizer_get_heap_size() != 0) return 1; - free(p); } + return 0; } Index: test/scudo/lit.cfg =================================================================== --- test/scudo/lit.cfg +++ test/scudo/lit.cfg @@ -19,12 +19,11 @@ # C flags. c_flags = ([config.target_cflags] + ["-std=c++11", - "-lrt", - "-ldl", "-pthread", "-fPIE", "-pie", - "-O0"]) + "-O0", + "-Wl,--gc-sections"]) def build_invocation(compile_flags): return " " + " ".join([config.clang] + compile_flags) + " " Index: test/scudo/quarantine.cpp =================================================================== --- test/scudo/quarantine.cpp +++ test/scudo/quarantine.cpp @@ -1,5 +1,6 @@ // 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 @@ -8,36 +9,57 @@ #include #include #include +#include + +#include int main(int argc, char **argv) { void *p, *old_p; - size_t size = 1U << 16; + size_t allocated_bytes, size = 1U << 16; + + assert(argc == 2); - // 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++) { + 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; + if (__sanitizer_get_current_allocated_bytes() <= allocated_bytes) + return 1; + free(p); + if (__sanitizer_get_current_allocated_bytes() != allocated_bytes) + return 1; + } + if (!strcmp(argv[1], "smallquarantine")) { + // 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; - found = (p == old_p); + 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++) { + p = malloc(size); + if (!p) + return 1; + found = (p == old_p); + free(p); + } + if (found == false) + return 1; } - if (found == false) - return 1; return 0; }