diff options
author | Elliott Hughes <enh@google.com> | 2017-09-20 13:53:23 -0700 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2017-10-01 16:55:06 -0500 |
commit | b89af5ed5c95ebe0466830d90eedd430593a3584 (patch) | |
tree | 946632d0bceddb6209681b04480bc969d529589c | |
parent | b542295cd8d906647e013056ab049323ce11e910 (diff) | |
download | toybox-b89af5ed5c95ebe0466830d90eedd430593a3584.tar.gz |
Fix xargs to obey POSIX's ARG_MAX restrictions.
This avoids "xargs: exec echo: Argument list too long" errors in practice.
find(1) needs to be fixed too, but that's a bit more complicated and a working
xargs provides a workaround.
Bug: http://b/65818597
Test: find /proc | strace -f -e execve ./toybox xargs echo > /dev/null
-rw-r--r-- | lib/lib.c | 13 | ||||
-rw-r--r-- | lib/lib.h | 1 | ||||
-rw-r--r-- | toys/posix/xargs.c | 14 |
3 files changed, 27 insertions, 1 deletions
@@ -1293,3 +1293,16 @@ void do_lines(int fd, void (*call)(char **pline, long len)) if (fd) fclose(fp); } + +// Returns the number of bytes taken by the environment variables. For use +// when calculating the maximum bytes of environment+argument data that can +// be passed to exec for find(1) and xargs(1). +long environ_bytes() +{ + long bytes = sizeof(char *); + char **ev; + + for (ev = environ; *ev; ev++) + bytes += sizeof(char *) + strlen(*ev) + 1; + return bytes; +} @@ -241,6 +241,7 @@ int regexec0(regex_t *preg, char *string, long len, int nmatch, char *getusername(uid_t uid); char *getgroupname(gid_t gid); void do_lines(int fd, void (*call)(char **pline, long len)); +long environ_bytes(); #define HR_SPACE 1 // Space between number and units #define HR_B 2 // Use "B" for single byte units diff --git a/toys/posix/xargs.c b/toys/posix/xargs.c index b4cb80a0..e7dd10b5 100644 --- a/toys/posix/xargs.c +++ b/toys/posix/xargs.c @@ -79,6 +79,8 @@ static char *handle_entries(char *data, char **entry) if (!*s) break; save = s; + TT.bytes += sizeof(char *); + for (;;) { if (++TT.bytes >= TT.max_bytes && TT.max_bytes) return save; if (!*s || isspace(*s)) break; @@ -95,7 +97,7 @@ static char *handle_entries(char *data, char **entry) // -0 support } else { - TT.bytes += strlen(data)+1; + TT.bytes += sizeof(char *)+strlen(data)+1; if (TT.max_bytes && TT.bytes >= TT.max_bytes) return data; if (TT.max_entries && TT.entries >= TT.max_entries) return (char *)1; @@ -112,6 +114,16 @@ void xargs_main(void) int entries, bytes, done = 0, status; char *data = NULL, **out; pid_t pid; + long posix_max_bytes; + + // POSIX requires that we never hit the ARG_MAX limit, even if we try to + // with -s. POSIX also says we have to reserve 2048 bytes "to guarantee + // that the invoked utility has room to modify its environment variables + // and command line arguments and still be able to invoke another utility", + // though obviously that's not really something you can guarantee. + posix_max_bytes = sysconf(_SC_ARG_MAX) - environ_bytes() - 2048; + if (TT.max_bytes == 0 || TT.max_bytes > posix_max_bytes) + TT.max_bytes = posix_max_bytes; if (!(toys.optflags & FLAG_0)) TT.delim = '\n'; |