Skip to content

Commit

Permalink
Initialize exec closure before calling sudo_fatal_callback_register()
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
millert committed Jan 22, 2025
1 parent 6fc816d commit 0be9f0f
Showing 1 changed file with 72 additions and 63 deletions.
135 changes: 72 additions & 63 deletions src/exec_pty.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2009-2024 Todd C. Miller <[email protected]>
* Copyright (c) 2009-2025 Todd C. Miller <[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
Expand Down Expand Up @@ -63,44 +63,6 @@ static void sync_ttysize(struct exec_closure *ec);
static void schedule_signal(struct exec_closure *ec, int signo);
static void send_command_status(struct exec_closure *ec, int type, int val);

/*
* Allocate a pty if /dev/tty is a tty.
* Fills in io_fds[SFD_USERTTY], io_fds[SFD_LEADER] and io_fds[SFD_FOLLOWER].
* Returns the dynamically allocated pty name on success, NULL on failure.
*/
static char *
pty_setup(struct command_details *details)
{
char *ptyname = NULL;
debug_decl(pty_setup, SUDO_DEBUG_EXEC);

io_fds[SFD_USERTTY] = open(_PATH_TTY, O_RDWR);
if (io_fds[SFD_USERTTY] == -1) {
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: no %s, not allocating a pty",
__func__, _PATH_TTY);
debug_return_ptr(NULL);
}

ptyname = get_pty(&io_fds[SFD_LEADER], &io_fds[SFD_FOLLOWER],
details->cred.euid);
if (ptyname == NULL)
sudo_fatal("%s", U_("unable to allocate pty"));

/* Add entry to utmp/utmpx? */
if (ISSET(details->flags, CD_SET_UTMP))
utmp_login(details->tty, ptyname, io_fds[SFD_FOLLOWER], details->utmp_user);

/* Update tty name in command details (used by monitor, SELinux, AIX). */
details->tty = ptyname;

sudo_debug_printf(SUDO_DEBUG_INFO,
"%s: %s fd %d, pty leader fd %d, pty follower fd %d",
__func__, _PATH_TTY, io_fds[SFD_USERTTY], io_fds[SFD_LEADER],
io_fds[SFD_FOLLOWER]);

debug_return_str(ptyname);
}

/*
* Restore user's terminal settings and update utmp, as needed.
*/
Expand Down Expand Up @@ -136,6 +98,49 @@ pty_cleanup_hook(void)
pty_cleanup(&pty_ec, 0);
}

/*
* Allocate a pty if /dev/tty is a tty.
* Fills in io_fds[SFD_USERTTY], io_fds[SFD_LEADER] and io_fds[SFD_FOLLOWER].
* Returns the dynamically allocated pty name on success, NULL on failure.
*/
static bool
pty_setup(struct exec_closure *ec)
{
struct command_details *details = ec->details;
debug_decl(pty_setup, SUDO_DEBUG_EXEC);

io_fds[SFD_USERTTY] = open(_PATH_TTY, O_RDWR);
if (io_fds[SFD_USERTTY] == -1) {
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: no %s, not allocating a pty",
__func__, _PATH_TTY);
debug_return_bool(false);
}

ec->ptyname = get_pty(&io_fds[SFD_LEADER], &io_fds[SFD_FOLLOWER],
details->cred.euid);
if (ec->ptyname == NULL)
sudo_fatal("%s", U_("unable to allocate pty"));

/* Add entry to utmp/utmpx? */
if (ISSET(details->flags, CD_SET_UTMP)) {
utmp_login(details->tty, ec->ptyname, io_fds[SFD_FOLLOWER],
details->utmp_user);
}

/* Update tty name in command details (used by monitor, SELinux, AIX). */
details->tty = ec->ptyname;

/* Register cleanup function. */
sudo_fatal_callback_register(pty_cleanup_hook);

sudo_debug_printf(SUDO_DEBUG_INFO,
"%s: %s fd %d, pty leader fd %d, pty follower fd %d",
__func__, _PATH_TTY, io_fds[SFD_USERTTY], io_fds[SFD_LEADER],
io_fds[SFD_FOLLOWER]);

debug_return_bool(true);
}

/*
* Check whether sudo is running in the foreground.
* Updates the foreground flag in the closure.
Expand Down Expand Up @@ -956,16 +961,14 @@ fwdchannel_cb(int sock, int what, void *v)
}

/*
* Fill in the exec closure and setup initial exec events.
* Allocates events for the signal pipe and backchannel.
* Forwarded signals on the backchannel are enabled on demand.
* Fill in the non-event part of the exec closure.
*/
static void
fill_exec_closure(struct exec_closure *ec, struct command_status *cstat,
init_exec_closure(struct exec_closure *ec, struct command_status *cstat,
struct command_details *details, const struct user_details *user_details,
struct sudo_event_base *evbase, pid_t sudo_pid, pid_t ppgrp, int backchannel)
pid_t sudo_pid, pid_t ppgrp)
{
debug_decl(fill_exec_closure, SUDO_DEBUG_EXEC);
debug_decl(init_exec_closure, SUDO_DEBUG_EXEC);

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

/* Reset cstat for running the command. */
cstat->type = CMD_INVALID;
cstat->val = 0;
debug_return;
}

/*
* Allocate and set events for the signal pipe and backchannel.
* Forwarded signals on the backchannel are enabled on demand.
*/
static void
init_exec_events(struct exec_closure *ec, struct sudo_event_base *evbase,
int backchannel)
{
debug_decl(init_exec_events, SUDO_DEBUG_EXEC);

/* Setup event base and events. */
ec->evbase = evbase;
Expand Down Expand Up @@ -1099,23 +1111,22 @@ exec_pty(struct command_details *details,
int sv[2], intercept_sv[2] = { -1, -1 };
struct exec_closure *ec = &pty_ec;
struct plugin_container *plugin;
const pid_t sudo_pid = getpid();
const pid_t ppgrp = getpgrp();
int evloop_retries = -1;
bool cmnd_foreground;
sigset_t set, oset;
struct sigaction sa;
pid_t ppgrp, sudo_pid;
debug_decl(exec_pty, SUDO_DEBUG_EXEC);

/*
* Allocate a pty if sudo is running in a terminal.
* Allocate a pty if sudo is running in a terminal. The exec
* closure must be set for pty_setup() and pty_cleanup_hook().
*/
ec->ptyname = pty_setup(details);
if (ec->ptyname == NULL)
init_exec_closure(ec, cstat, details, user_details, sudo_pid, ppgrp);
if (!pty_setup(ec))
debug_return_bool(false);

/* Register cleanup function */
sudo_fatal_callback_register(pty_cleanup_hook);

/*
* We communicate with the monitor over a bi-directional pair of sockets.
* Parent sends signal info to monitor and monitor sends back wait status.
Expand Down Expand Up @@ -1161,8 +1172,6 @@ exec_pty(struct command_details *details,
* to and from pty.
*/
init_ttyblock();
ppgrp = getpgrp(); /* parent's pgrp, so child can signal us */
sudo_pid = getpid();

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

/*
* Fill in exec closure, allocate event base, signal events and
* the backchannel event.
*/
fill_exec_closure(ec, cstat, details, user_details, evbase,
sudo_pid, ppgrp, sv[0]);
/* Allocate and set signal events and the backchannel event. */
init_exec_events(ec, evbase, sv[0]);

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

/* Reset cstat for running the command. */
cstat->type = CMD_INVALID;
cstat->val = 0;

/* Restore signal mask now that signal handlers are setup. */
sigprocmask(SIG_SETMASK, &oset, NULL);

Expand Down

0 comments on commit 0be9f0f

Please sign in to comment.