svn commit: [26063] trunk/busybox/shell: hush_test/hush-z_slow

vda at busybox.net vda at busybox.net
Sat Apr 11 10:37:11 UTC 2009


Author: vda
Date: 2009-04-11 10:37:10 +0000 (Sat, 11 Apr 2009)
New Revision: 26063

Log:
hush: fix "while...do f1() {a;}; f1; f1 {b;}; f1; done" bug



Modified:
   trunk/busybox/shell/hush.c
   trunk/busybox/shell/hush_test/hush-z_slow/leak_all1.tests


Changeset:
Modified: trunk/busybox/shell/hush.c
===================================================================
--- trunk/busybox/shell/hush.c	2009-04-11 00:08:47 UTC (rev 26062)
+++ trunk/busybox/shell/hush.c	2009-04-11 10:37:10 UTC (rev 26063)
@@ -330,7 +330,7 @@
 	/* note: for heredocs, rd_filename contains heredoc delimiter,
 	 * and subsequently heredoc itself; and rd_dup is a bitmask:
 	 * 1: do we need to trim leading tabs?
-	 * 2: is heredoc quoted (<<'dleim' syntax) ?
+	 * 2: is heredoc quoted (<<'delim' syntax) ?
 	 */
 };
 typedef enum redir_type {
@@ -357,25 +357,42 @@
 	int assignment_cnt;         /* how many argv[i] are assignments? */
 	smallint is_stopped;        /* is the command currently running? */
 	smallint grp_type;          /* GRP_xxx */
+#define GRP_NORMAL   0
+#define GRP_SUBSHELL 1
+#if ENABLE_HUSH_FUNCTIONS
+# define GRP_FUNCTION 2
+#endif
 	struct pipe *group;         /* if non-NULL, this "command" is { list },
 	                             * ( list ), or a compound statement */
 #if !BB_MMU
 	char *group_as_string;
 #endif
+#if ENABLE_HUSH_FUNCTIONS
+	struct function *child_func;
+/* This field is used to prevent a bug here:
+ * while...do f1() {a;}; f1; f1 {b;}; f1; done
+ * When we execute "f1() {a;}" cmd, we create new function and clear
+ * cmd->group, cmd->group_as_string, cmd->argv[0].
+ * when we execute "f1 {b;}", we notice that f1 exists,
+ * and that it's "parent cmd" struct is still "alive",
+ * we put those fields back into cmd->xxx
+ * (struct function has ->parent_cmd ptr to facilitate that).
+ * When we loop back, we can execute "f1() {a;}" again and set f1 correctly.
+ * Without this trick, loop would execute a;b;b;b;...
+ * instead of correct sequence a;b;a;b;...
+ * When command is freed, it severs the link
+ * (sets ->child_func->parent_cmd to NULL).
+ */
+#endif
 	char **argv;                /* command name and arguments */
-	struct redir_struct *redirects; /* I/O redirections */
-};
 /* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
  * and on execution these are substituted with their values.
  * Substitution can make _several_ words out of one argv[n]!
  * Example: argv[0]=='.^C*^C.' here: echo .$*.
  * References of the form ^C`cmd arg^C are `cmd arg` substitutions.
  */
-#define GRP_NORMAL   0
-#define GRP_SUBSHELL 1
-#if ENABLE_HUSH_FUNCTIONS
-# define GRP_FUNCTION 2
-#endif
+	struct redir_struct *redirects; /* I/O redirections */
+};
 
 struct pipe {
 	struct pipe *next;
@@ -456,6 +473,7 @@
 struct function {
 	struct function *next;
 	char *name;
+	struct command *parent_cmd;
 	struct pipe *body;
 #if !BB_MMU
 	char *body_as_string;
@@ -743,7 +761,6 @@
 	msg[0] = ch;
 	msg[1] = '\0';
 	die_if_script(lineno, "syntax error: unexpected %s", msg);
-	xfunc_die();
 }
 
 #if HUSH_DEBUG < 2
@@ -2573,6 +2590,14 @@
 			debug_printf_clean("%s   end group\n", indenter(indent));
 			command->group = NULL;
 		}
+		/* else is crucial here.
+		 * If group != NULL, child_func is meaningless */
+#if ENABLE_HUSH_FUNCTIONS
+		else if (command->child_func) {
+			debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
+			command->child_func->parent_cmd = NULL;
+		}
+#endif
 #if !BB_MMU
 		free(command->group_as_string);
 		command->group_as_string = NULL;
@@ -3173,9 +3198,24 @@
 
 			while ((funcp = *funcpp) != NULL) {
 				if (strcmp(funcp->name, command->argv[0]) == 0) {
-					debug_printf_exec("replacing function '%s'\n", funcp->name);
-					free(funcp->name);
-					free_pipe_list(funcp->body, /* indent: */ 0);
+					struct command *cmd = funcp->parent_cmd;
+
+					debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
+					if (!cmd) {
+						debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
+						free(funcp->name);
+						free_pipe_list(funcp->body, /* indent: */ 0);
+#if !BB_MMU
+						free(funcp->body_as_string);
+#endif
+					} else {
+						debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
+						cmd->argv[0] = funcp->name;
+						cmd->group = funcp->body;
+#if !BB_MMU
+						cmd->group_as_string = funcp->body_as_string;
+#endif
+					}
 					goto skip;
 				}
 				funcpp = &funcp->next;
@@ -3192,13 +3232,9 @@
 #endif
 			command->group = NULL;
 			command->argv[0] = NULL;
-			free_strings(command->argv);
-			command->argv = NULL;
-			/* note: if we are in a loop, future "executions"
-			 * of func def will see it as null command since
-			 * command->group == NULL and command->argv == NULL */
-//this isn't exactly right: while...do f1() {a;}; f1; f1 {b;}; done
-//second loop will execute b!
+			debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
+			funcp->parent_cmd = command;
+			command->child_func = funcp;
 
 			return EXIT_SUCCESS;
 		}

Modified: trunk/busybox/shell/hush_test/hush-z_slow/leak_all1.tests
===================================================================
--- trunk/busybox/shell/hush_test/hush-z_slow/leak_all1.tests	2009-04-11 00:08:47 UTC (rev 26062)
+++ trunk/busybox/shell/hush_test/hush-z_slow/leak_all1.tests	2009-04-11 10:37:10 UTC (rev 26063)
@@ -62,7 +62,8 @@
     echo >/dev/null ${var%%*}
     set -- par1_$i par2_$i par3_$i par4_$i
     trap "echo trap$i" WINCH
-    f() { echo $1; }
+    f() { true; true; true; true; true; true; true; true; }
+    f() { true; true; true; true; true; true; true; true; echo $1; }
     f >/dev/null
     : $((i++))
 done
@@ -127,7 +128,8 @@
     echo >/dev/null ${var%%*}
     set -- par1_$i par2_$i par3_$i par4_$i
     trap "echo trap$i" WINCH
-    f() { echo $1; }
+    f() { true; true; true; true; true; true; true; true; }
+    f() { true; true; true; true; true; true; true; true; echo $1; }
     f >/dev/null
     : $((i++))
 done



More information about the busybox-cvs mailing list