From 468751ba5f2cdb6f78f2a1573af1d643a741db8e Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Mon, 1 Dec 2014 21:05:13 -0600 Subject: [PATCH] Switch the vfork implementation to use procrecvtim() for waiting, and not signal the parent from the child. This avoids a problem where one of the SIGALRM signals could be delivered after the original SIGALRM handler was restored, which would normally cause the process to terminate. For now, we just rely on polling in a loop to determine when the child is done. This isn't optimal, but should be OK. If procsend() worked, we could use that in the child to signal the parent. However, procsend() is broken in GNO 2.0.6 -- it seems that it actually tries to invoke the send() call for sockets, but with the wrong arguments, leading to a crash. --- libbb/vfork.and.run.c | 29 +++++++++-------------------- shell/hush.c | 3 ++- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/libbb/vfork.and.run.c b/libbb/vfork.and.run.c index 3fcb8d9b9..d00c8d64c 100644 --- a/libbb/vfork.and.run.c +++ b/libbb/vfork.and.run.c @@ -52,11 +52,7 @@ pid_t vfork_and_run(void (*fn)(void*) NORETURN, void *arg) { * behavior like a traditional vfork() implementation, where the * parent blocks until the child terminates or execs. * - * Our approach will be to have the child send SIGALRM to the parent - * just before it terminates or execs, and block waiting for that here. - * We also set an alarm in case the child doesn't signal. - * - * When we get SIGALRM, we check the process tables to make sure the + * Our approach is to check the process tables to make sure the * child has actually finished or exec'd. If not, we loop and try again. * We can't just rely on the fact that the child signaled us, because * it may still be running in libc's implementation of exec*. @@ -64,32 +60,29 @@ pid_t vfork_and_run(void (*fn)(void*) NORETURN, void *arg) { long oldmask; bool environPushed; - sig_t prev_alarm_sig; pid_t pid; kvmt *kvm_context; struct pentry *proc_entry; bool done = 0; - /* Block all signals for now */ - oldmask = sigblock(-1); - /* Isolate child process's environment from parent */ environPushed = !environPush(); + /* Block all signals for now */ + oldmask = sigblock(-1); + pid = fork2(fork_thunk, 1024, 0, forked_child_name, (sizeof(fn) + sizeof(arg) + sizeof(oldmask) + 1) / 2, fn, arg, oldmask); if (pid < 0) goto ret; - prev_alarm_sig = signal(SIGALRM, SIG_IGN); - while (!done) { - /* Set alarm. This is a backup in case the child dies without signaling us. */ - alarm10(1); - - /* Wait until we get SIGALRM */ - sigpause(~sigmask(SIGALRM)); + /* Wait for ~100 ms. If procsend worked, the child could send a + * message with it to end the waiting earlier, but this isn't + * possible in GNO 2.0.6 because procsend is broken. This isn't + * too big an issue, since 100ms isn't very long to wait anyhow. */ + procrecvtim(1); /* Check if the child is really dead or forked by inspecting * the kernel's process entry for it. */ @@ -104,10 +97,6 @@ pid_t vfork_and_run(void (*fn)(void*) NORETURN, void *arg) { kvm_close(kvm_context); } - alarm10(0); - sigsetmask(~sigmask(SIGALRM)); - signal(SIGALRM, prev_alarm_sig); - ret: sigsetmask(oldmask); if (environPushed) diff --git a/shell/hush.c b/shell/hush.c index bf2d20bf4..f60c40396 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -1381,7 +1381,8 @@ int is_forked_child(void) { void signal_parent_to_resume(void) { #ifdef __GNO__ if (getpid() != G.last_execed_pid) { - kill(getppid(), SIGALRM); + /* If procsend wasn't broken, we could send a "ping" to the parent */ + //procsend(getppid(), 0); } #endif }