diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc --- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -4404,12 +4404,18 @@ INTERCEPTOR(int, backtrace, void **buffer, int size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, backtrace, buffer, size); - // FIXME: under ASan the call below may write to freed memory and corrupt - // its metadata. See - // https://github.com/google/sanitizers/issues/321. - int res = REAL(backtrace)(buffer, size); - if (res && buffer) + // 'buffer' might be freed memory, hence it is unsafe to directly call + // REAL(backtrace)(buffer, size). Instead, we use our own known-good + // scratch buffer. + void **scratch = (void**)InternalAlloc(sizeof(void*) * size); + int res = REAL(backtrace)(scratch, size); + if (res && buffer) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buffer, res * sizeof(*buffer)); + for (int i = 0; i < res; i++) { + buffer[i] = scratch[i]; + } + } + InternalFree(scratch); return res; } diff --git a/compiler-rt/test/asan/TestCases/backtrace_interceptor.cpp b/compiler-rt/test/asan/TestCases/backtrace_interceptor.cpp --- a/compiler-rt/test/asan/TestCases/backtrace_interceptor.cpp +++ b/compiler-rt/test/asan/TestCases/backtrace_interceptor.cpp @@ -1,9 +1,5 @@ // RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s -// Interceptor can cause use-after-free -// (https://github.com/google/sanitizers/issues/321) -// XFAIL: * - // Test the backtrace() interceptor. #include @@ -19,6 +15,8 @@ assert(buffer != NULL); free(buffer); + // Deliberate use-after-free of 'buffer'. We expect ASan to + // catch this, without triggering internal sanitizer errors. int numEntries = backtrace(buffer, MAX_BT); printf("backtrace returned %d entries\n", numEntries);