Index: compiler-rt/trunk/lib/asan/asan_allocator.cc =================================================================== --- compiler-rt/trunk/lib/asan/asan_allocator.cc +++ compiler-rt/trunk/lib/asan/asan_allocator.cc @@ -523,6 +523,18 @@ AsanThread *t = GetCurrentThread(); m->free_tid = t ? t->tid() : 0; m->free_context_id = StackDepotPut(*stack); + + Flags &fl = *flags(); + if (fl.max_free_fill_size > 0) { + // We have to skip the chunk header, it contains free_context_id. + uptr scribble_start = (uptr)m + kChunkHeaderSize + kChunkHeader2Size; + if (m->UsedSize() >= kChunkHeader2Size) { // Skip Header2 in user area. + uptr size_to_fill = m->UsedSize() - kChunkHeader2Size; + size_to_fill = Min(size_to_fill, (uptr)fl.max_free_fill_size); + REAL(memset)((void *)scribble_start, fl.free_fill_byte, size_to_fill); + } + } + // Poison the region. PoisonShadow(m->Beg(), RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), Index: compiler-rt/trunk/lib/asan/asan_flags.cc =================================================================== --- compiler-rt/trunk/lib/asan/asan_flags.cc +++ compiler-rt/trunk/lib/asan/asan_flags.cc @@ -95,6 +95,18 @@ RegisterCommonFlags(&ubsan_parser); #endif + if (SANITIZER_MAC) { + // Support macOS MallocScribble and MallocPreScribble: + // + if (GetEnv("MallocScribble")) { + f->max_free_fill_size = 0x1000; + } + if (GetEnv("MallocPreScribble")) { + f->malloc_fill_byte = 0xaa; + } + } + // Override from ASan compile definition. const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition(); asan_parser.ParseString(asan_compile_def); Index: compiler-rt/trunk/lib/asan/asan_flags.inc =================================================================== --- compiler-rt/trunk/lib/asan/asan_flags.inc +++ compiler-rt/trunk/lib/asan/asan_flags.inc @@ -63,8 +63,14 @@ int, max_malloc_fill_size, 0x1000, // By default, fill only the first 4K. "ASan allocator flag. max_malloc_fill_size is the maximal amount of " "bytes that will be filled with malloc_fill_byte on malloc.") +ASAN_FLAG( + int, max_free_fill_size, 0, + "ASan allocator flag. max_free_fill_size is the maximal amount of " + "bytes that will be filled with free_fill_byte during free.") ASAN_FLAG(int, malloc_fill_byte, 0xbe, "Value used to fill the newly allocated memory.") +ASAN_FLAG(int, free_fill_byte, 0x55, + "Value used to fill deallocated memory.") ASAN_FLAG(bool, allow_user_poisoning, true, "If set, user may manually mark memory regions as poisoned or " "unpoisoned.") Index: compiler-rt/trunk/test/asan/TestCases/Darwin/scribble.cc =================================================================== --- compiler-rt/trunk/test/asan/TestCases/Darwin/scribble.cc +++ compiler-rt/trunk/test/asan/TestCases/Darwin/scribble.cc @@ -0,0 +1,56 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-NOSCRIBBLE %s +// RUN: env MallocScribble=1 MallocPreScribble=1 %run %t 2>&1 | FileCheck --check-prefix=CHECK-SCRIBBLE %s +// RUN: %env_asan_opts=max_free_fill_size=4096 %run %t 2>&1 | FileCheck --check-prefix=CHECK-SCRIBBLE %s + +#include +#include +#include + +struct Isa { + const char *class_name; +}; + +struct MyClass { + long padding; + Isa *isa; + long data; + + void print_my_class_name(); +}; + +__attribute__((no_sanitize("address"))) +void MyClass::print_my_class_name() { + fprintf(stderr, "this = %p\n", this); + fprintf(stderr, "padding = 0x%lx\n", this->padding); + fprintf(stderr, "isa = %p\n", this->isa); + + if ((uint32_t)(uintptr_t)this->isa != 0x55555555) { + fprintf(stderr, "class name: %s\n", this->isa->class_name); + } +} + +int main() { + Isa *my_class_isa = (Isa *)malloc(sizeof(Isa)); + memset(my_class_isa, 0x77, sizeof(Isa)); + my_class_isa->class_name = "MyClass"; + + MyClass *my_object = (MyClass *)malloc(sizeof(MyClass)); + memset(my_object, 0x88, sizeof(MyClass)); + my_object->isa = my_class_isa; + my_object->data = 42; + + my_object->print_my_class_name(); + // CHECK-SCRIBBLE: class name: MyClass + // CHECK-NOSCRIBBLE: class name: MyClass + + free(my_object); + + my_object->print_my_class_name(); + // CHECK-NOSCRIBBLE: class name: MyClass + // CHECK-SCRIBBLE: isa = {{0x5555555555555555|0x55555555}} + + printf("okthxbai!\n"); + // CHECK-SCRIBBLE: okthxbai! + // CHECK-NOSCRIBBLE: okthxbai! +} Index: compiler-rt/trunk/test/asan/TestCases/scribble.cc =================================================================== --- compiler-rt/trunk/test/asan/TestCases/scribble.cc +++ compiler-rt/trunk/test/asan/TestCases/scribble.cc @@ -0,0 +1,55 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-NOSCRIBBLE %s +// RUN: %env_asan_opts=max_free_fill_size=4096 %run %t 2>&1 | FileCheck --check-prefix=CHECK-SCRIBBLE %s + +#include +#include +#include + +struct Isa { + const char *class_name; +}; + +struct MyClass { + long padding; + Isa *isa; + long data; + + void print_my_class_name(); +}; + +__attribute__((no_sanitize("address"))) +void MyClass::print_my_class_name() { + fprintf(stderr, "this = %p\n", this); + fprintf(stderr, "padding = 0x%lx\n", this->padding); + fprintf(stderr, "isa = %p\n", this->isa); + + if ((uint32_t)(uintptr_t)this->isa != 0x55555555) { + fprintf(stderr, "class name: %s\n", this->isa->class_name); + } +} + +int main() { + Isa *my_class_isa = (Isa *)malloc(sizeof(Isa)); + memset(my_class_isa, 0x77, sizeof(Isa)); + my_class_isa->class_name = "MyClass"; + + MyClass *my_object = (MyClass *)malloc(sizeof(MyClass)); + memset(my_object, 0x88, sizeof(MyClass)); + my_object->isa = my_class_isa; + my_object->data = 42; + + my_object->print_my_class_name(); + // CHECK-SCRIBBLE: class name: MyClass + // CHECK-NOSCRIBBLE: class name: MyClass + + free(my_object); + + my_object->print_my_class_name(); + // CHECK-NOSCRIBBLE: class name: MyClass + // CHECK-SCRIBBLE: isa = {{0x5555555555555555|0x55555555}} + + printf("okthxbai!\n"); + // CHECK-SCRIBBLE: okthxbai! + // CHECK-NOSCRIBBLE: okthxbai! +}