llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.68k stars 11.86k forks source link

[compiler-rt] heap-use-after-free may cause deadblock when program exit. #84009

Open ehds opened 7 months ago

ehds commented 7 months ago

Describtion

If a detached thread trigger heap-use-after-free during program exiting, it will cause a deadblock.

Reproduction

Creating 5 never join threads to write globa_p which will be released before program exit, theses threads would trigger heap-use-after-free error, then lead to deadblock.

clang++ version:

test.cc

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>

int *globa_p; // global int ptr.

void *task(void *argv) {
  for (;;) {
    *globa_p = 1;  // memory maybe released when process exit. it will cause
                   // heap-use-after-free error.
  }
}

int main() {
  globa_p = new int(1);

  pthread_t t1, t2, t3, t4, t5, t6;
  pthread_create(&t1, NULL, task, NULL);
  pthread_create(&t3, NULL, task, NULL);
  pthread_create(&t2, NULL, task, NULL);
  pthread_create(&t4, NULL, task, NULL);
  pthread_create(&t5, NULL, task, NULL);
  pthread_create(&t6, NULL, task, NULL);

  // waiting for tasks start.
  sleep(1);

  delete globa_p;  // release memory.
  // Not join threas.
}
root@506ddec0995f:/tmp# clang++  test.cc -o test -g  -fsanitize=address -O0
root@506ddec0995f:/tmp# ./test
=================================================================
==12019==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010 at pc 0x0000004c6b45 bp 0x7f5a585fbe40 sp 0x7f5a585fbe38
WRITE of size 4 at 0x602000000010 thread T4

Program print one line heap-use-after-free ERROR info, and blocked.

The stack information is as follows:

  thread #1, name = 'test', stop reason = signal SIGSTOP
    frame #0: 0x00000000004ab060 test`__sanitizer::BlockingMutex::Lock() + 64
    frame #1: 0x00000000004be129 test`__lsan::LockStuffAndStopTheWorldCallback(dl_phdr_info*, unsigned long, void*) + 9
    frame #2: 0x00007f5a5d3a83d5 libc.so.6`__GI___dl_iterate_phdr(callback=(test`__lsan::LockStuffAndStopTheWorldCallback(dl_phdr_info*, unsigned long, void*)), data=0x00007ffd56add758) at dl-iteratephdr.c:75:13
    frame #3: 0x00000000004be10f test`__lsan::LockStuffAndStopTheWorld(void (*)(__sanitizer::SuspendedThreadsList const&, void*), void*) + 31
    frame #4: 0x00000000004bb8c8 test`__lsan::CheckForLeaks() + 104
    frame #5: 0x00000000004bb842 test`__lsan::DoLeakCheck() + 34
    frame #6: 0x00007f5a5d28f8a7 libc.so.6`__run_exit_handlers(status=0, listp=0x00007f5a5d435718, run_list_atexit=true, run_dtors=true) at exit.c:108:8
    frame #7: 0x00007f5a5d28fa60 libc.so.6`__GI_exit(status=<unavailable>) at exit.c:139:3
    frame #8: 0x00007f5a5d26d08a libc.so.6`__libc_start_main(main=(test`main at test.cc:21), argc=1, argv=0x00007ffd56add958, init=<unavailable>, fini=<unavailable>, rtld_fini=<unavailable>, stack_end=0x00007ffd56add948) at libc-start.c:342:3
  thread #5, name = 'test', stop reason = signal SIGSTOP
    frame #0: 0x00007f5a5d47b170 libpthread.so.0`__lll_lock_wait(futex=0x00007f5a5d7fb990, private=0) at lowlevellock.c:52:7
    frame #1: 0x00007f5a5d473131 libpthread.so.0`__GI___pthread_mutex_lock(mutex=0x00007f5a5d7fb990) at pthread_mutex_lock.c:115:7
    frame #2: 0x00007f5a5d3a8291 libc.so.6`__GI___dl_iterate_phdr(callback=(libgcc_s.so.1`___lldb_unnamed_symbol290), data=0x00007f5a585fa340) at dl-iteratephdr.c:40:3
    frame #3: 0x00007f5a5d4506c1 libgcc_s.so.1`_Unwind_Find_FDE + 97
    frame #4: 0x00007f5a5d44c868 libgcc_s.so.1`___lldb_unnamed_symbol272 + 104
    frame #5: 0x00007f5a5d44da20 libgcc_s.so.1`___lldb_unnamed_symbol275 + 80
    frame #6: 0x00007f5a5d44e76c libgcc_s.so.1`_Unwind_Backtrace + 60
    frame #7: 0x00000000004ba7e4 test`__sanitizer::BufferedStackTrace::UnwindSlow(unsigned long, unsigned int) + 68
    frame #8: 0x000000000049d573 test`__sanitizer::BufferedStackTrace::UnwindImpl(unsigned long, unsigned long, void*, bool, unsigned int) + 179
    frame #9: 0x000000000042811d test`__asan::ErrorGeneric::Print() + 397
    frame #10: 0x00000000004988b9 test`__asan::ScopedInErrorReport::~ScopedInErrorReport() + 57
    frame #11: 0x000000000049a4ee test`__asan::ReportGenericError(unsigned long, unsigned long, unsigned long, unsigned long, bool, unsigned long, unsigned int, bool) + 1198
    frame #12: 0x000000000049b07b test`__asan_report_store4 + 43
    frame #13: 0x00000000004c6b45 test`task(argv=0x0000000000000000) at test.cc:14:14
    frame #14: 0x00007f5a5d470609 libpthread.so.0`start_thread(arg=<unavailable>) at pthread_create.c:477:8
    frame #15: 0x00007f5a5d368133 libc.so.6`__clone at clone.S:95
ehds commented 7 months ago

Possible fixes:

--- a/compiler-rt/lib/lsan/lsan_common_linux.cpp
+++ b/compiler-rt/lib/lsan/lsan_common_linux.cpp
@@ -122,7 +122,6 @@ void HandleLeaks() {

 static int LockStuffAndStopTheWorldCallback(struct dl_phdr_info *info,
                                             size_t size, void *data) {
-  ScopedStopTheWorldLock lock;
   DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data);
   StopTheWorld(param->callback, param->argument);
   return 1;
@@ -139,6 +138,7 @@ static int LockStuffAndStopTheWorldCallback(struct dl_phdr_info *info,
 void LockStuffAndStopTheWorld(StopTheWorldCallback callback,
                               CheckForLeaksParam *argument) {
   DoStopTheWorldParam param = {callback, argument};
+  ScopedStopTheWorldLock lock;
   dl_iterate_phdr(LockStuffAndStopTheWorldCallback, &param);
 }