So, if I've opened fd 16 and I'm about to run a sub-process which will use this new descriptor
(perhaps I thought to pass '16' as an argument to exec), then I should...
Use dup to copy fd 3 somewhere else, and ensure all of my code can cope with this, perhaps
by entirely replacing file descriptors and anything that uses them (FILE * etc.) with my own
private indirection...
Close the now unused fd 3 and replace it with a copy of fd 16 using dup2, then close that
too, incurring all the above problems again
Call closefrom(4)
Are you sure this is simpler than fixing the design by adding close-on-exec as a potential
property of all descriptors from birth ?
Your approach clearly solves only a fraction of the problem (it doesn't consider fork + exec
by sub-routines you didn't write, e.g. in libraries, which is the most serious problem CLOEXEC
fixes), yet it incurs most of the same costs as the fix that's already been chosen and pushed
into 2.6.27.
That's not to say that closefrom() isn't an interesting API, and one which might be welcome in
Linux, but just that it doesn't actually appear to be a simpler solution, just an incomplete
one.
Posted Aug 5, 2008 13:48 UTC (Tue) by mheily (guest, #27123)
[Link]
> Your approach clearly solves only a fraction of the problem
If by a "fraction", you mean 9/10ths of the problem.. The only fraction that Uli's solution
fixes that cannot be fixed using closefrom() is the case involving interaction with
proprietary plugins. I agree with an earlier poster; once you run a proprietary binary
program, you can never be sure that your data is safe. The possibility of leaking file
descriptors across an execve() call is the least of your worries.
The typical use case is a program calling fork() and then execve() to run an external program
that only needs access to stdin/stdout/stderr. Linux/GNU developers should optimize for making
it easy to close all other file descriptors. Instead, they have optimized for running
proprietary binary plugins. Yuck.
File descriptor handling changes in 2.6.27
Posted Aug 5, 2008 19:49 UTC (Tue) by nix (subscriber, #2304)
[Link]
closefrom() would only work if
1) it was a system call and thus could enforce atomicity
2) glibc took out a lock also taken by open(), dup(), et al, which means
yet more locking around those functions, harming performance
If you're adding a new system call anyway, why not adjust things so that
the *already existing* close-on-exec flag works properly, rather than
adding more band-aids atop the system to compensate for the unreliability
of the existing flag?
File descriptor handling changes in 2.6.27
Posted Aug 6, 2008 21:58 UTC (Wed) by quotemstr (subscriber, #45331)
[Link]
No!
Why would closefrom() need to be atomic? There is no race. After fork(), the new process only
has a *single* thread running.
Any race that required closefrom() to be atomic would also be a problem between the
closefrom() and the execve().
So no, closefrom does not need to be atomic.
File descriptor handling changes in 2.6.27
Posted Aug 6, 2008 22:37 UTC (Wed) by nix (subscriber, #2304)
[Link]
Yeah, sorry, missed that. Still *everyone* who forks off a child needs to
do it.
File descriptor handling changes in 2.6.27
Posted Mar 2, 2011 19:25 UTC (Wed) by gps (subscriber, #45638)
[Link]
But closefrom() does need to be async-signal-safe so that it can safely be called after a fork().
Posted Aug 5, 2008 19:53 UTC (Tue) by strcmp (guest, #46006)
[Link]
as tialaramex said, it is not only std* you want to inherit. you could want to inherit the
endpoints of a pipe, or some network sockets (plural...), but you may want to have the child's
output on the same tty for debugging purposes. as you don't always have control about the
order of fd-s (some gui library might open sockets depending on what the user clicked) you
will have to renumber painfully instead of just passing /dev/fd/1234 on the command line. and
even then your order of fd-s is just an implicit CLOEXEC flag, and as this flag already
existed it was simpler to just close the last holes than to implement a new, but more
primitive and more burdensome solution and still maintain the flag for backwards
compatibility.
File descriptor handling changes in 2.6.27
Posted Aug 6, 2008 9:16 UTC (Wed) by tialaramex (subscriber, #21167)
[Link]
I never mentioned proprietary plugins. We have these things called utility libraries. They use
fork+exec to run helper binaries among other reasons.
The utility library is not a trust boundary. If you think it is, then you've already screwed
up. However the resulting exec() is a trust boundary, the kernel provides for the executed
binary to receive different security privileges to the calling process.
If you think about it a little it's obvious that closefrom() isn't the appropriate interface
on its own because it requires you to export all the information about close-on-exec rules
into some arbitrary global structure and then have all utility libraries co-operate to use and
update that structure, whereas CLOEXEC pushes the relevant /security critical/ information
into each individual file descriptor. Sure enough the other systems you're talking about all
have CLOEXEC for exactly this reason. They just haven't fixed it yet and Linux has.
File descriptor handling changes in 2.6.27
Posted Aug 6, 2008 13:20 UTC (Wed) by mheily (guest, #27123)
[Link]
The addition of O_CLOEXEC to the open(2) system call is a good idea, and is part of the
solution to the problem of "secure file descriptor handling" as udrepper calls it. I didn't
mean to imply that the O_CLOEXEC flag is useless, or that closefrom(3) is the be-all-end-all
solution to the problem. They are both tools that programmers need to make software more
secure. My point is that closefrom() is simpler solution and is applicable to the majority of
the cases where fd leakage is a concern.
A utility library is the perfect example of where closefrom() is needed. Since a library runs
in the same process context as the program that it is linked against, it inherits all open
file descriptors after calling fork(2). These descriptors have meaning to the overall program,
but are meaningless to the library. The library should, on principle, close all unneeded
descriptors prior to calling execve(), but it cannot guarantee that the O_CLOEXEC flag has
been enabled on all of the descriptors. As an extra precaution, and a simple way of making
*certain* that there is no leakage, the library can call closefrom() to exclude an entire
range of descriptors.