OSDN Git Service

Fix xargs to obey POSIX's ARG_MAX restrictions.
authorElliott Hughes <enh@google.com>
Wed, 20 Sep 2017 20:53:23 +0000 (13:53 -0700)
committerRob Landley <rob@landley.net>
Sun, 1 Oct 2017 21:55:06 +0000 (16:55 -0500)
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

lib/lib.c
lib/lib.h
toys/posix/xargs.c

index a4b7229..70ad075 100644 (file)
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -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;
+}
index a9a92fd..889430b 100644 (file)
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -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
index b4cb80a..e7dd10b 100644 (file)
@@ -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';