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.
*/
#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_at(lineno, msg) syntax_error_at(msg)
# 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)
#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;
@ -1290,8 +1299,7 @@ static void die_if_script(unsigned lineno, const char *fmt, ...)
va_start(p, fmt);
bb_verror_msg(fmt, p, NULL);
va_end(p);
if (!G_interactive_fd)
xfunc_die();
die_if_script();
}
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);
else
bb_error_msg("syntax error");
die_if_script();
}
static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *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)
{
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)
@ -1327,17 +1339,18 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
bb_error_msg("hush.c:%u", lineno);
#endif
bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
die_if_script();
}
#if HUSH_DEBUG < 2
# undef die_if_script
# undef msg_and_die_if_script
# undef syntax_error
# undef syntax_error_at
# undef syntax_error_unterm_ch
# undef syntax_error_unterm_str
# undef syntax_error_unexpected_ch
#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_at(msg) syntax_error_at(__LINE__, msg)
# 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
* lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT".
* 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.
* 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 (ctx->pending_redirect) {
/* For example, "cmd >" (no filename to redirect to) */
die_if_script("syntax error: %s", "invalid redirect");
syntax_error("invalid redirect");
ctx->pending_redirect = NULL;
}
#endif
@ -3949,7 +3962,7 @@ static int parse_redirect(struct parse_context *ctx,
#if 0 /* Instead we emit error message at run time */
if (ctx->pending_redirect) {
/* For example, "cmd > <file" */
die_if_script("syntax error: %s", "invalid redirect");
syntax_error("invalid redirect");
}
#endif
/* Set ctx->pending_redirect, so we know what to do at the
@ -5021,10 +5034,16 @@ static struct pipe *parse_stream(char **pstring,
else
o_free_unsafe(&ctx.as_string);
#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: "
"end_trigger char found\n",
ctx.list_head);
debug_leave();
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:
* if we see {, we call parse_group(..., end_trigger='}')
* and it will match } earlier (not here). */
syntax_error_unexpected_ch(ch);
G.last_exitcode = 2;
syntax_error_unexpected_ch(ch);
goto parse_error2;
default:
if (HUSH_DEBUG)
@ -5513,7 +5532,7 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
if (errmsg_p)
*errmsg_p = math_state.errmsg;
if (math_state.errmsg)
die_if_script(math_state.errmsg);
msg_and_die_if_script(math_state.errmsg);
return res;
}
#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 */
len = (arith_t)strlen(val) - beg + len;
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)) {
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);
#else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */
die_if_script("malformed ${%s:...}", var);
msg_and_die_if_script("malformed ${%s:...}", var);
val = NULL;
#endif
} 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;
if (exp_op == '?') {
/* mimic bash message */
die_if_script("%s: %s",
msg_and_die_if_script("%s: %s",
var,
exp_word[0]
? 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]} */
if (isdigit(var[0]) || var[0] == '#') {
/* 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;
} else {
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 > <file" (2nd redirect starts too early)
*/
die_if_script("syntax error: %s", "invalid redirect");
syntax_error("invalid redirect");
continue;
}
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