[Python-Dev] Weird error handling in os._execvpe

Guido van Rossum guido@python.org
Thu, 01 Aug 2002 15:27:43 -0400


> > > Can anyone explain why it is done this way?
> > 
> > Because not all systems report the same error for this error condition
> > (attempting to execute a file that doesn't exist).
> 
> That's unfortunate.  The existing code is buggy on at least three
> grounds:
> 
> First and most important, it's currently trivial to cause any program
> that uses os.execvp[e] to invoke a program of the attacker's choice,
> rather than the intended one, on any platform that supports symbolic
> links and has predictable PIDs.  My tempfile rewrite will make this
> much harder, but still not impossible.

That's important.

> Second, the BeOS code will silently delete the file '/_#.# ## #.#' if
> it exists, which is unlikely, but not impossible.  A user who had
> created such a file would certainly be surprised to discover it gone
> after running an apparently-innocuous Python program.

I really don't care about that. :-)

> Third, if an error other than the expected one comes back, the loop
> clobbers the saved exception info and keeps going.  Consider the
> situation where PATH=/bin:/usr/bin, /bin/foobar exists but is not
> executable by the invoking user, and /usr/bin/foobar does not exist.
> The exception thrown will be 'No such file or directory', not the
> expected 'Permission denied'.

Hm, you're right.  The code (which I believe I wrote, except for the
BeOS bit) was attempting to get the opposite effect, but seems to be
broken. :-(

> Also, I'm not certain what will happen if two threads go through the
> if not _notfound: block at the same time, but it could be bad,
> depending on how much implicit locking there is in the interpreter.
> 
> I see three possible fixes.  In order of personal preference:
> 
> 1. Make os.execvp[e] just call the C library's execvp[e]; it has to
>    get this stuff right anyway.  We are already counting on it for
>    execv - I would be surprised to find a system that had execv and
>    not execvp, as long as PATH was a meaningful concept (it isn't, for
>    instance, on classic MacOS).

Probably agreed for execvpe().  All the non-env versions must call the
env version because not all platforms have putenv, and there changes
to os.environ don't get reflected in the process's environment.

> 2. Enumerate all the platform-specific errno values for this failure
>    mode, and check them all.  On Unix, ENOENT and arguably ENOTDIR.  I
>    don't know about others.
> 
> 3. If we must do the temporary file thing, create a temporary
>    _directory_; we control the contents of that directory, so we can
>    be sure that the file name we choose does not exist.  Cleanup is
>    messier than the other two possibilities.

I like to agree with this, but I don't recall exactly why we ended up
in this situation in the first place.  It's possible that it's an
unnecessary sacrifice of a dead chicken, but it's also possible that
there are platforms where this addressed a real need.  I'd like to
think that it was because I didn't want to add more cruft to
posixmodule.c (I've long given up on that :-).

Can you post a patch to SF?  Then we can ask for volunteers to test it
on various platforms.

--Guido van Rossum (home page: http://www.python.org/~guido/)