[LeapList]Warning: ridiculously long PATH truncated

John Simpson leaplist@lists.leap-cf.org
Thu Aug 29 00:35:00 2002


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Wednesday 28 August 2002 16:51, Vernon Singleton wrote:
>
> What is/controls the magic number of characters which can be forced int=
o
> a PATH environment variable?
> Are there any quick tricks around that magic number?

funny... i've run into problems with long command lines before but always=
=20
assumed it was the shell that had a limit on the length of a command line=
=2E

however, your question made me curious about what the real answer is.

warning: MASSIVELY HARDCORE technical stuff follows. skip to the end if y=
ou=20
just want the answer and don't feel the urge to follow me on a trip throu=
gh=20
the kernel source code...



now that they're all gone... (=3D

i did some testing with tcsh (the shell that i normally use) setting an=20
environment variable (using "setenv") to a value of almost 300K of text. =
as=20
soon as i did this, EVERY command i ran died with "Argument list too long=
"...=20
but built-in shell commands still run with no problems.

it turns out bash does the same thing. setting a variable to a huge value=
=20
seems to work okay, but as soon as you "export" that variable (so child=20
processes inherit the value) no external programs work, all complaining o=
f=20
"Argument list too long".

the pattern? external commands can't execute, but anything totally within=
 the=20
shell works. looks like exec() is having issues, or it's something in the=
=20
initialization stuff for glibc.

a little digging about shows that the issue is how the kernel is compiled=
=2E=20
remember the thread about the maximum number of open files for a process =
or=20
for the system as a whole? those limits are controlled by the file=20
"include/linux/limits.h" in your kernel source code.

and guess what else is in there?

#define ARG_MAX         131072  /* # bytes of args + environ for exec() *=
/

maybe when the kernel handles exec() (to start running a new process), it=
=20
needs to set aside a certain amount of memory (in this case 128K) to hold=
 the=20
argument list and environment that the parent is passing to the child?

grepping thorugh the kernel's source only shows the only place this value=
 gets=20
used is in the sparc/sparc64 interface stuff (the extra hooks needed to r=
un=20
"normal" 32-bit programs on top of a 64-bit processor.)

however, looking through fs/exec.c (which is where the do_execve() functi=
on=20
actually lives) i find the function setup_arg_pages(), which seems to be=20
responsible for setting up the initial memory space for a new process. it=
=20
contains this line (fs/exec.c:300)

=09stack_base =3D STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE ;

MAX_ARG_PAGES and PAGE_SIZE are defined elsewhere in the kernel source co=
de-=20
on ix86 and sparc64 these values are 16 and 4096 respectively. this is=20
because both processors do paging using 4K page sizes.

remember that exec() OVERWRITES an existing process with the new one. the=
 only=20
things that get saved are the arguments and an environment (and a few oth=
er=20
miscellaneous items not involving memory.)

when exec() sets up the memory space for a new process, it has to put cer=
tain=20
things in certain places. the stack is located at the "top" of each proce=
ss=20
(i.e. the highest memory addresses.) the only "safe" place to put the arg=
s=20
and environment (to be sure the new process won't overwrite it by acciden=
t)=20
is above the top of the stack. (a stack starts at the top and works its w=
ay=20
down as it gets used.)

setup_arg_pages() reserves a block of memory at the very top of the new=20
process's memory space to hold the args and environment, and starts the s=
tack=20
just below this block. do_execve() needs to know how much memory is reser=
ved=20
so it can set the initial stack pointer correctly, and also because it ha=
s to=20
map the pages containing the stored args and environment into the top end=
 of=20
the new process's memory space (so the new process can read its args and=20
environment.)

to make sure i'm at least on the right track, i compiled this little prog=
ram=20
on both machines (i686 redhat 7.3, sparc64 gentoo, both running 2.4.18=20
kernels)

#include <stdio.h>
#include <stdlib.h>

int main ( int argc , char *argv[] , char *envp[] )
{
        int a , b ;
        void *c , *d ;

        c =3D malloc ( 1024 ) ;
        d =3D malloc ( 1048576 ) ;

        printf ( "argv=3D%08X\n" , argv ) ;
        printf ( "envp=3D%08X\n" , envp ) ;
        printf ( "&a=3D%08X\n" , &a ) ;
        printf ( "&b=3D%08X\n" , &b ) ;
        printf ( "c=3D%08X\n" , c ) ;
        printf ( "d=3D%08X\n" , d ) ;

        return 0 ;
}

the results:

(i686)
argv=3DBFFFF914
envp=3DBFFFF91C
&a=3DBFFFF8A4
&b=3DBFFFF8A0
c=3D080496E0
d=3D40014008

(sparc64)
argv=3DEFFFFCE4
envp=3DEFFFFCEC
&a=3DEFFFFC14
&b=3DEFFFFC10
c=3D00020660
d=3D70170008

this tells me that:

- - the i686 looks like it has a process limit of 3GB (highest address is=
=20
BFFFFFFF), and sparc64 has a 3.75GB limit (highest address EFFFFFFF)

- - the "c" and "d" addresses show that both libc's handle allocations of=
 small=20
and large blocks differently (this is a carry-on of something i discovere=
d at=20
the last installfest, on steve litt's beater box.)

- - the "a" and "b" addresses are on the stack. on both architectures the=
 stack=20
is LOWER THAN the argv/envp addresses.



SO THE SIMPLE ANSWER IS THIS: the limit isn't so much your PATH as your e=
ntire=20
environment block. for ix86 and sparc64 processors, the limit is 128K, an=
d it=20
looks like it's also 128K on all of the other architectures as well.

the only way around it looks like re-compiling your kernel, making sure t=
o=20
change ARG_MAX in "include/linux/limits.h" as well as MAX_ARG_PAGES where=
ver=20
it's defined (different for each processor type.) there's probably more t=
o it=20
than that, i'm not about to go and mess with it.

which brings up my question- what possible reason could you have for need=
ing=20
such a long PATH? did i miss something at the beginning of this thread?

- --=20
- ----------------------------------------
| John Simpson     Programmer at Large |
| <jms1@jms1.net> http://www.jms1.net/ |
- ----------------------------------------
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)

iD8DBQE9baQvEB9RczMG/PsRAngzAJ9EG5h8qJRBhd7Ysd9AlbWCTEo+YgCfbF6q
LYHsVd0VHG9sUgnm/NIQLhw=3D
=3DqJtt
-----END PGP SIGNATURE-----