[Bug 3995] New: readpassphrase() spins at 100% CPU forever when SIGTTIN/SIGTTOU is SIG_IGN and no controlling TTY is available

bugzilla-daemon at mindrot.org bugzilla-daemon at mindrot.org
Sun Aug 23 04:43:12 AEST 2026


https://bugzilla.mindrot.org/show_bug.cgi?id=3995

            Bug ID: 3995
           Summary: readpassphrase() spins at 100% CPU forever when
                    SIGTTIN/SIGTTOU is SIG_IGN and no controlling TTY is
                    available
           Product: Portable OpenSSH
           Version: 10.5p1
          Hardware: Other
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P5
         Component: ssh-add
          Assignee: unassigned-bugs at mindrot.org
          Reporter: martin.monperrus at gnieh.org

Created attachment 3983
  --> https://bugzilla.mindrot.org/attachment.cgi?id=3983&action=edit
tentative patch for ssh-add  100% CPU forever

Summary
-------
When an OpenSSH tool that calls readpassphrase() (ssh-add is the common
case) is started with no controlling TTY, in a background process
group,
and with SIGTTIN and/or SIGTTOU already set to SIG_IGN by its parent
(e.g.
a systemd unit, or any supervisor/daemon that ignores job-control
signals), readpassphrase() spins at ~100% CPU indefinitely. It never
prints a prompt and never returns, so the caller hangs forever with no
way to detect or recover from the failure.

Root cause
----------
In openbsd-compat/readpassphrase.c, the signal handler for SIGTSTP,
SIGTTIN and SIGTTOU always sets need_restart = 1:

    case SIGTSTP:
    case SIGTTIN:
    case SIGTTOU:
        need_restart = 1;

readpassphrase() opens /dev/tty and attempts to read from it. Because
the
process is in a background process group, this raises SIGTTIN (or
SIGTTOU on the subsequent write of the prompt). The handler records the
signal and sets need_restart. readpassphrase() then restores the
*original* (saved) disposition for that signal — which is SIG_IGN in
this
scenario — and re-raises it via kill(getpid(), sig). Because the
disposition is now SIG_IGN, that kill() is a silent no-op: the
read/write
condition that caused SIGTTIN/SIGTTOU in the first place is never
resolved (there is still no controlling TTY to become the foreground
process group of). Since need_restart was already set, execution does
"goto restart", which reopens /dev/tty, writes the prompt again, hits
SIGTTIN/SIGTTOU again, and repeats forever at maximum CPU with no
prompt ever reaching a terminal and no error ever returned to the
caller.

This is distinct from bug 2619.

Steps to reproduce
------------------
1. Start a process tree with a controlling PTY (session leader in the
   foreground process group).
2. Set SIGTTIN and SIGTTOU to SIG_IGN in a supervisor process
(simulating e.g. a systemd service unit).
3. Fork ssh-add as a child in its *own*, background process group (so
it
   inherits the SIG_IGN dispositions but is not the foreground group of
   the PTY), pointed at any passphrase-protected key.
4. Observe ssh-add immediately pin one CPU core at ~100% and never
exit,
   never prompt, never time out.

Tentative patch attached

-- 
You are receiving this mail because:
You are watching the assignee of the bug.


More information about the openssh-bugs mailing list