Skip to content

Commit 0cb3e33

Browse files
committed
Wait on a socketpair for the parent to grant child the controlling tty.
This upgrades the error pipe to a bi-directional socketpair that the parent will write to after it has granted the child process the controlling terminal. That fixes an issue where the child could end up in a tight CPU loop waiting on the parent which may not be scheduled immediately.
1 parent a127ddf commit 0cb3e33

File tree

1 file changed

+47
-28
lines changed

1 file changed

+47
-28
lines changed

src/exec_monitor.c

+47-28
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
struct monitor_closure {
4545
const struct command_details *details;
4646
struct sudo_event_base *evbase;
47-
struct sudo_event *errpipe_event;
47+
struct sudo_event *errsock_event;
4848
struct sudo_event *backchannel_event;
4949
struct sudo_event *sigint_event;
5050
struct sudo_event *sigquit_event;
@@ -263,18 +263,18 @@ mon_signal_cb(int signo, int what, void *v)
263263
debug_return;
264264
}
265265

266-
/* Note: this is basically the same as errpipe_cb() in exec_nopty.c */
266+
/* This is essentially the same as errpipe_cb() in exec_nopty.c */
267267
static void
268-
mon_errpipe_cb(int fd, int what, void *v)
268+
mon_errsock_cb(int fd, int what, void *v)
269269
{
270270
struct monitor_closure *mc = v;
271271
ssize_t nread;
272272
int errval;
273-
debug_decl(mon_errpipe_cb, SUDO_DEBUG_EXEC);
273+
debug_decl(mon_errsock_cb, SUDO_DEBUG_EXEC);
274274

275275
/*
276276
* Read errno from child or EOF when command is executed.
277-
* Note that the error pipe is *blocking*.
277+
* Note that the error socket is *blocking*.
278278
*/
279279
nread = read(fd, &errval, sizeof(errval));
280280
switch (nread) {
@@ -286,22 +286,22 @@ mon_errpipe_cb(int fd, int what, void *v)
286286
mc->cstat->val = errno;
287287
}
288288
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
289-
"%s: failed to read error pipe", __func__);
289+
"%s: failed to read error socket", __func__);
290290
sudo_ev_loopbreak(mc->evbase);
291291
}
292292
break;
293293
default:
294294
if (nread == 0) {
295-
/* The error pipe closes when the command is executed. */
296-
sudo_debug_printf(SUDO_DEBUG_INFO, "EOF on error pipe");
295+
/* The error socket closes when the command is executed. */
296+
sudo_debug_printf(SUDO_DEBUG_INFO, "EOF on error socket");
297297
} else {
298298
/* Errno value when child is unable to execute command. */
299299
sudo_debug_printf(SUDO_DEBUG_INFO, "errno from child: %s",
300300
strerror(errval));
301301
mc->cstat->type = CMD_ERRNO;
302302
mc->cstat->val = errval;
303303
}
304-
sudo_ev_del(mc->evbase, mc->errpipe_event);
304+
sudo_ev_del(mc->evbase, mc->errsock_event);
305305
close(fd);
306306
break;
307307
}
@@ -374,13 +374,25 @@ exec_cmnd_pty(struct command_details *details, sigset_t *mask,
374374

375375
/* Wait for parent to grant us the tty if we are foreground. */
376376
if (foreground) {
377-
struct timespec ts = { 0, 1000 }; /* 1us */
378-
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: waiting for controlling tty",
379-
__func__);
380-
while (tcgetpgrp(io_fds[SFD_FOLLOWER]) != self)
381-
nanosleep(&ts, NULL);
382-
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: got controlling tty",
377+
char ch;
378+
379+
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: waiting for controlling tty",
383380
__func__);
381+
while (recv(errfd, &ch, sizeof(ch), 0) == -1) {
382+
if (errno != EINTR) {
383+
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
384+
"%s: unable to receive message from parent", __func__);
385+
debug_return;
386+
}
387+
}
388+
if (tcgetpgrp(io_fds[SFD_FOLLOWER]) == self) {
389+
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: got controlling tty",
390+
__func__);
391+
} else {
392+
sudo_debug_printf(SUDO_DEBUG_ERROR,
393+
"%s: unable to get controlling tty", __func__);
394+
foreground = false;
395+
}
384396
}
385397

386398
/* Done with the pty follower, don't leak it. */
@@ -418,11 +430,11 @@ fill_exec_closure_monitor(struct monitor_closure *mc,
418430
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
419431

420432
/* Event for command status via errfd. */
421-
mc->errpipe_event = sudo_ev_alloc(errfd,
422-
SUDO_EV_READ|SUDO_EV_PERSIST, mon_errpipe_cb, mc);
423-
if (mc->errpipe_event == NULL)
433+
mc->errsock_event = sudo_ev_alloc(errfd,
434+
SUDO_EV_READ|SUDO_EV_PERSIST, mon_errsock_cb, mc);
435+
if (mc->errsock_event == NULL)
424436
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
425-
if (sudo_ev_add(mc->evbase, mc->errpipe_event, NULL, false) == -1)
437+
if (sudo_ev_add(mc->evbase, mc->errsock_event, NULL, false) == -1)
426438
sudo_fatal("%s", U_("unable to add event to queue"));
427439

428440
/* Event for forwarded signals via backchannel. */
@@ -534,7 +546,7 @@ exec_monitor(struct command_details *details, sigset_t *oset,
534546
struct monitor_closure mc = { 0 };
535547
struct command_status cstat;
536548
struct sigaction sa;
537-
int errpipe[2];
549+
int errsock[2];
538550
debug_decl(exec_monitor, SUDO_DEBUG_EXEC);
539551

540552
/* Close fds the monitor doesn't use. */
@@ -568,10 +580,14 @@ exec_monitor(struct command_details *details, sigset_t *oset,
568580
}
569581

570582
/*
571-
* We use a pipe to get errno if execve(2) fails in the child.
583+
* The child waits on the other end of a socketpair for the
584+
* parent to set the controlling terminal. It also writes
585+
* error to the socket on execve(2) failure.
572586
*/
573-
if (pipe2(errpipe, O_CLOEXEC) != 0) {
574-
sudo_warn("%s", U_("unable to create pipe"));
587+
if (socketpair(PF_UNIX, SOCK_STREAM, 0, errsock) == -1 ||
588+
fcntl(errsock[0], F_SETFD, FD_CLOEXEC) == -1 ||
589+
fcntl(errsock[1], F_SETFD, FD_CLOEXEC) == -1) {
590+
sudo_warn("%s", U_("unable to create sockets"));
575591
goto bad;
576592
}
577593

@@ -608,15 +624,15 @@ exec_monitor(struct command_details *details, sigset_t *oset,
608624
case 0:
609625
/* child */
610626
close(backchannel);
611-
close(errpipe[0]);
627+
close(errsock[0]);
612628
/* setup tty and exec command */
613-
exec_cmnd_pty(details, oset, foreground, intercept_fd, errpipe[1]);
614-
if (write(errpipe[1], &errno, sizeof(int)) == -1)
629+
exec_cmnd_pty(details, oset, foreground, intercept_fd, errsock[1]);
630+
if (send(errsock[1], &errno, sizeof(int), 0) == -1)
615631
sudo_warn(U_("unable to execute %s"), details->command);
616632
_exit(EXIT_FAILURE);
617633
/* NOTREACHED */
618634
}
619-
close(errpipe[1]);
635+
close(errsock[1]);
620636
if (intercept_fd != -1)
621637
close(intercept_fd);
622638

@@ -635,7 +651,7 @@ exec_monitor(struct command_details *details, sigset_t *oset,
635651
* Create new event base and register read events for the
636652
* signal pipe, error pipe, and backchannel.
637653
*/
638-
fill_exec_closure_monitor(&mc, details, &cstat, errpipe[0], backchannel);
654+
fill_exec_closure_monitor(&mc, details, &cstat, errsock[0], backchannel);
639655

640656
/* Restore signal mask now that signal handlers are setup. */
641657
sigprocmask(SIG_SETMASK, oset, NULL);
@@ -659,6 +675,9 @@ exec_monitor(struct command_details *details, sigset_t *oset,
659675
"%s: unable to set foreground pgrp to %d (command)",
660676
__func__, (int)mc.cmnd_pgrp);
661677
}
678+
/* Tell the child to go ahead now that it is the foreground pgrp. */
679+
while (send(errsock[0], "", 1, 0) == -1 && errno == EINTR)
680+
continue;
662681
}
663682

664683
/*

0 commit comments

Comments
 (0)