On Friday 21 March 2008 10:47:27 am Kurt Miller wrote:
> On Friday 21 March 2008 6:25:59 am Philip Guenther wrote:
> > On Fri, Mar 21, 2008 at 1:44 AM, Tvrvk Edwin <[EMAIL PROTECTED]> wrote:
> > > Philip Guenther wrote:
> > >  > On Thu, Mar 20, 2008 at 3:01 PM, Tvrvk Edwin <[EMAIL PROTECTED]>
> > wrote:
> > >  >>  ClamAV has changed to call fork() after creating its local socket.
> > >  >>  This causes weird behaviours when communicating on the socket [1]
> > >  >>
> > >  >>  If fork() is called before creating the socket() it works.
> > >  >>
> > >  >>  Is it safe to create a socket, fork(), and then call pthread_create()
> > >  >>  and read from the socket?
> > ...
> > >  fork() is used to daemonize the process.
> > 
> > Okay, it's a bug in libpthread.  The user-space thread implementation
> > of libpthread sets all the file descriptors to be non-blocking
> > (O_NONBLOCK) so that it can catch blocking I/O and perform a context
> > switch to another thread when that happens.  This is hidden from the
> > application itself: if a threaded app calls fcntl(fd, F_GETFL), the
> > library will hide the O_NONBLOCK flag unless the app actually called
> > fcntl() to set it.  When a process exits, the library resets the fds
> > back to blocking if they were only non-blocking for the library; this
> > is so that other, non-threaded apps don't get confused when /dev/tty
> > is left non-blocking, and things like that.
> > 
> > That last bit is the catch: when the parent exits after calling fork,
> > the socket is reset to blocking and the child never sets it back to
> > non-blocking again.
> 
> Your analysis is correct. 
> 
> > It's not clear to me how the child can reliably detect that this has
> > occurred.  It could use a kqueue/kevent to detect when its parent has
> > exited, but the reset could just as well be done by the child's
> > grandparent (consider a double-fork daemonize).
> 
> Indeed this is a nasty limitation of userland threads that doesn't have
> an easy solution.
> 
> > As a gross kludge, I think things would "work" if you added the
> > following to the code called by the child after the fork.
> >    sleep(1); /* make sure the parent has a chance to exit */
> >    fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) & ~O_NONBLOCK);
> > 
> > (With the correct 'sockfd' variable, of course).  That'll bring the
> > kernel's O_NONBLOCK flag on the socket in sync with what libpthread
> > thinks it should be.
> 
> To avoid the race in the child (and the sleep() call), the parrent can set the
> fd's to non-blocking *before* the fork() and the child can set them back to
> blocking directly after the fork. Obviously not ideal but it will work-around
> the problem effectively.

After some more thought, a simpler solution would be to just have the parrent
set the fd's to non-blocking directly before the exit() call.

-Kurt

Reply via email to