ash: fix var_leak.tests so that it actually catches the NOFORK bug
authorDenys Vlasenko <vda.linux@googlemail.com>
Tue, 18 May 2010 14:13:56 +0000 (16:13 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Tue, 18 May 2010 14:13:56 +0000 (16:13 +0200)
+ document the bug better

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c
shell/ash_test/ash-vars/var_leak.right
shell/ash_test/ash-vars/var_leak.tests

index d082333ba3379028ba8ae7d7f48f0a4af3c5458f..83886c610087733f44e2975c18f861ae4f1be439 100644 (file)
@@ -9179,12 +9179,14 @@ evalcommand(union node *cmd, int flags)
 
        /* Execute the command. */
        switch (cmdentry.cmdtype) {
-       default:
+       default: {
 
 #if ENABLE_FEATURE_SH_NOFORK
-/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
- * Why "fork off a child process if necessary" doesn't apply to NOFORK? */
-       {
+/* (1) BUG: if variables are set, we need to fork, or save/restore them
+ *     around run_nofork_applet() call.
+ * (2) Should this check also be done in forkshell()?
+ *     (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
+ */
                /* find_command() encodes applet_no as (-2 - applet_no) */
                int applet_no = (- cmdentry.u.index - 2);
                if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
@@ -9193,10 +9195,13 @@ evalcommand(union node *cmd, int flags)
                        exitstatus = run_nofork_applet(applet_no, argv);
                        break;
                }
-       }
 #endif
-               /* Fork off a child process if necessary. */
+               /* Can we avoid forking off? For example, very last command
+                * in a script or a subshell does not need forking,
+                * we can just exec it.
+                */
                if (!(flags & EV_EXIT) || may_have_traps) {
+                       /* No, forking off a child is necessary */
                        INT_OFF;
                        jp = makejob(/*cmd,*/ 1);
                        if (forkshell(jp, cmd, FORK_FG) != 0) {
@@ -9213,7 +9218,7 @@ evalcommand(union node *cmd, int flags)
                listsetvar(varlist.list, VEXPORT|VSTACK);
                shellexec(argv, path, cmdentry.u.index);
                /* NOTREACHED */
-
+       } /* default */
        case CMDBUILTIN:
                cmdenviron = varlist.list;
                if (cmdenviron) {
@@ -9258,7 +9263,8 @@ evalcommand(union node *cmd, int flags)
                if (evalfun(cmdentry.u.func, argc, argv, flags))
                        goto raise;
                break;
-       }
+
+       } /* switch */
 
  out:
        popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
index be78112c8e2a190b2249e794c41a2cffcfa9873a..01a5e3263ee38f5067d8f3e640fe5663973bfd2c 100644 (file)
@@ -1,3 +1,4 @@
 should be empty: ''
+should be empty: ''
 should be not empty: 'val2'
 should be not empty: 'val3'
index 032059295e2eb7e3186a9461b911f485c40f128d..5242e24eb19a2a5031bde9f96561db3c4262010f 100755 (executable)
@@ -1,6 +1,11 @@
-# true is a regular builtin, varibale should not leak out of it
+# cat is an external program, variable should not leak out of it.
 # this currently fails with CONFIG_FEATURE_SH_NOFORK=y
 VAR=''
+VAR=val0 cat /dev/null
+echo "should be empty: '$VAR'"
+
+# true is a regular builtin, variable should not leak out of it.
+VAR=''
 VAR=val1 true
 echo "should be empty: '$VAR'"