Why can the execve system call run "/bin/sh" without any argv arguments, but not "/bin/ls"?
Asked Answered
R

1

12

I am confused with the syscall of __NR_execve. When I learn linux system call. The correct way that I know to use execve is like this:

char *sc[2]; 
sc[0]="/bin/sh"; 
sc[1]= NULL; 
execve(sc[0],sc,NULL); 

Then the function execve will call syscall() to get into system kernel with putting the arguments on Registers EAX, EBX, ECX and EDX. However, It still succeed if I use

execve("/bin/sh",NULL,NULL);

But if I replace "/bin/sh" with "/bin/ls",it fail with:

A NULL argv[0] was passed through an exec system call.

I wonder why "/bin/sh" can be executed successfully without enough parameters while "/bin/ls" fail?

Rapscallion answered 17/4, 2016 at 7:23 Comment(6)
I'd not rely on NULL working at all there; the execve man page says that the argv is an array of argument strings; a NULL pointer is not a valid pointer to an array. (Neither does using NULL for env look good; rather, use execv or execvp, or pass a pointer to char *p = NULL.)Sanfred
Arrays are pointers to its first element when passed as functions arguments, so NULL is certainly valid here.Afflictive
@AnttiHaapala: Linux's execve(2) does accept a NULL pointer, and treats it as a pointer to an empty list. This is discouraged and not portable, but is future-proof on Linux specifically. The main use case I've seen is for exploit shellcode that zeros a couple registers before a system call (x86 int 0x80 or syscall), instead of pushing a 0 and getting pointers to that into two registers. i.e. it saves a few bytes of exploit payload size, and doesn't require writing to memory if the "/bin/sh" string is already in the payload.Amblygonite
@petercordes good that the hellcodez stay compatibleSanfred
But yeah, the man pages do mention this exceptionSanfred
@AnttiHaapala: I know, right. If the only thing it would break is existing exploits for sys_execve to return -EFAULT there, it's not really a downside. But presumably there's some valid use-case, like maybe passing an empty environment, that some code uses. Or maybe it's easier / cleaner in the kernel implementation to accept that than reject. Although it does check early enough that it could easily return an error, unlike some later errors that result in the process dying with a signal or something, because the caller is already partially destroyed so execve can't return.Amblygonite
A
16

This is not a kernel issue. The kernel will run with the filename arg of execve regardless of whether argv and envp are NULL or not. It is just a Unix convention that argv[0] points to the program name.

And what you saw is just normal, i.e. nothing is wrong. Because ls is part of GNU's coreutils, and all programs in the coreutils package call set_program_name to do some setup work. You can see this in the source: it checks whether argv[0] is NULL, and it will call abort when it is.

On the other hand, /bin/sh is apparently a program that does not belong to coreutils, and does not check against argv[0]. That's why it runs without the problem.

Refer to the source code:

Afflictive answered 17/4, 2016 at 8:37 Comment(1)
And here is a minimal example without any argv: https://mcmap.net/q/175168/-when-can-argv-0-have-nullJamikajamil

© 2022 - 2024 — McMap. All rights reserved.