mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-02-13 11:42:21 +00:00
Fix 'return' emitting signals; allow arbitrary return values
When a global EXIT trap is set, and a ksh-style function exits with a status > 256 that could have been the result of a signal, then the shell incorrectly issues that signal to itself. Depending on the signal, this causes ksh to terminate itself ungracefully: $ cat /tmp/exit267 trap 'echo OK' EXIT # This trap triggers the crash function foo { return 267; } foo $ bash /tmp/exit267 OK $ ksh-3aee10d7 /tmp/exit267 OK $ ksh /tmp/exit267 Memory fault(coredump) On most systems, status 267 corresponds to SIGSEGV. The reported memory fault is not real; it results from ksh incorrectly killing itself with that signal. The problem is caused by two factors: 1. As of 93u+ 2012-08-01, ksh explicitly allows 'return' to use an exit status corresponding to a signal (from 257 to end of signal range). The rest of the integer range is trunctated to 8 bits. This is contrary to both 'man ksh' and 'return --man' which both say it's always truncated to 8 bits. Plus, combined with point 2 below, this new behaviour is nonsensical, as 'return' has no business actually generating signals. However, a couple of regression tests now depend on this, as may some scripts. 2. When a ksh-style function does not handle a signal, the signal is passed down to the parent environment and ksh does this by reissuing the signal to its own process after leaving the function scope. However, it does this by checking the exit status, which is very bad practice as there is no guarantee that an exit status corresponding to a signal was in fact produced by a signal, particularly after they changed the behaviour of 'return' per 1 above. This commit fixes both issues. It also takes a proper decision on allowable 'return' exit status arguments. Since 93u+ was released nearly a decade ago and some scripts may now rely on being able to pass certain exit statuses out of the 8-bit range, we should not disallow this now. But neither should we be half-hearted in allowing only some arbitrary selection of 9-bit statuses; 'return' values categorically should have nothing to do with signals, so this is no basis for limiting them. We're now allowing the full unsigned integer range, which is usually 32 bits. This is like zsh, and may create some interesting possibilities for scripts. Just don't forget that $? will still lose all but its 8 least significant bits when leaving the current (sub)shell environment. src/cmd/ksh93/sh/xec.c: sh_funscope(): - Fix passing down unhandled signals from interrupted ksh functions (jumpval==SH_JMPFUN) to the parent environment. Do not pay any attention to the exit status. Instead, use sh.lastsig (a.k.a. shp->lastsig). It is set by sh_fault() in fault.c for just this purpose and contains the last signal handled for the current command. It is reset in sh_exec() before running any new command. So if it contains a signal, that is the one that interrupted the ksh function, so it's the correct one to pass down. (Further evidence: sh_subshell() was already using this in the same way.) src/cmd/ksh93/bltins/cflow.c: b_return(): - Allow any signed int return value when invoked as and behaving like 'return'. - Add warning if a passed value is out of int range. Set the exit status to 128 in that case; int overflow is undefined behaviour in C and we want consistent behaviour across platforms. It should be safe enough to check if the long and int values are equal. - Refactor for clarity. src/cmd/ksh93/sh/subshell.c: sh_subshell(): - If a function returns with a status out of the 8 bit range in a virtual subshell, this status could be passed down to the parent shell in full. However, if the subshell forks, then the kernel will enforce an 8-bit exit status. That is inconsistent. Scripts should not be able to tell the difference between forked and non-forked subshells, so artificially enforce that limit here. Other changed files: - Documentation updates and copy-edits. - Update an AT&T functions.sh regress test to allow arbitrary integer return values for functions. - Add regression tests based in part on @JohnoKing's reproducers. - Rework some vaguely related regression tests to fail gracefully. Thanks to Johnothan King for the report and the testing. Fixes: https://github.com/ksh93/ksh/issues/364
This commit is contained in:
parent
7fb814e186
commit
b3050769ea
11 changed files with 149 additions and 74 deletions
12
NEWS
12
NEWS
|
@ -3,6 +3,18 @@ For full details, see the git log at: https://github.com/ksh93/ksh
|
|||
|
||||
Any uppercase BUG_* names are modernish shell bug IDs.
|
||||
|
||||
2021-12-08:
|
||||
|
||||
- Fixed: if a function returned with a status > 256 using the 'return' command
|
||||
and the return value corresponded to a value that could have resulted from a
|
||||
signal, and an EXIT trap was active, then the shell mistakenly issued that
|
||||
signal to itself. Depending on the signal, this could cause the shell to
|
||||
terminate ungracefully, e.g. 'return 267' caused SIGSEGV ("memory fault").
|
||||
|
||||
- For the 'return' built-in command, you can now freely specify any
|
||||
return value that fits in a signed integer, typically a 32-bit value.
|
||||
Note that $? is truncated to 8 bits when the current (sub)shell exits.
|
||||
|
||||
2021-12-05:
|
||||
|
||||
- Fixed an issue on illumos that caused some parameters in the getconf
|
||||
|
|
|
@ -164,6 +164,10 @@ For more details, see the NEWS file and for complete details, see the git log.
|
|||
29. It is now an error for arithmetic expressions to assign an out-of-range
|
||||
index value to a variable of an enumeration type created with 'enum'.
|
||||
|
||||
30. For the 'return' built-in command, you can now freely specify any
|
||||
return value that fits in a signed integer, typically a 32-bit value.
|
||||
Note that $? is truncated to 8 bits when the current (sub)shell exits.
|
||||
|
||||
____________________________________________________________________________
|
||||
|
||||
KSH-93 VS. KSH-88
|
||||
|
|
|
@ -46,11 +46,10 @@
|
|||
#endif
|
||||
int b_return(register int n, register char *argv[],Shbltin_t *context)
|
||||
{
|
||||
register char *arg;
|
||||
register Shell_t *shp = context->shp;
|
||||
struct checkpt *pp = (struct checkpt*)shp->jmplist;
|
||||
const char *options = (**argv=='r'?sh_optreturn:sh_optexit);
|
||||
while((n = optget(argv,options))) switch(n)
|
||||
/* 'return' outside of function, dotscript and profile behaves like 'exit' */
|
||||
char do_exit = **argv=='e' || sh.fn_depth==0 && sh.dot_depth==0 && !sh_isstate(SH_PROFILE);
|
||||
NOT_USED(context);
|
||||
while((n = optget(argv, **argv=='e' ? sh_optexit : sh_optreturn))) switch(n)
|
||||
{
|
||||
case ':':
|
||||
if(!strmatch(argv[opt_info.index],"[+-]+([0-9])"))
|
||||
|
@ -66,17 +65,27 @@ done:
|
|||
errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
|
||||
UNREACHABLE();
|
||||
}
|
||||
pp->mode = (**argv=='e'?SH_JMPEXIT:SH_JMPFUN);
|
||||
argv += opt_info.index;
|
||||
n = (arg = *argv) ? (int)strtol(arg, (char**)0, 10) : shp->savexit;
|
||||
if(n<0 || n==256 || n > SH_EXITMASK+shp->gd->sigmax+1)
|
||||
n &= ((unsigned int)n)&SH_EXITMASK;
|
||||
/* return outside of function, dotscript and profile is exit */
|
||||
if(shp->fn_depth==0 && shp->dot_depth==0 && !sh_isstate(SH_PROFILE))
|
||||
pp->mode = SH_JMPEXIT;
|
||||
shp->savexit = n;
|
||||
sh_exit((pp->mode == SH_JMPEXIT) ? (n & SH_EXITMASK) : n);
|
||||
return(1);
|
||||
if(*argv)
|
||||
{
|
||||
long l = strtol(*argv, NIL(char**), 10);
|
||||
if(do_exit)
|
||||
n = (int)(l & SH_EXITMASK); /* exit: apply bitmask before conversion to avoid undefined int overflow */
|
||||
else if((long)(n = (int)l) != l) /* return: convert to int and check for overflow (should be safe enough) */
|
||||
{
|
||||
errormsg(SH_DICT,ERROR_warn(0),"%s: out of range",*argv);
|
||||
n = 128; /* overflow is undefined, so use a consistent status for this */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
n = sh.savexit; /* no argument: pass down $? */
|
||||
if(do_exit)
|
||||
n &= SH_EXITMASK;
|
||||
}
|
||||
((struct checkpt*)sh.jmplist)->mode = do_exit ? SH_JMPEXIT : SH_JMPFUN;
|
||||
sh_exit(sh.savexit = n);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -637,7 +637,7 @@ const char sh_optexec[] =
|
|||
;
|
||||
|
||||
const char sh_optexit[] =
|
||||
"[-1c?\n@(#)$Id: exit (AT&T Research) 1999-07-07 $\n]"
|
||||
"[-1c?\n@(#)$Id: exit (ksh 93u+m) 2021-12-08 $\n]"
|
||||
"[--catalog?" SH_DICT "]"
|
||||
"[+NAME?exit - exit the current shell]"
|
||||
"[+DESCRIPTION?\bexit\b is shell special built-in that causes the "
|
||||
|
@ -647,10 +647,10 @@ const char sh_optexit[] =
|
|||
"\n"
|
||||
"\n[n]\n"
|
||||
"\n"
|
||||
"[+EXIT STATUS?If \an\a is specified, the exit status is the least significant "
|
||||
"eight bits of the value of \an\a. Otherwise, the exit status is the "
|
||||
"exit status of preceding command. When invoked inside a trap, the "
|
||||
"preceding command means the command that invoked the trap.]"
|
||||
"[+EXIT STATUS?The exit status is the least significant eight bits of the "
|
||||
"value of \an\a (if specified) or of the exit status of the preceding "
|
||||
"command. If \bexit\b is invoked inside a trap, the preceding command "
|
||||
"means the command that invoked the trap.]"
|
||||
"[+SEE ALSO?\bbreak\b(1), \breturn\b(1)]"
|
||||
;
|
||||
|
||||
|
@ -1515,25 +1515,28 @@ const char sh_optredirect[] =
|
|||
;
|
||||
|
||||
const char sh_optreturn[] =
|
||||
"[-1c?\n@(#)$Id: return (AT&T Research) 1999-07-07 $\n]"
|
||||
"[-1c?\n@(#)$Id: return (ksh 93u+m) 2021-12-08 $\n]"
|
||||
"[--catalog?" SH_DICT "]"
|
||||
"[+NAME?return - return from a function or dot script ]"
|
||||
"[+DESCRIPTION?\breturn\b is a shell special built-in that causes the "
|
||||
"function or dot script that invokes it to exit. "
|
||||
"If \breturn\b is invoked outside of a function or dot script "
|
||||
"it is equivalent to \bexit\b.]"
|
||||
"function, dot script or profile script that invokes it to exit. "
|
||||
"If \breturn\b is invoked outside of one of these, it behaves "
|
||||
"exactly like \bexit\b(1); see its manual page.]"
|
||||
"[+?If \breturn\b is invoked inside a function defined with the \bfunction\b "
|
||||
"reserved word syntax, then any \bEXIT\b trap set within the "
|
||||
"then function will be invoked in the context of the caller "
|
||||
"function will be invoked in the context of the caller "
|
||||
"before the function returns.]"
|
||||
"[+?If \an\a is given, it will be used to set the exit status.]"
|
||||
"\n"
|
||||
"\n[n]\n"
|
||||
"\n"
|
||||
"[+EXIT STATUS?If \an\a is specified, the exit status is the least significant "
|
||||
"eight bits of the value of \an\a. Otherwise, the exit status is the "
|
||||
"exit status of preceding command.]"
|
||||
"[+SEE ALSO?\bbreak\b(1), \bexit\b(1)]"
|
||||
"[+EXIT STATUS?If \an\a is not specified, the exit status is that of the "
|
||||
"preceding command. Otherwise, it is the value \an\a as a signed "
|
||||
"integer. An out-of-range value produces a warning and an exit "
|
||||
"status of 128. The range can be shown using \bgetconf INT_MIN\b "
|
||||
"and \bgetconf INT_MAX\b. When the current (sub)shell exits, "
|
||||
"the exit status is truncated to 8 bits as in \bexit\b.]"
|
||||
"[+SEE ALSO?\bbreak\b(1), \bexit\b(1), \bgetconf\b(1)]"
|
||||
;
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
|
||||
#define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */
|
||||
#define SH_RELEASE_DATE "2021-12-05" /* must be in this format for $((.sh.version)) */
|
||||
#define SH_RELEASE_DATE "2021-12-08" /* must be in this format for $((.sh.version)) */
|
||||
#define SH_RELEASE_CPYR "(c) 2020-2021 Contributors to ksh " SH_RELEASE_FORK
|
||||
|
||||
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */
|
||||
|
|
|
@ -1523,10 +1523,21 @@ the
|
|||
command.
|
||||
.TP
|
||||
.B ?
|
||||
The decimal value returned by the last executed command.
|
||||
The exit status returned by the last executed command. Its meaning depends
|
||||
on the command or function that defines it, but there are conventions that
|
||||
other commands often depend on: zero typically means 'success' or 'true',
|
||||
one typically means 'non-success' or 'false', and a value greater than one
|
||||
typically indicates some kind of error. Only the 8 least significant bits of
|
||||
\f3$?\fP (values 0 to 255) are preserved when the exit status is passed on
|
||||
to a parent process, but within the same (sub)shell environment, it is a
|
||||
signed integer value with a range of possible values as shown by the
|
||||
commands \f3getconf INT_MIN\fP and \f3getconf INT_MAX\fP. Shell functions
|
||||
that run in the current environment may return status values in this range.
|
||||
.TP
|
||||
.B $
|
||||
The process id of this shell.
|
||||
The process ID of the main shell process. Note that this value will not
|
||||
change in a subshell, even if the subshell runs in a different process.
|
||||
See also \f3.sh.pid\fP.
|
||||
.TP
|
||||
.B _
|
||||
Initially, the value of
|
||||
|
@ -6060,17 +6071,15 @@ then this command persistently modifies file descriptors as in
|
|||
Causes the shell to exit
|
||||
with the exit status specified by
|
||||
.IR n .
|
||||
The value will be the least significant 8 bits of the specified status.
|
||||
If
|
||||
The value will be the least significant 8 bits of
|
||||
.I n\^
|
||||
is omitted, then the exit status is that of the last command executed.
|
||||
An end-of-file will also cause the shell to exit
|
||||
except for a
|
||||
shell which has the
|
||||
(if specified) or of the exit status of the last command executed.
|
||||
An end-of-file will also cause the shell to exit,
|
||||
except for an interactive shell that has the
|
||||
.B ignoreeof
|
||||
option (see
|
||||
option turned on (see
|
||||
.B set
|
||||
below) turned on.
|
||||
below).
|
||||
.TP
|
||||
\(dg\(dd \f3export\fP \*(OK \f3\-p\fP \*(CK \*(OK \f2name\^\fP\*(OK\f3=\fP\f2value\^\fP\*(CK \*(CK .\|.\|.
|
||||
If
|
||||
|
@ -6959,25 +6968,20 @@ redirected to themselves as part of the invocation (e.g. \fB4>&4\fR)
|
|||
or if the \fBposix\fR option is set.
|
||||
.TP
|
||||
\(dg \f3return\fP \*(OK \f2n\^\fP \*(CK
|
||||
Causes a shell
|
||||
.I function
|
||||
or
|
||||
\f3\|.\fP
|
||||
script to return
|
||||
to the invoking script
|
||||
with the exit status specified by
|
||||
Causes a shell function, dot script (see \f3\|.\fP and \f3source\fP),
|
||||
or profile script to return to the invoking shell environment with the
|
||||
exit status specified by
|
||||
.IR n .
|
||||
The value will be the least significant 8 bits of the specified status.
|
||||
This status value can use the full signed integer range as shown by the
|
||||
commands \f3getconf INT_MIN\fP and \f3getconf INT_MAX\fP. A value
|
||||
outside that range will produce a warning and an exit status of 128.
|
||||
If
|
||||
.I n\^
|
||||
is omitted, then the return status is that of the last command executed.
|
||||
is omitted, then the value of \f3$?\fP is assumed, i.e., the exit
|
||||
status of the last command executed is passed on.
|
||||
If
|
||||
.B return
|
||||
is invoked while not in a
|
||||
.I function
|
||||
or a
|
||||
\f3\|.\fP
|
||||
script,
|
||||
is invoked while not in a function, dot script, or profile script,
|
||||
then it behaves the same as
|
||||
.BR exit .
|
||||
.TP
|
||||
|
@ -7169,7 +7173,7 @@ Same as
|
|||
.BR \-H .
|
||||
.TP 8
|
||||
.B ignoreeof
|
||||
The shell will not exit on end-of-file.
|
||||
An interactive shell will not exit on end-of-file.
|
||||
The command
|
||||
.B exit
|
||||
must be used.
|
||||
|
|
|
@ -871,6 +871,9 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub)
|
|||
srand(rp->rand_seed = sp->rand_seed);
|
||||
rp->rand_last = sp->rand_last;
|
||||
}
|
||||
/* Real subshells have their exit status truncated to 8 bits by the kernel.
|
||||
* Since virtual subshells should be indistinguishable, do the same here. */
|
||||
sh.exitval &= SH_EXITMASK;
|
||||
}
|
||||
shp->subshare = sp->subshare;
|
||||
shp->subdup = sp->subdup;
|
||||
|
|
|
@ -3259,8 +3259,8 @@ int sh_funscope(int argn, char *argv[],int(*fun)(void*),void *arg,int execflg)
|
|||
}
|
||||
if(jmpval)
|
||||
r=shp->exitval;
|
||||
if(nsig && r>SH_EXITSIG)
|
||||
kill(shgd->current_pid,r&SH_EXITMASK);
|
||||
if(jmpval==SH_JMPFUN && sh.lastsig)
|
||||
kill(shgd->current_pid, sh.lastsig); /* pass down unhandled signal that interrupted ksh function */
|
||||
if(jmpval > SH_JMPFUN)
|
||||
{
|
||||
sh_chktrap(shp);
|
||||
|
|
|
@ -1210,11 +1210,11 @@ function foo
|
|||
let 1
|
||||
return $1
|
||||
}
|
||||
invals=(135 255 256 267 -1)
|
||||
outvals=(135 255 0 267 255)
|
||||
for ((i=0; i < ${#invals[@]}; i++))
|
||||
do foo ${invals[i]}
|
||||
[[ $? == "${outvals[i]}" ]] || err_exit "function exit ${invals[i]} should set \$? to ${outvals[i]}"
|
||||
# As of ksh 93u+m 2021-12-08, you can use arbitrary signed integer return values in the current (sub)shell.
|
||||
vals=(135 255 256 267 -1 1234 -4567)
|
||||
for ((i=0; i < ${#vals[@]}; i++))
|
||||
do foo ${vals[i]}
|
||||
[[ $? == "${vals[i]}" ]] || err_exit "function exit ${vals[i]} should set \$? to ${vals[i]}"
|
||||
done
|
||||
|
||||
function foo
|
||||
|
@ -1262,12 +1262,15 @@ expect_status=0
|
|||
|
||||
# ======
|
||||
# When a function unsets itself, it should not fail to be unset
|
||||
$SHELL -c 'PATH=/dev/null; fn() { unset -f fn; true; }; fn' || err_exit 'unset of POSIX function in the calling stack fails'
|
||||
$SHELL -c 'PATH=/dev/null; function ftest { ftest2; }; function ftest2 { unset -f ftest; }; ftest' || err_exit 'unset of ksh function in the calling stack fails'
|
||||
$SHELL -c 'PATH=/dev/null; fn() { unset -f fn; true; }; fn; fn' 2> /dev/null
|
||||
[[ $? != 127 ]] && err_exit 'unset of POSIX function fails when it is still running'
|
||||
$SHELL -c 'PATH=/dev/null; function fn { unset -f fn; true; }; fn; fn' 2> /dev/null
|
||||
[[ $? != 127 ]] && err_exit 'unset of ksh function fails when it is still running'
|
||||
# https://github.com/ksh93/ksh/issues/21
|
||||
got=$( { "$SHELL" -c 'PATH=/dev/null; fn() { unset -f fn; true; }; fn'; } 2>&1 )
|
||||
(( (e=$?)==0 )) || err_exit 'unset of POSIX function in the calling stack fails' "(got status $e, $(printf %q "$got"))"
|
||||
got=$( { "$SHELL" -c 'PATH=/dev/null; function ftest { ftest2; }; function ftest2 { unset -f ftest; }; ftest'; } 2>&1 )
|
||||
(( (e=$?)==0 )) || err_exit 'unset of ksh function in the calling stack fails' "(got status $e, $(printf %q "$got"))"
|
||||
got=$( { "$SHELL" -c 'PATH=/dev/null; fn() { unset -f fn; true; }; fn; fn'; } 2>&1 )
|
||||
(( (e=$?)==127 )) || err_exit 'unset of POSIX function fails when it is still running' "(got status $e, $(printf %q "$got"))"
|
||||
got=$( { "$SHELL" -c 'PATH=/dev/null; function fn { unset -f fn; true; }; fn; fn'; } 2>&1 )
|
||||
(( (e=$?)==127 )) || err_exit 'unset of ksh function fails when it is still running' "(got status $e, $(printf %q "$got"))"
|
||||
|
||||
# ======
|
||||
# Check if environment variables passed while invoking a function are exported
|
||||
|
|
|
@ -466,17 +466,22 @@ let "${actual##*$'\n'} > 128" || err_exit "child process signal did not cause ex
|
|||
"(got ${actual##*$'\n'})"
|
||||
|
||||
# ======
|
||||
# Killing a non-existent job shouldn't cause a segfault. Note that `2> /dev/null` has no effect when
|
||||
# there is a segfault.
|
||||
$SHELL -c 'kill %% 2> /dev/null'; [[ $? == 1 ]] || err_exit $'`kill` doesn\'t handle a non-existent job correctly when passed \'%%\''
|
||||
$SHELL -c 'kill %+ 2> /dev/null'; [[ $? == 1 ]] || err_exit $'`kill` doesn\'t handle a non-existent job correctly when passed \'%+\''
|
||||
$SHELL -c 'kill %- 2> /dev/null'; [[ $? == 1 ]] || err_exit $'`kill` doesn\'t handle a non-existent job correctly when passed \'%-\''
|
||||
# Killing a non-existent job shouldn't cause a segfault.
|
||||
# https://github.com/ksh93/ksh/issues/34
|
||||
for c in % + -
|
||||
do got=$( { "$SHELL" -c "kill %$c"; } 2>&1 )
|
||||
[[ $? == 1 ]] || err_exit "'kill' doesn't handle a non-existent job correctly when passed '%$c'" \
|
||||
"(got $(printf %q "$got"))"
|
||||
done
|
||||
|
||||
# ======
|
||||
# SIGINFO should be supported by the kill builtin on platforms that have it.
|
||||
if "$(whence -p kill)" -INFO $$ 2> /dev/null
|
||||
then
|
||||
kill -INFO $$ || err_exit '`kill` cannot send SIGINFO to processes when passed `-INFO`'
|
||||
got=$(kill -INFO $$ 2>&1) || err_exit '`kill` cannot send SIGINFO to processes when passed `-INFO`' \
|
||||
"(got $(printf %q "$got"))"
|
||||
got=$(kill -s INFO $$ 2>&1) || err_exit '`kill` cannot send SIGINFO to processes when passed `-s INFO`' \
|
||||
"(got $(printf %q "$got"))"
|
||||
fi
|
||||
|
||||
# ======
|
||||
|
@ -523,5 +528,30 @@ got=${got% } # rm final space
|
|||
((!(e = $?))) && [[ $got == "$exp" ]] || err_exit "ksh function ignores global signal traps" \
|
||||
"(got status $e$( ((e>128)) && print -n / && kill -l "$e"), $(printf %q "$got"))"
|
||||
|
||||
# ======
|
||||
# Signal incorrectly issued when function returns with status > 256 and EXIT trap is active
|
||||
# https://github.com/ksh93/ksh/issues/364
|
||||
signum=${ kill -l SEGV; }
|
||||
cat > exit267 <<-EOF # unquoted delimiter; expansion active
|
||||
trap 'echo OK \$?' EXIT # This trap triggers the crash
|
||||
function foo { return $((signum+256)); }
|
||||
foo
|
||||
EOF
|
||||
exp="OK $((signum+256))"
|
||||
got=$( { "$SHELL" exit267; } 2>&1 )
|
||||
(( (e=$?)==signum+128 )) && [[ $got == "$exp" ]] || err_exit "'return' with status > 256:" \
|
||||
"(expected status $((signum+128)) and $(printf %q "$exp"), got status $e and $(printf %q "$got"))"
|
||||
|
||||
cat > bar <<-'EOF'
|
||||
trap : EXIT
|
||||
function foo { "$SHELL" -c 'kill -s SEGV $$'; }
|
||||
foo 2> /dev/null
|
||||
echo OK
|
||||
EOF
|
||||
exp="OK"
|
||||
got=$( { "$SHELL" bar; } 2>&1 )
|
||||
(( (e=$?)==0 )) && [[ $got == "$exp" ]] || err_exit "segfaulting child process:" \
|
||||
"(expected status 0 and $(printf %q "$exp"), got status $e and $(printf %q "$got"))"
|
||||
|
||||
# ======
|
||||
exit $((Errors<125?Errors:125))
|
||||
|
|
|
@ -1083,5 +1083,12 @@ then kill -9 $tpid
|
|||
err_exit 'backtick command substitution hangs on reproducer from issue 316'
|
||||
fi
|
||||
|
||||
# ======
|
||||
# Virtual subshells should clip $? to 8 bits, as real subshells get that enforced by the kernel.
|
||||
# (Note: 'ulimit' will reliably fork a virtual subshell into a real one.)
|
||||
e1=$( (f() { return 267; }; f); echo $? )
|
||||
e2=$( (ulimit -t unlimited 2>/dev/null; f() { return 267; }; f); echo $? )
|
||||
((e1==11 && e2==11)) || err_exit "exit status of virtual ($e1) and real ($e2) subshell should both be clipped to 8 bits (11)"
|
||||
|
||||
# ======
|
||||
exit $((Errors<125?Errors:125))
|
||||
|
|
Loading…
Reference in a new issue