[git commit master] hush: fix segfault in ${?:N:M}

Denys Vlasenko vda.linux at googlemail.com
Sat May 22 04:20:26 UTC 2010


commit: http://git.busybox.net/busybox/commit/?id=e85248afa23434b78e48fe09b57eea5f6657410d
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
expand_vars_to_list                                 2374    2409     +35
builtin_umask                                        132     133      +1
builtin_exit                                          47      48      +1
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 37/0)               Total: 37 bytes

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 shell/hush.c                                       |   81 +++++++++++--------
 shell/hush_test/hush-vars/param_expand_alt.right   |    2 +-
 shell/hush_test/hush-vars/param_expand_alt.tests   |    4 +-
 .../hush-vars/param_expand_bash_substring.right    |   13 +++
 .../hush-vars/param_expand_bash_substring.tests    |   15 ++++
 5 files changed, 78 insertions(+), 37 deletions(-)

diff --git a/shell/hush.c b/shell/hush.c
index 7645a34..08e6378 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -179,9 +179,13 @@
 
 #define ERR_PTR ((void*)(long)1)
 
-#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
+#define JOB_STATUS_FORMAT    "[%d] %-22s %.40s\n"
 
-#define SPECIAL_VAR_SYMBOL 3
+#define _SPECIAL_VARS_STR     "_*@$!?#"
+#define SPECIAL_VARS_STR     ("_*@$!?#" + 1)
+#define NUMERIC_SPECVARS_STR ("_*@$!?#" + 3)
+
+#define SPECIAL_VAR_SYMBOL   3
 
 struct variable;
 
@@ -2472,21 +2476,6 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
 
 		switch (first_ch & 0x7f) {
 		/* Highest bit in first_ch indicates that var is double-quoted */
-		case '$': /* pid */
-			val = utoa(G.root_pid);
-			break;
-		case '!': /* bg pid */
-			val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)"";
-			break;
-		case '?': /* exitcode */
-			val = utoa(G.last_exitcode);
-			break;
-		case '#': /* argc */
-			if (arg[1] != SPECIAL_VAR_SYMBOL)
-				/* actually, it's a ${#var} */
-				goto case_default;
-			val = utoa(G.global_argc ? G.global_argc-1 : 0);
-			break;
 		case '*':
 		case '@':
 			i = 1;
@@ -2581,27 +2570,35 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
 			break;
 		}
 #endif
-		default: /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */
-		case_default: {
-			char *var = arg;
-			char exp_len; /* '#' if it's ${#var} */
+		default: { /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */
+			char *var;
+			char first_char;
 			char exp_op;
 			char exp_save = exp_save; /* for compiler */
-			char *exp_saveptr = exp_saveptr; /* points to expansion operator */
+			char *exp_saveptr; /* points to expansion operator */
 			char *exp_word = exp_word; /* for compiler */
 
+			var = arg;
 			*p = '\0';
-			arg[0] = first_ch & 0x7f;
-
-			/* prepare for expansions */
+			exp_saveptr = arg[1] ? strchr("%#:-=+?", arg[1]) : NULL;
+			first_char = arg[0] = first_ch & 0x7f;
 			exp_op = 0;
-			exp_len = var[0];
-			if (exp_len == '#') {
+
+			if (first_char == '#' && arg[1] && !exp_saveptr) {
 				/* handle length expansion ${#var} */
 				var++;
+				exp_op = 'L';
 			} else {
 				/* maybe handle parameter expansion */
-				exp_saveptr = var + strcspn(var, "%#:-=+?");
+				if (exp_saveptr /* if 2nd char is one of expansion operators */
+				 && strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */
+				) {
+					/* ${?:0}, ${#[:]%0} etc */
+					exp_saveptr = var + 1;
+				} else {
+					/* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
+					exp_saveptr = var+1 + strcspn(var+1, "%#:-=+?");
+				}
 				exp_op = exp_save = *exp_saveptr;
 				if (exp_op) {
 					exp_word = exp_saveptr + 1;
@@ -2616,7 +2613,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
 						}
 					}
 					*exp_saveptr = '\0';
-				}
+				} /* else: it's not an expansion op, but bare ${var} */
 			}
 
 			/* lookup the variable in question */
@@ -2626,11 +2623,27 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
 				if (i < G.global_argc)
 					val = G.global_argv[i];
 				/* else val remains NULL: $N with too big N */
-			} else
-				val = get_local_var_value(var);
+			} else {
+				switch (var[0]) {
+				case '$': /* pid */
+					val = utoa(G.root_pid);
+					break;
+				case '!': /* bg pid */
+					val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)"";
+					break;
+				case '?': /* exitcode */
+					val = utoa(G.last_exitcode);
+					break;
+				case '#': /* argc */
+					val = utoa(G.global_argc ? G.global_argc-1 : 0);
+					break;
+				default:
+					val = get_local_var_value(var);
+				}
+			}
 
 			/* handle any expansions */
-			if (exp_len == '#') {
+			if (exp_op == 'L') {
 				debug_printf_expand("expand: length(%s)=", val);
 				val = utoa(val ? strlen(val) : 0);
 				debug_printf_expand("%s\n", val);
@@ -2761,7 +2774,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
 							}
 						}
 					}
-				}
+				} /* one of "-=+?" */
 
 				*exp_saveptr = exp_save;
 			} /* if (exp_op) */
@@ -6031,7 +6044,7 @@ static int handle_dollar(o_string *as_string,
 		 * or even ${?+subst} - operator acting on a special variable,
 		 * or the beginning of variable name.
 		 */
-		if (!strchr("$!?#*@_", ch) && !isalnum(ch)) { /* not one of those */
+		if (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) { /* not one of those */
  bad_dollar_syntax:
 			syntax_error_unterm_str("${name}");
 			debug_printf_parse("handle_dollar return 1: unterminated ${name}\n");
diff --git a/shell/hush_test/hush-vars/param_expand_alt.right b/shell/hush_test/hush-vars/param_expand_alt.right
index 4d2197a..67f18d6 100644
--- a/shell/hush_test/hush-vars/param_expand_alt.right
+++ b/shell/hush_test/hush-vars/param_expand_alt.right
@@ -1,6 +1,6 @@
 hush: syntax error: unterminated ${name}
 hush: syntax error: unterminated ${name}
-_0 _0
+__ __
 _ _ _ _ _
 _aaaa _ _ _word _word
 _ _ _ _ _
diff --git a/shell/hush_test/hush-vars/param_expand_alt.tests b/shell/hush_test/hush-vars/param_expand_alt.tests
index dcdca86..3b646b1 100755
--- a/shell/hush_test/hush-vars/param_expand_alt.tests
+++ b/shell/hush_test/hush-vars/param_expand_alt.tests
@@ -2,8 +2,8 @@
 "$THIS_SH" -c 'echo ${+}  ; echo moo'
 "$THIS_SH" -c 'echo ${:+} ; echo moo'
 
-# now some funky ones
-echo _${#+} _${#:+}
+# now some funky ones. (bash doesn't accept ${#+})
+echo _${#+}_ _${#:+}_
 
 # now some valid ones
 set --
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.right b/shell/hush_test/hush-vars/param_expand_bash_substring.right
index 53b8836..2f4c51d 100644
--- a/shell/hush_test/hush-vars/param_expand_bash_substring.right
+++ b/shell/hush_test/hush-vars/param_expand_bash_substring.right
@@ -39,6 +39,19 @@ f:1:2=|12|
 f::2 =|01|
 f:1: =||
 f::  =||
+Substrings from special vars
+?    =|0|
+?:1  =||
+?:1:2=||
+?::2 =|0|
+?:1: =||
+?::  =||
+#    =|11|
+#:1  =|1|
+#:1:2=|1|
+#::2 =|11|
+#:1: =||
+#::  =||
 Substrings with expressions
 f            =|01234567|
 f:1+1:2+2    =|2345|
diff --git a/shell/hush_test/hush-vars/param_expand_bash_substring.tests b/shell/hush_test/hush-vars/param_expand_bash_substring.tests
index a80523a..5c9552d 100755
--- a/shell/hush_test/hush-vars/param_expand_bash_substring.tests
+++ b/shell/hush_test/hush-vars/param_expand_bash_substring.tests
@@ -55,6 +55,21 @@ f=0123456789; echo "f::2 =|${f::2}|"
 f=0123456789; echo "f:1: =|${f:1:}|"
 f=0123456789; echo "f::  =|${f::}|"
 
+echo "Substrings from special vars"
+echo '?    '"=|$?|"
+echo '?:1  '"=|${?:1}|"
+echo '?:1:2'"=|${?:1:2}|"
+echo '?::2 '"=|${?::2}|"
+echo '?:1: '"=|${?:1:}|"
+echo '?::  '"=|${?::}|"
+set -- 1 2 3 4 5 6 7 8 9 10 11
+echo '#    '"=|$#|"
+echo '#:1  '"=|${#:1}|"
+echo '#:1:2'"=|${#:1:2}|"
+echo '#::2 '"=|${#::2}|"
+echo '#:1: '"=|${#:1:}|"
+echo '#::  '"=|${#::}|"
+
 echo "Substrings with expressions"
 f=01234567; echo 'f            '"=|$f|"
 f=01234567; echo 'f:1+1:2+2    '"=|${f:1+1:2+2}|"
-- 
1.6.3.3



More information about the busybox-cvs mailing list