Bug 13502 - SEGFAULT in fork() when pthread_atfork() was called from a library loaded/unloaded with dlopen/dlclose
Summary: SEGFAULT in fork() when pthread_atfork() was called from a library loaded/unl...
Status: RESOLVED WORKSFORME
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: 2.12
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-12-15 12:16 UTC by Olga Malysheva
Modified: 2019-02-28 13:56 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments
reproducer (470 bytes, application/octet-stream)
2011-12-15 12:16 UTC, Olga Malysheva
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Olga Malysheva 2011-12-15 12:16:54 UTC
Created attachment 6112 [details]
reproducer

There is a shared library that calls pthread_atfork() (at least one handler must be NOT NULL) during initialization.
There is an application that loads this shared library with dlopen() and then unloads it with dlclose().
Then the application calls fork(). 
Segfault occurs.

It looks like atfork handlers registered with pthread_atfork() were not removed during dlclose().

The problem can be reproduced with the attached test. 
Compile libfoo.c - "gcc -c -fPIC -o libfoo.o libfoo.c"
Create the shared library - "gcc -shared -o libfoo.so libfoo.o"
Compile fork_main.c - "gcc -o fork_main fork_main.c -ldl -lpthread"
Run ./fork_main.
Comment 1 Ondrej Bilka 2013-09-21 20:21:02 UTC
This problem cannot be solved without introducing new API.

Problem here is lack of function that removes fork handler.
Comment 2 Rich Felker 2013-09-22 05:22:07 UTC
Moreover, there's nothing safe that can be done from pthread_atfork handlers anyway; the whole concept of pthread_atfork was a mistake, and this is largely acknowledged in the Rationale section for pthread_atfork in POSIX.
Comment 3 Carlos O'Donell 2013-10-03 01:17:51 UTC
(In reply to Ondrej Bilka from comment #1)
> This problem cannot be solved without introducing new API.
> 
> Problem here is lack of function that removes fork handler.

The removal of the handlers could be done automatically when the DSO is unloaded.

Each dso has a unique __dso_handle inserted by the compiler which is used when registering the handlers via pthread_atfork.

When the DSO is unloaded we could look through the registered handlers and remove those that belong to the DSO being unloaded.

The only other robust alternatives are:

* mark the DSOs as unloadable e.g. -z nodelete.

* don't use pthread_atfork
Comment 4 Florian Weimer 2019-02-28 13:56:03 UTC
The way this is expected to work is via the libc_nonshared.a version of pthread_atfork, which provides the caller's identity through the __dso_handle argument.  (Previously, pthread_atfork wa in libpthread_nonshared.a.)

This has been fixed since the bug was reported, maybe as a result of commit 825adeeed1e95990fd1efb70d9ac3eb7f1ea802a ("Mark __dso_handle as hidden [BZ #18822]").

Note that if you declare pthread_atfork on your own, as a weak symbol, you will get the compat symbol from libpthread at run time, which cannot arrange for unregistration.  libcap-ng does that, and it needs to be fixed.