main: Don't treat si_pid from pidfd as child exiting

We might repeatedly get si_pid == 0 for a child that hasn't exited,
meaning we won't get a correct exit status. This seems to happen when
the glib application tracks a ptrace():ed child process; the correct
exit status of the process using e.g. a BPF program, where one can
observe that glib appears to get it wrong.

Fixes: #3071
This commit is contained in:
Jonas Ådahl 2023-05-19 12:20:14 +02:00 committed by Philip Withnall
parent 3513be4e35
commit 8d78fa7887

View File

@ -5907,24 +5907,34 @@ g_child_watch_dispatch (GSource *source,
}; };
/* Get the exit status */ /* Get the exit status */
if (waitid (P_PIDFD, child_watch_source->poll.fd, &child_info, WEXITED | WNOHANG) >= 0 && if (waitid (P_PIDFD, child_watch_source->poll.fd, &child_info, WEXITED | WNOHANG) >= 0)
child_info.si_pid != 0)
{ {
/* waitid() helpfully provides the wait status in a decomposed if (child_info.si_pid != 0)
* form which is quite useful. Unfortunately we have to report it {
* to the #GChildWatchFunc as a waitpid()-style platform-specific /* waitid() helpfully provides the wait status in a decomposed
* wait status, so that the user code in #GChildWatchFunc can then * form which is quite useful. Unfortunately we have to report it
* call WIFEXITED() (etc.) on it. That means re-composing the * to the #GChildWatchFunc as a waitpid()-style platform-specific
* status information. */ * wait status, so that the user code in #GChildWatchFunc can then
wait_status = siginfo_t_to_wait_status (&child_info); * call WIFEXITED() (etc.) on it. That means re-composing the
* status information. */
wait_status = siginfo_t_to_wait_status (&child_info);
child_exited = TRUE;
}
else
{
g_debug (G_STRLOC ": pidfd signaled but pid %d didn't exit",
child_watch_source->pid);
return TRUE;
}
} }
else else
{ {
/* Unknown error. We got signaled that the process might be exited, /* Unknown error. We got signaled that the process might be exited,
* but now we failed to reap it? Assume the process is gone and proceed. */ * but now we failed to reap it? Assume the process is gone and proceed. */
g_warning (G_STRLOC ": pidfd signaled ready but failed"); g_warning (G_STRLOC ": pidfd signaled ready but failed for pid %d",
child_watch_source->pid);
child_exited = TRUE;
} }
child_exited = TRUE;
} }
#endif /* HAVE_PIDFD*/ #endif /* HAVE_PIDFD*/