posix_spawn: parent can get stuck in uninterruptible sleep if child receives SIGTSTP early enough

Florian Weimer fweimer@redhat.com
Mon Oct 10 13:45:26 GMT 2022


* Adhemerval Zanella Netto:

> On 22/09/22 14:38, Florian Weimer wrote:
>> * Adhemerval Zanella Netto:
>> 
>>> On 22/09/22 09:18, Florian Weimer wrote:
>>>>> Is there anything that prevents to avoid using CLONE_VFORK? The code already
>>>>> uses a allocated stack and do synchronizes with waitpid.
>>>>
>>>> Assuming there is a way to create a thread which gets replaced by execve
>>>> only (instead the whole process), this won't work because we have to
>>>> block all signals for the new thread (it must not be visible to
>>>> application code, and signal handlers must not run on it), and we can't
>>>> unblock those signals prior to execve.  With vfork, we can unblock them
>>>> after changing the signal handler disposition to SIG_DFL (preventing the
>>>> handler execution), but per-thread signal handlers have been removed
>>>> from Linux.  So even if we somehow could prevent the termination signal
>>>> from beign sent to the whole process (and not just the fake thread), we
>>>> still have a gap.
>>>
>>> But we already block all internal signals with internal_signal_block_all
>>> prior clone call and it does not use CLONE_SIGHAND on the clone call. 
>>> Also, independently of CLONE_SIGHAND, the calling process and child still 
>>> have distinct signal masks.  Recall for posix_spawn we do not use
>>> CLONE_THREAD, so per-thread signal handlers does not apply here.
>> 
>> This only works because we restore SIG_DFL before unblocking signals in
>> the new process.  And that depends on a separate set of signal handlers.
>> 
>>> Doing some tests, the main problem is in fact how to synchronize 
>>> the deallocation of the stack, since without CLONE_VFORK there is no way
>>> to advertise on a success call when execve has been called.
>>>
>>> But I agree that even without CLONE_VFORK we still have a small window,
>>> between the sigprocmask and execve, that the signal might act upon the
>>> child.
>> 
>> And that window shouldn't exist in the current implementation.
>
> But that's the main issue described in this first message, isn't? The child 
> unblocks signals by calling sigprocmask, SIGTSTP is delivered to the child,
> but since clone hasn't exited due CLONE_VFORK, it remains stuck in clone
> until child receives SIGCONT.

Yes, we do it this way to avoid a different bug, and trade it for
another.

> I think to actually fix it we need a execve/execveat where the signal mask
> is set atomically, so SIGTSTP is sent to the spawned process instead of
> the libc helper one.

Right, I don't see a way around that.

I don't think switching back to fork by default is really an option.
The impact on latency is much worse than with vfork.

Thanks,
Florian



More information about the Libc-help mailing list