1
0
Fork 0
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:
Martijn Dekker 2020-08-13 23:29:27 +01:00
parent 64d04e717b
commit 56805b25af
10 changed files with 75 additions and 18 deletions

View file

@ -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

View file

@ -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 << '+++'