system() using vfork() or posix_spawn() and libthr

Konstantin Belousov kostikbel at gmail.com
Tue Aug 14 09:41:32 UTC 2012


On Tue, Aug 14, 2012 at 05:16:56PM +0800, David Xu wrote:
> On 2012/08/14 16:18, Konstantin Belousov wrote:
> >On Tue, Aug 14, 2012 at 12:42:15PM +0800, David Xu wrote:
> >>I simply duplicated idea from OpenSolaris, here is my patch
> >>which has similar feature as your patch, and it also tries to
> >>prevent vforked child from corrupting parent's data:
> >>http://people.freebsd.org/~davidxu/patch/libthr-vfork.diff
> >You shall not return from vfork() frame in the child. Otherwise, the
> >same frame is appears to be destroyed in parent, and parent dies. More
> >often on !x86, but right combination of events on x86 is deadly too.
> >If pid or curthread local variables are spilled into stack save area,
> >then child will override them, and e.g. parent could see pid == 0,
> >returning it to caller.
> >
> >This was the reason why I went to asm wrapper for vfork.
> >
> OK.
> 
> >Also, it seems that in mt process, malloc and rtld are still broken,
> >or am I missing something ?
> 
> I will not call it as broken, malloc and rtld are working properly.
> vfork is not a normal function, for MT process, fork() is even
> very special too.
> 
> POSIX says a lot about multi-threaded:
> http://pubs.opengroup.org/onlinepubs/000095399/functions/fork.html
> 
> I quoted some POSIX document here:
> > 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 the pthread_atfork() function
> > in order to maintain application invariants across fork() calls.
> 
> This means child process should only do very simple things, and
> quickly call execv().
Sure.

But to call execv*, the child may need a working rtld, so we fixed it.
User code routinely called malloc() or even created threads in the child,
so we fixed that too. Otherwise, we have nothing to answer for the demands,
and 'other' OSes do support such usage. It is beyond POSIX, but this does
not matter, since the feature is expected to be available by application
writers.

> 
> For mt process, fork() is already a very complicated problem,
> one of problems I still remembered is that when fork() is called in
> signal handler, should the thread library execute pthread_atfork
> handler ?  if it should do, but none of lock is async-signal safe,
> though our internal rwlock allows to be used in signal handler,
> but it is not supported by POSIX.
> Also are those atfork handler prepared to be executed in signal
> handler ? it is undefined. POSIX had opened a door here.
POSIX authors were aware of this problem.
In the rationale for SUSv4, they wrote

"While the fork() function is async-signal-safe, there is no way for an
implementation to determine whether the fork handlers established by
pthread_atfork() are async-signal-safe. The fork handlers may attempt to
execute portions of the implementation that are not async-signal-safe,
such as those that are protected by mutexes, leading to a deadlock
condition. It is therefore undefined for the fork handlers to execute
functions that are not async-signal-safe when fork() is called from a
signal handler."

IMO, since fork() is specified to be async-signal safe, and since fork()
is specified to call atfork() handlers, SUSv4 requires, without any
misinterpreations, that atfork calling machinery must be async-signal
safe. The only possibility for undefined behaviour is the application
code registering non-async safe handlers.

> Above is one of complicated problem, the vfork is even more
> restrictive than fork().
> If it is possible, I would avoid such a complicated problem
> which vfork() would cause.

I fully agree that the issues caused by vfork() in multithreaded code
are complicated, but ignoring them lowers the quality of our implementation.
Fixing vfork in multithreaded process is not trivial, but it is possible.

My patch aims at working rtld and malloc in child.
As I said earlier, we might even try to call parent atfork handlers in child.

Sure, if child dies at wrong time, then rtld and malloc locks and data
structures can be left in unusable state for parent, but currently we
do not work even if child is relatively well-behaving.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 196 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/freebsd-hackers/attachments/20120814/4aebb4ee/attachment.pgp


More information about the freebsd-hackers mailing list