From bb3527aea558cbded31c7fccaa3e0c1c5389aa0e Mon Sep 17 00:00:00 2001 From: Johnothan King Date: Wed, 23 Feb 2022 07:11:24 -0800 Subject: [PATCH] Fix infinite loop when posix_spawn fails (re: 0863a8eb) (#468) This commit fixes an infinite loop introduced in commit 0863a8eb that caused ksh to enter an infinite loop if posix_spawn failed to start a new process after setting the terminal process group. Reproducer (warning: it will cause ksh to crash Wayland sessions and drives up CPU usage by a ton): $ /tmp/this/file/does/not/exist /usr/bin/ksh: /tmp/this/file/does/not/exist: not found $ (ksh now prints $PS1 in a loop until killed with SIGKILL) The first bug fixed is the infinite loop that occurs when posix_spawn fails to execute a command. This was fixed by setting the terminal process group to the main interactive shell. The second bug fixed is related to the signal handling of the SIGTTIN, SIGTTOU and SIGTSTP signals. In sh_ntfork() these signals are set to their default signal handlers (SIG_DFL) before running a command. The signal handlers were only restored to SIG_IGN (ignore signal) when sh_ntfork() successfully ran a command. This could cause a SIGTTOU lockup under strace when a command failed to execute in an interactive shell, while also being one cause of the infinite loop. src/cmd/ksh93/sh/xec.c: sh_ntfork(): - Restore the terminal process group if posix_spawn failed to launch a new process. This is necessary because posix_spawn will set the terminal process group before it attempts to run a command and doesn't restore it on failure. --- NEWS | 9 ++++++++- src/cmd/ksh93/sh/xec.c | 36 ++++++++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/NEWS b/NEWS index 4cd349e44..3c762a14f 100644 --- a/NEWS +++ b/NEWS @@ -3,12 +3,19 @@ For full details, see the git log at: https://github.com/ksh93/ksh/tree/1.0 Any uppercase BUG_* names are modernish shell bug IDs. -2022-08-23: +2022-02-23: - When reading input from the keyboard, ksh now turns off nonblocking I/O mode for standard input if a previously ran program left it on, so that interactive programs that expect it to be off work properly. +- Fixed a regression introduced on 2022-02-02 that caused interactive shells + to enter an infinite loop when a command failed to execute on Linux + systems with version 2.35 of glibc. + +- Fixed a SIGTTOU lockup that could cause ksh to freeze under strace(1) after + a command failed to execute in an interactive shell. + 2022-02-18: - Fixed a regression introduced on 2021-04-11 that caused the += operator in diff --git a/src/cmd/ksh93/sh/xec.c b/src/cmd/ksh93/sh/xec.c index 7d5c7c74d..0fbdaadeb 100644 --- a/src/cmd/ksh93/sh/xec.c +++ b/src/cmd/ksh93/sh/xec.c @@ -3598,19 +3598,35 @@ static pid_t sh_ntfork(const Shnode_t *t,char *argv[],int *jobid,int flag) fail: if(jobfork && spawnpid<0) job_fork(-2); - if(spawnpid == -1) switch(errno=sh.path_err) + if(spawnpid == -1) { - case ENOENT: - errormsg(SH_DICT,ERROR_exit(ERROR_NOENT),e_found+4); - UNREACHABLE(); +#if _use_ntfork_tcpgrp + if(jobwasset) + { + signal(SIGTTIN,SIG_IGN); + signal(SIGTTOU,SIG_IGN); + if(sh_isstate(SH_INTERACTIVE)) + signal(SIGTSTP,SIG_IGN); + else + signal(SIGTSTP,SIG_DFL); + } + if(job.jobcontrol) + tcsetpgrp(job.fd,sh.pid); +#endif /* _use_ntfork_tcpgrp */ + switch(errno=sh.path_err) + { + case ENOENT: + errormsg(SH_DICT,ERROR_exit(ERROR_NOENT),e_found+4); + UNREACHABLE(); #ifdef ENAMETOOLONG - case ENAMETOOLONG: - errormsg(SH_DICT,ERROR_exit(ERROR_NOENT),e_toolong+4); - UNREACHABLE(); + case ENAMETOOLONG: + errormsg(SH_DICT,ERROR_exit(ERROR_NOENT),e_toolong+4); + UNREACHABLE(); #endif - default: - errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec+4); - UNREACHABLE(); + default: + errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec+4); + UNREACHABLE(); + } } job_unlock(); }