Skip to content

Commit 0be9f0f

Browse files
committed
Initialize exec closure before calling sudo_fatal_callback_register()
The pty_cleanup() function, which may be called via fatal()/fatalx(), expects that ec->details is set. If there is a fatal error after the cleanup hook is registered but before the exec closure it filled in, pty_cleanup() would dereference a NULL pointer. Reported by Bjorn Baron.
1 parent 6fc816d commit 0be9f0f

File tree

1 file changed

+72
-63
lines changed

1 file changed

+72
-63
lines changed

src/exec_pty.c

Lines changed: 72 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* SPDX-License-Identifier: ISC
33
*
4-
* Copyright (c) 2009-2024 Todd C. Miller <[email protected]>
4+
* Copyright (c) 2009-2025 Todd C. Miller <[email protected]>
55
*
66
* Permission to use, copy, modify, and distribute this software for any
77
* purpose with or without fee is hereby granted, provided that the above
@@ -63,44 +63,6 @@ static void sync_ttysize(struct exec_closure *ec);
6363
static void schedule_signal(struct exec_closure *ec, int signo);
6464
static void send_command_status(struct exec_closure *ec, int type, int val);
6565

66-
/*
67-
* Allocate a pty if /dev/tty is a tty.
68-
* Fills in io_fds[SFD_USERTTY], io_fds[SFD_LEADER] and io_fds[SFD_FOLLOWER].
69-
* Returns the dynamically allocated pty name on success, NULL on failure.
70-
*/
71-
static char *
72-
pty_setup(struct command_details *details)
73-
{
74-
char *ptyname = NULL;
75-
debug_decl(pty_setup, SUDO_DEBUG_EXEC);
76-
77-
io_fds[SFD_USERTTY] = open(_PATH_TTY, O_RDWR);
78-
if (io_fds[SFD_USERTTY] == -1) {
79-
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: no %s, not allocating a pty",
80-
__func__, _PATH_TTY);
81-
debug_return_ptr(NULL);
82-
}
83-
84-
ptyname = get_pty(&io_fds[SFD_LEADER], &io_fds[SFD_FOLLOWER],
85-
details->cred.euid);
86-
if (ptyname == NULL)
87-
sudo_fatal("%s", U_("unable to allocate pty"));
88-
89-
/* Add entry to utmp/utmpx? */
90-
if (ISSET(details->flags, CD_SET_UTMP))
91-
utmp_login(details->tty, ptyname, io_fds[SFD_FOLLOWER], details->utmp_user);
92-
93-
/* Update tty name in command details (used by monitor, SELinux, AIX). */
94-
details->tty = ptyname;
95-
96-
sudo_debug_printf(SUDO_DEBUG_INFO,
97-
"%s: %s fd %d, pty leader fd %d, pty follower fd %d",
98-
__func__, _PATH_TTY, io_fds[SFD_USERTTY], io_fds[SFD_LEADER],
99-
io_fds[SFD_FOLLOWER]);
100-
101-
debug_return_str(ptyname);
102-
}
103-
10466
/*
10567
* Restore user's terminal settings and update utmp, as needed.
10668
*/
@@ -136,6 +98,49 @@ pty_cleanup_hook(void)
13698
pty_cleanup(&pty_ec, 0);
13799
}
138100

101+
/*
102+
* Allocate a pty if /dev/tty is a tty.
103+
* Fills in io_fds[SFD_USERTTY], io_fds[SFD_LEADER] and io_fds[SFD_FOLLOWER].
104+
* Returns the dynamically allocated pty name on success, NULL on failure.
105+
*/
106+
static bool
107+
pty_setup(struct exec_closure *ec)
108+
{
109+
struct command_details *details = ec->details;
110+
debug_decl(pty_setup, SUDO_DEBUG_EXEC);
111+
112+
io_fds[SFD_USERTTY] = open(_PATH_TTY, O_RDWR);
113+
if (io_fds[SFD_USERTTY] == -1) {
114+
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: no %s, not allocating a pty",
115+
__func__, _PATH_TTY);
116+
debug_return_bool(false);
117+
}
118+
119+
ec->ptyname = get_pty(&io_fds[SFD_LEADER], &io_fds[SFD_FOLLOWER],
120+
details->cred.euid);
121+
if (ec->ptyname == NULL)
122+
sudo_fatal("%s", U_("unable to allocate pty"));
123+
124+
/* Add entry to utmp/utmpx? */
125+
if (ISSET(details->flags, CD_SET_UTMP)) {
126+
utmp_login(details->tty, ec->ptyname, io_fds[SFD_FOLLOWER],
127+
details->utmp_user);
128+
}
129+
130+
/* Update tty name in command details (used by monitor, SELinux, AIX). */
131+
details->tty = ec->ptyname;
132+
133+
/* Register cleanup function. */
134+
sudo_fatal_callback_register(pty_cleanup_hook);
135+
136+
sudo_debug_printf(SUDO_DEBUG_INFO,
137+
"%s: %s fd %d, pty leader fd %d, pty follower fd %d",
138+
__func__, _PATH_TTY, io_fds[SFD_USERTTY], io_fds[SFD_LEADER],
139+
io_fds[SFD_FOLLOWER]);
140+
141+
debug_return_bool(true);
142+
}
143+
139144
/*
140145
* Check whether sudo is running in the foreground.
141146
* Updates the foreground flag in the closure.
@@ -956,16 +961,14 @@ fwdchannel_cb(int sock, int what, void *v)
956961
}
957962

958963
/*
959-
* Fill in the exec closure and setup initial exec events.
960-
* Allocates events for the signal pipe and backchannel.
961-
* Forwarded signals on the backchannel are enabled on demand.
964+
* Fill in the non-event part of the exec closure.
962965
*/
963966
static void
964-
fill_exec_closure(struct exec_closure *ec, struct command_status *cstat,
967+
init_exec_closure(struct exec_closure *ec, struct command_status *cstat,
965968
struct command_details *details, const struct user_details *user_details,
966-
struct sudo_event_base *evbase, pid_t sudo_pid, pid_t ppgrp, int backchannel)
969+
pid_t sudo_pid, pid_t ppgrp)
967970
{
968-
debug_decl(fill_exec_closure, SUDO_DEBUG_EXEC);
971+
debug_decl(init_exec_closure, SUDO_DEBUG_EXEC);
969972

970973
/* Fill in the non-event part of the closure. */
971974
ec->sudo_pid = sudo_pid;
@@ -976,9 +979,18 @@ fill_exec_closure(struct exec_closure *ec, struct command_status *cstat,
976979
ec->rows = user_details->ts_rows;
977980
ec->cols = user_details->ts_cols;
978981

979-
/* Reset cstat for running the command. */
980-
cstat->type = CMD_INVALID;
981-
cstat->val = 0;
982+
debug_return;
983+
}
984+
985+
/*
986+
* Allocate and set events for the signal pipe and backchannel.
987+
* Forwarded signals on the backchannel are enabled on demand.
988+
*/
989+
static void
990+
init_exec_events(struct exec_closure *ec, struct sudo_event_base *evbase,
991+
int backchannel)
992+
{
993+
debug_decl(init_exec_events, SUDO_DEBUG_EXEC);
982994

983995
/* Setup event base and events. */
984996
ec->evbase = evbase;
@@ -1099,23 +1111,22 @@ exec_pty(struct command_details *details,
10991111
int sv[2], intercept_sv[2] = { -1, -1 };
11001112
struct exec_closure *ec = &pty_ec;
11011113
struct plugin_container *plugin;
1114+
const pid_t sudo_pid = getpid();
1115+
const pid_t ppgrp = getpgrp();
11021116
int evloop_retries = -1;
11031117
bool cmnd_foreground;
11041118
sigset_t set, oset;
11051119
struct sigaction sa;
1106-
pid_t ppgrp, sudo_pid;
11071120
debug_decl(exec_pty, SUDO_DEBUG_EXEC);
11081121

11091122
/*
1110-
* Allocate a pty if sudo is running in a terminal.
1123+
* Allocate a pty if sudo is running in a terminal. The exec
1124+
* closure must be set for pty_setup() and pty_cleanup_hook().
11111125
*/
1112-
ec->ptyname = pty_setup(details);
1113-
if (ec->ptyname == NULL)
1126+
init_exec_closure(ec, cstat, details, user_details, sudo_pid, ppgrp);
1127+
if (!pty_setup(ec))
11141128
debug_return_bool(false);
11151129

1116-
/* Register cleanup function */
1117-
sudo_fatal_callback_register(pty_cleanup_hook);
1118-
11191130
/*
11201131
* We communicate with the monitor over a bi-directional pair of sockets.
11211132
* Parent sends signal info to monitor and monitor sends back wait status.
@@ -1161,8 +1172,6 @@ exec_pty(struct command_details *details,
11611172
* to and from pty.
11621173
*/
11631174
init_ttyblock();
1164-
ppgrp = getpgrp(); /* parent's pgrp, so child can signal us */
1165-
sudo_pid = getpid();
11661175

11671176
/* Determine whether any of std{in,out,err} should be logged. */
11681177
TAILQ_FOREACH(plugin, &io_plugins, entries) {
@@ -1400,12 +1409,8 @@ exec_pty(struct command_details *details,
14001409
if (ISSET(details->flags, CD_SET_TIMEOUT))
14011410
alarm(details->timeout);
14021411

1403-
/*
1404-
* Fill in exec closure, allocate event base, signal events and
1405-
* the backchannel event.
1406-
*/
1407-
fill_exec_closure(ec, cstat, details, user_details, evbase,
1408-
sudo_pid, ppgrp, sv[0]);
1412+
/* Allocate and set signal events and the backchannel event. */
1413+
init_exec_events(ec, evbase, sv[0]);
14091414

14101415
/* Create event and closure for intercept mode. */
14111416
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
@@ -1414,6 +1419,10 @@ exec_pty(struct command_details *details,
14141419
terminate_command(ec->cmnd_pid, true);
14151420
}
14161421

1422+
/* Reset cstat for running the command. */
1423+
cstat->type = CMD_INVALID;
1424+
cstat->val = 0;
1425+
14171426
/* Restore signal mask now that signal handlers are setup. */
14181427
sigprocmask(SIG_SETMASK, &oset, NULL);
14191428

0 commit comments

Comments
 (0)