This is the mail archive of the mailing list for the glibc project.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH v2 1/3] posix: Remove dynamic memory allocation from execl{e,p}

On Tue, Feb 02 2016, Rich Felker <> wrote:

> On Mon, Feb 01, 2016 at 04:52:15PM +0000, Joseph Myers wrote:
>> On Mon, 1 Feb 2016, Adhemerval Zanella wrote:
>> > +  char *argv[argc+1];
>> > +  va_start (ap, arg);
>> > +  argv[0] = (char*) arg;
>> > +  for (i = 1; i < argc; i++)
>> > +     argv[i] = va_arg (ap, char *);
>> > +  argv[i] = NULL;
>> I don't see how you're ensuring this stack allocation is safe (i.e. if 
>> it's too big, it doesn't corrupt memory that's in use by other threads).  
> There's no obligation to. If you pass something like a million
> arguments to a variadic function, the compiler will generate code in
> the caller that overflows the stack before the callee is even reached.
> The size of the vla used in execl is exactly the same size as the
> argument block on the stack used for passing arguments to execl from
> its caller, and it's nobody's fault but the programmer's if this is
> way too big. It's not a runtime variable.

This is true, and maybe it's not worth the extra complication, but if
we're willing to make arch-specific versions of execl and execle we can
avoid the double stack use and the time spent copying the argv
array. That won't remove the possible stack overflow, of course, but
then it'll in all likelihood happen in the user code and not glibc.

On i386 it's pretty straight-forward since all args are already passed
on the stack, while on x86-64 we can, roughly speaking, make a local
change of the ABI by just stashing the return address somewhere else so
that we can prepend the few passed-by-register to the stack-passed
parameters. I suppose something similar is possible on most other

Something like this passes some quick testing (nevermind the z_ prefix;
I just used that to avoid any clashes with the actual functions).


.globl z_execl ; .align 4,0x90 ; z_execl:
	pushl z_environ
	lea 0x0c(%esp), %eax
	push %eax
	pushl 0x0c(%esp)

	call z_execve

	add $0x0c, %esp
.type z_execl, @function ; .size z_execl, .-z_execl

.globl z_execle ; .align 4,0x90 ; z_execle:
	lea 0x0c(%esp), %eax // eax = address of first vararg (&argv[1])
	movl (%eax), %edx // we'll test this
1:	add $0x4, %eax // move on to the next word
	test %edx,%edx // have we found NULL?
	movl (%eax), %edx // load the next word into edx
	jnz 1b
	pushl %edx
	lea 0x0c(%esp), %eax // same offset as above, but because of the push this is now &argv[0]
	push %eax
	pushl 0x0c(%esp)

	call z_execve

	add $0x0c, %esp
.type z_execle, @function ; .size z_execle, .-z_execle


.globl z_execl  ; .align 4,0x90 ; z_execl:
	movq (%rsp),%rax // rax is caller-saved and dead at this point
	sub $0x28, %rsp // Only allocates room for five pointers, we store six!
	movq %rax, (%rsp) // Stash the return address here.
	movq %rsi, 0x8(%rsp)  // Put the paramaters passed via registers
	movq %rdx, 0x10(%rsp) // at the bottom of the array.
	movq %rcx, 0x18(%rsp)
	movq %r8,  0x20(%rsp)
	movq %r9,  0x28(%rsp) // Overwrites return address, adjacent to stack-passed parameters (arg 7+)

	lea 0x8(%rsp),%rsi // Put the address of our argv array in rsi.
	movq z_environ, %rdx // Third argument is the global environment.
	call z_execve // %rdi is unchanged since the beginning

	// If all goes well, this never returns. Otherwise, we need to clean up.
	// %rax should be preserved, but we're free to use any other caller-saved register.
	movq (%rsp), %r8 // Fetch the return address.
	add $0x28, %rsp // Clean up the stack.
	movq %r8, (%rsp) // Put the return address back where it belongs.
.type z_execl, @function ; .size z_execl, .-z_execl

.globl z_execle ; .align 4,0x90 ; z_execle:
	movq (%rsp),%rax
	sub $0x28, %rsp
	movq %rax, (%rsp)
	movq %rsi, 0x8(%rsp)
	movq %rdx, 0x10(%rsp)
	movq %rcx, 0x18(%rsp)
	movq %r8,  0x20(%rsp)
	movq %r9,  0x28(%rsp)

	// Find the env argument; it is the array element after the
	// argv NULL pointer, which cannot be located before argv[1].
	lea 0x10(%rsp),%r8
	movq (%r8), %rdx
1:	add $0x8, %r8
	test %rdx, %rdx
	movq (%r8), %rdx
	jnz 1b
	lea 0x8(%rsp),%rsi
	call z_execve

	movq (%rsp), %r8 
	add $0x28, %rsp
	movq %r8, (%rsp)
.type z_execle, @function ; .size z_execle, .-z_execle

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]