hush: finally make cmd
safe on NOMMU
function old new delta generate_stream_from_string - 157 +157 expand_variables 2050 2003 -47 generate_stream_from_list 139 - -139 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 0/1 up/down: 157/-186) Total: -29 bytes
This commit is contained in:
parent
e0755e58ca
commit
db2a9b683a
98
shell/hush.c
98
shell/hush.c
@ -90,14 +90,6 @@
|
|||||||
#define SKIP_FEATURE_SH_STANDALONE(...) __VA_ARGS__
|
#define SKIP_FEATURE_SH_STANDALONE(...) __VA_ARGS__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !BB_MMU && ENABLE_HUSH_TICK
|
|
||||||
//#undef ENABLE_HUSH_TICK
|
|
||||||
//#define ENABLE_HUSH_TICK 0
|
|
||||||
#warning On NOMMU, hush command substitution is dangerous.
|
|
||||||
#warning Dont use it for commands which produce lots of output.
|
|
||||||
#warning For more info see shell/hush.c, generate_stream_from_list().
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !ENABLE_HUSH_INTERACTIVE
|
#if !ENABLE_HUSH_INTERACTIVE
|
||||||
#undef ENABLE_FEATURE_EDITING
|
#undef ENABLE_FEATURE_EDITING
|
||||||
#define ENABLE_FEATURE_EDITING 0
|
#define ENABLE_FEATURE_EDITING 0
|
||||||
@ -1649,7 +1641,7 @@ static char **o_finalize_list(o_string *o, int n)
|
|||||||
|
|
||||||
/* Expansion can recurse */
|
/* Expansion can recurse */
|
||||||
#if ENABLE_HUSH_TICK
|
#if ENABLE_HUSH_TICK
|
||||||
static int process_command_subs(o_string *dest, struct in_str *input);
|
static int process_command_subs(o_string *dest, const char *s);
|
||||||
#endif
|
#endif
|
||||||
static char *expand_string_to_string(const char *str);
|
static char *expand_string_to_string(const char *str);
|
||||||
static int parse_stream_dquoted(o_string *dest, struct in_str *input, int dquote_end);
|
static int parse_stream_dquoted(o_string *dest, struct in_str *input, int dquote_end);
|
||||||
@ -1798,18 +1790,15 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
|
|||||||
ored_ch = 0x80;
|
ored_ch = 0x80;
|
||||||
break;
|
break;
|
||||||
#if ENABLE_HUSH_TICK
|
#if ENABLE_HUSH_TICK
|
||||||
case '`': { /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
|
case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
|
||||||
struct in_str input;
|
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
arg++;
|
arg++;
|
||||||
//TODO: can we just stuff it into "output" directly?
|
//TODO: can we just stuff it into "output" directly?
|
||||||
debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
|
debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
|
||||||
setup_string_in_str(&input, arg);
|
process_command_subs(&subst_result, arg);
|
||||||
process_command_subs(&subst_result, &input);
|
|
||||||
debug_printf_subst("SUBST RES '%s'\n", subst_result.data);
|
debug_printf_subst("SUBST RES '%s'\n", subst_result.data);
|
||||||
val = subst_result.data;
|
val = subst_result.data;
|
||||||
goto store_val;
|
goto store_val;
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#if ENABLE_SH_MATH_SUPPORT
|
#if ENABLE_SH_MATH_SUPPORT
|
||||||
case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */
|
case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */
|
||||||
@ -3711,35 +3700,20 @@ static int redirect_opt_num(o_string *o)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct pipe *parse_stream(struct in_str *input, int end_trigger);
|
static struct pipe *parse_stream(struct in_str *input, int end_trigger);
|
||||||
|
static void parse_and_run_string(const char *s);
|
||||||
|
|
||||||
#if ENABLE_HUSH_TICK
|
#if ENABLE_HUSH_TICK
|
||||||
static FILE *generate_stream_from_list(struct pipe *head)
|
static FILE *generate_stream_from_string(const char *s)
|
||||||
{
|
{
|
||||||
FILE *pf;
|
FILE *pf;
|
||||||
int pid, channel[2];
|
int pid, channel[2];
|
||||||
|
|
||||||
xpipe(channel);
|
xpipe(channel);
|
||||||
/* *** NOMMU WARNING *** */
|
|
||||||
/* By using vfork here, we suspend parent till child exits or execs.
|
|
||||||
* If child will not do it before it fills the pipe, it can block forever
|
|
||||||
* in write(STDOUT_FILENO), and parent (shell) will be also stuck.
|
|
||||||
* Try this script:
|
|
||||||
* yes "0123456789012345678901234567890" | dd bs=32 count=64k >TESTFILE
|
|
||||||
* huge=`cat TESTFILE` # will block here forever
|
|
||||||
* echo OK
|
|
||||||
*/
|
|
||||||
pid = BB_MMU ? fork() : vfork();
|
pid = BB_MMU ? fork() : vfork();
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork");
|
bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork");
|
||||||
|
|
||||||
if (pid == 0) { /* child */
|
if (pid == 0) { /* child */
|
||||||
if (ENABLE_HUSH_JOB)
|
|
||||||
die_sleep = 0; /* let nofork's xfuncs die */
|
|
||||||
close(channel[0]); /* NB: close _first_, then move fd! */
|
|
||||||
xmove_fd(channel[1], 1);
|
|
||||||
/* Prevent it from trying to handle ctrl-z etc */
|
|
||||||
#if ENABLE_HUSH_JOB
|
|
||||||
G.run_list_level = 1;
|
|
||||||
#endif
|
|
||||||
/* Process substitution is not considered to be usual
|
/* Process substitution is not considered to be usual
|
||||||
* 'command execution'.
|
* 'command execution'.
|
||||||
* SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
|
* SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
|
||||||
@ -3749,40 +3723,47 @@ static FILE *generate_stream_from_list(struct pipe *head)
|
|||||||
+ (1 << SIGTTIN)
|
+ (1 << SIGTTIN)
|
||||||
+ (1 << SIGTTOU)
|
+ (1 << SIGTTOU)
|
||||||
, SIG_IGN);
|
, SIG_IGN);
|
||||||
|
if (ENABLE_HUSH_JOB)
|
||||||
/* Note: freeing 'head' here would break NOMMU. */
|
die_sleep = 0; /* let nofork's xfuncs die */
|
||||||
_exit(run_list(head));
|
close(channel[0]); /* NB: close _first_, then move fd! */
|
||||||
|
xmove_fd(channel[1], 1);
|
||||||
|
/* Prevent it from trying to handle ctrl-z etc */
|
||||||
|
USE_HUSH_JOB(G.run_list_level = 1;)
|
||||||
|
#if BB_MMU
|
||||||
|
parse_and_run_string(s);
|
||||||
|
_exit(G.last_return_code);
|
||||||
|
#else
|
||||||
|
/* We re-execute after vfork on NOMMU. This makes this script safe:
|
||||||
|
* yes "0123456789012345678901234567890" | dd bs=32 count=64k >TESTFILE
|
||||||
|
* huge=`cat TESTFILE` # was blocking here forever
|
||||||
|
* echo OK
|
||||||
|
*/
|
||||||
|
//TODO: pass non-exported variables, traps, and functions
|
||||||
|
execl(CONFIG_BUSYBOX_EXEC_PATH, "hush", "-c", s, NULL);
|
||||||
|
_exit(127);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* parent */
|
||||||
close(channel[1]);
|
close(channel[1]);
|
||||||
pf = fdopen(channel[0], "r");
|
pf = fdopen(channel[0], "r");
|
||||||
return pf;
|
return pf;
|
||||||
/* 'head' is freed by the caller */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return code is exit status of the process that is run. */
|
/* Return code is exit status of the process that is run. */
|
||||||
static int process_command_subs(o_string *dest,
|
static int process_command_subs(o_string *dest, const char *s)
|
||||||
struct in_str *input)
|
|
||||||
{
|
{
|
||||||
int retcode, ch, eol_cnt;
|
FILE *pf;
|
||||||
struct pipe *pipe_list;
|
|
||||||
FILE *p;
|
|
||||||
struct in_str pipe_str;
|
struct in_str pipe_str;
|
||||||
|
int ch, eol_cnt;
|
||||||
|
|
||||||
/* Recursion to generate command */
|
pf = generate_stream_from_string(s);
|
||||||
pipe_list = parse_stream(input, '\0');
|
if (pf == NULL)
|
||||||
if (pipe_list == NULL)
|
|
||||||
return 0; /* EOF: empty `cmd`: ``, ` ` etc */
|
|
||||||
if (pipe_list == ERR_PTR)
|
|
||||||
return 1; /* parse error. can this really happen? */
|
|
||||||
|
|
||||||
p = generate_stream_from_list(pipe_list);
|
|
||||||
free_pipe_list(pipe_list, /* indent: */ 0);
|
|
||||||
if (p == NULL)
|
|
||||||
return 1;
|
return 1;
|
||||||
close_on_exec_on(fileno(p));
|
close_on_exec_on(fileno(pf));
|
||||||
|
|
||||||
/* Now send results of command back into original context */
|
/* Now send results of command back into original context */
|
||||||
setup_file_in_str(&pipe_str, p);
|
setup_file_in_str(&pipe_str, pf);
|
||||||
eol_cnt = 0;
|
eol_cnt = 0;
|
||||||
while ((ch = i_getch(&pipe_str)) != EOF) {
|
while ((ch = i_getch(&pipe_str)) != EOF) {
|
||||||
if (ch == '\n') {
|
if (ch == '\n') {
|
||||||
@ -3799,12 +3780,13 @@ static int process_command_subs(o_string *dest,
|
|||||||
debug_printf("done reading from pipe, pclose()ing\n");
|
debug_printf("done reading from pipe, pclose()ing\n");
|
||||||
/* Note: we got EOF, and we just close the read end of the pipe.
|
/* Note: we got EOF, and we just close the read end of the pipe.
|
||||||
* We do not wait for the `cmd` child to terminate. bash and ash do.
|
* We do not wait for the `cmd` child to terminate. bash and ash do.
|
||||||
* Try this:
|
* Try these:
|
||||||
* echo `echo Hi; exec 1>&-; sleep 2`
|
* echo `echo Hi; exec 1>&-; sleep 2` - bash waits 2 sec
|
||||||
|
* `false`; echo $? - bash outputs "1"
|
||||||
*/
|
*/
|
||||||
retcode = fclose(p);
|
fclose(pf);
|
||||||
debug_printf("closed FILE from child, retcode=%d\n", retcode);
|
debug_printf("closed FILE from child. return 0\n");
|
||||||
return retcode;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
shown
|
shown
|
||||||
hush: syntax error: unterminated '
|
hush: syntax error: unterminated '
|
||||||
|
test
|
||||||
|
not shown
|
||||||
|
Loading…
Reference in New Issue
Block a user