From e85248afa23434b78e48fe09b57eea5f6657410d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 22 May 2010 06:20:26 +0200 Subject: [PATCH] hush: fix segfault in ${?:N:M} 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 --- 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: /* varname */ - case_default: { - char *var = arg; - char exp_len; /* '#' if it's ${#var} */ + default: { /* varname */ + 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}|" -- 2.7.4