Index: include/sanitizer/msan_interface.h =================================================================== --- include/sanitizer/msan_interface.h +++ include/sanitizer/msan_interface.h @@ -92,6 +92,9 @@ Memory will be marked uninitialized, with origin at the call site. */ void __msan_allocated_memory(const volatile void* data, size_t size); + /* Tell MSan about newly destroyed memory. Mark memory as uninitialized. */ + void __sanitizer_dtor_callback(const volatile void* data, size_t size); + /* This function may be optionally provided by user and should return a string containing Msan runtime options. See msan_flags.h for details. */ const char* __msan_default_options(); Index: lib/msan/msan.cc =================================================================== --- lib/msan/msan.cc +++ lib/msan/msan.cc @@ -511,7 +511,7 @@ internal_memcpy((void*)MEM_TO_SHADOW((uptr)data), shadow, size); } -void __msan_load_unpoisoned(void *src, uptr size, void *dst) { +void __msan_load_unpoisoned(const void *src, uptr size, void *dst) { internal_memcpy(dst, src, size); __msan_unpoison(dst, size); } Index: lib/msan/msan_flags.inc =================================================================== --- lib/msan/msan_flags.inc +++ lib/msan/msan_flags.inc @@ -24,6 +24,7 @@ MSAN_FLAG(bool, poison_stack_with_zeroes, false, "") MSAN_FLAG(bool, poison_in_malloc, true, "") MSAN_FLAG(bool, poison_in_free, true, "") +MSAN_FLAG(bool, poison_in_dtor, false, "") MSAN_FLAG(bool, report_umrs, true, "") MSAN_FLAG(bool, wrap_signals, true, "") MSAN_FLAG(bool, print_stats, false, "") Index: lib/msan/msan_interceptors.cc =================================================================== --- lib/msan/msan_interceptors.cc +++ lib/msan/msan_interceptors.cc @@ -1005,6 +1005,14 @@ } } +void __sanitizer_dtor_callback(const void *data, uptr size) { + GET_MALLOC_STACK_TRACE; + if (flags()->poison_in_dtor) { + stack.tag = STACK_TRACE_TAG_POISON; + PoisonMemory(data, size, &stack); + } +} + INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags, int fd, OFF_T offset) { if (msan_init_is_running) Index: lib/msan/msan_interface_internal.h =================================================================== --- lib/msan/msan_interface_internal.h +++ lib/msan/msan_interface_internal.h @@ -140,6 +140,11 @@ SANITIZER_INTERFACE_ATTRIBUTE void __msan_allocated_memory(const void* data, uptr size); +// Tell MSan about newly destroyed memory. Memory will be marked +// uninitialized. +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_dtor_callback(const void* data, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE u16 __sanitizer_unaligned_load16(const uu16 *p); Index: test/msan/dtor-member.cc =================================================================== --- /dev/null +++ test/msan/dtor-member.cc @@ -0,0 +1,48 @@ +// RUN: %clangxx_msan %s -fsanitize=memory -fsanitize-memory-use-after-dtor -o %t && MSAN_OPTIONS=poison_in_dtor=1 %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan %s -O1 -fsanitize=memory -fsanitize-memory-use-after-dtor -o %t && MSAN_OPTIONS=poison_in_dtor=1 %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan %s -O2 -fsanitize=memory -fsanitize-memory-use-after-dtor -o %t && MSAN_OPTIONS=poison_in_dtor=1 %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan %s -fsanitize=memory -o %t && MSAN_OPTIONS=poison_in_dtor=1 %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK-NO-FLAG < %t.out + +// RUN: %clangxx_msan -fsanitize=memory -fsanitize-memory-use-after-dtor %s -o %t && MSAN_OPTIONS=poison_in_dtor=0 %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK-NO-FLAG < %t.out + +#include +#include +#include +#include + +struct Simple { + int x_; + Simple() { + x_ = 5; + } + ~Simple() { } +}; + +int main() { + unsigned long buf[1]; + assert(sizeof(Simple) <= sizeof(buf)); + + // The placement new operator forces the object to be constructed in the + // memory location &buf. Since objects made in this way must be explicitly + // destroyed, there are no implicit calls inserted that would interfere with + // test behavior. + Simple *s = new(&buf) Simple(); + s->~Simple(); + + if (__msan_test_shadow(s, sizeof(*s)) != -1) + printf("s is poisoned\n"); + else + printf("s is not poisoned\n"); + // CHECK: s is poisoned + // CHECK-NO-FLAG: s is not poisoned + + return 0; +}