[compiler-rt] [libcxx] [mlir] [clang] [clang-tools-extra] [lld] [libcxxabi] [lldb] [libc] [openmp] [llvm] [msan] Unwind stack before fatal reports (PR #77168)

Vitaly Buka via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 8 17:14:24 PST 2024


https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/77168

>From a127373cf1ac1676ce17ce8dca909d0c3bce9d18 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Mon, 8 Jan 2024 11:45:37 -0800
Subject: [PATCH 1/2] [NFC][msan] Switch allocator interface to use
 BufferedStackTrace

We will need it to unwind for fatal errors.

Pull Request: https://github.com/llvm/llvm-project/pull/77363
---
 compiler-rt/lib/msan/msan.h             | 23 ++++++++++----------
 compiler-rt/lib/msan/msan_allocator.cpp | 29 +++++++++++++------------
 2 files changed, 27 insertions(+), 25 deletions(-)

diff --git a/compiler-rt/lib/msan/msan.h b/compiler-rt/lib/msan/msan.h
index 25fa2212bdadd3..753e6b260734f9 100644
--- a/compiler-rt/lib/msan/msan.h
+++ b/compiler-rt/lib/msan/msan.h
@@ -255,18 +255,19 @@ char *GetProcSelfMaps();
 void InitializeInterceptors();
 
 void MsanAllocatorInit();
-void MsanDeallocate(StackTrace *stack, void *ptr);
-
-void *msan_malloc(uptr size, StackTrace *stack);
-void *msan_calloc(uptr nmemb, uptr size, StackTrace *stack);
-void *msan_realloc(void *ptr, uptr size, StackTrace *stack);
-void *msan_reallocarray(void *ptr, uptr nmemb, uptr size, StackTrace *stack);
-void *msan_valloc(uptr size, StackTrace *stack);
-void *msan_pvalloc(uptr size, StackTrace *stack);
-void *msan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack);
-void *msan_memalign(uptr alignment, uptr size, StackTrace *stack);
+void MsanDeallocate(BufferedStackTrace *stack, void *ptr);
+
+void *msan_malloc(uptr size, BufferedStackTrace *stack);
+void *msan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack);
+void *msan_realloc(void *ptr, uptr size, BufferedStackTrace *stack);
+void *msan_reallocarray(void *ptr, uptr nmemb, uptr size,
+                        BufferedStackTrace *stack);
+void *msan_valloc(uptr size, BufferedStackTrace *stack);
+void *msan_pvalloc(uptr size, BufferedStackTrace *stack);
+void *msan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace *stack);
+void *msan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack);
 int msan_posix_memalign(void **memptr, uptr alignment, uptr size,
-                        StackTrace *stack);
+                        BufferedStackTrace *stack);
 
 void InstallTrapHandler();
 void InstallAtExitHandler();
diff --git a/compiler-rt/lib/msan/msan_allocator.cpp b/compiler-rt/lib/msan/msan_allocator.cpp
index 72a7f980d39fb0..987c894c79d45e 100644
--- a/compiler-rt/lib/msan/msan_allocator.cpp
+++ b/compiler-rt/lib/msan/msan_allocator.cpp
@@ -178,7 +178,7 @@ void MsanThreadLocalMallocStorage::CommitBack() {
   allocator.DestroyCache(GetAllocatorCache(this));
 }
 
-static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment,
+static void *MsanAllocate(BufferedStackTrace *stack, uptr size, uptr alignment,
                           bool zeroise) {
   if (size > max_malloc_size) {
     if (AllocatorMayReturnNull()) {
@@ -229,7 +229,7 @@ static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment,
   return allocated;
 }
 
-void MsanDeallocate(StackTrace *stack, void *p) {
+void MsanDeallocate(BufferedStackTrace *stack, void *p) {
   CHECK(p);
   UnpoisonParam(1);
   RunFreeHooks(p);
@@ -259,8 +259,8 @@ void MsanDeallocate(StackTrace *stack, void *p) {
   }
 }
 
-static void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size,
-                            uptr alignment) {
+static void *MsanReallocate(BufferedStackTrace *stack, void *old_p,
+                            uptr new_size, uptr alignment) {
   Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(old_p));
   uptr old_size = meta->requested_size;
   uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p);
@@ -284,7 +284,7 @@ static void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size,
   return new_p;
 }
 
-static void *MsanCalloc(StackTrace *stack, uptr nmemb, uptr size) {
+static void *MsanCalloc(BufferedStackTrace *stack, uptr nmemb, uptr size) {
   if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
     if (AllocatorMayReturnNull())
       return nullptr;
@@ -320,15 +320,15 @@ static uptr AllocationSizeFast(const void *p) {
   return reinterpret_cast<Metadata *>(allocator.GetMetaData(p))->requested_size;
 }
 
-void *msan_malloc(uptr size, StackTrace *stack) {
+void *msan_malloc(uptr size, BufferedStackTrace *stack) {
   return SetErrnoOnNull(MsanAllocate(stack, size, sizeof(u64), false));
 }
 
-void *msan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
+void *msan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
   return SetErrnoOnNull(MsanCalloc(stack, nmemb, size));
 }
 
-void *msan_realloc(void *ptr, uptr size, StackTrace *stack) {
+void *msan_realloc(void *ptr, uptr size, BufferedStackTrace *stack) {
   if (!ptr)
     return SetErrnoOnNull(MsanAllocate(stack, size, sizeof(u64), false));
   if (size == 0) {
@@ -338,7 +338,8 @@ void *msan_realloc(void *ptr, uptr size, StackTrace *stack) {
   return SetErrnoOnNull(MsanReallocate(stack, ptr, size, sizeof(u64)));
 }
 
-void *msan_reallocarray(void *ptr, uptr nmemb, uptr size, StackTrace *stack) {
+void *msan_reallocarray(void *ptr, uptr nmemb, uptr size,
+                        BufferedStackTrace *stack) {
   if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
     errno = errno_ENOMEM;
     if (AllocatorMayReturnNull())
@@ -348,11 +349,11 @@ void *msan_reallocarray(void *ptr, uptr nmemb, uptr size, StackTrace *stack) {
   return msan_realloc(ptr, nmemb * size, stack);
 }
 
-void *msan_valloc(uptr size, StackTrace *stack) {
+void *msan_valloc(uptr size, BufferedStackTrace *stack) {
   return SetErrnoOnNull(MsanAllocate(stack, size, GetPageSizeCached(), false));
 }
 
-void *msan_pvalloc(uptr size, StackTrace *stack) {
+void *msan_pvalloc(uptr size, BufferedStackTrace *stack) {
   uptr PageSize = GetPageSizeCached();
   if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
     errno = errno_ENOMEM;
@@ -365,7 +366,7 @@ void *msan_pvalloc(uptr size, StackTrace *stack) {
   return SetErrnoOnNull(MsanAllocate(stack, size, PageSize, false));
 }
 
-void *msan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack) {
+void *msan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace *stack) {
   if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
     errno = errno_EINVAL;
     if (AllocatorMayReturnNull())
@@ -375,7 +376,7 @@ void *msan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack) {
   return SetErrnoOnNull(MsanAllocate(stack, size, alignment, false));
 }
 
-void *msan_memalign(uptr alignment, uptr size, StackTrace *stack) {
+void *msan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack) {
   if (UNLIKELY(!IsPowerOfTwo(alignment))) {
     errno = errno_EINVAL;
     if (AllocatorMayReturnNull())
@@ -386,7 +387,7 @@ void *msan_memalign(uptr alignment, uptr size, StackTrace *stack) {
 }
 
 int msan_posix_memalign(void **memptr, uptr alignment, uptr size,
-                        StackTrace *stack) {
+                        BufferedStackTrace *stack) {
   if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
     if (AllocatorMayReturnNull())
       return errno_EINVAL;

>From 1de5a3d46d64d9ca8c051f11afa98f80e99064f2 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Fri, 5 Jan 2024 18:15:58 -0800
Subject: [PATCH 2/2] [msan] Unwind stack before fatal reports

Msan does not unwind stack in malloc without origins, but we still need trace
for fatal errors.

Pull Request: https://github.com/llvm/llvm-project/pull/77168
---
 compiler-rt/lib/msan/msan.h                   | 11 ++++++++
 compiler-rt/lib/msan/msan_allocator.cpp       | 11 +++++++-
 compiler-rt/lib/msan/msan_new_delete.cpp      | 26 ++++++++++++-------
 .../TestCases/max_allocation_size.cpp         | 10 ++++++-
 4 files changed, 46 insertions(+), 12 deletions(-)

diff --git a/compiler-rt/lib/msan/msan.h b/compiler-rt/lib/msan/msan.h
index 753e6b260734f9..c3e45b38db2a13 100644
--- a/compiler-rt/lib/msan/msan.h
+++ b/compiler-rt/lib/msan/msan.h
@@ -322,6 +322,17 @@ const int STACK_TRACE_TAG_VPTR = STACK_TRACE_TAG_FIELDS + 1;
     stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal); \
   }
 
+#define GET_FATAL_STACK_TRACE \
+  GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME())
+
+// Unwind the stack for fatal error, as the parameter `stack` is
+// empty without origins.
+#define GET_FATAL_STACK_TRACE_AGAIN_IF_EMPTY(STACK)                           \
+  if (msan_inited && (STACK)->size == 0) {                                    \
+    (STACK)->Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), nullptr, \
+                    common_flags()->fast_unwind_on_fatal);                    \
+  }
+
 class ScopedThreadLocalStateBackup {
  public:
   ScopedThreadLocalStateBackup() { Backup(); }
diff --git a/compiler-rt/lib/msan/msan_allocator.cpp b/compiler-rt/lib/msan/msan_allocator.cpp
index 987c894c79d45e..847e0e26f0504b 100644
--- a/compiler-rt/lib/msan/msan_allocator.cpp
+++ b/compiler-rt/lib/msan/msan_allocator.cpp
@@ -180,16 +180,18 @@ void MsanThreadLocalMallocStorage::CommitBack() {
 
 static void *MsanAllocate(BufferedStackTrace *stack, uptr size, uptr alignment,
                           bool zeroise) {
-  if (size > max_malloc_size) {
+  if (UNLIKELY(size > max_malloc_size)) {
     if (AllocatorMayReturnNull()) {
       Report("WARNING: MemorySanitizer failed to allocate 0x%zx bytes\n", size);
       return nullptr;
     }
+    GET_FATAL_STACK_TRACE_AGAIN_IF_EMPTY(stack);
     ReportAllocationSizeTooBig(size, max_malloc_size, stack);
   }
   if (UNLIKELY(IsRssLimitExceeded())) {
     if (AllocatorMayReturnNull())
       return nullptr;
+    GET_FATAL_STACK_TRACE_AGAIN_IF_EMPTY(stack);
     ReportRssLimitExceeded(stack);
   }
   MsanThread *t = GetCurrentThread();
@@ -206,6 +208,7 @@ static void *MsanAllocate(BufferedStackTrace *stack, uptr size, uptr alignment,
     SetAllocatorOutOfMemory();
     if (AllocatorMayReturnNull())
       return nullptr;
+    GET_FATAL_STACK_TRACE_AGAIN_IF_EMPTY(stack);
     ReportOutOfMemory(size, stack);
   }
   Metadata *meta =
@@ -288,6 +291,7 @@ static void *MsanCalloc(BufferedStackTrace *stack, uptr nmemb, uptr size) {
   if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
     if (AllocatorMayReturnNull())
       return nullptr;
+    GET_FATAL_STACK_TRACE_AGAIN_IF_EMPTY(stack);
     ReportCallocOverflow(nmemb, size, stack);
   }
   return MsanAllocate(stack, nmemb * size, sizeof(u64), true);
@@ -344,6 +348,7 @@ void *msan_reallocarray(void *ptr, uptr nmemb, uptr size,
     errno = errno_ENOMEM;
     if (AllocatorMayReturnNull())
       return nullptr;
+    GET_FATAL_STACK_TRACE_AGAIN_IF_EMPTY(stack);
     ReportReallocArrayOverflow(nmemb, size, stack);
   }
   return msan_realloc(ptr, nmemb * size, stack);
@@ -359,6 +364,7 @@ void *msan_pvalloc(uptr size, BufferedStackTrace *stack) {
     errno = errno_ENOMEM;
     if (AllocatorMayReturnNull())
       return nullptr;
+    GET_FATAL_STACK_TRACE_AGAIN_IF_EMPTY(stack);
     ReportPvallocOverflow(size, stack);
   }
   // pvalloc(0) should allocate one page.
@@ -371,6 +377,7 @@ void *msan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace *stack) {
     errno = errno_EINVAL;
     if (AllocatorMayReturnNull())
       return nullptr;
+    GET_FATAL_STACK_TRACE_AGAIN_IF_EMPTY(stack);
     ReportInvalidAlignedAllocAlignment(size, alignment, stack);
   }
   return SetErrnoOnNull(MsanAllocate(stack, size, alignment, false));
@@ -381,6 +388,7 @@ void *msan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack) {
     errno = errno_EINVAL;
     if (AllocatorMayReturnNull())
       return nullptr;
+    GET_FATAL_STACK_TRACE_AGAIN_IF_EMPTY(stack);
     ReportInvalidAllocationAlignment(alignment, stack);
   }
   return SetErrnoOnNull(MsanAllocate(stack, size, alignment, false));
@@ -391,6 +399,7 @@ int msan_posix_memalign(void **memptr, uptr alignment, uptr size,
   if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
     if (AllocatorMayReturnNull())
       return errno_EINVAL;
+    GET_FATAL_STACK_TRACE_AGAIN_IF_EMPTY(stack);
     ReportInvalidPosixMemalignAlignment(alignment, stack);
   }
   void *ptr = MsanAllocate(stack, size, alignment, false);
diff --git a/compiler-rt/lib/msan/msan_new_delete.cpp b/compiler-rt/lib/msan/msan_new_delete.cpp
index d4e95c0f65137d..fe6399f5910737 100644
--- a/compiler-rt/lib/msan/msan_new_delete.cpp
+++ b/compiler-rt/lib/msan/msan_new_delete.cpp
@@ -30,16 +30,22 @@ namespace std {
 
 
 // TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
-#define OPERATOR_NEW_BODY(nothrow) \
-  GET_MALLOC_STACK_TRACE; \
-  void *res = msan_malloc(size, &stack);\
-  if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
-  return res
-#define OPERATOR_NEW_BODY_ALIGN(nothrow) \
-  GET_MALLOC_STACK_TRACE;\
-  void *res = msan_memalign((uptr)align, size, &stack);\
-  if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
-  return res;
+#  define OPERATOR_NEW_BODY(nothrow)                \
+    GET_MALLOC_STACK_TRACE;                         \
+    void *res = msan_malloc(size, &stack);          \
+    if (!nothrow && UNLIKELY(!res)) {               \
+      GET_FATAL_STACK_TRACE_AGAIN_IF_EMPTY(&stack); \
+      ReportOutOfMemory(size, &stack);              \
+    }                                               \
+    return res
+#  define OPERATOR_NEW_BODY_ALIGN(nothrow)                \
+    GET_MALLOC_STACK_TRACE;                               \
+    void *res = msan_memalign((uptr)align, size, &stack); \
+    if (!nothrow && UNLIKELY(!res)) {                     \
+      GET_FATAL_STACK_TRACE_AGAIN_IF_EMPTY(&stack);       \
+      ReportOutOfMemory(size, &stack);                    \
+    }                                                     \
+    return res;
 
 INTERCEPTOR_ATTRIBUTE
 void *operator new(size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
diff --git a/compiler-rt/test/sanitizer_common/TestCases/max_allocation_size.cpp b/compiler-rt/test/sanitizer_common/TestCases/max_allocation_size.cpp
index ace28965f3c1ba..c74f241c32b754 100644
--- a/compiler-rt/test/sanitizer_common/TestCases/max_allocation_size.cpp
+++ b/compiler-rt/test/sanitizer_common/TestCases/max_allocation_size.cpp
@@ -35,7 +35,7 @@
 // RUN:   | FileCheck %s --check-prefix=CHECK-nnCRASH
 // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \
 // RUN:   %run %t new-nothrow 2>&1 | FileCheck %s --check-prefix=CHECK-NULL
-// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \
+// RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0:fast_unwind_on_malloc=0 \
 // RUN:   not %run %t strndup 2>&1 | FileCheck %s --check-prefix=CHECK-sCRASH
 // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \
 // RUN:   %run %t strndup 2>&1 | FileCheck %s --check-prefix=CHECK-NULL
@@ -123,20 +123,28 @@ int main(int Argc, char **Argv) {
 }
 
 // CHECK-mCRASH: malloc:
+// CHECK-mCRASH: #{{[0-9]+.*}}max_allocation_size.cpp
 // CHECK-mCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
 // CHECK-cCRASH: calloc:
+// CHECK-cCRASH: #{{[0-9]+.*}}max_allocation_size.cpp
 // CHECK-cCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
 // CHECK-rCRASH: realloc:
+// CHECK-rCRASH: #{{[0-9]+.*}}max_allocation_size.cpp
 // CHECK-rCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
 // CHECK-mrCRASH: realloc-after-malloc:
+// CHECK-mrCRASH: #{{[0-9]+.*}}max_allocation_size.cpp
 // CHECK-mrCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
 // CHECK-nCRASH: new:
+// CHECK-nCRASH: #{{[0-9]+.*}}max_allocation_size.cpp
 // CHECK-nCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
 // CHECK-nCRASH-OOM: new:
+// CHECK-nCRASH-OOM: #{{[0-9]+.*}}max_allocation_size.cpp
 // CHECK-nCRASH-OOM: {{SUMMARY: .*Sanitizer: out-of-memory}}
 // CHECK-nnCRASH: new-nothrow:
+// CHECK-nnCRASH: #{{[0-9]+.*}}max_allocation_size.cpp
 // CHECK-nnCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
 // CHECK-sCRASH: strndup:
+// CHECK-sCRASH: #{{[0-9]+.*}}max_allocation_size.cpp
 // CHECK-sCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big}}
 
 // CHECK-NULL: {{malloc|calloc|calloc-overflow|realloc|realloc-after-malloc|new-nothrow|strndup}}



More information about the cfe-commits mailing list