forked from OSchip/llvm-project
[asan] Re-poison all redzones on activation.
When running with start_deactivated=1 in ASAN_OPTIONS, heap redzones are not poisoned until the first instrumented module is loaded. This can cause false negatives even on memory allocated after activation, because redzones are normally poisoned only once when a new allocator region is mapped. This change attempts to fix it by iterating over all existing allocator chunks and poisoning their redzones. llvm-svn: 281364
This commit is contained in:
parent
ffa0241a03
commit
b48348fec5
|
@ -266,9 +266,43 @@ struct Allocator {
|
||||||
SharedInitCode(options);
|
SharedInitCode(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RePoisonChunk(uptr chunk) {
|
||||||
|
// This could a user-facing chunk (with redzones), or some internal
|
||||||
|
// housekeeping chunk, like TransferBatch. Start by assuming the former.
|
||||||
|
AsanChunk *ac = GetAsanChunk((void *)chunk);
|
||||||
|
uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)ac);
|
||||||
|
uptr beg = ac->Beg();
|
||||||
|
uptr end = ac->Beg() + ac->UsedSize(true);
|
||||||
|
uptr chunk_end = chunk + allocated_size;
|
||||||
|
if (chunk < beg && beg < end && end <= chunk_end) {
|
||||||
|
// Looks like a valid AsanChunk. Or maybe not. Be conservative and only
|
||||||
|
// poison the redzones.
|
||||||
|
PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic);
|
||||||
|
uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY);
|
||||||
|
FastPoisonShadowPartialRightRedzone(
|
||||||
|
end_aligned_down, end - end_aligned_down,
|
||||||
|
chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic);
|
||||||
|
} else {
|
||||||
|
// This can not be an AsanChunk. Poison everything. It may be reused as
|
||||||
|
// AsanChunk later.
|
||||||
|
PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ReInitialize(const AllocatorOptions &options) {
|
void ReInitialize(const AllocatorOptions &options) {
|
||||||
allocator.SetMayReturnNull(options.may_return_null);
|
allocator.SetMayReturnNull(options.may_return_null);
|
||||||
SharedInitCode(options);
|
SharedInitCode(options);
|
||||||
|
|
||||||
|
// Poison all existing allocation's redzones.
|
||||||
|
if (CanPoisonMemory()) {
|
||||||
|
allocator.ForceLock();
|
||||||
|
allocator.ForEachChunk(
|
||||||
|
[](uptr chunk, void *alloc) {
|
||||||
|
((Allocator *)alloc)->RePoisonChunk(chunk);
|
||||||
|
},
|
||||||
|
this);
|
||||||
|
allocator.ForceUnlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetOptions(AllocatorOptions *options) const {
|
void GetOptions(AllocatorOptions *options) const {
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
// Main executable is uninstrumented, but linked to ASan runtime. The shared
|
// Main executable is uninstrumented, but linked to ASan runtime. The shared
|
||||||
// library is instrumented. Memory errors before dlopen are not detected.
|
// library is instrumented. Memory errors before dlopen are not detected.
|
||||||
|
|
||||||
// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
|
// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -std=c++11 -fPIC -shared -o %t-so.so
|
||||||
// RUN: %clangxx -O0 %s -c -o %t.o
|
// RUN: %clangxx -O0 %s -std=c++11 -c -o %t.o
|
||||||
// RUN: %clangxx_asan -O0 %t.o %libdl -o %t
|
// RUN: %clangxx_asan -O0 %t.o %libdl -o %t
|
||||||
// RUN: %env_asan_opts=start_deactivated=1,allocator_may_return_null=0 \
|
// RUN: %env_asan_opts=start_deactivated=1,allocator_may_return_null=0 \
|
||||||
// RUN: ASAN_ACTIVATION_OPTIONS=allocator_may_return_null=1 not %run %t 2>&1 | FileCheck %s
|
// RUN: ASAN_ACTIVATION_OPTIONS=allocator_may_return_null=1 not %run %t 2>&1 | FileCheck %s
|
||||||
|
@ -32,18 +32,25 @@
|
||||||
|
|
||||||
#include "sanitizer/asan_interface.h"
|
#include "sanitizer/asan_interface.h"
|
||||||
|
|
||||||
void test_malloc_shadow() {
|
constexpr unsigned nPtrs = 200;
|
||||||
char *p = (char *)malloc(100);
|
char *ptrs[nPtrs];
|
||||||
char *q = (char *)__asan_region_is_poisoned(p + 95, 8);
|
|
||||||
fprintf(stderr, "=%zd=\n", q ? q - (p + 95) : -1);
|
void test_malloc_shadow(char *p, size_t sz, bool expect_redzones) {
|
||||||
free(p);
|
assert((char *)__asan_region_is_poisoned(p - 1, sz + 1) ==
|
||||||
|
(expect_redzones ? p - 1 : nullptr));
|
||||||
|
assert((char *)__asan_region_is_poisoned(p, sz) == nullptr);
|
||||||
|
assert((char *)__asan_region_is_poisoned(p, sz + 1) ==
|
||||||
|
(expect_redzones ? p + sz : nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef void (*Fn)();
|
typedef void (*Fn)();
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
test_malloc_shadow();
|
// Before activation: no redzones.
|
||||||
// CHECK: =-1=
|
for (size_t sz = 1; sz < nPtrs; ++sz) {
|
||||||
|
ptrs[sz] = (char *)malloc(sz);
|
||||||
|
test_malloc_shadow(ptrs[sz], sz, false);
|
||||||
|
}
|
||||||
|
|
||||||
std::string path = std::string(argv[0]) + "-so.so";
|
std::string path = std::string(argv[0]) + "-so.so";
|
||||||
void *dso = dlopen(path.c_str(), RTLD_NOW);
|
void *dso = dlopen(path.c_str(), RTLD_NOW);
|
||||||
|
@ -52,9 +59,6 @@ int main(int argc, char *argv[]) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
test_malloc_shadow();
|
|
||||||
// CHECK: =5=
|
|
||||||
|
|
||||||
// After this line ASan is activated and starts detecting errors.
|
// After this line ASan is activated and starts detecting errors.
|
||||||
void *fn = dlsym(dso, "do_another_bad_thing");
|
void *fn = dlsym(dso, "do_another_bad_thing");
|
||||||
if (!fn) {
|
if (!fn) {
|
||||||
|
@ -62,6 +66,19 @@ int main(int argc, char *argv[]) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// After activation: redzones.
|
||||||
|
{
|
||||||
|
char *p = (char *)malloc(100);
|
||||||
|
test_malloc_shadow(p, 100, true);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-existing allocations got redzones, too.
|
||||||
|
for (size_t sz = 1; sz < nPtrs; ++sz) {
|
||||||
|
test_malloc_shadow(ptrs[sz], sz, true);
|
||||||
|
free(ptrs[sz]);
|
||||||
|
}
|
||||||
|
|
||||||
// Test that ASAN_ACTIVATION_OPTIONS=allocator_may_return_null=1 has effect.
|
// Test that ASAN_ACTIVATION_OPTIONS=allocator_may_return_null=1 has effect.
|
||||||
void *p = malloc((unsigned long)-2);
|
void *p = malloc((unsigned long)-2);
|
||||||
assert(!p);
|
assert(!p);
|
||||||
|
|
Loading…
Reference in New Issue