sh: Fix execution of multiple statements in a trap when evalskip is set
authorPeter Avalos <pavalos@dragonflybsd.org>
Sun, 5 Feb 2012 20:07:56 +0000 (12:07 -0800)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sun, 5 Feb 2012 21:04:02 +0000 (13:04 -0800)
Before this fix, only the first statement of the trap was executed if
evalskip was set. This is for example the case when:
    o  "-e" is set for this shell
    o  a trap is set on EXIT
    o  a function returns 1 and causes the script to abort

Obtained-from:  FreeBSD 230212
(cherry picked from commit a1cf61ab1fd76b3e90e17fc4bc03767015deb2e1)

bin/sh/eval.c
bin/sh/eval.h
bin/sh/trap.c
tools/regression/bin/sh/builtins/trap10.0 [new file with mode: 0644]
tools/regression/bin/sh/builtins/trap11.0 [new file with mode: 0644]

index 513af93..3b3dea0 100644 (file)
@@ -34,7 +34,7 @@
  * SUCH DAMAGE.
  *
  * @(#)eval.c  8.9 (Berkeley) 6/8/95
- * $FreeBSD: src/bin/sh/eval.c,v 1.117 2012/01/15 21:39:38 jilles Exp $
+ * $FreeBSD: src/bin/sh/eval.c,v 1.118 2012/01/16 11:07:46 dumbbell Exp $
  */
 
 #include <sys/time.h>
@@ -76,7 +76,7 @@
 
 
 int evalskip;                  /* set if we are skipping commands */
-static int skipcount;          /* number of levels to skip */
+int skipcount;                 /* number of levels to skip */
 MKINIT int loopnest;           /* current loop nesting level */
 int funcnest;                  /* depth of function calls */
 static int builtin_flags;      /* evalcommand flags for builtins */
index 2bdb216..d9ef734 100644 (file)
@@ -34,7 +34,7 @@
  * SUCH DAMAGE.
  *
  *     @(#)eval.h      8.2 (Berkeley) 5/4/95
- * $FreeBSD: src/bin/sh/eval.h,v 1.15 2011/06/13 21:03:27 jilles Exp $
+ * $FreeBSD: src/bin/sh/eval.h,v 1.16 2012/01/16 11:07:46 dumbbell Exp $
  */
 
 extern const char *commandname;        /* currently executing command */
@@ -64,6 +64,7 @@ void evalbackcmd(union node *, struct backcmd *);
 #define in_function()  funcnest
 extern int funcnest;
 extern int evalskip;
+extern int skipcount;
 
 /* reasons for skipping commands (see comment on breakcmd routine) */
 #define SKIPBREAK      1
index 7863751..e65a997 100644 (file)
@@ -34,7 +34,7 @@
  * SUCH DAMAGE.
  *
  * @(#)trap.c  8.5 (Berkeley) 6/5/95
- * $FreeBSD: src/bin/sh/trap.c,v 1.47 2012/01/14 21:54:12 jilles Exp $
+ * $FreeBSD: src/bin/sh/trap.c,v 1.48 2012/01/16 11:07:46 dumbbell Exp $
  */
 
 #include <signal.h>
@@ -411,7 +411,7 @@ void
 dotrap(void)
 {
        int i;
-       int savestatus;
+       int savestatus, prev_evalskip, prev_skipcount;
 
        in_dotrap++;
        for (;;) {
@@ -426,10 +426,36 @@ dotrap(void)
                                         */
                                        if (i == SIGCHLD)
                                                ignore_sigchld++;
+
+                                       /*
+                                        * Backup current evalskip
+                                        * state and reset it before
+                                        * executing a trap, so that the
+                                        * trap is not disturbed by an
+                                        * ongoing break/continue/return
+                                        * statement.
+                                        */
+                                       prev_evalskip  = evalskip;
+                                       prev_skipcount = skipcount;
+                                       evalskip = 0;
+
                                        last_trapsig = i;
                                        savestatus = exitstatus;
                                        evalstring(trap[i], 0);
                                        exitstatus = savestatus;
+
+                                       /*
+                                        * If such a command was not
+                                        * already in progress, allow a
+                                        * break/continue/return in the
+                                        * trap action to have an effect
+                                        * outside of it.
+                                        */
+                                       if (prev_evalskip != 0) {
+                                               evalskip  = prev_evalskip;
+                                               skipcount = prev_skipcount;
+                                       }
+
                                        if (i == SIGCHLD)
                                                ignore_sigchld--;
                                }
@@ -500,6 +526,11 @@ exitshell_savedstatus(void)
        }
        handler = &loc1;
        if ((p = trap[0]) != NULL && *p != '\0') {
+               /*
+                * Reset evalskip, or the trap on EXIT could be
+                * interrupted if the last command was a "return".
+                */
+               evalskip = 0;
                trap[0] = NULL;
                evalstring(p, 0);
        }
diff --git a/tools/regression/bin/sh/builtins/trap10.0 b/tools/regression/bin/sh/builtins/trap10.0
new file mode 100644 (file)
index 0000000..6e67dbf
--- /dev/null
@@ -0,0 +1,6 @@
+# $FreeBSD: src/tools/regression/bin/sh/builtins/trap10.0,v 1.1 2012/01/16 11:07:46 dumbbell Exp $
+
+# Check that the return statement will not break the EXIT trap, ie. all
+# trap commands are executed before the script exits.
+
+test "$(trap 'printf trap; echo ped' EXIT; f() { return; }; f)" = trapped || exit 1
diff --git a/tools/regression/bin/sh/builtins/trap11.0 b/tools/regression/bin/sh/builtins/trap11.0
new file mode 100644 (file)
index 0000000..972c2ec
--- /dev/null
@@ -0,0 +1,8 @@
+# $FreeBSD: src/tools/regression/bin/sh/builtins/trap11.0,v 1.1 2012/01/16 11:07:46 dumbbell Exp $
+
+# Check that the return statement will not break the USR1 trap, ie. all
+# trap commands are executed before the script resumes.
+
+result=$(${SH} -c 'trap "printf trap; echo ped" USR1; f() { return $(kill -USR1 $$); }; f')
+test $? -eq 0 || exit 1
+test "$result" = trapped || exit 1