From 1ff7002b1d229c678fdffebec602fb4c54439a31 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Fri, 24 Jan 2020 13:16:45 +0000 Subject: [PATCH] xargs: fix handling of quoted arguments, closes 11441 As reported in bug 11441 when presented with a large number of quoted arguments xargs can return 'argument line too long': seq 10000 29999 | sed -e 's/^/"/' -e 's/$/"/' | busybox xargs echo This happens because the variant of process_stdin() which handles quoted arguments doesn't preserve state between calls. If the allowed number of characters is exceeded part way through a quoted argument the next call to process_stdin() incorrectly treats the terminating quote as a starting quote, thus quoting all of the argument separators. function old new delta process_stdin 274 303 +29 xargs_main 731 745 +14 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 43/0) Total: 43 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- findutils/xargs.c | 27 ++++++++++++++++++--------- testsuite/xargs.tests | 9 +++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/findutils/xargs.c b/findutils/xargs.c index 726315803..4fb306bb8 100644 --- a/findutils/xargs.c +++ b/findutils/xargs.c @@ -114,17 +114,28 @@ struct globals { int max_procs; #endif smalluint xargs_exitcode; +#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES +#define NORM 0 +#define QUOTE 1 +#define BACKSLASH 2 +#define SPACE 4 + smalluint process_stdin__state; + char process_stdin__q; +#endif } FIX_ALIASING; #define G (*(struct globals*)bb_common_bufsiz1) #define INIT_G() do { \ setup_common_bufsiz(); \ - G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \ + IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \ + IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \ + /* Even zero values are set because we are NOEXEC applet */ \ + G.eof_str = NULL; \ G.idx = 0; \ IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.running_procs = 0;) \ IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.max_procs = 1;) \ G.xargs_exitcode = 0; \ - IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \ - IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \ + IF_FEATURE_XARGS_SUPPORT_QUOTES(G.process_stdin__state = NORM;) \ + IF_FEATURE_XARGS_SUPPORT_QUOTES(G.process_stdin__q = '\0';) \ } while (0) @@ -257,12 +268,8 @@ static void store_param(char *s) #if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) { -#define NORM 0 -#define QUOTE 1 -#define BACKSLASH 2 -#define SPACE 4 - char q = '\0'; /* quote char */ - char state = NORM; +#define q G.process_stdin__q +#define state G.process_stdin__state char *s = buf; /* start of the word */ char *p = s + strlen(buf); /* end of the word */ @@ -339,6 +346,8 @@ static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) /* store_param(NULL) - caller will do it */ dbg_msg("return:'%s'", s); return s; +#undef q +#undef state } #else /* The variant does not support single quotes, double quotes or backslash */ diff --git a/testsuite/xargs.tests b/testsuite/xargs.tests index 2d0a201b7..855b33bc2 100755 --- a/testsuite/xargs.tests +++ b/testsuite/xargs.tests @@ -41,4 +41,13 @@ testing "xargs -sNUM test 2" \ "echo 1 2 3 4 5 6 7 8 9 0\n""echo 1 2 3 4 5 6 7 8 9\n""echo 1 00\n" \ "" "2 3 4 5 6 7 8 9 0 2 3 4 5 6 7 8 9 00\n" +# see that we don't get "argument line too long", +# but do see the last word, 99999, instead +optional FEATURE_XARGS_SUPPORT_QUOTES +testing "xargs argument line too long" \ + "seq 10000 99999 | sed -e 's/^/\"/' -e 's/$/\"/' | xargs echo | grep -o 99999; echo \$?" \ + "99999\n0\n" \ + "" "" +SKIP= + exit $FAILCOUNT