LWN.net Logo

File descriptor handling changes in 2.6.27

File descriptor handling changes in 2.6.27

Posted Aug 6, 2008 22:13 UTC (Wed) by quotemstr (subscriber, #45331)
In reply to: File descriptor handling changes in 2.6.27 by njs
Parent article: File descriptor handling changes in 2.6.27

the nominal benefit of close-on-exec is that it allows locality of control -- the code that creates the fd is (often) the code that is best prepared to know whether it should be kept local to the process or not.
I disagree with the locality of control argument. In a well written program, any code which creates a file descriptor to be inherited across an exec boundary ought to be intimately tied to that exec: consider shell pipeline setup. A piece of code unrelated to that exec (say, X11, or the DNS resolver) should not expect its file descriptors to propagate across an exec.

However, not all libraries will hygienically mark their internal file descriptors as close-on-exec. So, in a well-written program:

  1. Code unrelated to an exec SHOULD mark internal file descriptors with O_CLOEXEC.
  2. fork/exec code MUST close all extraneous file descriptors, as not every library will obey rule #1


(Log in to post comments)

File descriptor handling changes in 2.6.27

Posted Aug 7, 2008 6:48 UTC (Thu) by njs (guest, #40338) [Link]

So it sounds like you're arguing that close-on-exec should be the default -- and if backwards
compatibility forbids it *actually* being default, then we should write code in such a way
that it becomes the default.  I tend to agree.  The original argument that I was responding
to, though, was suggesting that it didn't much matter if close-on-exec were broken, which
seems like the opposite of your point...

File descriptor handling changes in 2.6.27

Posted Aug 15, 2008 17:27 UTC (Fri) by sethml (subscriber, #8471) [Link]

How about a call similar to closefrom(), but which takes a list of fds not to close, and
closes 
all fds but those in the list?  This avoids the brain-dead assumptions about fd ordering which

closefrom() makes, but makes it easy to leave just a select few fds open for the child.  As
the 
parent comment points out, any code which relies on leaving fds other than stdout/stdin/
stderr open for the child probably knows exactly which fds the child will need.

File descriptor handling changes in 2.6.27

Posted Mar 2, 2011 20:53 UTC (Wed) by nybble41 (subscriber, #55106) [Link]

There are perfectly legitimate situations in which the code which calls exec() may not know which file descriptors need to be open. For example, let's say you have a shell script which takes a filename parameter and passes it to some other executable for processing. You run it as follows: "./my_script <(some_cmdline)". This causes the (bash) shell to create a pipe file descriptor, say FD 3, and pass it as "/dev/fd/3" to the script. In order for this to work, the script *must* preserve FD 3 when calling exec() for the lower-level executable so that the child's open() call can access the original descriptor. However, without knowing how the script will be called there is no way to know that FD 3 will even be open, much less that the executable will need access to it after the exec() call.

This can come up not only in shell scripts, but in any case where you might pass a filename received on the command-line to a child process. I would say that the current Linux model of marking file descriptors as "current process only" or "inheritable" in the open() call is the correct one, apart from the choice of default. Once an FD has been designated for use by child processes it should remain open by default across fork()/exec() calls, unless there is a compelling reason to close it. (I would, however, be in favor of a safe and simple way to explicitly close all but a designated set of descriptors without performing a close() syscall for potentially millions of possible FDs.)

File descriptor handling changes in 2.6.27

Posted Mar 2, 2011 21:12 UTC (Wed) by foom (subscriber, #14868) [Link]

That's not true: bash knows (or could easily keep track of) which fds the shell script it's running has requested be opened, and thus which it should pass to future executed programs. (since the semantics of the shell scripting language are that all the opened fds get passed to all programs you run).

That doesn't imply that all *other* non-shell-script-requested exec that get called from bash (e.g. execing a program from a NSS plugin) should also pass those same FDs! The right place really would've been for the list to be specified in exec. But...it's too late for that.

File descriptor handling changes in 2.6.27

Posted Mar 2, 2011 23:18 UTC (Wed) by nybble41 (subscriber, #55106) [Link]

The top-level interactive instance of bash knows, but the script *doesn't*, at least not without parsing the filename it was given. (I assume you agree that it would normally be a bad idea for programs to assign meaning to specific filename patterns?)

The first exec() is not the problem; as you say, bash knows that it opened a certain FD to pass to the script and would avoid closing it. The issue arises when the script tries to pass the /dev/fd/N filename it received to some other command. If the script closes all the file descriptors apart from stdin/stdout/stderr and any others *it* knows about--which would not include the FD opened by its parent process--the child process will either receive an error, or even duplicate an unrelated FD, when attempting to open the original path.

Keep in mind that this is a simple case; there could be any number of levels of fork()/exec() between that interactive session and the actual user(s) of the /dev/fd/N path; only the first is likely to be aware of the need to preserve the associated file descriptor.

I agree that there are cases (such as your NSS helper example) where it makes sense to close most or all file descriptors between fork() and exec(). However, at the very least, any time you pass on a filename received directly or indirectly from a parent process you should also pass on any file descriptors which were open when your process was started; anything less risks breaking the ability to use <(...) or >(...) from the shell in place of a regular file (among other uses).

File descriptor handling changes in 2.6.27

Posted Mar 2, 2011 23:40 UTC (Wed) by foom (subscriber, #14868) [Link]

Ah, indeed. I had forgotten about that evil little non-portable hack. Well, if you instead use the temporary fifo implementation of <() (which bash already supports), you won't have that problem. And since we're talking in hypotheticals here (it's not like exec is actually going to change), I declare that a perfectly acceptable solution to the issue.

Copyright © 2013, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds