Skip to content

[msan] Unwind stack before fatal reports #77168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

vitalybuka
Copy link
Collaborator

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

@llvmbot
Copy link
Member

llvmbot commented Jan 6, 2024

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: Vitaly Buka (vitalybuka)

Changes

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


Full diff: https://github.com/llvm/llvm-project/pull/77168.diff

4 Files Affected:

  • (modified) compiler-rt/lib/msan/msan.h (+3)
  • (modified) compiler-rt/lib/msan/msan_allocator.cpp (+19-10)
  • (modified) compiler-rt/lib/msan/msan_new_delete.cpp (+16-10)
  • (modified) compiler-rt/test/sanitizer_common/TestCases/max_allocation_size.cpp (+9-1)
diff --git a/compiler-rt/lib/msan/msan.h b/compiler-rt/lib/msan/msan.h
index 25fa2212bdadd3..b717161577a1db 100644
--- a/compiler-rt/lib/msan/msan.h
+++ b/compiler-rt/lib/msan/msan.h
@@ -321,6 +321,9 @@ 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())
+
 class ScopedThreadLocalStateBackup {
  public:
   ScopedThreadLocalStateBackup() { Backup(); }
diff --git a/compiler-rt/lib/msan/msan_allocator.cpp b/compiler-rt/lib/msan/msan_allocator.cpp
index 72a7f980d39fb0..f71f59cedf820e 100644
--- a/compiler-rt/lib/msan/msan_allocator.cpp
+++ b/compiler-rt/lib/msan/msan_allocator.cpp
@@ -180,17 +180,19 @@ void MsanThreadLocalMallocStorage::CommitBack() {
 
 static void *MsanAllocate(StackTrace *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;
     }
-    ReportAllocationSizeTooBig(size, max_malloc_size, stack);
+    GET_FATAL_STACK_TRACE;
+    ReportAllocationSizeTooBig(size, max_malloc_size, &stack);
   }
   if (UNLIKELY(IsRssLimitExceeded())) {
     if (AllocatorMayReturnNull())
       return nullptr;
-    ReportRssLimitExceeded(stack);
+    GET_FATAL_STACK_TRACE;
+    ReportRssLimitExceeded(&stack);
   }
   MsanThread *t = GetCurrentThread();
   void *allocated;
@@ -206,7 +208,8 @@ static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment,
     SetAllocatorOutOfMemory();
     if (AllocatorMayReturnNull())
       return nullptr;
-    ReportOutOfMemory(size, stack);
+    GET_FATAL_STACK_TRACE;
+    ReportOutOfMemory(size, &stack);
   }
   Metadata *meta =
       reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated));
@@ -288,7 +291,8 @@ static void *MsanCalloc(StackTrace *stack, uptr nmemb, uptr size) {
   if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
     if (AllocatorMayReturnNull())
       return nullptr;
-    ReportCallocOverflow(nmemb, size, stack);
+    GET_FATAL_STACK_TRACE;
+    ReportCallocOverflow(nmemb, size, &stack);
   }
   return MsanAllocate(stack, nmemb * size, sizeof(u64), true);
 }
@@ -343,7 +347,8 @@ void *msan_reallocarray(void *ptr, uptr nmemb, uptr size, StackTrace *stack) {
     errno = errno_ENOMEM;
     if (AllocatorMayReturnNull())
       return nullptr;
-    ReportReallocArrayOverflow(nmemb, size, stack);
+    GET_FATAL_STACK_TRACE;
+    ReportReallocArrayOverflow(nmemb, size, &stack);
   }
   return msan_realloc(ptr, nmemb * size, stack);
 }
@@ -358,7 +363,8 @@ void *msan_pvalloc(uptr size, StackTrace *stack) {
     errno = errno_ENOMEM;
     if (AllocatorMayReturnNull())
       return nullptr;
-    ReportPvallocOverflow(size, stack);
+    GET_FATAL_STACK_TRACE;
+    ReportPvallocOverflow(size, &stack);
   }
   // pvalloc(0) should allocate one page.
   size = size ? RoundUpTo(size, PageSize) : PageSize;
@@ -370,7 +376,8 @@ void *msan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack) {
     errno = errno_EINVAL;
     if (AllocatorMayReturnNull())
       return nullptr;
-    ReportInvalidAlignedAllocAlignment(size, alignment, stack);
+    GET_FATAL_STACK_TRACE;
+    ReportInvalidAlignedAllocAlignment(size, alignment, &stack);
   }
   return SetErrnoOnNull(MsanAllocate(stack, size, alignment, false));
 }
@@ -380,7 +387,8 @@ void *msan_memalign(uptr alignment, uptr size, StackTrace *stack) {
     errno = errno_EINVAL;
     if (AllocatorMayReturnNull())
       return nullptr;
-    ReportInvalidAllocationAlignment(alignment, stack);
+    GET_FATAL_STACK_TRACE;
+    ReportInvalidAllocationAlignment(alignment, &stack);
   }
   return SetErrnoOnNull(MsanAllocate(stack, size, alignment, false));
 }
@@ -390,7 +398,8 @@ int msan_posix_memalign(void **memptr, uptr alignment, uptr size,
   if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
     if (AllocatorMayReturnNull())
       return errno_EINVAL;
-    ReportInvalidPosixMemalignAlignment(alignment, stack);
+    GET_FATAL_STACK_TRACE;
+    ReportInvalidPosixMemalignAlignment(alignment, &stack);
   }
   void *ptr = MsanAllocate(stack, size, alignment, false);
   if (UNLIKELY(!ptr))
diff --git a/compiler-rt/lib/msan/msan_new_delete.cpp b/compiler-rt/lib/msan/msan_new_delete.cpp
index d4e95c0f65137d..3329a05cca2a86 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;               \
+      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;                              \
+      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}}

@MaskRay
Copy link
Member

MaskRay commented Jan 7, 2024

In some functions there is a parameter *stack and a variable stack in {} that shadows the parameter. I wonder whether the variable name for FATAL should be changed to reduce confusion...

@vitalybuka
Copy link
Collaborator Author

In some functions there is a parameter *stack and a variable stack in {} that shadows the parameter. I wonder whether the variable name for FATAL should be changed to reduce confusion...

That's kind of convention for all sanitizers, if we have macro, it create stack local object.
I am not rely concerned with shadowing, at it's only for fatal cases, but if you strong about that, I'd rather change parameters in allocator.

@vitalybuka vitalybuka requested a review from MaskRay January 8, 2024 19:30
We will need it to unwind for fatal errors.

Pull Request: #77363
Msan does not unwind stack in malloc without origins, but we still need trace
for fatal errors.

Pull Request: #77168
@vitalybuka vitalybuka changed the base branch from main to users/vitalybuka/spr/nfcmsan-switch-allocator-interface-to-use-bufferedstacktrace January 8, 2024 19:50
@vitalybuka vitalybuka force-pushed the users/vitalybuka/spr/msan-unwind-stack-before-fatal-reports branch from 4301f9a to 1de5a3d Compare January 8, 2024 19:51
Base automatically changed from users/vitalybuka/spr/nfcmsan-switch-allocator-interface-to-use-bufferedstacktrace to main January 8, 2024 19:52
Created using spr 1.3.4
@MaskRay
Copy link
Member

MaskRay commented Jan 8, 2024

I am not rely concerned with shadowing, at it's only for fatal cases, but if you strong about that, I'd rather change parameters in allocator.

Ah, I'm not concerned. You make the call whether the parameter or the local variable (both named stack) should be renamed to avoid confusion.

@MaskRay
Copy link
Member

MaskRay commented Jan 8, 2024

Ah, I'm not concerned. You make the call whether the parameter or the local variable (both named stack) should be renamed to avoid confusion.

nvm. You changed the patch to reuse the variable BufferedStackTrace stack. This is better!

Created using spr 1.3.4
@vitalybuka vitalybuka merged commit 8980936 into main Jan 9, 2024
@vitalybuka vitalybuka deleted the users/vitalybuka/spr/msan-unwind-stack-before-fatal-reports branch January 9, 2024 01:15
justinfargnoli pushed a commit to justinfargnoli/llvm-project that referenced this pull request Jan 28, 2024
Msan does not unwind stack in malloc without origins, but we still need
trace
for fatal errors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants