Index: include/sanitizer/hwasan_interface.h =================================================================== --- include/sanitizer/hwasan_interface.h +++ include/sanitizer/hwasan_interface.h @@ -50,6 +50,10 @@ // does would cause false reports. void __hwasan_handle_longjmp(const void *sp_dst); + // Set memory tag for the part of the current thread stack below sp_dst to + // zero. Call this in vfork() before returning in the parent process. + void __hwasan_handle_vfork(const void *sp_dst); + // Libc hook for thread creation. Should be called in the child thread before // any instrumented code. void __hwasan_thread_enter(); @@ -65,6 +69,10 @@ // Print one-line report about the memory usage of the current process. void __hwasan_print_memory_usage(); + /* Returns the offset of the first byte in the memory range that can not be + * accessed through the pointer in x, or -1 if the whole range is good. */ + intptr_t __hwasan_test_shadow(const volatile void *x, size_t size); + int __sanitizer_posix_memalign(void **memptr, size_t alignment, size_t size); void * __sanitizer_memalign(size_t alignment, size_t size); void * __sanitizer_aligned_alloc(size_t alignment, size_t size); Index: lib/asan/CMakeLists.txt =================================================================== --- lib/asan/CMakeLists.txt +++ lib/asan/CMakeLists.txt @@ -13,6 +13,7 @@ asan_globals_win.cc asan_interceptors.cc asan_interceptors_memintrinsics.cc + asan_interceptors_vfork.S asan_linux.cc asan_mac.cc asan_malloc_linux.cc Index: lib/asan/asan_interceptors.cc =================================================================== --- lib/asan/asan_interceptors.cc +++ lib/asan/asan_interceptors.cc @@ -579,6 +579,8 @@ } #endif // ASAN_INTERCEPT___CXA_ATEXIT +DEFINE_REAL(void, vfork); + // ---------------------- InitializeAsanInterceptors ---------------- {{{1 namespace __asan { void InitializeAsanInterceptors() { @@ -656,6 +658,8 @@ ASAN_INTERCEPT_FUNC(__cxa_atexit); #endif + __interception::GetRealFunctionAddress("vfork", (uptr *)&REAL(vfork), 0, 0); + InitializePlatformInterceptors(); VReport(1, "AddressSanitizer: libc interceptors initialized\n"); Index: lib/asan/asan_interceptors_vfork.S =================================================================== --- lib/asan/asan_interceptors_vfork.S +++ lib/asan/asan_interceptors_vfork.S @@ -0,0 +1,7 @@ +#define COMMON_INTERCEPTOR_SPILL_AREA __asan_extra_spill_area +#define COMMON_INTERCEPTOR_HANDLE_VFORK __asan_handle_vfork +#include "sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S" + +#if defined(__linux__) +.section .note.GNU-stack,"",@progbits +#endif Index: lib/asan/asan_interface.inc =================================================================== --- lib/asan/asan_interface.inc +++ lib/asan/asan_interface.inc @@ -26,6 +26,7 @@ INTERFACE_FUNCTION(__asan_exp_store8) INTERFACE_FUNCTION(__asan_exp_store16) INTERFACE_FUNCTION(__asan_exp_storeN) +INTERFACE_FUNCTION(__asan_extra_spill_area) INTERFACE_FUNCTION(__asan_get_alloc_stack) INTERFACE_FUNCTION(__asan_get_current_fake_stack) INTERFACE_FUNCTION(__asan_get_free_stack) @@ -38,6 +39,7 @@ INTERFACE_FUNCTION(__asan_get_report_sp) INTERFACE_FUNCTION(__asan_get_shadow_mapping) INTERFACE_FUNCTION(__asan_handle_no_return) +INTERFACE_FUNCTION(__asan_handle_vfork) INTERFACE_FUNCTION(__asan_init) INTERFACE_FUNCTION(__asan_load_cxx_array_cookie) INTERFACE_FUNCTION(__asan_load1) Index: lib/asan/asan_interface_internal.h =================================================================== --- lib/asan/asan_interface_internal.h +++ lib/asan/asan_interface_internal.h @@ -249,6 +249,9 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char* __asan_default_suppressions(); + + SANITIZER_INTERFACE_ATTRIBUTE void *__asan_extra_spill_area(); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_handle_vfork(void *sp); } // extern "C" #endif // ASAN_INTERFACE_INTERNAL_H Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -597,6 +597,21 @@ curr_thread->fake_stack()->HandleNoReturn(); } +void *__asan_extra_spill_area() { + AsanThread *t = GetCurrentThread(); + CHECK(t); + return t->extra_spill_area(); +} + +void __asan_handle_vfork(void *sp) { + AsanThread *t = GetCurrentThread(); + CHECK(t); + uptr PageSize = GetPageSizeCached(); + uptr top = t->stack_top(); + uptr bottom = ((uptr)sp - PageSize) & ~(PageSize - 1); + PoisonShadow(bottom, top - bottom, 0); +} + void NOINLINE __asan_set_death_callback(void (*callback)(void)) { SetUserDieCallback(callback); } Index: lib/asan/asan_thread.h =================================================================== --- lib/asan/asan_thread.h +++ lib/asan/asan_thread.h @@ -130,6 +130,8 @@ AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; } AsanStats &stats() { return stats_; } + void *extra_spill_area() { return &extra_spill_area_; } + private: // NOTE: There is no AsanThread constructor. It is allocated // via mmap() and *must* be valid in zero-initialized state. @@ -165,6 +167,7 @@ AsanThreadLocalMallocStorage malloc_storage_; AsanStats stats_; bool unwinding_; + uptr extra_spill_area_; }; // ScopedUnwinding is a scope for stacktracing member of a context Index: lib/hwasan/CMakeLists.txt =================================================================== --- lib/hwasan/CMakeLists.txt +++ lib/hwasan/CMakeLists.txt @@ -6,6 +6,7 @@ hwasan_allocator.cc hwasan_dynamic_shadow.cc hwasan_interceptors.cc + hwasan_interceptors_vfork.S hwasan_linux.cc hwasan_memintrinsics.cc hwasan_poisoning.cc Index: lib/hwasan/hwasan.cc =================================================================== --- lib/hwasan/hwasan.cc +++ lib/hwasan/hwasan.cc @@ -477,6 +477,30 @@ TagMemory(sp, dst - sp, 0); } +void __hwasan_handle_vfork(const void *sp_dst) { + uptr sp = (uptr)sp_dst; + Thread *t = GetCurrentThread(); + CHECK(t); + uptr top = t->stack_top(); + uptr bottom = t->stack_bottom(); + static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M + if (top == 0 || bottom == 0 || sp < bottom || sp >= top || + sp - bottom > kMaxExpectedCleanupSize) { + Report( + "WARNING: HWASan is ignoring requested __hwasan_handle_vfork: " + "stack top: %zx; current %zx; bottom: %zx \n" + "False positive error reports may follow\n", + top, sp, bottom); + return; + } + TagMemory(bottom, sp - bottom, 0); +} + +void *__hwasan_extra_spill_area() { + Thread *t = GetCurrentThread(); + return &t->vfork_spill(); +} + void __hwasan_print_memory_usage() { InternalScopedString s(kMemoryUsageBufferSize); HwasanFormatMemoryUsage(s); Index: lib/hwasan/hwasan_interceptors.cc =================================================================== --- lib/hwasan/hwasan_interceptors.cc +++ lib/hwasan/hwasan_interceptors.cc @@ -227,6 +227,10 @@ } #endif +#if HWASAN_WITH_INTERCEPTORS +DEFINE_REAL(void, vfork); +#endif + static void BeforeFork() { StackDepotLockAll(); } @@ -266,6 +270,7 @@ INTERCEPT_FUNCTION(fork); #if HWASAN_WITH_INTERCEPTORS + __interception::GetRealFunctionAddress("vfork", (uptr *)&REAL(vfork), 0, 0); #if !defined(__aarch64__) INTERCEPT_FUNCTION(pthread_create); #endif Index: lib/hwasan/hwasan_interceptors_vfork.S =================================================================== --- lib/hwasan/hwasan_interceptors_vfork.S +++ lib/hwasan/hwasan_interceptors_vfork.S @@ -0,0 +1,7 @@ +#define COMMON_INTERCEPTOR_SPILL_AREA __hwasan_extra_spill_area +#define COMMON_INTERCEPTOR_HANDLE_VFORK __hwasan_handle_vfork +#include "sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S" + +#if defined(__linux__) +.section .note.GNU-stack,"",@progbits +#endif Index: lib/hwasan/hwasan_interface_internal.h =================================================================== --- lib/hwasan/hwasan_interface_internal.h +++ lib/hwasan/hwasan_interface_internal.h @@ -117,6 +117,9 @@ void __hwasan_handle_longjmp(const void *sp_dst); SANITIZER_INTERFACE_ATTRIBUTE +void __hwasan_handle_vfork(const void *sp_dst); + +SANITIZER_INTERFACE_ATTRIBUTE u16 __sanitizer_unaligned_load16(const uu16 *p); SANITIZER_INTERFACE_ATTRIBUTE @@ -200,6 +203,9 @@ void *__hwasan_memset(void *s, int c, uptr n); SANITIZER_INTERFACE_ATTRIBUTE void *__hwasan_memmove(void *dest, const void *src, uptr n); + +SANITIZER_INTERFACE_ATTRIBUTE +void *__hwasan_extra_spill_area(); } // extern "C" #endif // HWASAN_INTERFACE_INTERNAL_H Index: lib/hwasan/hwasan_thread.h =================================================================== --- lib/hwasan/hwasan_thread.h +++ lib/hwasan/hwasan_thread.h @@ -67,11 +67,14 @@ Print("Thread: "); } + uptr &vfork_spill() { return vfork_spill_; } + private: // NOTE: There is no Thread constructor. It is allocated // via mmap() and *must* be valid in zero-initialized state. void ClearShadowForThreadStackAndTLS(); void Print(const char *prefix); + uptr vfork_spill_; uptr stack_top_; uptr stack_bottom_; uptr tls_begin_; Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -126,6 +126,7 @@ sanitizer_common_interceptors.inc sanitizer_common_interceptors_format.inc sanitizer_common_interceptors_ioctl.inc + sanitizer_common_interceptors_vfork_aarch64.inc.S sanitizer_common_interface.inc sanitizer_common_interface_posix.inc sanitizer_common_syscalls.inc Index: lib/sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S =================================================================== --- lib/sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S +++ lib/sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S @@ -0,0 +1,43 @@ +#if defined(__aarch64__) && defined(__linux__) + +#include "sanitizer_common/sanitizer_asm.h" + +ASM_HIDDEN(COMMON_INTERCEPTOR_SPILL_AREA) + +.comm _ZN14__interception10real_vforkE,8,8 +.globl vfork +ASM_TYPE_FUNCTION(vfork) +vfork: + // Save x30 in the off-stack spill area. + stp xzr, x30, [sp, #-16]! + bl COMMON_INTERCEPTOR_SPILL_AREA + ldp xzr, x30, [sp], 16 + str x30, [x0] + + // Call real vfork. This may return twice. User code that runs between the first and the second return + // may clobber the stack frame of the interceptor; that's why it does not have a frame. + adrp x0, _ZN14__interception10real_vforkE + ldr x0, [x0, :lo12:_ZN14__interception10real_vforkE] + blr x0 + + stp x0, xzr, [sp, #-16]! + cmp x0, #0 + b.eq .L_exit + + // x0 != 0 => parent process. Clear stack shadow. + add x0, sp, #16 + bl COMMON_INTERCEPTOR_HANDLE_VFORK + +.L_exit: + // Restore x30. + bl COMMON_INTERCEPTOR_SPILL_AREA + ldr x30, [x0] + ldp x0, xzr, [sp], 16 + + ret +ASM_SIZE(vfork) + +.globl __interceptor_vfork +.set __interceptor_vfork, vfork + +#endif Index: test/asan/TestCases/Linux/vfork.cc =================================================================== --- test/asan/TestCases/Linux/vfork.cc +++ test/asan/TestCases/Linux/vfork.cc @@ -0,0 +1,31 @@ +// https://github.com/google/sanitizers/issues/925 +// RUN: %clang_asan -O0 %s -o %t && %run %t 2>&1 + +// REQUIRES: aarch64-android-target-arch + +#include +#include +#include +#include +#include +#include + +__attribute__((noinline, no_sanitize("address"))) void child() { + char x[10000]; + __asan_poison_memory_region(x, sizeof(x)); + _exit(0); +} + +__attribute__((noinline, no_sanitize("address"))) void parent() { + char x[10000]; + assert(__asan_address_is_poisoned(x + 5000) == 0); +} + +int main(int argc, char **argv) { + if (vfork()) + parent(); + else + child(); + + return 0; +} Index: test/hwasan/TestCases/Linux/vfork.c =================================================================== --- test/hwasan/TestCases/Linux/vfork.c +++ test/hwasan/TestCases/Linux/vfork.c @@ -0,0 +1,32 @@ +// https://github.com/google/sanitizers/issues/925 +// RUN: %clang_hwasan -O0 %s -o %t && %run %t 2>&1 + +// REQUIRES: aarch64-target-arch + +#include +#include +#include +#include +#include +#include + +__attribute__((noinline, no_sanitize("hwaddress"))) void child() { + char x[10000]; + __hwasan_tag_memory(x, 0xAA, sizeof(x)); + _exit(0); +} + +__attribute__((noinline, no_sanitize("hwaddress"))) void parent() { + char x[10000]; + __hwasan_print_shadow(&x, sizeof(x)); + assert(__hwasan_test_shadow(x, sizeof(x)) == -1); +} + +int main(int argc, char **argv) { + if (vfork()) + parent(); + else + child(); + + return 0; +}