Bug 8408 - execve() wrongly permits argv and envp to be NULL
execve() wrongly permits argv and envp to be NULL
Status: REJECTED DOCUMENTED
Product: Process Management
Classification: Unclassified
Component: Other
i386 Linux
: P2 normal
Assigned To: Alan
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2007-04-30 03:34 UTC by Michael Kerrisk
Modified: 2008-10-02 07:21 UTC (History)
4 users (show)

See Also:
Kernel Version: 2.6.20 and all earlier kernels
Tree: Mainline
Regression: ---


Attachments

Description Michael Kerrisk 2007-04-30 03:34:47 UTC
Most recent kernel where this bug did *NOT* occur: present in all kernels in
2.6.x and at least as far back as 2.2.
Distribution: all
Hardware Environment: all
Software Environment: all

Problem Description:
The current Linux implementation of execve() permits the argv and envp arguments
to be NULL, with the same meaning as if they were pointers to lists consisting
of a single null pointer.  I.e., the following are equivalent:

char *envp[] = { NULL };
execve(path, argv, envp);

and

exceve(path, argv, NULL);

The second form is non-standard -- SUSv3 requires argv and envp each to be a
NULL-terminate array of pointers to character strings.  On most other Unix
systems (e.g., tested FreBSD 6.1 and Solaris 8), the second of the above forms
will cause execve() to fail with the error EFAULT.  (However, HP-UX 11 behaves
the same as Linux -- but then HP-UX is among the least conformant of the
traditional systems.)  Linux should do the same as FreBSD and Solaris.  

However, changing Linux to conform will result in an ABI change for userland.

The change required is in fs/exec.c, in the function do_execve().  This function
should include a check of the form:

if (argv == NULL || envp == NULL)
    return -EFAULT;


Steps to reproduce:
The following program should fail in execve() with the error EFAULT, but on
Linux it succeeds.

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


int
main(int argc, char *argv[])
{
    execve("/bin/echo", NULL, NULL);
    perror("execve");   
    exit(EXIT_FAILURE);
}
Comment 1 Natalie Protasevich 2007-08-26 11:59:55 UTC
Probably the best would be to post this bug on LKML, for general discussion on the issue. We definitely need a category for issues of this kind.
Placing it in process management for now.
Thanks.
Comment 2 Alan 2007-09-27 05:37:43 UTC
This isn't a kernel bug. If address zero is mapped then it is valid for it to contain environment or argument. On systems where page zero isn't mapped it'll fault as expected.

NULL is a valid pointer in some cases, it points to a page which contains NULL as its start so happens to be a valid argument vector, by chance.
Comment 3 Michael Kerrisk 2008-06-12 02:41:40 UTC
(In reply to comment #2)
> This isn't a kernel bug. If address zero is mapped then it is valid for it to
> contain environment or argument. On systems where page zero isn't mapped it'll
> fault as expected.
> 
> NULL is a valid pointer in some cases, it points to a page which contains NULL
> as its start so happens to be a valid argument vector, by chance.
> 

I missed the closure of this bug.

As initially formulated, my bug report was faulty.  Indeed it seems that most systems permit envp to be NULL (I tested FreeBSD and Solaris), with the meaning that the environment list for the new program is empty.

However, my report is correct regarding argv: most other systems (e.g., FreeBSD, Solaris) fail the execve() with EFAULT in the example below.  (The man pages on FreeBSD and Solaris are explicit in specifying that at least one argument must be supplied in argv.)

Alan, your comments about NULL as a pointer to a mapping at page 0 are bogus.  Whether or not there is mapping there does not even come into play, because in fs/exec.c, use is made of this function to check argv and envp:

 static int count(char __user * __user * argv, int max)
 {
         int i = 0;
 
         if (argv != NULL) {
               ...
         }
         return i;
 }

Given a count of 0, copy_strings() then copies no arguments, providing behavior as though (argv != NULL && argv[0] == NULL).

Linux should be giving -EFAULT here, for consistency with other systems.

=====

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

int
main()
{

    char *argv[10];

    argv[0] = "date";
    argv[1] = NULL;
    execve("/bin/date", NULL, NULL);
    perror("execve");
    exit(EXIT_FAILURE);
}
Comment 4 Alan 2008-10-02 07:21:53 UTC
Well the spec doesn't say what has to happen if you pass invalid values (-EFAULT is just the correct error if we error it for this).

It is an ABI change and that makes it seem rather excessive for a philosophical point upon which no app will depend on receiving -EFAULT in normal usage, but old Linux code might use NULL NULL

So I still don't think we should fix it.

Note You need to log in before you can comment on or make changes to this bug.