diff options
| author | Ron Yorston <rmy@pobox.com> | 2020-01-24 13:16:45 +0000 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2020-01-29 14:39:13 +0100 |
| commit | 1ff7002b1d229c678fdffebec602fb4c54439a31 (patch) | |
| tree | 71b5f1f29037301c29e6ce41ddca51da3cd237ce | |
| parent | 16bcd504a32e6a7bf2015ffb241133f9ead6100b (diff) | |
| download | busybox-w32-1ff7002b1d229c678fdffebec602fb4c54439a31.tar.gz busybox-w32-1ff7002b1d229c678fdffebec602fb4c54439a31.tar.bz2 busybox-w32-1ff7002b1d229c678fdffebec602fb4c54439a31.zip | |
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 <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
| -rw-r--r-- | findutils/xargs.c | 27 | ||||
| -rwxr-xr-x | 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 { | |||
| 114 | int max_procs; | 114 | int max_procs; |
| 115 | #endif | 115 | #endif |
| 116 | smalluint xargs_exitcode; | 116 | smalluint xargs_exitcode; |
| 117 | #if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES | ||
| 118 | #define NORM 0 | ||
| 119 | #define QUOTE 1 | ||
| 120 | #define BACKSLASH 2 | ||
| 121 | #define SPACE 4 | ||
| 122 | smalluint process_stdin__state; | ||
| 123 | char process_stdin__q; | ||
| 124 | #endif | ||
| 117 | } FIX_ALIASING; | 125 | } FIX_ALIASING; |
| 118 | #define G (*(struct globals*)bb_common_bufsiz1) | 126 | #define G (*(struct globals*)bb_common_bufsiz1) |
| 119 | #define INIT_G() do { \ | 127 | #define INIT_G() do { \ |
| 120 | setup_common_bufsiz(); \ | 128 | setup_common_bufsiz(); \ |
| 121 | G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \ | 129 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \ |
| 130 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \ | ||
| 131 | /* Even zero values are set because we are NOEXEC applet */ \ | ||
| 132 | G.eof_str = NULL; \ | ||
| 122 | G.idx = 0; \ | 133 | G.idx = 0; \ |
| 123 | IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.running_procs = 0;) \ | 134 | IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.running_procs = 0;) \ |
| 124 | IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.max_procs = 1;) \ | 135 | IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.max_procs = 1;) \ |
| 125 | G.xargs_exitcode = 0; \ | 136 | G.xargs_exitcode = 0; \ |
| 126 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \ | 137 | IF_FEATURE_XARGS_SUPPORT_QUOTES(G.process_stdin__state = NORM;) \ |
| 127 | IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \ | 138 | IF_FEATURE_XARGS_SUPPORT_QUOTES(G.process_stdin__q = '\0';) \ |
| 128 | } while (0) | 139 | } while (0) |
| 129 | 140 | ||
| 130 | 141 | ||
| @@ -257,12 +268,8 @@ static void store_param(char *s) | |||
| 257 | #if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES | 268 | #if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES |
| 258 | static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) | 269 | static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) |
| 259 | { | 270 | { |
| 260 | #define NORM 0 | 271 | #define q G.process_stdin__q |
| 261 | #define QUOTE 1 | 272 | #define state G.process_stdin__state |
| 262 | #define BACKSLASH 2 | ||
| 263 | #define SPACE 4 | ||
| 264 | char q = '\0'; /* quote char */ | ||
| 265 | char state = NORM; | ||
| 266 | char *s = buf; /* start of the word */ | 273 | char *s = buf; /* start of the word */ |
| 267 | char *p = s + strlen(buf); /* end of the word */ | 274 | char *p = s + strlen(buf); /* end of the word */ |
| 268 | 275 | ||
| @@ -339,6 +346,8 @@ static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) | |||
| 339 | /* store_param(NULL) - caller will do it */ | 346 | /* store_param(NULL) - caller will do it */ |
| 340 | dbg_msg("return:'%s'", s); | 347 | dbg_msg("return:'%s'", s); |
| 341 | return s; | 348 | return s; |
| 349 | #undef q | ||
| 350 | #undef state | ||
| 342 | } | 351 | } |
| 343 | #else | 352 | #else |
| 344 | /* The variant does not support single quotes, double quotes or backslash */ | 353 | /* 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" \ | |||
| 41 | "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" \ | 41 | "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" \ |
| 42 | "" "2 3 4 5 6 7 8 9 0 2 3 4 5 6 7 8 9 00\n" | 42 | "" "2 3 4 5 6 7 8 9 0 2 3 4 5 6 7 8 9 00\n" |
| 43 | 43 | ||
| 44 | # see that we don't get "argument line too long", | ||
| 45 | # but do see the last word, 99999, instead | ||
| 46 | optional FEATURE_XARGS_SUPPORT_QUOTES | ||
| 47 | testing "xargs argument line too long" \ | ||
| 48 | "seq 10000 99999 | sed -e 's/^/\"/' -e 's/$/\"/' | xargs echo | grep -o 99999; echo \$?" \ | ||
| 49 | "99999\n0\n" \ | ||
| 50 | "" "" | ||
| 51 | SKIP= | ||
| 52 | |||
| 44 | exit $FAILCOUNT | 53 | exit $FAILCOUNT |
