Pointer arithmetic on a null pointer is undefined behavior.
The bug can be reproduced by running bin/sh/tests/builtins/wait6.0 with
UBSAN.
Reported by: Mark Millard
Reviewed by: imp
Differential Revision: https://reviews.freebsd.org/D34011
The shell maintains a count of the number of times SIGINT processing has
been disabled via INTOFF, so SIGINT processing resumes when all disables
have enabled again (INTON).
If an error occurs in a vfork() child, the processing of the error enables
SIGINT processing again, and the INTON in vforkexecshell() causes the count
to become negative.
As a result, a later INTOFF may not actually disable SIGINT processing. This
might cause memory corruption if a SIGINT arrives at an inopportune time. As
of r360452, it causes the shell to abort when it would unsafely allocate or
free memory in certain ways.
Note that various places such as errors in non-special builtins
unconditionally reset the count to 0, so the problem might still not always
be visible.
PR: 246497
Reported by: jbeich
MFC after: 2 weeks
The pipefail option allows checking the exit status of all commands in a
pipeline more easily, at a limited cost of complexity in sh itself. It works
similarly to the option in bash, ksh93 and mksh.
Like ksh93 and unlike bash and mksh, the state of the option is saved when a
pipeline is started. Therefore, even in the case of commands like
A | B &
a later change of the option does not change the exit status, the same way
(A | B) &
works.
Since SIGPIPE is not handled specially, more work in the script is required
for a proper exit status for pipelines containing commands such as head that
may terminate successfully without reading all input. This can be something
like
(
cmd1
r=$?
if [ "$r" -gt 128 ] && [ "$(kill -l "$r")" = PIPE ]; then
exit 0
else
exit "$r"
fi
) | head
PR: 224270
Relnotes: yes
The difference between EXERROR and EXEXEC was that EXEXEC passed along
exitstatus and EXERROR set exitstatus to 2 in the handling code.
By changing the places that raised EXERROR to set exitstatus to 2, the
handling of EXERROR and EXEXEC becomes the same.
Renumber cluase 4 to 3, per what everybody else did when BSD granted
them permission to remove clause 3. My insistance on keeping the same
numbering for legal reasons is too pedantic, so give up on that point.
Submitted by: Jan Schaumann <jschauma@stevens.edu>
Pull Request: https://github.com/freebsd/freebsd/pull/96
interactive.
I added the interactive check in r208881 to be safe, but in actual use
(scripts in set -m mode) passing along SIGINT seems best.
Discussed with: bdrewery
Only store exit status for a process if that process has not terminated yet.
Test (slow):
exit 7 & p1=$!; until exit 8 & p2=$!; [ "$p1" = "$p2" ]; do wait "$p2";
done; sleep 0.1; wait %1; echo $?
should write "7".
When killing a %job started without job control, kill all processes in it.
As with process groups and zombies, if any process in the job can be killed
or has already terminated, the command is successful.
This also fixes occasional failures of the builtins/kill1.0 test.
If a job has terminated but is still known, silently do nothing when using
the kill builtin with the job specifier. Formerly, the shell called kill()
with the process group ID that might have been reused.
The getpgrp() call is unnecessary: if there is no job control then the
result was not used at all and if there is job control then we are not a
subshell and our process group ID is equal to our process ID (rootpid).
The change in r238888 was incomplete. It was still possible for a trapped
signal to arrive before the shell went to sleep (sigsuspend()) because a
check was missing or because the signal arrived before in_waitcmd was set.
On SMP, this bug sometimes caused the builtins/wait4.0 test to take 1 second
to execute; it then might or might not fail. On UP, the test almost always
failed.
If a job is specified to 'wait', wait for it to complete. Formerly, in
interactive mode, the job was deleted if it stopped.
If no jobs are specified in interactive mode, 'wait' still waits for all jobs
to complete or stop.
In non-interactive mode, WUNTRACED is not passed to wait3() so stopped jobs
are not detected.
PR: bin/181435
This is required by POSIX, at least for pids that are not known child
processes.
Other problems with job specifications still cause wait to abort with
exit status 2.
PR: 176916
This is only part of the PR; the behaviour for unknown/invalid pids/jobs
remains unchanged (aborts the builtin with status 2).
PR: 176916
Submitted by: Vadim Goncharov
It now passes WARNS=7 with clang on i386.
GCC 4.2.1 does not understand setjmp() properly so will always trigger
-Wuninitialized. I will not add the volatile keywords to suppress this.
If syntactically invalid job identifiers are to be taken as jobs that exited
with status 127, this should not apply to options, so that we can add
options later if need be.
Accessing sys_siglist directly requires rtld to copy it from libc to the sh
executable's BSS. Also, strsignal() will put in the signal number for
unknown signals (FreeBSD-specific) so we need not do that ourselves.
Unfortunately, there is no function for sys_signame.
This reduces code duplication and code size.
/usr/bin/printf is not affected.
Side effect: different error messages when certain builtins are passed
invalid options.
When waiting for child processes using "wait" or if "set -T" is in effect, a
signal interrupts the wait. Make sure there is no window where the signal
handler may be invoked (setting a flag) just before going to sleep.
There is a similar race condition in the shell language, but scripts can
avoid it by exiting from the trap handler or enforcing synchronization using
a fifo.
If SIGCHLD is not trapped, a signal handler must be installed for it. Only
install this handler for the duration of the wait to avoid triggering
unexpected [EINTR] errors elsewhere.
Note that for some reason only SIGINT and SIGQUIT interrupt a "wait"
command. This remains the case.
This uses vfork() for simple commands and command substitutions containing a
single simple command, invoking an external program under certain conditions
(no redirections or variable assignments, non-interactive shell, no job
control). These restrictions limit the amount of code executed in a vforked
child.
There is a large speedup (for example 35%) in microbenchmarks. The
difference in buildkernel is smaller (for example 0.5%) but still
statistically significant. See
http://lists.freebsd.org/pipermail/freebsd-hackers/2012-January/037581.html
for some numbers.
The use of vfork() can be disabled by setting a variable named
SH_DISABLE_VFORK.
Have mkbuiltins write the prototypes for the *cmd functions to builtins.h
instead of builtins.c and include builtins.h in more .c files instead of
duplicating prototypes for *cmd functions in other headers.
In optimized command substitution, save and restore any variables changed by
expansions (${var=value} and $((var=assigned))), instead of trying to
determine if an expansion may cause such changes.
If $! is referenced in optimized command substitution, do not cause jobs to
be remembered longer.
This fixes $(jobs $!) again, simplifies the man page and shortens the code.
These are called "shell procedures" in the source.
If execve() failed with [ENOEXEC], the shell would reinitialize itself
and execute the program as a script. This requires a fair amount of code
which is not frequently used (most scripts have a #! magic number).
Therefore just execute a new instance of sh (_PATH_BSHELL) to run the
script.