steleman / address-sanitizer

Automatically exported from code.google.com/p/address-sanitizer
0 stars 0 forks source link

Forking threads fails with clang 3.4 #255

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
I don't know what exactly is the source of the issue. I have a program that 
performs the following steps:
* fork 4 threads
* fork a new process
* fork 4 threads in the new process

The parent process performs some work before this that involves:
* (un)poisoning some memory
* handling some segv

Forking the new threads in the child process ends with the following errors (in 
every single thread):

=================================================================
==8377==ERROR: AddressSanitizer: stack-buffer-overflow on address 
0x7f9922ffdf28 at pc 0x4606d1 bp 0x7f9922ffdf00 sp 0x7f9922ffded8
WRITE of size 8 at 0x7f9922ffdf28 thread T7
==8377==AddressSanitizer: while reporting a bug found another one.Ignoring.
==8377==AddressSanitizer: while reporting a bug found another one.Ignoring.
    #0 0x4606d0 in __interceptor_pthread_attr_getstack /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc:2549
    #1 0x48d28a in __sanitizer::GetThreadStackTopAndBottom(bool, unsigned long*, unsigned long*) /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc:77
    #2 0x48d65d in __sanitizer::GetThreadStackAndTls(bool, unsigned long*, unsigned long*, unsigned long*, unsigned long*) /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc:248
    #3 0x484ab3 in SetThreadStackAndTls /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/asan/asan_thread.cc:184
    #4 0x484ab3 in __asan::AsanThread::Init() /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/asan/asan_thread.cc:142
    #5 0x484cff in __asan::AsanThread::ThreadStart(unsigned long) /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/asan/asan_thread.cc:157
    #6 0x7f99296c8e0d in start_thread /home/aurel32/eglibc/eglibc-2.17/nptl/pthread_create.c:311
    #7 0x7f99287900fc (/lib/x86_64-linux-gnu/libc.so.6+0xe90fc)

==8377==AddressSanitizer CHECK failed: 
/home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/asan/asan_thread.cc:23
1 "((ptr[0] == kCurrentStackFrameMagic)) != (0)" (0x0, 0x0)
    #0 0x4838ff in __asan::AsanCheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/asan/asan_rtl.cc:66
    #1 0x489161 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/sanitizer_common/sanitizer_common.cc:69
    #2 0x484f7e in __asan::AsanThread::GetFrameNameByAddr(unsigned long, unsigned long*, unsigned long*) /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/asan/asan_thread.cc:206
    #3 0x48048f in __asan::DescribeAddressIfStack(unsigned long, unsigned long) /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/asan/asan_report.cc:317
    #4 0x480edc in __asan::DescribeAddress(unsigned long, unsigned long) /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/asan/asan_report.cc:466
    #5 0x481d01 in __asan_report_error /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/asan/asan_report.cc:775
    #6 0x4606ee in __interceptor_pthread_attr_getstack /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc:2549
    #7 0x48d28a in __sanitizer::GetThreadStackTopAndBottom(bool, unsigned long*, unsigned long*) /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc:77
    #8 0x48d65d in __sanitizer::GetThreadStackAndTls(bool, unsigned long*, unsigned long*, unsigned long*, unsigned long*) /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc:248
    #9 0x484ab3 in SetThreadStackAndTls /home/fruneau/dev/tools/llvm_3.4/projects/compiler-rt/lib/asan/asan_thread.cc:184

This has been tested with clang 3.4 and compiler-rt revision 197381 (branch 
release_34). The issue does not occur with clang 3.3. I can provide more 
details if needed.

Original issue reported on code.google.com by florent....@intersec.com on 27 Dec 2013 at 9:38

GoogleCodeExporter commented 9 years ago
I afraid we can't do anything here. 
mixing fork and threads is not supposed to work even w/o asan, 
you can find many poofs of that in the net. 
Sorry. 

Original comment by konstant...@gmail.com on 28 Dec 2013 at 9:18

GoogleCodeExporter commented 9 years ago
Mixing fork and threads is clearly tricky, this is not forbidden and have some 
working use cases (in my case, snapshotting the state of a database)... you 
mostly have to make sure that the forked thread don't use mutexes from the 
parent process.

I'll find a workaround of my own, but I really think this is a regression from 
ASan since it worked perfectly from day 1 and only broke when using clang 3.4.

Original comment by florent....@intersec.com on 28 Dec 2013 at 10:03

GoogleCodeExporter commented 9 years ago
http://cppwisdom.quora.com/Why-threads-and-fork-dont-mix

Original comment by konstant...@gmail.com on 28 Dec 2013 at 5:06

GoogleCodeExporter commented 9 years ago
I've attached a patch that fix my issue (only on Linux, in a quick and dirty 
fashion). The idea is to have a registry of AsanThreads. When a thread is 
created or destroyed, the registry is updated. After forking, in the child 
process, we scan the registry in order to destroy all the threads that didn't 
survive the fork. The patch depends on pthread_atfork() to perform the cleanup. 
The registry implementation is naïve and limited. And only linux is 
implemented and tested. Also I didn't include a test.

I still perfectly agree that mixing fork and threads in the general case is 
dangerous. However, I also still believe that my use case is perfectly valid: I 
use fork() as a way to take a snapshot of the memory of my database in order to 
be able to make a snapshot while still accepting updates in the parent process 
(I don't think there is a better solution for this). The threads are not 
strictly required in the child process, however they are mandatory in the 
parent process. In my use case the child process only writes data that is in 
memory onto the disk, since it works on a snapshot of the memory it does not 
need synchronization (so all the argument about critical sections don't apply).

Hope this can help.

Original comment by florent....@intersec.com on 28 Dec 2013 at 9:54

Attachments:

GoogleCodeExporter commented 9 years ago
I am skeptical about anything that involves pthread_atfork -- 
from what I've heard this thing is broken beyond repair and only seems to work 
sometimes.
Still, if you provide a test case and a proper patch we may consider it, 
please see https://code.google.com/p/address-sanitizer/wiki/HowToContribute

Original comment by konstant...@gmail.com on 9 Jan 2014 at 7:58

GoogleCodeExporter commented 9 years ago
A comment from dvyukov@ that didn't make it into the bug tracker:

========================================
> I'll find a workaround of my own, but I really think this is a regression
> from ASan since it worked perfectly from day 1 and only broke when using
> clang 3.4.

Yes, strictly saying it is legal.
POSIX says:

"A process shall be created with a single thread. If a multi-threaded
process calls fork(), the new process shall contain a replica of the
calling thread and its entire address space, possibly including the
states of mutexes and other resources. Consequently, to avoid errors,
the child process may only execute async-signal-safe operations until
such time as one of the exec functions is called. [THR]   Fork
handlers may be established by means of thepthread_atfork() function
in order to maintain application invariants across fork() calls."
http://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html

It's not even required to execute only async-signal safe functions. If
you are careful to establish invariants, you can use any functions.
========================================

I'm afraid we'll finally need to intercept fork() (instead of using 
pthread_atfork()) and at least clean up the thread registry in 
sanitizer_common, because other tools may need it.
As we've seen recently, even in the case fork() is followed by exec() (which is 
totally legitimate) we should be really careful to avoid deadlocks in the 
tools' guts.

Original comment by ramosian.glider@gmail.com on 10 Jan 2014 at 2:39

GoogleCodeExporter commented 9 years ago
> I still perfectly agree that mixing fork and threads in the general case is 
> dangerous. However, I also still believe that my use case is perfectly valid: 
I use 
> fork() as a way to take a snapshot of the memory of my database in order to 
be able 
> to make a snapshot while still accepting updates in the parent process (I 
don't 
> think there is a better solution for this).
Side note: this is how process snapshotting works in Breakpad (an error 
reporting system used by Chromium). I don't think we want to prohibit this 
pattern.

Original comment by ramosian.glider@gmail.com on 10 Jan 2014 at 2:41