On 03/27/2011 10:21 PM, Rich Felker wrote:
> On Sat, Mar 26, 2011 at 08:28:04PM +0200, Timo Teräs wrote:
>> Otherwise other threads can leave malloc state locked, and the child
>> will hang indefinitely if it tries to malloc something.
>
> This change by itself breaks as much as it fixes, unless the mutex
> implementation is reentrant and recursive. fork() is specified to be
> async-signal-safe, which means it's legal for a signal handler to call
> fork() while malloc() is being executed. With your patch, this will
> hang the program.

It is recursive mutex. It's works perfectly fine if same thread locks
that mutex multiple times.

Doing fork from signal handler seems utterly stupid. That would need
extra precautions to work properly, because the malloc code is not
re-entrant.

But this is not the case I'm fixing. It's thread A doing malloc while
thread B is forking.

As such, this does not break anything. Or could you provide example code
on scenario that this breaks (and which was not broken before this)?

> I ran into the same issue developing musl's implementation of malloc,
> and for now I have left it alone. POSIX 2008 (SUSv4) went back on some
> of the promises of previous versions, and has now declared that it's
> UB to call any async-signal-unsafe functions (such as malloc()) after
> fork() in a multi-threaded program, so your patch is not needed to
> conform to the current standard, only to previous versions (and even
> then it's not clear that the older standard required malloc() after
> fork() to work in a threaded program).

Yes. I agree, it's bad practice do things like that. But it just so
happens, that there are programs doing this. This can also happen
indirectly in certain scenarios: e.g. using opendir/readdir of
/proc/self/fd to close open fd's. (opendir in uclibc seems ot use
malloc). This patch is intended to provide glibc compatibility with the
more or less broken programs.

> I may later revisit this issue and "fix" it, but the fix is not simply
> using locks. I would either need to ignore locking and actively repair
> all the potentially-corrupted malloc structures in the child process,
> or selectively determine which locks are held by the current thread
> (due to having interrupted malloc() with a signal handler) and only
> attempt to lock the other locks before forking. For uClibc which only
> has one big malloc() lock, it may be easier, but I think you'll at
> least need to fix the lock to be reentrant and either recursive or
> error-checking. And that of course has a cost...

This prevents other threads not to execute malloc while we are forking.
Thus assuring that we have consistent malloc-state for the child
process. Since the thread doing fork has malloc-lock, it will own the
lock, thus the lock can be safely reinitialized in the child (we need to
reinitilize it since TID change on fork). When the parent continues, the
lock is unlocked regularly and memory allocation can continue as normal.
This exactly what glibc does and it works.

This does not address the case that the thread forking is doing malloc
and forks from signal handler. That case is just utterly broken and not
worth fixing IMHO.

- Timo
_______________________________________________
uClibc mailing list
uClibc@uclibc.org
http://lists.busybox.net/mailman/listinfo/uclibc

Reply via email to