That said, why is the obvious fix not simply "always populate argv[0] with pathname if called with NULL argv"?
That said, why is the obvious fix not simply "always populate argv[0] with pathname if called with NULL argv"?
> Given the code we've found that depends on NULL argv, I think we likely can't make the change outright, so we're down this weird rabbit hole of trying to reject what we can and create work-around behaviors for the cases that currently exist.
(edit: found an answer here: https://lwn.net/ml/linux-kernel/5e963fab-88d4-2039-1cf4-6661...)
multicall programs will try to use argv[0] and might crash in this scenario. If we're going to fake an argv, I guess we should try to do it right.
It really isn't a kernel problem at all, of course.
The POSIX spec says "The first argument, argv[0], is required and must contain the name of the executable file for the new process image."[1] Linux is not fully POSIX-compliant, but it generally follows the POSIX standard for kernel calls defined in POSIX.
FreeBSD's man page says "At least one argument must be present in the array; by custom, the first element should be the name of the executed program (for example, the last component of path)."[2] So, "must", compatible with the POSIX spec.
QNX implements the POSIX API, pretty much per the spec. The QNX spec says: "The value in argv[0] must point to a filename that's associated with the process being started."[4] So, "must", again compatible with the spec.
The Linux man page says, unfortunately, "By convention, the first of these strings (i.e., argv[0]) should contain the filename associated with the file being executed."[3] That's the problem. POSIX says "must" but the Linux page only says "should". That was a mistake.
One somewhat kludgy fix would be to disallow argv[0] == NULL && envp[0] != NULL. That allows launching a program with no args at all, which might be used somewhere in some ancient program, while disallowing launch with no command line args but with environment args, which is probably a bug.
(I liked the QNX approach to program loading. The kernel isn't involved. The "exec" functions link to a shared object, which requests memory and does the loading using regular I/O operations. The kernel contains no loader and is thus immune to problems from malformed executables.)
[1] https://support.sas.com/documentation/onlinedoc/sasc/doc750/...
[2] https://manpages.debian.org/unstable/freebsd-manpages/execve...
[3] https://www.man7.org/linux/man-pages/man2/execve.2.html
[4] https://www.qnx.com/developers/docs/6.4.0/neutrino/lib_ref/e...
"The arguments represented by arg0,... are pointers to null-terminated character strings. These strings shall constitute the argument list available to the new process image. The list is terminated by a null pointer. The argument arg0 should point to a filename string that is associated with the process being started by one of the exec functions."
"The argument argv is an array of character pointers to null-terminated strings. The application shall ensure that the last member of this array is a null pointer. These strings shall constitute the argument list available to the new process image. The value in argv[0] should point to a filename string that is associated with the process being started by one of the exec functions."
[1] https://pubs.opengroup.org/onlinepubs/9699919799/functions/e...
I don't agree with your reading that argv being non-NULL is a "should."
The POSIX Specification does not say "The value in argv[0] should exist" it says "The value in argv[0] should point to a filename string..."
The value existing is a foundational assumption in the specification. It is therefore required.
(This is consistent with the fact that it's explicitly required that argv[argc] == NULL, which necessarily means that argv != NULL. Consider this: https://pastebin.com/1gieMqEd If argv is NULL, then pointer arithmetic on it and dereferencing it is undefined behavior, and cannot possibly conform to the guarantee.)
Edit: delete commentary
https://github.com/freebsd/freebsd-src/commit/773fa8cd136a57...
It's worth noting that as far as changes in FreeBSD forbidding long-standing permitted behavior goes, this one was notably very uncontroversial.
> The manpage has contained the following verbiage on the matter for just
> under 31 years:
>
> "At least one argument must be present in the array"
"Fixing this issue is a simple matter of making pkexec check that argc is at least one. But there are surely other programs out there containing similar assumptions."
That's _not_ an assumption. That's just a bug, in a setuid binary.
It's a bug that results from the developers incorrectly assuming that argc is always at least 1.
If the change fails at the kernel level but succeeds at the glibc level that might still be a net win overall, and given Ariadne as "instigator" here I'd be hopeful that she could convince musl to come along for the ride if that was how it had to happen. (assuming she thought that was a good idea in the first place, of course, I'm exploring counterfactuals here)
Otoh if you want libc to be possibly a little too clever, perhaps it could make an effort to try to discover a sensible value for argv[0] based on looking around /proc etc to find the path to the binary. That seems like way too much complexity and failure points for a few mostly inconsequential corner cases.