hush: more rodust detection of unterminated strings etc;
authorDenis Vlasenko <vda.linux@googlemail.com>
Wed, 8 Apr 2009 21:51:33 +0000 (21:51 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Wed, 8 Apr 2009 21:51:33 +0000 (21:51 -0000)
 fix a case where we forget to copy `cmd` text;
 optimize nommu heredoc helper by not passing environment to it;
 add several tests

function                                             old     new   delta
add_till_closing_paren                               256     308     +52
parse_stream                                        2337    2378     +41
add_till_backquote                                    82     111     +29
re_execute_shell                                     269     284     +15
handle_dollar                                        802     812     +10
parse_stream_dquoted                                 316     320      +4
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 6/0 up/down: 151/0)             Total: 151 bytes

shell/hush.c
shell/hush_test/hush-misc/heredoc_huge.right [new file with mode: 0644]
shell/hush_test/hush-misc/heredoc_huge.tests [new file with mode: 0755]
shell/hush_test/hush-psubst/tick_huge.right [new file with mode: 0644]
shell/hush_test/hush-psubst/tick_huge.tests [new file with mode: 0755]
shell/hush_test/hush-z_slow/leak_all2.right [new file with mode: 0644]
shell/hush_test/hush-z_slow/leak_all2.tests [new file with mode: 0755]

index 4641dca11766034f14abb5b0ef391bfb610cfa30..9920e98c65b99a1c56ab4f81b4078a58ac6e27ba 100644 (file)
@@ -2241,7 +2241,10 @@ static void re_execute_shell(const char *s, int is_heredoc)
 
        debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s);
        sigprocmask(SIG_SETMASK, &G.inherited_set, NULL);
-       execv(bb_busybox_exec_path, G.argv_from_re_execing);
+       execve(bb_busybox_exec_path,
+               G.argv_from_re_execing,
+               (is_heredoc ? pp /* points to NULL ptr */ : environ)
+               );
        /* Fallback. Useful for init=/bin/hush usage etc */
        if (G.argv0_for_re_execing[0] == '/')
                execv(G.argv0_for_re_execing, G.argv_from_re_execing);
@@ -4402,35 +4405,40 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
 
 #if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT
 /* Subroutines for copying $(...) and `...` things */
-static void add_till_backquote(o_string *dest, struct in_str *input);
+static int add_till_backquote(o_string *dest, struct in_str *input);
 /* '...' */
-static void add_till_single_quote(o_string *dest, struct in_str *input)
+static int add_till_single_quote(o_string *dest, struct in_str *input)
 {
        while (1) {
                int ch = i_getch(input);
-               if (ch == EOF)
-                       break;
+               if (ch == EOF) {
+                       syntax("unterminated '");
+                       return 1;
+               }
                if (ch == '\'')
-                       break;
+                       return 0;
                o_addchr(dest, ch);
        }
 }
 /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
-static void add_till_double_quote(o_string *dest, struct in_str *input)
+static int add_till_double_quote(o_string *dest, struct in_str *input)
 {
        while (1) {
                int ch = i_getch(input);
+               if (ch == EOF) {
+                       syntax("unterminated \"");
+                       return 1;
+               }
                if (ch == '"')
-                       break;
+                       return 0;
                if (ch == '\\') {  /* \x. Copy both chars. */
                        o_addchr(dest, ch);
                        ch = i_getch(input);
                }
-               if (ch == EOF)
-                       break;
                o_addchr(dest, ch);
                if (ch == '`') {
-                       add_till_backquote(dest, input);
+                       if (add_till_backquote(dest, input))
+                               return 1;
                        o_addchr(dest, ch);
                        continue;
                }
@@ -4451,20 +4459,27 @@ static void add_till_double_quote(o_string *dest, struct in_str *input)
  * Example                               Output
  * echo `echo '\'TEST\`echo ZZ\`BEST`    \TESTZZBEST
  */
-static void add_till_backquote(o_string *dest, struct in_str *input)
+static int add_till_backquote(o_string *dest, struct in_str *input)
 {
        while (1) {
                int ch = i_getch(input);
+               if (ch == EOF) {
+                       syntax("unterminated `");
+                       return 1;
+               }
                if (ch == '`')
-                       break;
-               if (ch == '\\') {  /* \x. Copy both chars unless it is \` */
+                       return 0;
+               if (ch == '\\') {
+                       /* \x. Copy both chars unless it is \` */
                        int ch2 = i_getch(input);
+                       if (ch2 == EOF) {
+                               syntax("unterminated `");
+                               return 1;
+                       }
                        if (ch2 != '`' && ch2 != '$' && ch2 != '\\')
                                o_addchr(dest, ch);
                        ch = ch2;
                }
-               if (ch == EOF)
-                       break;
                o_addchr(dest, ch);
        }
 }
@@ -4480,13 +4495,15 @@ static void add_till_backquote(o_string *dest, struct in_str *input)
  * echo $(echo 'TEST)' BEST)            TEST) BEST
  * echo $(echo \(\(TEST\) BEST)         ((TEST) BEST
  */
-static void add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl)
+static int add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl)
 {
        int count = 0;
        while (1) {
                int ch = i_getch(input);
-               if (ch == EOF)
-                       break;
+               if (ch == EOF) {
+                       syntax("unterminated )");
+                       return 1;
+               }
                if (ch == '(')
                        count++;
                if (ch == ')') {
@@ -4501,23 +4518,29 @@ static void add_till_closing_paren(o_string *dest, struct in_str *input, bool db
                }
                o_addchr(dest, ch);
                if (ch == '\'') {
-                       add_till_single_quote(dest, input);
+                       if (add_till_single_quote(dest, input))
+                               return 1;
                        o_addchr(dest, ch);
                        continue;
                }
                if (ch == '"') {
-                       add_till_double_quote(dest, input);
+                       if (add_till_double_quote(dest, input))
+                               return 1;
                        o_addchr(dest, ch);
                        continue;
                }
-               if (ch == '\\') { /* \x. Copy verbatim. Important for  \(, \) */
+               if (ch == '\\') {
+                       /* \x. Copy verbatim. Important for  \(, \) */
                        ch = i_getch(input);
-                       if (ch == EOF)
-                               break;
+                       if (ch == EOF) {
+                               syntax("unterminated )");
+                               return 1;
+                       }
                        o_addchr(dest, ch);
                        continue;
                }
        }
+       return 0;
 }
 #endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */
 
@@ -4658,7 +4681,8 @@ static int handle_dollar(o_string *as_string,
 #  if !BB_MMU
                        pos = dest->length;
 #  endif
-                       add_till_closing_paren(dest, input, true);
+                       if (add_till_closing_paren(dest, input, true))
+                               return 1;
 #  if !BB_MMU
                        if (as_string) {
                                o_addstr(as_string, dest->data + pos);
@@ -4671,20 +4695,19 @@ static int handle_dollar(o_string *as_string,
                }
 # endif
 # if ENABLE_HUSH_TICK
-               //int pos = dest->length;
                o_addchr(dest, SPECIAL_VAR_SYMBOL);
                o_addchr(dest, quote_mask | '`');
 #  if !BB_MMU
                pos = dest->length;
 #  endif
-               add_till_closing_paren(dest, input, false);
+               if (add_till_closing_paren(dest, input, false))
+                       return 1;
 #  if !BB_MMU
                if (as_string) {
                        o_addstr(as_string, dest->data + pos);
                        o_addchr(as_string, '`');
                }
 #  endif
-               //debug_printf_subst("SUBST RES2 '%s'\n", dest->data + pos);
                o_addchr(dest, SPECIAL_VAR_SYMBOL);
 # endif
                break;
@@ -4778,7 +4801,8 @@ static int parse_stream_dquoted(o_string *as_string,
                //int pos = dest->length;
                o_addchr(dest, SPECIAL_VAR_SYMBOL);
                o_addchr(dest, 0x80 | '`');
-               add_till_backquote(dest, input);
+               if (add_till_backquote(dest, input))
+                       return 1;
                o_addchr(dest, SPECIAL_VAR_SYMBOL);
                //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
                goto again;
@@ -5043,10 +5067,20 @@ static struct pipe *parse_stream(char **pstring,
                        break;
 #if ENABLE_HUSH_TICK
                case '`': {
-                       //int pos = dest.length;
+#if !BB_MMU
+                       int pos;
+#endif
                        o_addchr(&dest, SPECIAL_VAR_SYMBOL);
                        o_addchr(&dest, '`');
-                       add_till_backquote(&dest, input);
+#if !BB_MMU
+                       pos = dest.length;
+#endif
+                       if (add_till_backquote(&dest, input))
+                               goto parse_error;
+#if !BB_MMU
+                       o_addstr(&ctx.as_string, dest.data + pos);
+                       o_addchr(&ctx.as_string, '`');
+#endif
                        o_addchr(&dest, SPECIAL_VAR_SYMBOL);
                        //debug_printf_subst("SUBST RES3 '%s'\n", dest.data + pos);
                        break;
diff --git a/shell/hush_test/hush-misc/heredoc_huge.right b/shell/hush_test/hush-misc/heredoc_huge.right
new file mode 100644 (file)
index 0000000..11740f6
--- /dev/null
@@ -0,0 +1,3 @@
+546ed3f5c81c780d3ab86ada14824237  -
+546ed3f5c81c780d3ab86ada14824237  -
+End
diff --git a/shell/hush_test/hush-misc/heredoc_huge.tests b/shell/hush_test/hush-misc/heredoc_huge.tests
new file mode 100755 (executable)
index 0000000..c2ec281
--- /dev/null
@@ -0,0 +1,9 @@
+# This creates 120k heredoc
+echo 'cat <<HERE | md5sum' >"$0.tmp"
+yes "123456789 123456789 123456789 123456789" | head -3000 >>"$0.tmp"
+echo 'HERE' >>"$0.tmp"
+
+yes "123456789 123456789 123456789 123456789" | head -3000 | md5sum
+. "$0.tmp"
+rm "$0.tmp"
+echo End
diff --git a/shell/hush_test/hush-psubst/tick_huge.right b/shell/hush_test/hush-psubst/tick_huge.right
new file mode 100644 (file)
index 0000000..11740f6
--- /dev/null
@@ -0,0 +1,3 @@
+546ed3f5c81c780d3ab86ada14824237  -
+546ed3f5c81c780d3ab86ada14824237  -
+End
diff --git a/shell/hush_test/hush-psubst/tick_huge.tests b/shell/hush_test/hush-psubst/tick_huge.tests
new file mode 100755 (executable)
index 0000000..acce92f
--- /dev/null
@@ -0,0 +1,7 @@
+# This creates 120k file
+yes "123456789 123456789 123456789 123456789" | head -3000 >>"$0.tmp"
+
+echo "`cat $0.tmp`" | md5sum
+rm "$0.tmp"
+yes "123456789 123456789 123456789 123456789" | head -3000 | md5sum
+echo End
diff --git a/shell/hush_test/hush-z_slow/leak_all2.right b/shell/hush_test/hush-z_slow/leak_all2.right
new file mode 100644 (file)
index 0000000..c6f0334
--- /dev/null
@@ -0,0 +1,3 @@
+Warm up
+Measuring memory leak...
+Ok
diff --git a/shell/hush_test/hush-z_slow/leak_all2.tests b/shell/hush_test/hush-z_slow/leak_all2.tests
new file mode 100755 (executable)
index 0000000..8fb1ca9
--- /dev/null
@@ -0,0 +1,87 @@
+# "Check many leaks" test #2
+# Cramming all kinds of weird commands in here.
+# As you find leaks, please create separate, small test
+# for each leak.
+# Narrowing down the leak using this large test may be difficult.
+# It is intended to be a blanket "is everything ok?" test
+
+echo "Warm up"
+local_var="local val"
+export dev_null="/dev/null"
+>$dev_null
+echo hi1 $local_var `echo ho` >>/dev/null
+echo hi2 $local_var </dev/null | echo 2>&- | cat 1<>/dev/null
+{ echo hi4 $local_var `echo ho` 1<>/dev/null; }
+( echo hi4 $local_var `echo ho` 1<>/dev/null )
+if echo $local_var; false
+    then echo not run
+    elif false <$dev_null
+    then none
+    else cat 0<>$dev_null 1<>"$dev_null"
+fi >>/dev/null
+{
+    if echo $local_var; then cat <<HERE
+Hi cat
+HERE
+    fi >>/dev/null
+} 1<>/dev/null
+while { echo $dev_null >>$dev_null; }; do cat <"$dev_null"; break; done
+( until { echo $dev_null >>$dev_null | false; }; do cat <"$dev_null"; break; done ) <$dev_null
+
+memleak
+
+echo "Measuring memory leak..."
+# Please copy the entire block from above verbatim
+local_var="local val"
+export dev_null="/dev/null"
+>$dev_null
+echo hi1 $local_var `echo ho` >>/dev/null
+echo hi2 $local_var </dev/null | echo 2>&- | cat 1<>/dev/null
+{ echo hi4 $local_var `echo ho` 1<>/dev/null; }
+( echo hi4 $local_var `echo ho` 1<>/dev/null )
+if echo $local_var; false
+    then echo not run
+    elif false <$dev_null
+    then none
+    else cat 0<>$dev_null 1<>"$dev_null"
+fi >>/dev/null
+{
+    if echo $local_var; then cat <<HERE
+Hi cat
+HERE
+    fi >>/dev/null
+} 1<>/dev/null
+while { echo $dev_null >>$dev_null; }; do cat <"$dev_null"; break; done
+( until { echo $dev_null >>$dev_null | false; }; do cat <"$dev_null"; break; done ) <$dev_null
+
+# And same again
+
+local_var="local val"
+export dev_null="/dev/null"
+>$dev_null
+echo hi1 $local_var `echo ho` >>/dev/null
+echo hi2 $local_var </dev/null | echo 2>&- | cat 1<>/dev/null
+{ echo hi4 $local_var `echo ho` 1<>/dev/null; }
+( echo hi4 $local_var `echo ho` 1<>/dev/null )
+if echo $local_var; false
+    then echo not run
+    elif false <$dev_null
+    then none
+    else cat 0<>$dev_null 1<>"$dev_null"
+fi >>/dev/null
+{
+    if echo $local_var; then cat <<HERE
+Hi cat
+HERE
+    fi >>/dev/null
+} 1<>/dev/null
+while { echo $dev_null >>$dev_null; }; do cat <"$dev_null"; break; done
+( until { echo $dev_null >>$dev_null | false; }; do cat <"$dev_null"; break; done ) <$dev_null
+
+memleak
+kb=$?
+if test $kb -le 4; then
+    echo Ok #$kb
+else
+    echo "Bad: $kb kb (or more) leaked"
+fi