busybox/shell
Denys Vlasenko 458c1f218b ash: [JOBS] Fix dowait signal race
Upstream commit:

    Date: Sun, 22 Feb 2009 18:10:01 +0800
    [JOBS] Fix dowait signal race

    This test program by Alexey Gladkov can cause dash to enter an
    infinite loop in waitcmd.

    #!/bin/dash
    trap "echo TRAP" USR1
    stub() {
        echo ">>> STUB $1" >&2
        sleep $1
        echo "<<< STUB $1" >&2
        kill -USR1 $$
    }
    stub 3 &
    stub 2 &
    until { echo "###"; wait; } do
    echo "*** $?"
    done

    The problem is that if we get a signal after the wait3 system
    call has returned but before we get to INTON in dowait, then
    we can jump back up to the top and lose the exit status.  So
    if we then wait for the job that has just exited, then it'll
    stay there forever.

    I made the original change that caused this bug to fix pretty
    much the same bug but in the opposite direction.  That is, if
    we get a signal after we enter wait3 but before we hit the kernel
    then it too can cause the wait to go on forever (assuming the
    child doesn't exit).

    In fact this is pretty much exactly the scenario that you'll
    find in glibc's documentation on pause().  The solution is given
    there too, in the form of sigsuspend, which is the only way to
    do the check and wait atomically.

    So this patch fixes Alexey's race without reintroducing the old
    bug by converting the blocking wait3 to a sigsuspend.

    In order to do this we need to set a signal handler for SIGCHLD,
    so the code has been modified to always do that.

    Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

I failed to reproduce the bug (it requires precise timing), but it seems real.

function                                             old     new   delta
dowait                                               284     463    +179
setsignal                                            301     326     +25
signal_handler                                        59      76     +17
ash_main                                            1481    1487      +6
localcmd                                             350     348      -2
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 4/1 up/down: 227/-2)            Total: 225 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2016-10-27 23:51:19 +02:00
..
ash_test ash: [PARSER] Recognise here-doc delimiters terminated by EOF 2016-10-26 16:26:45 +02:00
hush_test ash: [PARSER] Recognise here-doc delimiters terminated by EOF 2016-10-26 16:26:45 +02:00
ash_doc.txt
ash_ptr_hack.c *: make GNU licensing statement forms more regular 2010-08-16 20:14:46 +02:00
ash.c ash: [JOBS] Fix dowait signal race 2016-10-27 23:51:19 +02:00
brace.txt
Config.src config: disentangle PREFER_APPLETS from SH_STANDALONE and SH_NOFORK 2016-07-22 18:48:38 +02:00
cttyhack.c cttyhack: handle multiple consoles found in sysfs 2012-02-04 21:55:01 +01:00
hush_doc.txt
hush_leaktool.sh
hush.c ash: [SHELL] Expand ENV before using it 2016-10-27 11:28:59 +02:00
Kbuild.src *: make GNU licensing statement forms more regular 2010-08-16 20:14:46 +02:00
match.c shell/match.c: shrink by dropping double bool inversion 2010-09-12 15:06:42 +02:00
match.h hush: optimize #[#] and %[%] for speed. size -2 bytes. 2010-09-04 21:21:07 +02:00
math.c typo fix in comment 2014-11-20 01:43:30 +01:00
math.h move endofname() to libbb 2013-02-26 00:36:53 +01:00
random.c ash,hush: fix a thinko about 2^64-1 factorization 2014-03-15 09:25:46 +01:00
random.h ash,hush: improve randomness of $RANDOM, add easy-ish way to test it 2014-03-13 12:52:43 +01:00
README update shell/README 2010-05-20 12:56:14 +02:00
README.job
shell_common.c ash: [VAR] Initialise OPTIND after importing environment 2016-09-30 14:46:41 +02:00
shell_common.h ash: [VAR] Initialise OPTIND after importing environment 2016-09-30 14:46:41 +02:00

http://www.opengroup.org/onlinepubs/9699919799/
Open Group Base Specifications Issue 7


http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap01.html
Shell & Utilities

It says that any of the standard utilities may be implemented
as a regular shell built-in. It gives a list of utilities which
are usually implemented that way (and some of them can only
be implemented as built-ins, like "alias"):

alias
bg
cd
command
false
fc
fg
getopts
jobs
kill
newgrp
pwd
read
true
umask
unalias
wait


http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
Shell Command Language

It says that shell must implement special built-ins. Special built-ins
differ from regular ones by the fact that variable assignments
done on special builtin are *PRESERVED*. That is,

VAR=VAL special_builtin; echo $VAR

should print VAL.

(Another distinction is that an error in special built-in should
abort the shell, but this is not such a critical difference,
and moreover, at least bash's "set" does not follow this rule,
which is even codified in autoconf configure logic now...)

List of special builtins:

. file
: [argument...]
break [n]
continue [n]
eval [argument...]
exec [command [argument...]]
exit [n]
export name[=word]...
export -p
readonly name[=word]...
readonly -p
return [n]
set [-abCefhmnuvx] [-o option] [argument...]
set [+abCefhmnuvx] [+o option] [argument...]
set -- [argument...]
set -o
set +o
shift [n]
times
trap n [condition...]
trap [action condition...]
unset [-fv] name...

In practice, no one uses this obscure feature - none of these builtins
gives any special reasons to play such dirty tricks.

However. This section also says that *function invocation* should act
similar to special built-in. That is, variable assignments
done on function invocation should be preserved after function invocation.

This is significant: it is not unthinkable to want to run a function
with some variables set to special values. But because of the above,
it does not work: variable will "leak" out of the function.