1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00

Fix signal handling due to exit status > 256

This fixes two bugs: issuing the 'exit' command with a value > 256
would cause ksh 93u+ to kill itself with the corresponding signal
(try 'exit 265' to SIGKILL your interactive shell), and, if the
last command of a script exits due to a signal, the shell would
repeat that signal to itself, causing any parent ksh to also be
killed.

Discussion:
https://bugzilla.redhat.com/show_bug.cgi?id=1469624
https://rainbow.chard.org/2017/03/21/ksh-deliberately-segfaults-if-the-last-command-in-a-script-crashes/

This commit is loosely based on a patch applied to the 93v- beta
and the abandoned ksh2020, but that patch was incomplete & broken:
  $ ksh-2020.0.0 -c 'exit 265'; echo $?
  137
Expected: 9. Since the exit was *not* due to a signal, the value
should simply be cropped to the 8 bits supported by the OS.

src/cmd/ksh93/bltins/cflow.c: b_exit():
- For the 'exit' builtin command, bitwise-AND the argument to
  'exit' with SH_EXITMASK (8 bits, crop to 0-255) before passing it
  on to sh_exit(). This restores the behaviour of <=2011 ksh93
  versions and is in line with all other POSIX shells.
  It also fixes this bogosity:
    $ (exit 265); echo $?                   # non-forked subshell
    265
    $ (ulimit -t unlimited; exit 265); echo $?  # forked subshell
    9
  Forked or non-forked should make no difference at all
  (see commit message a0e0e29e for why).

src/cmd/ksh93/sh/fault.c: sh_done():
- If the current exit status is equal to the status for the last
  signal that was received from a child process, remove the
  SH_EXITSIG (9th) bit, so that the shell doesn't kill itself.
- If the shell's last child process exits due to a signal, exit
  with a portable 8-bit exit status (128 + signal number). This
  avoids the exit status being < 128 by being cropped to 8 bits.

src/cmd/ksh93/tests/signal.sh:
- Add regression test for exit with status > 256.
- Add regression test verifying the shell no longer kills itself.

(cherry picked from commit 98e0fc94393e175ce6adfee390327c320795bf12)
This commit is contained in:
Martijn Dekker 2020-06-08 12:23:37 +02:00
parent f88f302c38
commit d024d4c895
4 changed files with 33 additions and 2 deletions

View file

@ -437,4 +437,26 @@ a
[[ $endb ]] && err_exit 'TERM signal did not kill function b'
[[ $enda == 1 ]] || err_exit 'TERM signal killed function a'
# ======
# Exit status checks
# Verify that 'exit x' for x > 256 does not make the shell send a signal to itself
"$SHELL" -c 'exit $((256+9))'
let "$? == 256+9" && err_exit 'exit with status > 256 makes shell kill itself'
# Verify that the shell does not kill itself after detecting that a child process is killed by a signal,
# and that a signal still causes the exit status to be set to a value > 128
cat >"$tmp/sigtest.sh" <<\EOF
echo begin
sh -c 'kill -9 "$$"'
EOF
expect=$'^begin\n/.*/sigtest.sh: line 2: [1-9][0-9]*: Killed\n[1-9][0-9]{1,2}$'
actual=$("$SHELL" -c 'ksh "$1"; echo "$?"' x "$tmp/sigtest.sh" 2>&1)
if ! [[ $actual =~ $expect ]]
then [[ $actual == *Killed*Killed* ]] && msg='ksh killed itself' || msg='unexpected output'
err_exit "$msg after child process signal (expected match to $(printf %q "$expect"); got $(printf %q "$actual"))"
fi
let "${actual##*$'\n'} > 128" || err_exit "child process signal did not cause exit status > 128"
# ======
exit $((Errors<125?Errors:125))