[Python-Dev] fork or exec?

Charles-François Natali neologix at free.fr
Thu Jan 10 11:35:29 CET 2013


> So, I read your e-mail again and I'm wondering if you're making a logic
> error, or if I'm misunderstanding something:
>
> 1. first you're talking about duplicate file or socket objects after
> *fork()* (which is an issue I agree is quite annoying)
>
> 2. the solution you're proposing doesn't close the file descriptors
> after fork() but after *exec()*.
>
> Basically the solution doesn't address the problem. Many fork() calls
> aren't followed by an exec() call (multiprocessing comes to mind).

Yes.
In this specific case, the proper solution is to close the server
socket right after fork() in the child process.

We can't do anything about file descriptors inherited upon fork() (and
shouldn't do anything of course, except on an individual basis like
this socket server example).

On the other hand, setting file descriptors close-on-exec has the
advantage of avoiding file descriptor inheritance to spawned
(fork()+exec()) child processes, which, in 99% of cases, don't need
them (apart from stdin/stdout/stderr). Not only can this cause subtle
bugs (socket/file not being closed when the parent closes the file
descriptor, deadlocks, there are several such examples in the bug
tracker), but also a security issue, because contrarily to a fork()ed
process which runs code controlled by the library/user, after exec()
you might be running arbitrary code.

Let's take the example of CGIHTTPServer:
"""
          # Child
          try:
              try:
                  os.setuid(nobody)
              except os.error:
                  pass
              os.dup2(self.rfile.fileno(), 0)
              os.dup2(self.wfile.fileno(), 1)
              os.execve(scriptfile, args, env)

"""

The code tries to execute a CGI script as user "nobody" to minimize
privilege, but if the current process has an sensitive file opened,
the file descriptor will be leaked to the CGI script, which can do
anything with it.

In short, close-on-exec can solve a whole class of problems (but does
not really apply to this specific case).

> On the other hand, the one widespread user of exec() after fork() in the
> stdlib, namely subprocess, *already* closes file descriptors by
> default, so the exec() issue doesn't really exist anymore for us (or
> is at least quite exotic).

See the above example. There can be valid reasons to use fork()+exec()
instead of subprocess.

Disclaimer: I'm not saying we should be changing all FDs to
close-on-exec by default like Ruby did, I'm just saying that there's a
real problem.


More information about the Python-Dev mailing list