mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
Fix leak and crash upon defining functions in subshells
A memory leak occurred upon leaving a virtual subshell if a
function was defined within it. If this was done more than 32766
(= 2^15-2 = the 'short' max value - 1) times, the shell crashed.
Discussion and reproducer: https://github.com/ksh93/ksh/issues/114
src/cmd/ksh93/sh/subshell.c: table_unset():
- A subshell-defined function was never freed because a broken
check for autoloaded functions (which must not be freed[*]). It
looked for an initial '/' in the canonical path of the script
file that defined the function, but that path is also stored for
regular functions. Now use a check that executes nv_search() in
fpathdict, the same method used in _nv_unset() in name.c for a
regular function unset.
src/cmd/ksh93/bltins/misc.c: b_dot_cmd():
- Fix an additional memory leak introduced in bd88cc7f
, that caused
POSIX functions (which are run with b_dot_cmd() like dot scripts)
to leak extra. This fix avoids both the crash fixed there and the
memory leak by introducing a 'tofree' variable remembering the
filename to free. Thanks to Johnothan King for the patch.
src/lib/libast/include/stk.h,
src/lib/libast/misc/stk.c,
src/lib/libast/man/stk.3,
src/lib/libast/man/stak.3:
- Make the stack more resilient by extending the stack reference
counter 'stkref' from (signed) short to unsigned int. On modern
systems with 32-bit ints, this extends the maximum number of
elements on a stack from 2^15-1==32767 to 2^32-1==4294967295.
The ref counter can never be negative, so there is no reason for
signedness. sizeof(int) is defined as the size of a single CPU
word, so this should not affect performance at all.
On a 16-bit system (not that ksh still compiles there), this
doubles the max number of entries to 2^16-1=65535.
src/cmd/ksh93/tests/leaks.sh:
- Add leak regression tests for ksh functions, POSIX functions, dot
scripts run with '.', and dot scripts run with 'source'.
src/cmd/ksh93/tests/path.sh:
- Add an output builtin with a redirect to an autoloaded function
so that a crash[*] is triggered if the check for an autoloaded
function is ever removed from table_unset(), as was done in ksh
93v- (which crashed).
[*] Freeing autoloaded functions after leaving a virtual subshell
causes a crashing bug: https://github.com/att/ast/issues/803
Co-authored-by: Johnothan King <johnothanking@protonmail.com>
Fixes: https://github.com/ksh93/ksh/issues/114
This commit is contained in:
parent
64d04e717b
commit
56805b25af
10 changed files with 75 additions and 18 deletions
|
@ -60,11 +60,14 @@ before=0 after=0 i=0 u=0
|
|||
# Number of iterations for each test
|
||||
N=512
|
||||
|
||||
# Check results. Add a tolerance of N/4 bytes to avoid false leaks.
|
||||
# Check results. Add a tolerance of 2 bytes per iteration. Some tests appear to have tiny leaks, but they
|
||||
# must be vmalloc artefacts; they may increase with the number of iterations, then suddenly go away when the
|
||||
# number of iterations is large enough (e.g. 10000) and don't return when increasing iterations further.
|
||||
#
|
||||
# The function has 'err_exit' in the name so that shtests counts each call as at test.
|
||||
function err_exit_if_leak
|
||||
{
|
||||
if ((after > before + N / 4))
|
||||
if ((after > before + 2 * N))
|
||||
then err\_exit "$1" "$2 (leaked $((after - before)) bytes after $N iterations)"
|
||||
fi
|
||||
}
|
||||
|
@ -142,5 +145,47 @@ done >/dev/null
|
|||
after=$(getmem)
|
||||
err_exit_if_leak 'memory leak on PATH reset before subshell PATH search'
|
||||
|
||||
# ======
|
||||
# Defining a function in a virtual subshell
|
||||
# https://github.com/ksh93/ksh/issues/114
|
||||
|
||||
unset -f foo
|
||||
before=$(getmem)
|
||||
for ((i=0; i < N; i++))
|
||||
do (function foo { :; }; foo)
|
||||
done
|
||||
after=$(getmem)
|
||||
err_exit_if_leak 'ksh function defined in virtual subshell'
|
||||
typeset -f foo >/dev/null && err_exit 'ksh function leaks out of subshell'
|
||||
|
||||
unset -f foo
|
||||
before=$(getmem)
|
||||
for ((i=0; i < N; i++))
|
||||
do (foo() { :; }; foo)
|
||||
done
|
||||
after=$(getmem)
|
||||
err_exit_if_leak 'POSIX function defined in virtual subshell'
|
||||
typeset -f foo >/dev/null && err_exit 'POSIX function leaks out of subshell'
|
||||
|
||||
# ======
|
||||
# Sourcing a dot script in a virtual subshell
|
||||
|
||||
echo 'echo "$@"' > $tmp/dot.sh
|
||||
before=$(getmem)
|
||||
for ((i=0; i < N; i++))
|
||||
do (. "$tmp/dot.sh" dot one two three >/dev/null)
|
||||
done
|
||||
after=$(getmem)
|
||||
err_exit_if_leak 'script dotted in virtual subshell'
|
||||
|
||||
echo 'echo "$@"' > $tmp/dot.sh
|
||||
before=$(getmem)
|
||||
for ((i=0; i < N; i++))
|
||||
do (source "$tmp/dot.sh" source four five six >/dev/null)
|
||||
done
|
||||
after=$(getmem)
|
||||
err_exit_if_leak 'script sourced in virtual subshell'
|
||||
|
||||
# ======
|
||||
exit $((Errors<125?Errors:125))
|
||||
[A
|
|
@ -36,7 +36,7 @@ type /xxxxxx > out1 2> out2
|
|||
[[ -s out2 ]] || err_exit 'type should write on stderr for not found case'
|
||||
mkdir dir1 dir2
|
||||
cat > dir1/foobar << '+++'
|
||||
foobar() { print foobar1;}
|
||||
foobar() { print foobar1 >foobar1.txt; cat <foobar1.txt;}
|
||||
function dir1 { print dir1;}
|
||||
+++
|
||||
cat > dir2/foobar << '+++'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue