mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
Fix 'unset -f' to work in subshells without forking (re: 047cb330)
This commit implements unsetting functions in virtual subshells, removing the need for the forking workaround. This is done by either invalidating the function found in the current subshell function tree by unsetting its NV_FUNCTION attribute bits (which will cause sh_exec() to skip it) or, if the function exists in a parent shell, by creating an empty dummy subshell node in the current function tree without that attribute. As a beneficial side effect, it seems that bug 228 (unset -f fails in forked subshells if a function is defined before forking) is now also fixed. src/cmd/ksh93/include/defs.h, src/cmd/ksh93/sh/init.c: - Add sh.fun_base for a saved pointer to the main shell's function tree for checking when in a subshell, analogous to sh.var_base. src/cmd/ksh93/bltins/typeset.c: unall(): - Remove the fork workaround. - When unsetting a function found in the current function tree (troot) and that tree is not sh.var_base (which checks if we're in a virtual subshell in a way that handles shared-state command substitutions correctly), then do not delete the function but invalidate it by unsetting its NV_FUNCTION attribute bits. - When unsetting a function not found in the current function tree, search for it in sh.fun_base and if found, add an empty dummy node to mask the parent shell environment's function. The dummy node will not have NV_FUNCTION set, so sh_exec() will skip it. src/cmd/ksh93/sh/subshell.c: - sh_subfuntree(): For 'unset -f' to work correctly with shared-state command substitutions (subshares), this function needs a fix similar to the one applied to sh_assignok() for variables in commit911d6b06. Walk up on the subshells tree until we find a non-subshare. - sh_subtracktree(): Apply the same fix for the hash table. - Remove table_unset() and incorporate an updated version of its code in sh_subshell(). As ofec888867, this function was only used to clean up the subshell function table as the alias table no longer exists. - sh_subshell(): * Simplify the loop to free the subshell hash table. * Add table_unset() code, slightly refactored for readability. Treat dummy nodes now created by unall() separately to avoid a memory leak; they must be nv_delete()d without passing the NV_FUNCTION bits. For non-dummy nodes, turn on the NV_FUNCTION attribute in case they were invalidated by unall(); this is needed for _nv_unset() to free the function definition. src/cmd/ksh93/tests/subshell.sh: - Update the test for multiple levels of subshell functions to test a subshare as well. While we're add it, add a very similar test for multiple levels of subshell variables that was missing. - Add @JohnoKing's reproducer from #228. src/cmd/ksh93/tests/leaks.sh: - Add leak tests for unsetting functions in a virtual subshell. Test both the simple unset case (unall() creates a dummy node) and the define/unset case (unall() invalidates existing node). Resolves: https://github.com/ksh93/ksh/issues/228
This commit is contained in:
parent
086d504393
commit
13c57e4b58
6 changed files with 142 additions and 74 deletions
|
|
@ -195,6 +195,40 @@ 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'
|
||||
|
||||
# Unsetting a function in a virtual subshell
|
||||
|
||||
function foo { echo bar; }
|
||||
before=$(getmem)
|
||||
for ((i=0; i < N; i++))
|
||||
do (unset -f foo)
|
||||
done
|
||||
after=$(getmem)
|
||||
err_exit_if_leak 'ksh function unset in virtual subshell'
|
||||
typeset -f foo >/dev/null || err_exit 'ksh function unset in subshell was unset in main shell'
|
||||
|
||||
foo() { echo bar; }
|
||||
before=$(getmem)
|
||||
for ((i=0; i < N; i++))
|
||||
do (unset -f foo)
|
||||
done
|
||||
after=$(getmem)
|
||||
err_exit_if_leak 'POSIX function unset in virtual subshell'
|
||||
typeset -f foo >/dev/null || err_exit 'POSIX function unset in subshell was unset in main shell'
|
||||
|
||||
before=$(getmem)
|
||||
for ((i=0; i < N; i++))
|
||||
do (function foo { echo baz; }; unset -f foo)
|
||||
done
|
||||
after=$(getmem)
|
||||
err_exit_if_leak 'ksh function defined and unset in virtual subshell'
|
||||
|
||||
before=$(getmem)
|
||||
for ((i=0; i < N; i++))
|
||||
do (foo() { echo baz; }; unset -f foo)
|
||||
done
|
||||
after=$(getmem)
|
||||
err_exit_if_leak 'POSIX function defined and unset in virtual subshell'
|
||||
|
||||
# ======
|
||||
# Sourcing a dot script in a virtual subshell
|
||||
|
||||
|
|
|
|||
|
|
@ -656,6 +656,7 @@ v=$("$SHELL" -c '. "$1"' x "$a") && [[ $v == ok ]] || err_exit "fail: more fun 4
|
|||
# ...multiple levels of subshell
|
||||
func() { echo mainfunction; }
|
||||
v=$(
|
||||
set +x
|
||||
(
|
||||
func() { echo sub1; }
|
||||
(
|
||||
|
|
@ -664,20 +665,34 @@ v=$(
|
|||
func() { echo sub3; }
|
||||
func
|
||||
PATH=/dev/null
|
||||
unset -f func
|
||||
dummy=${ dummy=${ dummy=${ dummy=${ unset -f func; }; }; }; }; # test subshare within subshell
|
||||
func 2>/dev/null
|
||||
(($? == 127)) && echo ok_nonexistent || echo fail_zombie
|
||||
)
|
||||
func
|
||||
)
|
||||
func
|
||||
)
|
||||
func
|
||||
) 2>&1
|
||||
func 2>&1
|
||||
)
|
||||
expect=$'sub3\nok_nonexistent\nsub2\nsub1\nmainfunction'
|
||||
[[ $v == "$expect" ]] \
|
||||
|| err_exit "multi-level subshell function failure (expected $(printf %q "$expect"), got $(printf %q "$v"))"
|
||||
|
||||
# ... https://github.com/ksh93/ksh/issues/228
|
||||
fail() {
|
||||
echo 'Failure'
|
||||
}
|
||||
exp=Success
|
||||
got=$(
|
||||
foo() { true; } # Define function before forking
|
||||
ulimit -t unlimited 2>/dev/null # Fork the subshell
|
||||
unset -f fail
|
||||
PATH=/dev/null fail 2>/dev/null || echo "$exp"
|
||||
)
|
||||
[[ $got == "$exp" ]] || err_exit 'unset -f fails in forked subshells if a function is defined before forking' \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
|
||||
# ======
|
||||
# Unsetting or redefining aliases within subshells
|
||||
|
||||
|
|
@ -945,6 +960,29 @@ v=main
|
|||
(v=sub; (d=${ v=shared; }; [[ $v == shared ]]) ) || err_exit "shared comsub in nested subshell wrongly scoped (2)"
|
||||
[[ $v == main ]] || err_exit "shared comsub leaks out of subshell (7)"
|
||||
|
||||
# ...multiple levels of subshell
|
||||
v=main
|
||||
got=$(
|
||||
(
|
||||
v=sub1
|
||||
(
|
||||
v=sub2
|
||||
(
|
||||
v=sub3
|
||||
echo $v
|
||||
dummy=${ dummy=${ dummy=${ dummy=${ unset v; }; }; }; }; # test subshare within subshell
|
||||
[[ -n $v || -v v ]] && echo fail_zombie || echo ok_nonexistent
|
||||
)
|
||||
echo $v
|
||||
)
|
||||
echo $v
|
||||
)
|
||||
echo $v
|
||||
)
|
||||
exp=$'sub3\nok_nonexistent\nsub2\nsub1\nmain'
|
||||
[[ $got == "$exp" ]] || err_exit "multi-level subshell function failure" \
|
||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||
|
||||
# ======
|
||||
# After the memory leak patch for rhbz#982142, this minor regression was introduced:
|
||||
# if a backtick command substitution expanded an alias, an extra space was inserted.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue