Skip to content

Commit

Permalink
[scudo] 32-bit quarantine sizes adjustments and bug fixes
Browse files Browse the repository at this point in the history
Summary:
The local and global quarantine sizes were not offering a distinction for
32-bit and 64-bit platforms. This is addressed with lower values for 32-bit.

When writing additional tests for the quarantine, it was discovered that when
calling some of the allocator interface function prior to any allocation
operation having occured, the test would crash due to the allocator not being
initialized. This was addressed by making sure the allocator is initialized
for those scenarios.

Relevant tests were added in interface.cpp and quarantine.cpp.

Last change being the removal of the extraneous link dependencies for the
tests thanks to rL293220, anf the addition of the gc-sections linker flag.

Reviewers: kcc, alekseyshl

Reviewed By: alekseyshl

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D29341

llvm-svn: 294037
  • Loading branch information
Kostya Kortchinsky committed Feb 3, 2017
1 parent 90505a0 commit 8d6257b
Show file tree
Hide file tree
Showing 18 changed files with 143 additions and 125 deletions.
18 changes: 12 additions & 6 deletions compiler-rt/lib/scudo/scudo_allocator.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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() {
Expand Down
9 changes: 7 additions & 2 deletions compiler-rt/lib/scudo/scudo_flags.cpp
Expand Up @@ -68,22 +68,27 @@ 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.
if (f->QuarantineSizeMb > (4 * 1024)) {
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.
if (f->ThreadLocalQuarantineSizeKb > (128 * 1024)) {
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() {
Expand Down
6 changes: 4 additions & 2 deletions compiler-rt/lib/scudo/scudo_flags.inc
Expand Up @@ -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.")
Expand Down
3 changes: 1 addition & 2 deletions compiler-rt/test/scudo/alignment.cpp
Expand Up @@ -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;
Expand Down
12 changes: 4 additions & 8 deletions compiler-rt/test/scudo/double-free.cpp
Expand Up @@ -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);
}
Expand Down
45 changes: 32 additions & 13 deletions compiler-rt/test/scudo/interface.cpp
@@ -1,28 +1,47 @@
// 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>

#include <sanitizer/allocator_interface.h>

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;
}
6 changes: 3 additions & 3 deletions compiler-rt/test/scudo/lit.cfg
Expand Up @@ -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) + " "
Expand Down
7 changes: 3 additions & 4 deletions compiler-rt/test/scudo/malloc.cpp
Expand Up @@ -5,6 +5,7 @@
// intended. Tests various sizes serviced by the primary and secondary
// allocators.

#include <assert.h>
#include <stdlib.h>
#include <string.h>

Expand All @@ -18,17 +19,15 @@ 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) {
ssize_t actual_size = size + offset;
if (actual_size <= 0)
continue;
p = malloc(actual_size);
if (!p)
return 1;
assert(p);
memset(p, 0xff, actual_size);
free(p);
}
Expand Down
12 changes: 4 additions & 8 deletions compiler-rt/test/scudo/memalign.cpp
Expand Up @@ -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++) {
Expand All @@ -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);
}
}
Expand All @@ -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);
}
}
Expand Down
11 changes: 4 additions & 7 deletions compiler-rt/test/scudo/mismatch.cpp
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions compiler-rt/test/scudo/options.cpp
Expand Up @@ -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.
Expand All @@ -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;
}
Expand Down
10 changes: 5 additions & 5 deletions compiler-rt/test/scudo/overflow.cpp
Expand Up @@ -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;
Expand Down
5 changes: 2 additions & 3 deletions compiler-rt/test/scudo/preinit.cpp
Expand Up @@ -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>

Expand All @@ -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;
Expand All @@ -34,4 +34,3 @@ __attribute__((section(".preinit_array"), used))
void (*__local_preinit)(void) = __init;
__attribute__((section(".fini_array"), used))
void (*__local_fini)(void) = __fini;

0 comments on commit 8d6257b

Please sign in to comment.