Skip to content
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

[asan] Use InternalMmapVectorNoCtor for error_message_buffer, reallocate if needed #77488

Merged
merged 7 commits into from
Feb 19, 2024

Conversation

Enna1
Copy link
Contributor

@Enna1 Enna1 commented Jan 9, 2024

  • Add a param raw_report which defaults to false for function UnmapOrDie() like we do for MmapOrDie()
  • Add a template param raw_report which defaults to false for class InternalMmapVectorNoCtor. raw_report will be passed to MmapOrDie() and UnmapOrDie() when they are called in class InternalMmapVectorNoCtor member functions.
  • Use InternalMmapVectorNoCtor<char, true> for error_message_buffer and reallocate if needed.

…en the size of error_message_buffer is not enough
@llvmbot
Copy link
Collaborator

llvmbot commented Jan 9, 2024

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

Author: Enna1 (Enna1)

Changes

…en the size of error_message_buffer is not enough


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

1 Files Affected:

  • (modified) compiler-rt/lib/asan/asan_report.cpp (+13-16)
diff --git a/compiler-rt/lib/asan/asan_report.cpp b/compiler-rt/lib/asan/asan_report.cpp
index 7603e8131154ba..1cd21bfbc0bfeb 100644
--- a/compiler-rt/lib/asan/asan_report.cpp
+++ b/compiler-rt/lib/asan/asan_report.cpp
@@ -32,8 +32,7 @@ namespace __asan {
 
 // -------------------- User-specified callbacks ----------------- {{{1
 static void (*error_report_callback)(const char*);
-static char *error_message_buffer = nullptr;
-static uptr error_message_buffer_pos = 0;
+static InternalMmapVector<char> *error_message_buffer = nullptr;
 static Mutex error_message_buf_mutex;
 static const unsigned kAsanBuggyPcPoolSize = 25;
 static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize];
@@ -42,17 +41,15 @@ void AppendToErrorMessageBuffer(const char *buffer) {
   Lock l(&error_message_buf_mutex);
   if (!error_message_buffer) {
     error_message_buffer =
-      (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__);
-    error_message_buffer_pos = 0;
+        new (GetGlobalLowLevelAllocator()) InternalMmapVector<char>();
+    error_message_buffer->reserve(kErrorMessageBufferSize);
+    error_message_buffer->push_back('\0');
   }
-  uptr length = internal_strlen(buffer);
-  RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos);
-  uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos;
-  internal_strncpy(error_message_buffer + error_message_buffer_pos,
-                   buffer, remaining);
-  error_message_buffer[kErrorMessageBufferSize - 1] = '\0';
-  // FIXME: reallocate the buffer instead of truncating the message.
-  error_message_buffer_pos += Min(remaining, length);
+  uptr error_message_buffer_len = error_message_buffer->size() - 1;
+  uptr buffer_len = internal_strlen(buffer);
+  error_message_buffer->resize(error_message_buffer_len + buffer_len + 1);
+  internal_memcpy(error_message_buffer->data() + error_message_buffer_len,
+                  buffer, buffer_len + 1);
 }
 
 // ---------------------- Helper functions ----------------------- {{{1
@@ -158,14 +155,14 @@ class ScopedInErrorReport {
 
     // Copy the message buffer so that we could start logging without holding a
     // lock that gets acquired during printing.
-    InternalMmapVector<char> buffer_copy(kErrorMessageBufferSize);
+    InternalScopedString buffer_copy;
     {
       Lock l(&error_message_buf_mutex);
-      internal_memcpy(buffer_copy.data(),
-                      error_message_buffer, kErrorMessageBufferSize);
+      buffer_copy.Append(error_message_buffer->data());
       // Clear error_message_buffer so that if we find other errors
       // we don't re-log this error.
-      error_message_buffer_pos = 0;
+      error_message_buffer->clear();
+      error_message_buffer->push_back('\0');
     }
 
     LogFullErrorReport(buffer_copy.data());

@Enna1
Copy link
Contributor Author

Enna1 commented Jan 9, 2024

Not sure if InternalScopedString is more appropriate for error_message_buffer,
I did a grep, not found any usage of InternalScopedString as a global variable...

@@ -42,17 +41,15 @@ void AppendToErrorMessageBuffer(const char *buffer) {
Lock l(&error_message_buf_mutex);
if (!error_message_buffer) {
error_message_buffer =
(char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__);
error_message_buffer_pos = 0;
new (GetGlobalLowLevelAllocator()) InternalMmapVector<char>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of Allocator, can you use instead approach used for thread_registry_placeholder?

error_message_buffer_pos = 0;
new (GetGlobalLowLevelAllocator()) InternalMmapVector<char>();
error_message_buffer->reserve(kErrorMessageBufferSize);
error_message_buffer->push_back('\0');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need \0?

error_message_buffer_pos += Min(remaining, length);
uptr error_message_buffer_len = error_message_buffer->size() - 1;
uptr buffer_len = internal_strlen(buffer);
error_message_buffer->resize(error_message_buffer_len + buffer_len + 1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error_message_buffer->Append() ?

@vitalybuka
Copy link
Collaborator

Not sure if InternalScopedString is more appropriate for error_message_buffer, I did a grep, not found any usage of InternalScopedString as a global variable...

I believe we need some refactoring to avoid dynamic initializer of InternalScopedString global.
If you like, you can try to investigate.

If not, please try thread_registry_placeholder approach

@Enna1
Copy link
Contributor Author

Enna1 commented Jan 10, 2024

Hi @vitalybuka , thanks for looking into this.
I have reverted the previous change using InternalMmapVector,
because it caused a deadlock when running the asan/TestCases/Linux/rlimit_mmap_test.cpp test.
See https://buildkite.com/llvm-project/github-pull-requests/builds/27725.

This is because when asan reports an OOM, the PrintfAndReportCallback AppendToErrorMessageBuffer() is called.
As I use InternalMmapVector for error_message_buffer, MmapOrDie() is called for allocating buffer, but will fail because of OOM.
When MmapOrDie() fails, ReportMmapFailureAndDie() is called, then Report() is called, finally the PrintfAndReportCallback AppendToErrorMessageBuffer() is called again, causes a deadlock.

The following is the deadlock stack trace:

#0  0x0000560049fb9ae9 in __sanitizer::FutexWait (p=p@entry=0x56004a023180 <__asan::error_message_buf_mutex+16>, cmp=cmp@entry=0)
    at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp:724
#1  0x0000560049fba9f5 in __sanitizer::Semaphore::Wait (this=this@entry=0x56004a023180 <__asan::error_message_buf_mutex+16>)
    at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_mutex.cpp:35
#2  0x0000560049fa418c in __sanitizer::Mutex::Lock (this=0x56004a023170 <__asan::error_message_buf_mutex>)
    at llvm-project/compiler-rt/lib/asan/../sanitizer_common/sanitizer_mutex.h:196
#3  __sanitizer::GenericScopedLock<__sanitizer::Mutex>::GenericScopedLock (mu=0x56004a023170 <__asan::error_message_buf_mutex>, this=<synthetic pointer>)
    at llvm-project/compiler-rt/lib/asan/../sanitizer_common/sanitizer_mutex.h:383
#4  __asan::AppendToErrorMessageBuffer (
    buffer=0x7fff8eb03640 "==225129==ERROR: AddressSanitizer: out of memory: failed to allocate 0x10000 (65536) bytes of InternalMmapVector (error code: 12)\n")
    at llvm-project/compiler-rt/lib/asan/asan_report.cpp:41
#5  0x0000560049fbd294 in __sanitizer::CallPrintfAndReportCallback (
    str=0x7fff8eb03640 "==225129==ERROR: AddressSanitizer: out of memory: failed to allocate 0x10000 (65536) bytes of InternalMmapVector (error code: 12)\n")
    at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_printf.cpp:244
#6  __sanitizer::SharedPrintfCodeNoBuffer(bool, char *, int, const char *, typedef __va_list_tag __va_list_tag *) (append_pid=append_pid@entry=true,
    local_buffer=local_buffer@entry=0x7fff8eb03640 "==225129==ERROR: AddressSanitizer: out of memory: failed to allocate 0x10000 (65536) bytes of InternalMmapVector (error code: 12)\n",
    buffer_size=buffer_size@entry=400, format=format@entry=0x560049ff4200 "ERROR: %s: out of memory: failed to %s 0x%zx (%zd) bytes of %s (error code: %d)\n", args=args@entry=0x7fff8eb037e8)
    at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_printf.cpp:296
#7  0x0000560049fbd2d8 in __sanitizer::SharedPrintfCode(bool, const char *, typedef __va_list_tag __va_list_tag *) (append_pid=append_pid@entry=true,
    format=format@entry=0x560049ff4200 "ERROR: %s: out of memory: failed to %s 0x%zx (%zd) bytes of %s (error code: %d)\n", args=args@entry=0x7fff8eb037e8)
    at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_printf.cpp:309
#8  0x0000560049fbd410 in __sanitizer::Report (format=format@entry=0x560049ff4200 "ERROR: %s: out of memory: failed to %s 0x%zx (%zd) bytes of %s (error code: %d)\n")
    at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_printf.cpp:324
#9  0x0000560049fb3566 in __sanitizer::ReportMmapFailureAndDie (size=size@entry=65536, mem_type=mem_type@entry=0x560049fea341 "InternalMmapVector",
    mmap_type=mmap_type@entry=0x560049ff7db0 "allocate", err=12, raw_report=raw_report@entry=false)
    at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_common.cpp:50
#10 0x0000560049fbbb58 in __sanitizer::MmapOrDie (size=size@entry=65536, mem_type=mem_type@entry=0x560049fea341 "InternalMmapVector", raw_report=raw_report@entry=false)
    at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_posix.cpp:52
#11 0x0000560049f0905b in __sanitizer::InternalMmapVectorNoCtor<char>::Realloc (this=0x7f27f87f2e18, new_capacity=new_capacity@entry=65536)
    at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_common.h:599
#12 0x0000560049fa422a in __sanitizer::InternalMmapVectorNoCtor<char>::reserve (new_size=65536, this=<optimized out>)
    at llvm-project/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common.h:558
#13 __asan::AppendToErrorMessageBuffer (buffer=0x7fff8eb03a40 '=' <repeats 65 times>, "\n") at llvm-project/compiler-rt/lib/asan/asan_report.cpp:45
#14 0x0000560049fbd294 in __sanitizer::CallPrintfAndReportCallback (str=0x7fff8eb03a40 '=' <repeats 65 times>, "\n")
    at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_printf.cpp:244
#15 __sanitizer::SharedPrintfCodeNoBuffer(bool, char *, int, const char *, typedef __va_list_tag __va_list_tag *) (append_pid=append_pid@entry=false,
    local_buffer=local_buffer@entry=0x7fff8eb03a40 '=' <repeats 65 times>, "\n", buffer_size=buffer_size@entry=400, format=format@entry=0x560049ff2628 '=' <repeats 65 times>, "\n",
    args=args@entry=0x7fff8eb03be8) at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_printf.cpp:296
#16 0x0000560049fbd2d8 in __sanitizer::SharedPrintfCode(bool, const char *, typedef __va_list_tag __va_list_tag *) (append_pid=append_pid@entry=false,
    format=format@entry=0x560049ff2628 '=' <repeats 65 times>, "\n", args=args@entry=0x7fff8eb03be8)
    at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_printf.cpp:309
#17 0x0000560049fbd374 in __sanitizer::Printf (format=format@entry=0x560049ff2628 '=' <repeats 65 times>, "\n")
    at llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_printf.cpp:316
#18 0x0000560049fa6818 in __asan::ScopedInErrorReport::ScopedInErrorReport (fatal=true, this=0x7fff8eb0476e)
    at llvm-project/compiler-rt/lib/asan/asan_report.cpp:130
#19 __asan::ReportOutOfMemory (requested_size=requested_size@entry=10000000, stack=stack@entry=0x7fff8eb04870)
    at llvm-project/compiler-rt/lib/asan/asan_report.cpp:321
--Type <RET> for more, q to quit, c to continue without paging--
#20 0x0000560049f0196d in __asan::Allocator::Allocate (this=this@entry=0x56004a025d60 <__asan::instance>, size=<optimized out>, size@entry=10000000, alignment=alignment@entry=8,
    stack=stack@entry=0x7fff8eb04870, alloc_type=alloc_type@entry=__asan::FROM_MALLOC, can_fill=can_fill@entry=true)
    at llvm-project/compiler-rt/lib/asan/asan_allocator.cpp:592
#21 0x0000560049eff57f in __asan::asan_malloc (size=size@entry=10000000, stack=stack@entry=0x7fff8eb04870)
    at llvm-project/compiler-rt/lib/asan/asan_allocator.cpp:1000
#22 0x0000560049f9e009 in ___interceptor_malloc (size=10000000) at llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:69
#23 0x0000560049fe8426 in main () at llvm-project/compiler-rt/test/asan/TestCases/Linux/rlimit_mmap_test.cpp:13

So in the lastest patch, I manually call MmapOrDieQuietly() and UnmapOrDieQuietly() to reallocate error_message_buffer.

If we can provide a variant InternalMmapVector, which calls MmapOrDie() with raw_report=true instead of calling MmapOrDie() with default raw_report=false, the deadlock will be fixed.
I think we can add raw_report as a template parameter for InternalMmapVector ?

@Enna1 Enna1 requested a review from vitalybuka January 11, 2024 00:11
…ocate when the size of error_message_buffer is not enough"

Fix deadlock

Address review comments
@Enna1
Copy link
Contributor Author

Enna1 commented Jan 16, 2024

PTAL.

@Enna1
Copy link
Contributor Author

Enna1 commented Jan 18, 2024

ping

@Enna1
Copy link
Contributor Author

Enna1 commented Jan 25, 2024

ping :)

@Enna1
Copy link
Contributor Author

Enna1 commented Jan 31, 2024

ping @vitalybuka

@Enna1
Copy link
Contributor Author

Enna1 commented Feb 9, 2024

ping!

BTW, do you think move error_message_buffer into ScopedInErrorReport as a class member is a better approach?
IMHO, this approach is more clear.

But with this approach, there will be a behavior change:

  • Currently, if asan and ubsan both enabled, ubsan error report will be mixed with asan error report, and passed to error report callback
  • With this approach, if asan and ubsan both enabled, ubsan error report will not be mixed with asan error report, only asan error report will be passed to error report callback

WDYT?

--- a/compiler-rt/lib/asan/asan_report.cpp
+++ b/compiler-rt/lib/asan/asan_report.cpp
@@ -24,6 +24,7 @@
 // -------------------- User-specified callbacks ----------------- {{{1
 static void (*error_report_callback)(const char*);
-static char *error_message_buffer = nullptr;
-static uptr error_message_buffer_pos = 0;
+typedef InternalMmapVectorNoCtor<char, true> ErrorMessageBuffer;
+static ErrorMessageBuffer *error_message_buf_ptr = nullptr;
 static Mutex error_message_buf_mutex;
 static const unsigned kAsanBuggyPcPoolSize = 25;
 static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize];
 
 void AppendToErrorMessageBuffer(const char *buffer) {
   Lock l(&error_message_buf_mutex);
-  if (!error_message_buffer) {
-    error_message_buffer =
-      (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__);
-    error_message_buffer_pos = 0;
-  }
-  uptr length = internal_strlen(buffer);
-  RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos);
-  uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos;
-  internal_strncpy(error_message_buffer + error_message_buffer_pos,
-                   buffer, remaining);
-  error_message_buffer[kErrorMessageBufferSize - 1] = '\0';
-  // FIXME: reallocate the buffer instead of truncating the message.
-  error_message_buffer_pos += Min(remaining, length);
+  if (!error_message_buf_ptr)
+    return;
+  uptr error_message_buf_len = error_message_buf_ptr->size();
+  uptr buffer_len = internal_strlen(buffer);
+  error_message_buf_ptr->resize(error_message_buf_len + buffer_len);
+  internal_memcpy(error_message_buf_ptr->data() + error_message_buf_len, buffer,
+                  buffer_len);
 }
 
 // ---------------------- Helper functions ----------------------- {{{1
@@ -130,6 +125,11 @@ class ScopedInErrorReport {
     // We can lock them only here to avoid self-deadlock in case of
     // recursive reports.
     asanThreadRegistry().Lock();
+    {
+      Lock l(&error_message_buf_mutex);
+      error_message_buffer_.Initialize(kErrorMessageBufferSize);
+      error_message_buf_ptr = &error_message_buffer_;
+    }
     Printf(
         "=================================================================\n");
   }
@@ -156,22 +156,16 @@ class ScopedInErrorReport {
     if (common_flags()->print_module_map == 2)
       DumpProcessMap();
 
-    // Copy the message buffer so that we could start logging without holding a
-    // lock that gets acquired during printing.
-    InternalMmapVector<char> buffer_copy(kErrorMessageBufferSize);
     {
       Lock l(&error_message_buf_mutex);
-      internal_memcpy(buffer_copy.data(),
-                      error_message_buffer, kErrorMessageBufferSize);
-      // Clear error_message_buffer so that if we find other errors
-      // we don't re-log this error.
-      error_message_buffer_pos = 0;
+      error_message_buf_ptr = nullptr;
+      error_message_buffer_.push_back('\0');
     }
 
-    LogFullErrorReport(buffer_copy.data());
+    LogFullErrorReport(error_message_buffer_.data());
 
     if (error_report_callback) {
-      error_report_callback(buffer_copy.data());
+      error_report_callback(error_message_buffer_.data());
     }
 
     if (halt_on_error_ && common_flags()->abort_on_error) {
@@ -179,7 +173,7 @@ class ScopedInErrorReport {
       // FIXME: implement "compact" error format, possibly without, or with
       // highly compressed stack traces?
       // FIXME: or just use the summary line as abort message?
-      SetAbortMessage(buffer_copy.data());
+      SetAbortMessage(error_message_buffer_.data());
     }
 
     // In halt_on_error = false mode, reset the current error object (before
@@ -205,6 +199,7 @@ class ScopedInErrorReport {
 
  private:
   ScopedErrorReportLock error_report_lock_;
+  ErrorMessageBuffer error_message_buffer_;
   // Error currently being reported. This enables the destructor to interact
   // with the debugger and point it to an error description.
   static ErrorDescription current_error_;

@Enna1
Copy link
Contributor Author

Enna1 commented Feb 16, 2024

ping :) @vitalybuka

@@ -32,8 +33,11 @@ namespace __asan {

// -------------------- User-specified callbacks ----------------- {{{1
static void (*error_report_callback)(const char*);
static char *error_message_buffer = nullptr;
static uptr error_message_buffer_pos = 0;
typedef InternalMmapVectorNoCtor<char, true> ErrorMessageBuffer;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typedef -> using

@llvmbot llvmbot added the compiler-rt:asan Address sanitizer label Feb 17, 2024
@Enna1 Enna1 changed the title [asan] Use InternalMmapVector for error_message_buffer, reallocate wh… [asan] Use InternalMmapVector for error_message_buffer, reallocate if needed Feb 17, 2024
@Enna1 Enna1 changed the title [asan] Use InternalMmapVector for error_message_buffer, reallocate if needed [asan] Use InternalMmapVectorNoCtor for error_message_buffer, reallocate if needed Feb 17, 2024
@Enna1 Enna1 merged commit 6479006 into main Feb 19, 2024
6 checks passed
@Enna1 Enna1 deleted the users/Enna1/asan-error-message-buffer branch February 19, 2024 02:27
Enna1 added a commit that referenced this pull request Feb 19, 2024
In #77488, a param `raw_report`
is added for function `UnmapOrDie()`.
But missing the corresponding change for fuchsia, causes the buildbot
failure, see https://lab.llvm.org/buildbot/#/builders/98/builds/33593.
This patch should fix the fuchsia buildbot failure.
@vitalybuka
Copy link
Collaborator

Windows also needs a fix https://lab.llvm.org/buildbot/#/builders/127/builds/62395

@Enna1
Copy link
Contributor Author

Enna1 commented Feb 19, 2024

Thanks, will done soon!

Enna1 added a commit that referenced this pull request Feb 19, 2024
In #77488, a param raw_report is added for function UnmapOrDie(),
causes the Windows buildbot failure, see https://lab.llvm.org/buildbot/#/builders/127/builds/62395.
This patch should fix the Windows buildbot failure.
Enna1 added a commit that referenced this pull request Feb 19, 2024
In #77488, a param raw_report is added for function UnmapOrDie(), causes
the Windows buildbot failure:
- https://lab.llvm.org/buildbot/#/builders/127/builds/62395
- https://lab.llvm.org/buildbot/#/builders/265/builds/2662

This patch should fix the Windows buildbot failure.
PiJoules added a commit to PiJoules/llvm-project that referenced this pull request Feb 22, 2024
As of llvm#77488, UnmapOrDie now accepts raw_report which allows the program
to crash without calling Report(). We should propogate this value
through UnmapOrDieVmar and have that call ReportMunmapFailureAndDie
which uses `raw_report`.
PiJoules added a commit that referenced this pull request Feb 22, 2024
As of #77488, UnmapOrDie now accepts raw_report which allows the program
to crash without calling Report(). We should propogate this value
through UnmapOrDieVmar and have that call ReportMunmapFailureAndDie
which uses `raw_report`.
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.

None yet

3 participants