Ron Yorston e6a63bf683 ash: ensure variables are fully initialised when unset
When a variable is unset by calling setvar(name, NULL, 0) the code
to initialise the new, empty variable fails to initialise the last
character of the string.

Attempts to read the contents of the unset variable will result
in the uninitialised character at the end of the string being
accessed.

For example, running BusyBox under Valgrind and unsetting PATH:

$ valgrind ./busybox_unstripped sh
==21249== Memcheck, a memory error detector
==21249== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==21249== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==21249== Command: ./busybox_unstripped sh
==21249==
/data2/git/build_fix_8721 $ unset PATH
/data2/git/build_fix_8721 $ 0
==21249== Conditional jump or move depends on uninitialised value(s)
==21249==    at 0x451371: path_advance (ash.c:2555)
==21249==    by 0x456E22: find_command (ash.c:13407)
==21249==    by 0x458425: evalcommand (ash.c:10139)
==21249==    by 0x454CBC: evaltree (ash.c:9131)
==21249==    by 0x456C80: cmdloop (ash.c:13164)

Closes https://bugs.busybox.net/show_bug.cgi?id=8721

v2: On the dash mailing list Harald van Dijk was kind enough to point
    out a flaw in my reasoning and provide an alternative patch.  Sadly
    his patch adds 2 bytes of bloat.  Using xzalloc to zero the whole
    string gives a bloat of -3 bytes.

function                                             old     new   delta
setvar                                               172     169      -3

Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2018-11-16 17:28:01 +01:00
..
2009-03-19 23:09:58 +00:00
2018-03-02 20:48:36 +01:00
2018-07-17 15:04:17 +02:00
2010-05-20 12:56:14 +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.