-
Notifications
You must be signed in to change notification settings - Fork 11
Description
After the parent finishes TraceExecutor::onPostForkParent
, every time the child receives a signal, it stops, waiting for for a PTRACE_CONT
. This not a problem when PerfListener
isn't enabled, as when the parent begins its waitid loop in Executor
, it will receive the ptrace notification, and the child will be resumed by TraceExecutor::onExecuteEvent
.
However, if PerfListener
is enabled, it executes after TraceExecutor
. It contains a pthread barrier, which requires the presence of both the parent and the child. If the child receives a signal before either of them reach the barrier, the program will be deadlocked, with the child never receiving the PTRACE_CONT
, and the parent never moving past the barrier.
Keep in mind this problem is probably mostly theoretical, as it would require:
- A non-critical signal to be delivered to the child at all in that timespan, which is almost impossible with normal execution (however can happen, for example with
SIGWINCH
) - The signal needs to hit an extremely precise point in time between
TraceExecutor
andPerfListener
With that said, I've managed to reproduce it by adding a sleep(1)
at the end of TraceExecutor::onPostForkParent
and using that time to resize the window and cause a SIGWINCH
to be sent to the child, deadlocking the process. I believe this issue can probably be fixed by moving TraceExecutor
after PerfListener
in the listener order, however I don't know if that won't break anything else.
A similar problem might also theoretically exist in the TraceExecutor
, this time not requiring PerfListener
. TraceExecutor::onPostForkParent
does only one waitpid
call, assuming it will receive the exact SIGTRAP
sent by the child in TraceExecutor::onPostForkChild
. This might theoretically not be correct if it receives a SIGTSTP
before the SIGTRAP
or finishes with EINTR
, but that seems even more unlikely than the previous scenario to me.