Skip to content

Commit 2f24226

Browse files
committed
Avoid using SIGUSR1 for interlock with ptrace-based intercept.
Instead, the child sleeping reading the intercept socketpair. The tracer writes to the other end of the socketpair after it has seized the child. Fixes intercept/log_subcmds after the signal changes in 8781032.
1 parent 068ff99 commit 2f24226

7 files changed

Lines changed: 77 additions & 54 deletions

File tree

src/exec.c

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <config.h>
2020

2121
#include <sys/resource.h>
22+
#include <sys/socket.h>
2223
#include <sys/stat.h>
2324
#include <stdlib.h>
2425
#include <string.h>
@@ -43,14 +44,6 @@
4344
#include <sudo_plugin.h>
4445
#include <sudo_plugin_int.h>
4546

46-
#ifdef HAVE_PTRACE_INTERCEPT
47-
static void
48-
handler(int signo)
49-
{
50-
/* just return */
51-
}
52-
#endif /* HAVE_PTRACE_INTERCEPT */
53-
5447
static void
5548
close_fds(struct command_details *details, int errfd, int intercept_fd)
5649
{
@@ -261,23 +254,11 @@ exec_cmnd(struct command_details *details, sigset_t *mask,
261254

262255
#ifdef HAVE_PTRACE_INTERCEPT
263256
if (ISSET(details->flags, CD_USE_PTRACE)) {
264-
struct sigaction sa;
265-
sigset_t set;
266-
267-
/* Tracer will send us SIGUSR1 when it is time to proceed. */
268-
memset(&sa, 0, sizeof(sa));
269-
sigemptyset(&sa.sa_mask);
270-
sa.sa_flags = SA_RESTART;
271-
sa.sa_handler = handler;
272-
if (sudo_sigaction(SIGUSR1, &sa, NULL) != 0) {
273-
sudo_warn(U_("unable to set handler for signal %d"),
274-
SIGUSR1);
275-
}
257+
struct command_status cstat;
276258

277-
/* Suspend child until tracer seizes control and sends SIGUSR1. */
278-
sigfillset(&set);
279-
sigdelset(&set, SIGUSR1);
280-
sigsuspend(&set);
259+
/* Wait for tracer to seizes control and notify us. */
260+
if (recv(intercept_fd, &cstat, sizeof(cstat), MSG_WAITALL) == -1)
261+
sudo_warn("%s", U_("unable to receive message from parent"));
281262
}
282263
#endif /* HAVE_PTRACE_INTERCEPT */
283264

src/exec_monitor.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -620,9 +620,10 @@ exec_monitor(struct command_details *details, sigset_t *oset,
620620
* Create new event base and register read events for the
621621
* signal pipe, error pipe, and backchannel.
622622
*/
623-
init_exec_events_monitor(&mc, errsock[0]);
624-
/* Restore signal mask now that signal handlers are setup. */
625-
sigprocmask(SIG_SETMASK, oset, NULL);
623+
init_exec_events_monitor(&mc, errsock[0]);
624+
625+
/* Restore signal mask now that signal handlers are setup. */
626+
sigprocmask(SIG_SETMASK, oset, NULL);
626627

627628
mc.cmnd_pid = sudo_debug_fork();
628629
switch (mc.cmnd_pid) {

src/exec_nopty.c

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -569,13 +569,19 @@ exec_nopty(struct command_details *details,
569569
sudo_fatal("%s", U_("unable to create pipe"));
570570

571571
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
572-
if (!ISSET(details->flags, CD_USE_PTRACE)) {
573-
/*
574-
* Allocate a socketpair for communicating with sudo_intercept.so.
575-
* This must be inherited across exec, hence no FD_CLOEXEC.
576-
*/
577-
if (socketpair(PF_UNIX, SOCK_STREAM, 0, intercept_sv) == -1)
572+
/*
573+
* Allocate a socketpair for communicating with sudo_intercept.so.
574+
* This must be inherited across exec for the non-ptrace case.
575+
*/
576+
if (socketpair(PF_UNIX, SOCK_STREAM, 0, intercept_sv) == -1)
577+
sudo_fatal("%s", U_("unable to create sockets"));
578+
if (ISSET(details->flags, CD_USE_PTRACE)) {
579+
if (fcntl(intercept_sv[0], F_SETFD, FD_CLOEXEC) == -1 ||
580+
fcntl(intercept_sv[1], F_SETFD, FD_CLOEXEC) == -1) {
578581
sudo_fatal("%s", U_("unable to create sockets"));
582+
}
583+
/* Used to wake up command as it waits for parent to seize it. */
584+
ec.intercept_fd = intercept_sv[0];
579585
}
580586
}
581587

@@ -608,8 +614,10 @@ exec_nopty(struct command_details *details,
608614

609615
/* Allocate and set signal events and the error pipe event.*/
610616
init_exec_events(&ec, evbase, errpipe[0]);
617+
611618
/* Restore signal mask now that signal handlers are setup. */
612619
sigprocmask(SIG_SETMASK, &oset, NULL);
620+
613621
ec.cmnd_pid = sudo_debug_fork();
614622
switch (ec.cmnd_pid) {
615623
case -1:
@@ -680,7 +688,7 @@ exec_nopty(struct command_details *details,
680688
rc = -1;
681689
} else if (ISSET(details->flags, CD_USE_PTRACE)) {
682690
/* Try to seize control of the command using ptrace(2). */
683-
rc = exec_ptrace_seize(ec.cmnd_pid);
691+
rc = exec_ptrace_seize(ec.cmnd_pid, intercept_sv[0]);
684692
if (rc == 0) {
685693
/* There is another tracer present. */
686694
CLR(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS|CD_USE_PTRACE);

src/exec_ptrace.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include <config.h>
1818

19+
#include <sys/socket.h>
1920
#include <sys/stat.h>
2021
#include <sys/types.h>
2122
#include <sys/uio.h>
@@ -1228,11 +1229,12 @@ set_exec_filter(void)
12281229
* being traced and -1 on error.
12291230
*/
12301231
int
1231-
exec_ptrace_seize(pid_t child)
1232+
exec_ptrace_seize(pid_t child, int intercept_fd)
12321233
{
12331234
const long ptrace_opts = PTRACE_O_TRACESECCOMP|PTRACE_O_TRACECLONE|
12341235
PTRACE_O_TRACEFORK|PTRACE_O_TRACEVFORK|
12351236
PTRACE_O_TRACEEXEC;
1237+
struct command_status cstat;
12361238
int ret = -1;
12371239
int status;
12381240
debug_decl(exec_ptrace_seize, SUDO_DEBUG_EXEC);
@@ -1264,11 +1266,15 @@ exec_ptrace_seize(pid_t child)
12641266
ret = false;
12651267
}
12661268

1267-
/* The child is suspended waiting for SIGUSR1, wake it up. */
1268-
if (kill(child, SIGUSR1) == -1) {
1269-
sudo_warn("kill(%d, SIGUSR1)", (int)child);
1269+
/* The child is waiting on intercept_fd, wake it up. */
1270+
cstat.type = CMD_SIGNO;
1271+
cstat.val = 0;
1272+
if (send(intercept_fd, &cstat, sizeof(cstat), 0) == -1) {
1273+
sudo_warn("%s", U_("unable to wake up child"));
12701274
goto done;
12711275
}
1276+
1277+
/* If PTRACE_SEIZE failed, we are done. */
12721278
if (!ret)
12731279
goto done;
12741280

@@ -2100,7 +2106,7 @@ exec_ptrace_stopped(pid_t pid, int status, void *intercept)
21002106

21012107
/* STUB */
21022108
int
2103-
exec_ptrace_seize(pid_t child)
2109+
exec_ptrace_seize(pid_t child, int intercept_fd)
21042110
{
21052111
return true;
21062112
}

src/exec_pty.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ backchannel_cb(int fd, int what, void *v)
716716
ec->details->command, (int)ec->cmnd_pid);
717717
if (ISSET(ec->details->flags, CD_USE_PTRACE)) {
718718
/* Try to seize control of the command using ptrace(2). */
719-
int rc = exec_ptrace_seize(ec->cmnd_pid);
719+
const int rc = exec_ptrace_seize(ec->cmnd_pid, ec->intercept_fd);
720720
if (rc == 0) {
721721
/* There is another tracer present. */
722722
CLR(ec->details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS|CD_USE_PTRACE);
@@ -974,6 +974,7 @@ init_exec_closure(struct exec_closure *ec, struct command_status *cstat,
974974
ec->cmnd_pid = -1;
975975
ec->cstat = cstat;
976976
ec->details = details;
977+
ec->intercept_fd = -1;
977978
ec->rows = user_details->ts_rows;
978979
ec->cols = user_details->ts_cols;
979980

@@ -1135,13 +1136,19 @@ exec_pty(struct command_details *details,
11351136
sudo_fatal("%s", U_("unable to create sockets"));
11361137

11371138
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
1138-
if (!ISSET(details->flags, CD_USE_PTRACE)) {
1139-
/*
1140-
* Allocate a socketpair for communicating with sudo_intercept.so.
1141-
* This must be inherited across exec, hence no FD_CLOEXEC.
1142-
*/
1143-
if (socketpair(PF_UNIX, SOCK_STREAM, 0, intercept_sv) == -1)
1139+
/*
1140+
* Allocate a socketpair for communicating with sudo_intercept.so.
1141+
* This must be inherited across exec for the non-ptrace case.
1142+
*/
1143+
if (socketpair(PF_UNIX, SOCK_STREAM, 0, intercept_sv) == -1)
1144+
sudo_fatal("%s", U_("unable to create sockets"));
1145+
if (ISSET(details->flags, CD_USE_PTRACE)) {
1146+
if (fcntl(intercept_sv[0], F_SETFD, FD_CLOEXEC) == -1 ||
1147+
fcntl(intercept_sv[1], F_SETFD, FD_CLOEXEC) == -1) {
11441148
sudo_fatal("%s", U_("unable to create sockets"));
1149+
}
1150+
/* Used to wake up command as it waits for parent to seize it. */
1151+
ec->intercept_fd = intercept_sv[0];
11451152
}
11461153
}
11471154

@@ -1337,8 +1344,10 @@ exec_pty(struct command_details *details,
13371344

13381345
/* Allocate and set signal events and the backchannel event. */
13391346
init_exec_events(ec, evbase, sv[0]);
1347+
13401348
/* Restore signal mask now that signal handlers are setup. */
13411349
sigprocmask(SIG_SETMASK, &oset, NULL);
1350+
13421351
ec->monitor_pid = sudo_debug_fork();
13431352
switch (ec->monitor_pid) {
13441353
case -1:

src/regress/intercept/test_ptrace.c

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ handler(int signo)
4040
void
4141
intercept_closure_reset(struct intercept_closure *closure)
4242
{
43+
/* Preserve closure->details, zero everything else. */
44+
const struct command_details *details = closure->details;
4345
memset(closure, 0, sizeof(*closure));
46+
closure->details = details;
4447
}
4548

4649
bool
@@ -106,6 +109,8 @@ main(int argc, char *argv[])
106109
struct sudo_debug_file debug_file;
107110
const char *base, *shell = _PATH_SUDO_BSHELL;
108111
struct intercept_closure closure = { 0 };
112+
struct command_details details = { 0 };
113+
int intercept_sv[2];
109114
const char *errstr;
110115
sigset_t blocked, empty;
111116
struct sigaction sa;
@@ -145,20 +150,29 @@ main(int argc, char *argv[])
145150
if (sudo_debug_instance == SUDO_DEBUG_INSTANCE_ERROR)
146151
return EXIT_FAILURE;
147152

148-
/* Block SIGCHLD and SIGUSR during critical section. */
153+
/* Use socketpair to coordinate between parent and child. */
154+
if (socketpair(PF_UNIX, SOCK_STREAM, 0, intercept_sv) == -1)
155+
sudo_fatal("%s", U_("unable to create sockets"));
156+
if (fcntl(intercept_sv[0], F_SETFD, FD_CLOEXEC) == -1 ||
157+
fcntl(intercept_sv[1], F_SETFD, FD_CLOEXEC) == -1) {
158+
sudo_fatal("%s", U_("unable to create sockets"));
159+
}
160+
161+
/* Block SIGCHLD during critical section. */
149162
sigemptyset(&empty);
150163
sigemptyset(&blocked);
151164
sigaddset(&blocked, SIGCHLD);
152-
sigaddset(&blocked, SIGUSR1);
153165
sigprocmask(SIG_BLOCK, &blocked, NULL);
154166

155-
/* Signal handler sets a flag for SIGCHLD, nothing for SIGUSR1. */
167+
/* Avoid NULL deref of closure.details. */
168+
closure.details = &details;
169+
170+
/* Signal handler sets a flag for SIGCHLD. */
156171
memset(&sa, 0, sizeof(sa));
157172
sigemptyset(&sa.sa_mask);
158173
sa.sa_flags = SA_RESTART;
159174
sa.sa_handler = handler;
160175
sigaction(SIGCHLD, &sa, NULL);
161-
sigaction(SIGUSR1, &sa, NULL);
162176

163177
/* Fork a shell. */
164178
my_pid = getpid();
@@ -174,13 +188,16 @@ main(int argc, char *argv[])
174188
if (!set_exec_filter())
175189
_exit(EXIT_FAILURE);
176190

177-
/* Suspend child until tracer seizes control and sends SIGUSR1. */
178-
sigsuspend(&empty);
191+
/* Child waits until tracer seizes control and sends SIGUSR1. */
192+
close(intercept_sv[0]);
193+
recv(intercept_sv[1], &ch, sizeof(ch), 0);
194+
179195
execl(shell, base, NULL);
180196
sudo_fatal("execl");
181197
default:
182198
/* Parent attaches to child and allows it to continue. */
183-
if (exec_ptrace_seize(child) == -1)
199+
close(intercept_sv[1]);
200+
if (exec_ptrace_seize(child, intercept_sv[0]) == -1)
184201
return EXIT_FAILURE;
185202
break;
186203
}

src/sudo_exec.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ struct exec_closure {
7676
pid_t monitor_pid;
7777
pid_t cmnd_pid;
7878
pid_t ppgrp;
79+
int intercept_fd;
7980
int rows;
8081
int cols;
8182
bool foreground;
@@ -228,7 +229,7 @@ char **sudo_preload_dso_mmap(char *const envp[], const char *dso_file, int inter
228229
/* exec_ptrace.c */
229230
bool exec_ptrace_stopped(pid_t pid, int status, void *intercept);
230231
bool set_exec_filter(void);
231-
int exec_ptrace_seize(pid_t child);
232+
int exec_ptrace_seize(pid_t child, int intercept_fd);
232233

233234
/* suspend_parent.c */
234235
void sudo_suspend_parent(int signo, pid_t my_pid, pid_t my_pgrp, pid_t cmnd_pid, void *closure, void (*callback)(void *, int));

0 commit comments

Comments
 (0)