You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Dean Gaudet <dg...@arctic.org> on 1998/05/10 23:19:05 UTC

threaded apps and default file descriptor flags (fwd)

Here's something I just sent to linux-kernel. 

I'm convinced that we may actually have DoS/security bugs right now --
suppose a module causes a db library to open up a connection to the
server, and this connection is kept open for the life of the child.  If
that module doesn't properly set up a cleanup or mark the db socket to be
closed on exec, then the CGI will have full access to the socket, and
could play fun games on the db.

Dean

---------- Forwarded message ----------
Date: Sun, 10 May 1998 14:03:29 -0700 (PDT)
From: Dean Gaudet <dg...@arctic.org>
To: linux-kernel@vger.rutgers.edu
Subject: threaded apps and default file descriptor flags
X-Comment: Visit http://www.arctic.org/~dgaudet/legal for information regarding copyright and disclaimer.

As I work more on the threaded port of Apache I'm starting to really curse
the default unix semantics on file descriptors.  In particular I want all
my descriptors (sockets, files, whatever) to be opened O_NONBLOCK, and to
be *non-inheritable across exec* (both are the opposite of the unix
defaults).  This is a long description of the problems and solutions,
fodder for folks thinking about linux 2.3.

Consider a busy threaded webserver, it could easily be pushing the 1024
limit of open file descriptors, and trying to fork CGIs left and right. 
AFAIK, my current choices for the exec() problem are: 

    - do fcntl(F_SETFD) on every file descriptor as it is created

    - remember all the open filehandles, and after fork() do close() on
	all the necessary ones

Apache currently implements the second option, because in a multiprocess
setting it's not very expensive -- there's typically a dozen or so
filehandles to close.  But in a threaded server it sucks because there
could be hundreds of filehandles to close, and these are allocated by
multiple threads so keeping a list of them is painful -- my threads don't
have to synchronize for very many things at all, and I don't want to add
this to the list. 

The first option totally sucks, enough said. 

I took a brief look at Solaris man pages, because I know Sun has been
doing heavy threading work for some time, but I couldn't see any solution
to these problems.  Maybe someone else knows of something like this in
other unixes.  The default on WIN32 is for opened files/sockets to be
"close-on-exec" (excuse the abuse of terminology). 

I essentially want to be able to set two flags for the process.  Maybe as
parameters to personality(), doesn't matter to me where, I just care about
the semantics.  Like this: 

    NONBLOCK_DEFAULT
	If set, all descriptors created in this process will be created
	with O_NONBLOCK set.  This includes descriptors created by open(),
	dup() (and F_DUPFD), accept(), socket(), socketpair(), and pipe().
	This flag is inherited across clone()/fork(), but is cleared on
	exec().

    CLOEXEC_DEFAULT
	If set, all descriptors created in this process will be created
	with FD_CLOEXEC set.  This includes descriptors created by open(),
	dup() (and F_DUPFD), accept(), socket(), socketpair(), and pipe().
	This flag is inherited across clone()/fork(), but is cleared on
	exec().

Note that socketpair() and pipe() are generally only of use when creating
children to read/write them.  It's almost always the case that you'll have
to remove O_NONBLOCK or FD_CLOEXEC on one of the two filedescriptors
created.  But that's the same amount of work you'd have to do if you had
to add those flags to the other descriptor... and so for consistency I'd
rather have to remove the flags. 

dup() is almost always used in a similar setting and could easily clear
O_NONBLOCK or FD_CLOEXEC.  I don't care either way -- I just suggested it
for consistency. 

F_DUPFD, on the other hand, has some very specific uses that dup() doesn't
get used for.  We use it in Apache to guarantee that there are a few
descriptors in the 3..15 range available for use by 3rd party libraries
(the ones that generally have pre-compiled FD_SETSIZE limitations or other
such nonsense).  In multithreaded apache this isn't even feasible, and I'm
just not worrying about it.  But again... I'm just trying to make the
above flags as consistant as possible. 

The last paragraph gives us a hint as to why this interface is completely
broken: 3rd party libraries (remember that the Apache which we "ship"
can be linked against anything by apache module developers, I have to
worry about how things will behave in that situation).  It's unlikely,
but definately impossible, for a 3rd party library to care about the
FD_CLOEXEC flag.  On the other hand, it's very likely to be confused
by O_NONBLOCK.

So perhaps this draconian process-wide option isn't the best.
Let's consider the other end of the scale.  I really only need this
functionality on sockets created by accept(), and files opened by open().
open() already has O_NONBLOCK, it only needs O_CLOEXEC, and I'd be happy.
Then for accept() if there were two flags I could set via fcntl() --
O_INHERIT_NONBLOCK, and O_INHERIT_CLOEXEC, which affected the new socket
created, that would satisfy my needs.  Like I said, for pipe()s I already
have to modify one half of the pipe.

This solution allows 3rd party libraries to get the default semantics
on stuff they open() or accept(), while apache itself gets the semantics
it needs.  But it's unlikely that a 3rd party library is going to mark a
descriptor as close-on-exec... and I just realised that apache probably
already has problems with CGIs getting 3rd party library descriptors to
play with.  I bet there are some DoS attacks and even worse possible right
now because a socket to a database remains open across an exec() of a CGI.

There are two cases to consider:

    - apache itself does the exec(), in which case it knows that everything
	it opened will be closed because it used O_INHERIT_CLOEXEC or
	O_CLOEXEC.  But it doesn't know if a 3rd party library opened
	something -- if it did then it almost certainly shouldn't be
	inherited.

    - a 3rd party library does the exec().  In which case it knows only about
	stuff it opened -- and will likely do the right thing with those.
	If apache used O_INHERIT_CLOEXEC or O_CLOEXEC then those, too,
	will be handled properly.

I'm willing to argue that 3rd party libraries (or the apache modules which
invoke them) should handle their own descriptors' FD_CLOEXEC flags.

So I think this second interface is the most useable.

Dean