From 759157bdb2384b55c42d26f74d39da183c5f24c9 Mon Sep 17 00:00:00 2001 From: Martijn Dekker Date: Thu, 4 Jun 2020 18:16:49 +0200 Subject: [PATCH] Fix hang in unsetting functions in subshells (re: dde38782) This fixes a really stupid bug in my own code for unsetting a function in a subshell. The algorithm for walking through the subshell tree was broken, resulting in an infinite loop if there were multiple levels of subshell. src/cmd/ksh93/bltins/typeset.c: - Correct the subshell function tree walk that deletes functions from zombie parent scopes. src/cmd/ksh93/tests/subshell.sh: - Add a regression test for setting and unsetting identically named functions in multiple levels of subshell. (cherry picked from commit 972a7999c7f16469138daf3d86dfd6c0db3f4879) --- src/cmd/ksh93/bltins/typeset.c | 4 ++-- src/cmd/ksh93/tests/subshell.sh | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/cmd/ksh93/bltins/typeset.c b/src/cmd/ksh93/bltins/typeset.c index 7aa1e06fe..276490350 100644 --- a/src/cmd/ksh93/bltins/typeset.c +++ b/src/cmd/ksh93/bltins/typeset.c @@ -1267,8 +1267,8 @@ static int unall(int argc, char **argv, register Dt_t *troot, Shell_t* shp) * from the main shell scope. So walk though troot's parent views and delete any such zombie * functions. Note that this only works because 'unset -f' now forks if we're in a subshell. */ - Dt_t *troottmp; - while((troottmp = troot->view) && (np = nv_search(name,troottmp,0)) && is_afunction(np)) + Dt_t *troottmp = troot; + while((troottmp = troottmp->view) && (np = nv_search(name,troottmp,0)) && is_afunction(np)) nv_delete(np,troottmp,0); } #if 0 diff --git a/src/cmd/ksh93/tests/subshell.sh b/src/cmd/ksh93/tests/subshell.sh index 2b6881064..4a96c5e48 100755 --- a/src/cmd/ksh93/tests/subshell.sh +++ b/src/cmd/ksh93/tests/subshell.sh @@ -652,6 +652,31 @@ v=$("$SHELL" -c "$(cat "$a")") && [[ $v == ok ]] || err_exit 'fail: more fun 2' v=$("$SHELL" -c 'eval "$(cat "$1")"' x "$a") && [[ $v == ok ]] || err_exit "fail: more fun 3" v=$("$SHELL" -c '. "$1"' x "$a") && [[ $v == ok ]] || err_exit "fail: more fun 4" +# ...multiple levels of subshell +func() { echo mainfunction; } +v=$( + ( + func() { echo sub1; } + ( + func() { echo sub2; } + ( + func() { echo sub3; } + func + PATH=/dev/null + unset -f func + func 2>/dev/null + (($? == 127)) && echo ok_nonexistent || echo fail_zombie + ) + func + ) + func + ) + func +) +expect=$'sub3\nok_nonexistent\nsub2\nsub1\nmainfunction' +[[ $v == "$expect" ]] \ +|| err_exit "multi-level subshell function failure (expected $(printf %q "$expect"), got $(printf %q "$v"))" + # ====== # Unsetting or redefining aliases within subshells