[Toybox] [landley/toybox] xargs: exec ... Argument list too long (#40)

enh enh at google.com
Sat Aug 13 14:36:36 PDT 2016


On Wed, Aug 10, 2016 at 1:08 PM, enh <enh at google.com> wrote:
> On Wed, Aug 10, 2016 at 11:28 AM, Rob Landley <rob at landley.net> wrote:
>> On 08/10/2016 07:44 AM, Matthias Urhahn wrote:
>>> On some Android devices (N5 at 6.0) using a chain of |find .... | xargs
>>> .... stat ....|, yields the error |Argument list too long|.
>>>
>>> Some googlfu shows that this is likely related to the argument exceeding
>>> the kernels ARG_MAX value.
>>> Androids ARG_MAX according to this 131072
>>> <https://github.com/android/platform_bionic/blob/master/libc/kernel/uapi/linux/limits.h>.
>>
>> The Linux kernel removed that limit in 2007
>> (https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=b6a2fea39318
>> went into the 2.6.22 release).
>>
>> That said, let's cd to a directory with a lot of files in it:
>>
>>   $ find . | wc
>>     68971   69024 3159459
>>   $ find . | toybox xargs echo
>>   xargs: exec echo: Argument list too long
>>
>> And I'm seeing a limit here too. Hmmm...
>>
>>> Manually limiting the xargs argument length using |-s bytes| fixes this
>>> for me.
>>
>> The kernel should not be enforcing this? Maybe something else is? There
>> isn't an obvious ulimit for this... Ah, in include/uapi/linux/limits.h:
>>
>>   #define ARG_MAX    131072 /* # bytes of args + environ for exec() */
>>
>> And that's used in fs/exec.c:
>>
>>   /*
>>    * We've historically supported up to 32 pages (ARG_MAX)
>>    * of argument strings even with small stacks
>>    */
>>   if (size <= ARG_MAX)
>>     return page;
>>
>>   /*
>>    * Limit to 1/4-th the stack size for the argv+env strings.
>>    * This ensures that:
>>    *  - the remaining binfmt code will not run out of stack space,
>>    *  - the program will have a reasonable amount of stack left
>>    *    to work from.
>>    */
>>   rlim = current->signal->rlim;
>>   if (size > ACCESS_ONCE(rlim[RLIMIT_STACK].rlim_cur) / 4) {
>>     put_page(page);
>>     return NULL;
>>   }
>>
>> Ok, so that's still there as a MINIMUM, and the maximum is 1/4 the
>> current stack size ulimit. According to ulimit -s that defaults to 8192
>> kilobytes (at least on xubuntu 14.04), so 1/4 of 8 megs is 2 megs. And...
>>
>>   $ find . | head -n 40000 | wc
>>     40000   40002 1853620
>>   $ find . | head -n 40000 | toybox xargs echo | wc
>>         1   40002 1853620
>>
>> Yup, the _actual_ default limit is way more than 131072, and that
>> default is arbitrarily adjustable even for normal users:
>>
>>   $ ulimit -Hs
>>   unlimited
>>   $ ulimit -s 9999999
>>   $ find . | toybox xargs echo | wc
>>         1   69023 3159436
>>
>> (Huh, what happens if I set the stack size limit to _less_ than 131072
>> and then try to exec stuff? Not trying it on my work netbook just now,
>> thanks...)
>>
>>> I've looked at the code for xarg in toybox but could not figure out yet
>>> how the max argument length is determined.
>>
>> It didn't enforce a default maximum length limit because as far as I
>> knew the kernel hadn't required one for 9 years. I worked out the math
>> for enforcing such a limit back when I first did xargs:
>>
>>   http://landley.net/notes-2011.html#17-12-2011
>>
>> So it's not hard to add a default, but what the default should be isn't
>> obvious. Back when I noticed the kernel had changed its logic I
>> _checked_ that I could feed it more than 131072 bytes, and I could, so I
>> removed the limit. (Not just from xargs, but from find in commit
>> aa784b09a9fb and possibly elsewhere.)
>>
>>> Busybox doesn't seem to run into this issue using the same command on
>>> the same device. Busybox seems to have some sanity checks
>>> <https://git.busybox.net/busybox/tree/findutils/xargs.c#n529>, adjusting
>>> the argument length if necessary.
>>
>> The sanity checks are there (-s is implemented), toybox just doesn't
>> have a default value if you don't specify one.
>>
>>> Could there be an issue with how toybox's |xarg| determines the default
>>> argument length on Android?
>>
>> See above.
>>
>> There's a sysconf(_SC_ARG_MAX), which does appear to be calculating this
>> properly (at least built against glibc, musl is giving me 131072 and I
>> still haven't got a host bionic toolchain because building bionic
>> outside of AOSP was nontrivial when I looked at it).
>
> yeah, bionic currently just returns ARG_MAX. looks lik glibc divides
> the stack by 4 as you describe above.
>
> running
>
> #include <unistd.h>
> int main() {
>  return sysconf(_SC_ARG_MAX)/1024;
> }
>
> under strace gets me
>
> getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
> exit_group(2048)                        = ?
>
>> Oh well, use the sysconf and then file a bug against musl. (Elliott will
>> probably notice this, and if not he'll notice the commit.)
>
> yeah, this is as good a bionic bug reporting forum as any :-)

fixed in AOSP. (took a while to kill code in the internal tree making
bad assumptions about ARG_MAX...)

>> Thanks for the heads up,
>>
>> Rob
>> _______________________________________________
>> Toybox mailing list
>> Toybox at lists.landley.net
>> http://lists.landley.net/listinfo.cgi/toybox-landley.net
>
>
>
> --
> Elliott Hughes - http://who/enh - http://jessies.org/~enh/
> Android native code/tools questions? Mail me/drop by/add me as a reviewer.



-- 
Elliott Hughes - http://who/enh - http://jessies.org/~enh/
Android native code/tools questions? Mail me/drop by/add me as a reviewer.



More information about the Toybox mailing list