-
Notifications
You must be signed in to change notification settings - Fork 154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ksh93: Changes to functions within subshells are ignored #73
Comments
This is definitely a bug. But the subject line is misleading. The problem is that unless you force the Some things, most notably variable assignment, are handled as expected by parenthesized blocks:
|
No, your understanding is incorrect. The Your example Instead, try this:
The subshell counter goes up as the subshell level increases. |
Some more fun:
|
@stephane-chazelas gave an essential hint in a comment on #480: running the So I went to look in the source code for how the
With a lot of grepping, I figured out that So the line above means: if we're in a subshell of the non-forking variety, and it's not the ${ ...; } variant, then fork off and make the forked process continue where we left off (that is what And forking avoids #73.
|
This looks broken to me:
outputs
outputs |
I applied the patch by @McDutchie (using The patch uses sh_subfork() to fork the subshell so that sh_subfuntree() doesn't create the subshell's function tree. The patch doesn't fix
This is because diff --git a/src/cmd/ksh93/sh/subshell.c b/src/cmd/ksh93/sh/subshell.c
index 90196bf2..97b8e3eb 100644
--- a/src/cmd/ksh93/sh/subshell.c
+++ b/src/cmd/ksh93/sh/subshell.c
@@ -373,10 +373,15 @@ Dt_t *sh_subfuntree(Shell_t *shp, int create) {
struct subshell *sp = subshell_data;
if (!sp || shp->curenv == 0) return (shp->fun_tree);
if (!sp->sfun && create) {
+#if 1
+ // Fork to avoid https://github.com/att/ast/issues/73
+ sh_subfork();
+#else
sp->sfun = dtopen(&_Nvdisc, Dtoset);
dtuserdata(sp->sfun, shp, 1);
dtview(sp->sfun, shp->fun_tree);
shp->fun_tree = sp->sfun;
+#endif
}
return sp->shp->fun_tree;
} I also have a different patch that fixes function replacement, but doesn't fix diff --git a/src/cmd/ksh93/sh/parse.c b/src/cmd/ksh93/sh/parse.c
index d8020382..9a6f642c 100644
--- a/src/cmd/ksh93/sh/parse.c
+++ b/src/cmd/ksh93/sh/parse.c
@@ -1370,8 +1370,8 @@ static Shnode_t *simple(Lex_t *lexp, int flag, struct ionod *io) {
np = nv_bfsearch(argp->argval, lexp->sh->fun_tree, (Namval_t **)&t->comnamq,
NULL);
}
- if (cmdarg == 0) t->comnamp = (void *)np;
if (np && is_abuiltin(np)) {
+ if (cmdarg == 0) t->comnamp = (void *)np;
if (nv_isattr(np, BLT_DCL)) {
assignment = 1 + !strcmp(argp->argval, "alias");
if (np == SYSTYPESET) { The parser can set t->comnamp to the wrong function. Suppose that the shell has executed With this patch,
|
I don't think it is a bug, although it feels strange:
Also semicolon separated commands are not equivalent to newline separated commands. I am still wrapping my head around this but it makes sense when reading the book and is consistent.
is a list command, which is a compound command - which is read all at once. It is not equivalent to
which are two simple commands. |
Functions can now be correctly redefined and unset in subshell environments (such as ( ... ), $(command substitutions), etc). Before this fix, attempts to do this were silently ignored (!!!), causing the wrong code (i.e.: the function by the same name from the parent shell environment) to be executed. Redefining and unsetting functions within "shared" command substitutions of the form '${ ...; }' is also fixed. Prior discussion: att#73 src/cmd/ksh93/sh/parse.c: - A fix from George Koelher (URL above). He writes: | The parser can set t->comnamp to the wrong function. | Suppose that the shell has executed | foo() { echo WRONG; } | and is now parsing | (foo() { echo ok; } && foo) | The parser was setting t->comnamp to the wrong foo. [This | fix] doesn't set t->comnamp unless it was a builtin. Now the | subshell can't call t->comnamp, so it looks for foo and finds | the ok foo in the subshell's function tree. src/cmd/ksh93/bltins/typeset.c: - Unsetting functions in a virtual/non-forked subshell still doesn't work: nv_open() fails to find the function. To work around this problem, make 'unset -f' fork the subshell into its own process with sh_subfork(). - The workaround exposed another bug: if we unset a function in a subshell tree that overrode a function by the same name in the main shell, then nv_delete() exposes the function from the main shell scope. Since 'unset -f' now always forks a subshell, the fix is to simply walk though troot's parent views and delete any such zombie functions as well. (Without this, the 4 'more fun' tests in tests/subshell.sh fail.) src/cmd/ksh93/sh/subshell.c: sh_subfuntree(): - Fix function (re)definitions and unsetting in "shared" command substitutions of the form '${ commandlist; }' (i.e.: if sp->shp->subshare is true). Though internally this is a weird form of virtual subshell, the manual page says it does not execute in a subshell (meaning, all changes must survive it), so a subshell function tree must not be created for these. src/cmd/ksh93/tests/subshell.sh: - Add regression tests related to these bugfixes. Test unsetting and redefining a function in all three forms of virtual subshell. (cherry picked from commit dde387825ab1bbd9f2eafc5dc38d5fd0bf9c3652)
Functions can now be correctly redefined and unset in subshell environments (such as ( ... ), $(command substitutions), etc). Before this fix, attempts to do this were silently ignored (!!!), causing the wrong code (i.e.: the function by the same name from the parent shell environment) to be executed. Redefining and unsetting functions within "shared" command substitutions of the form '${ ...; }' is also fixed. Prior discussion: att#73 src/cmd/ksh93/sh/parse.c: - A fix from George Koelher (URL above). He writes: | The parser can set t->comnamp to the wrong function. | Suppose that the shell has executed | foo() { echo WRONG; } | and is now parsing | (foo() { echo ok; } && foo) | The parser was setting t->comnamp to the wrong foo. [This | fix] doesn't set t->comnamp unless it was a builtin. Now the | subshell can't call t->comnamp, so it looks for foo and finds | the ok foo in the subshell's function tree. src/cmd/ksh93/bltins/typeset.c: - Unsetting functions in a virtual/non-forked subshell still doesn't work: nv_open() fails to find the function. To work around this problem, make 'unset -f' fork the subshell into its own process with sh_subfork(). - The workaround exposed another bug: if we unset a function in a subshell tree that overrode a function by the same name in the main shell, then nv_delete() exposes the function from the main shell scope. Since 'unset -f' now always forks a subshell, the fix is to simply walk though troot's parent views and delete any such zombie functions as well. (Without this, the 4 'more fun' tests in tests/subshell.sh fail.) src/cmd/ksh93/sh/subshell.c: sh_subfuntree(): - Fix function (re)definitions and unsetting in "shared" command substitutions of the form '${ commandlist; }' (i.e.: if sp->shp->subshare is true). Though internally this is a weird form of virtual subshell, the manual page says it does not execute in a subshell (meaning, all changes must survive it), so a subshell function tree must not be created for these. src/cmd/ksh93/tests/subshell.sh: - Add regression tests related to these bugfixes. Test unsetting and redefining a function in all three forms of virtual subshell. (cherry picked from commit dde387825ab1bbd9f2eafc5dc38d5fd0bf9c3652)
The following seems another manifestation of this bug which, through an extra level of indirection, makes assignments in subshells to affect unset variables in the parent shell:
As mentioned by @McDutchie in #73 (comment) and #478 (comment) the following are known workarounds. Replace the definition of
or
Here is a third workaround that shows how bad subshells step on each other's toes. By setting
With either of the three above we get
|
Hi @cassioneri , thank you for that report. This AT&T repo is no longer active, but the (to the best of my knowledge) only actively and openly developed fork of ksh93 is at https://github.com/ksh93/ksh -- with much of this bug already fixed, but sure enough, your test script exposes another bug that's not fixed yet. Would you mind opening an issue there, please? |
@McDutchie. Will do. |
Now that I'm here, I'd like to give belated thanks to @kernigh for the parser fix and to @stephane-chazelas for the reproducers, both of which I've incorporated in what is, to the best of my knowledge, currently the only actively and openly developed fork of ksh93. It's based off the "stable" 93u+ 2012-08-01 version (which might be the least buggy version ever released but is still nothing like actually stable). See my new repo, particularly 047cb33 and 759157b. I'd like to invite you to follow the development and provide any input you feel called to give. The idea is to get a rock-solid ksh93 release out there, so the more eyes on this code, the better. |
This bug was reported on the old mailing list: https://www.mail-archive.com/ast-developers@lists.research.att.com/msg00207.html A fork bomb can occur when SIGTSTP is sent to the vi editor. Vi must be launched from a script run with exec (tested with BusyBox vi, nvi and vim): $ cat /tmp/foo vi /tmp/bar echo end $ ksh $ chmod +x /tmp/foo $ exec /tmp/foo While in vi, send SIGTSTP using Ctrl-Z src/cmd/ksh93/sh/fault.c: - Only fork after Ctrl-Z if job control is available. The patch used checks 'job.jobcontrol' instead of 'SH_MONITOR': https://git.centos.org/rpms/ksh/blob/c8/f/SOURCES/ksh-20120801-forkbomb.patch
Function definitions within non-forked subshells (including command substitutions) are silently ignored if a function by the same name exists in the main shell, so the wrong code is executed.
unset -f
is also silently ignored. Test script:Output:
Forcing the subshell to be forked (by making it a background job, or by including a redirection within a command substitution) is an effective workaround.
Silently executing the wrong code under any circumstances is a bad bug, but it seems to have been in ksh93 since 1993...
The text was updated successfully, but these errors were encountered: