issue #481: core: preserve stderr TTY FD if one is present.

Since 802de6a8d5, sudo on CentOS 5 had
begun failing due to a TTY FD leak in the parent process being fixed.

The old versions of sudo doesn't hang around after starting a child --
they exec the privilege-escalated child process on top of themselves,
meaning no spare copy of the TTY FD is kept alive by sudo.

When the child starts up, it replaces stdio with IoLoggers, including
the inherited stderr FD connected to DiagLogStream/the slave PTY. When
the last process closes a slave PTY, the kernel sends SIGHUP to any
processes still having it as the controlling TTY.

Therefore we must either ignore SIGHUP until the first stage has been
waited on (since the first stage also preserve the FD), or dup the
inherited TTY FD and keep it around forever.

Wasting one FD seems less annoying than modifying process signals for
all potential library users, so that is the approach taken here.
pull/564/head
David Wilson 5 years ago
parent b263e01867
commit 78ec634dab

@ -3295,6 +3295,18 @@ class ExternalContext(object):
os.close(fd)
def _setup_stdio(self):
# #481: when stderr is a TTY due to being started via
# tty_create_child()/hybrid_tty_create_child(), and some privilege
# escalation tool like prehistoric versions of sudo exec this process
# over the top of itself, there is nothing left to keep the slave PTY
# open after we replace our stdio. Therefore if stderr is a TTY, keep
# around a permanent dup() to avoid receiving SIGHUP.
try:
if os.isatty(2):
self.reserve_tty_fd = os.dup(2)
set_cloexec(self.reserve_tty_fd)
except OSError:
pass
# When sys.stdout was opened by the runtime, overwriting it will not
# close FD 1. However when forking from a child that previously used
# fdopen(), overwriting it /will/ close FD 1. So we must swallow the

Loading…
Cancel
Save