hush: do not accept "if() { echo; }" function def

function                                             old     new   delta
parse_stream                                        2634    2692     +58
msg_and_die_if_script                                  -      21     +21
syntax_error_unexpected_ch                            41      46      +5
syntax_error_at                                       14      18      +4
die_if_script                                         31      28      -3
setup_redirects                                      319     308     -11
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/2 up/down: 88/-14)             Total: 74 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-08-02 19:44:05 +02:00
parent 84ea60ed65
commit 39701204cf
5 changed files with 60 additions and 17 deletions

View File

@ -0,0 +1,3 @@
./groups_and_keywords2.tests: eval: line 1: syntax error: unexpected ")"
Fail:2
./groups_and_keywords2.tests: line 8: syntax error: unexpected ")"

View File

@ -0,0 +1,9 @@
# This is an error
(eval 'if() { echo; }')
echo Fail:$?
# ^^^^^^ bash prints 1, but interactively it sets $? = 2
# we print 2
# This is an error, and it aborts in script
if() { echo; }
echo Not reached

View File

@ -1272,7 +1272,7 @@ static void xxfree(void *ptr)
* HUSH_DEBUG >= 2 prints line number in this file where it was detected. * HUSH_DEBUG >= 2 prints line number in this file where it was detected.
*/ */
#if HUSH_DEBUG < 2 #if HUSH_DEBUG < 2
# define die_if_script(lineno, ...) die_if_script(__VA_ARGS__) # define msg_and_die_if_script(lineno, ...) msg_and_die_if_script(__VA_ARGS__)
# define syntax_error(lineno, msg) syntax_error(msg) # define syntax_error(lineno, msg) syntax_error(msg)
# define syntax_error_at(lineno, msg) syntax_error_at(msg) # define syntax_error_at(lineno, msg) syntax_error_at(msg)
# define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) # define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch)
@ -1280,7 +1280,16 @@ static void xxfree(void *ptr)
# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch) # define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch)
#endif #endif
static void die_if_script(unsigned lineno, const char *fmt, ...) static void die_if_script(void)
{
if (!G_interactive_fd) {
if (G.last_exitcode) /* sometines it's 2, not 1 (bash compat) */
xfunc_error_retval = G.last_exitcode;
xfunc_die();
}
}
static void msg_and_die_if_script(unsigned lineno, const char *fmt, ...)
{ {
va_list p; va_list p;
@ -1290,8 +1299,7 @@ static void die_if_script(unsigned lineno, const char *fmt, ...)
va_start(p, fmt); va_start(p, fmt);
bb_verror_msg(fmt, p, NULL); bb_verror_msg(fmt, p, NULL);
va_end(p); va_end(p);
if (!G_interactive_fd) die_if_script();
xfunc_die();
} }
static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg) static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
@ -1300,16 +1308,20 @@ static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
bb_error_msg("syntax error: %s", msg); bb_error_msg("syntax error: %s", msg);
else else
bb_error_msg("syntax error"); bb_error_msg("syntax error");
die_if_script();
} }
static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg) static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
{ {
bb_error_msg("syntax error at '%s'", msg); bb_error_msg("syntax error at '%s'", msg);
die_if_script();
} }
static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s) static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s)
{ {
bb_error_msg("syntax error: unterminated %s", s); bb_error_msg("syntax error: unterminated %s", s);
//? source4.tests fails: in bash, echo ${^} in script does not terminate the script
// die_if_script();
} }
static void syntax_error_unterm_ch(unsigned lineno, char ch) static void syntax_error_unterm_ch(unsigned lineno, char ch)
@ -1327,17 +1339,18 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
bb_error_msg("hush.c:%u", lineno); bb_error_msg("hush.c:%u", lineno);
#endif #endif
bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg); bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
die_if_script();
} }
#if HUSH_DEBUG < 2 #if HUSH_DEBUG < 2
# undef die_if_script # undef msg_and_die_if_script
# undef syntax_error # undef syntax_error
# undef syntax_error_at # undef syntax_error_at
# undef syntax_error_unterm_ch # undef syntax_error_unterm_ch
# undef syntax_error_unterm_str # undef syntax_error_unterm_str
# undef syntax_error_unexpected_ch # undef syntax_error_unexpected_ch
#else #else
# define die_if_script(...) die_if_script(__LINE__, __VA_ARGS__) # define msg_and_die_if_script(...) msg_and_die_if_script(__LINE__, __VA_ARGS__)
# define syntax_error(msg) syntax_error(__LINE__, msg) # define syntax_error(msg) syntax_error(__LINE__, msg)
# define syntax_error_at(msg) syntax_error_at(__LINE__, msg) # define syntax_error_at(msg) syntax_error_at(__LINE__, msg)
# define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) # define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch)
@ -1800,7 +1813,7 @@ static void restore_ttypgrp_and__exit(void)
* echo END_OF_SCRIPT * echo END_OF_SCRIPT
* lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT". * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT".
* This makes "echo END_OF_SCRIPT" executed twice. * This makes "echo END_OF_SCRIPT" executed twice.
* Similar problems can be seen with die_if_script() -> xfunc_die() * Similar problems can be seen with msg_and_die_if_script() -> xfunc_die()
* and in `cmd` handling. * and in `cmd` handling.
* If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
*/ */
@ -3383,7 +3396,7 @@ static int done_command(struct parse_context *ctx)
#if 0 /* Instead we emit error message at run time */ #if 0 /* Instead we emit error message at run time */
if (ctx->pending_redirect) { if (ctx->pending_redirect) {
/* For example, "cmd >" (no filename to redirect to) */ /* For example, "cmd >" (no filename to redirect to) */
die_if_script("syntax error: %s", "invalid redirect"); syntax_error("invalid redirect");
ctx->pending_redirect = NULL; ctx->pending_redirect = NULL;
} }
#endif #endif
@ -3949,7 +3962,7 @@ static int parse_redirect(struct parse_context *ctx,
#if 0 /* Instead we emit error message at run time */ #if 0 /* Instead we emit error message at run time */
if (ctx->pending_redirect) { if (ctx->pending_redirect) {
/* For example, "cmd > <file" */ /* For example, "cmd > <file" */
die_if_script("syntax error: %s", "invalid redirect"); syntax_error("invalid redirect");
} }
#endif #endif
/* Set ctx->pending_redirect, so we know what to do at the /* Set ctx->pending_redirect, so we know what to do at the
@ -5021,10 +5034,16 @@ static struct pipe *parse_stream(char **pstring,
else else
o_free_unsafe(&ctx.as_string); o_free_unsafe(&ctx.as_string);
#endif #endif
debug_leave(); if (ch != ';' && IS_NULL_PIPE(ctx.list_head)) {
/* Example: bare "{ }", "()" */
G.last_exitcode = 2; /* bash compat */
syntax_error_unexpected_ch(ch);
goto parse_error2;
}
debug_printf_parse("parse_stream return %p: " debug_printf_parse("parse_stream return %p: "
"end_trigger char found\n", "end_trigger char found\n",
ctx.list_head); ctx.list_head);
debug_leave();
return ctx.list_head; return ctx.list_head;
} }
} }
@ -5282,8 +5301,8 @@ static struct pipe *parse_stream(char **pstring,
/* proper use of this character is caught by end_trigger: /* proper use of this character is caught by end_trigger:
* if we see {, we call parse_group(..., end_trigger='}') * if we see {, we call parse_group(..., end_trigger='}')
* and it will match } earlier (not here). */ * and it will match } earlier (not here). */
syntax_error_unexpected_ch(ch);
G.last_exitcode = 2; G.last_exitcode = 2;
syntax_error_unexpected_ch(ch);
goto parse_error2; goto parse_error2;
default: default:
if (HUSH_DEBUG) if (HUSH_DEBUG)
@ -5513,7 +5532,7 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
if (errmsg_p) if (errmsg_p)
*errmsg_p = math_state.errmsg; *errmsg_p = math_state.errmsg;
if (math_state.errmsg) if (math_state.errmsg)
die_if_script(math_state.errmsg); msg_and_die_if_script(math_state.errmsg);
return res; return res;
} }
#endif #endif
@ -5780,7 +5799,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
/* in bash, len=-n means strlen()-n */ /* in bash, len=-n means strlen()-n */
len = (arith_t)strlen(val) - beg + len; len = (arith_t)strlen(val) - beg + len;
if (len < 0) /* bash compat */ if (len < 0) /* bash compat */
die_if_script("%s: substring expression < 0", var); msg_and_die_if_script("%s: substring expression < 0", var);
} }
if (len <= 0 || !val || beg >= strlen(val)) { if (len <= 0 || !val || beg >= strlen(val)) {
arith_err: arith_err:
@ -5794,7 +5813,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
} }
debug_printf_varexp("val:'%s'\n", val); debug_printf_varexp("val:'%s'\n", val);
#else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */ #else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */
die_if_script("malformed ${%s:...}", var); msg_and_die_if_script("malformed ${%s:...}", var);
val = NULL; val = NULL;
#endif #endif
} else { /* one of "-=+?" */ } else { /* one of "-=+?" */
@ -5831,7 +5850,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
exp_word = to_be_freed; exp_word = to_be_freed;
if (exp_op == '?') { if (exp_op == '?') {
/* mimic bash message */ /* mimic bash message */
die_if_script("%s: %s", msg_and_die_if_script("%s: %s",
var, var,
exp_word[0] exp_word[0]
? exp_word ? exp_word
@ -5848,7 +5867,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
/* ${var=[word]} or ${var:=[word]} */ /* ${var=[word]} or ${var:=[word]} */
if (isdigit(var[0]) || var[0] == '#') { if (isdigit(var[0]) || var[0] == '#') {
/* mimic bash message */ /* mimic bash message */
die_if_script("$%s: cannot assign in this way", var); msg_and_die_if_script("$%s: cannot assign in this way", var);
val = NULL; val = NULL;
} else { } else {
char *new_var = xasprintf("%s=%s", var, val); char *new_var = xasprintf("%s=%s", var, val);
@ -6862,7 +6881,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
* "cmd >" (no filename) * "cmd >" (no filename)
* "cmd > <file" (2nd redirect starts too early) * "cmd > <file" (2nd redirect starts too early)
*/ */
die_if_script("syntax error: %s", "invalid redirect"); syntax_error("invalid redirect");
continue; continue;
} }
mode = redir_table[redir->rd_type].mode; mode = redir_table[redir->rd_type].mode;

View File

@ -0,0 +1,3 @@
hush: syntax error: unexpected )
Fail:2
hush: syntax error: unexpected )

View File

@ -0,0 +1,9 @@
# This is an error
(eval 'if() { echo; }')
echo Fail:$?
# ^^^^^^ bash prints 1, but interactively it sets $? = 2
# we print 2
# This is an error, and it aborts in script
if() { echo; }
echo Not reached