[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Server recurses and cores on unexpected client connection loss

From: David Sainty
Subject: Re: Server recurses and cores on unexpected client connection loss
Date: Tue, 30 Apr 2002 11:57:43 +1200

Some more notes:

It's probably a mistake to NULL the global pointers and not use buf_free() in 
my patch.  On the other hand, I don't think historically buf_free() has ever 
been called in the cleanup path.  My patch is consistent with this :)

It isn't entirely clear what the interface to the buffers is intended to be, 
but if a buffer is still meant to be "valid" after a buf_shutdown(), another 
approach would be to change buf_shutdown() so that it can be called multiple 
times without crashing.  An obvious way to do this is to NULL all the virtual 
function pointers once buf_shutdown() has completed.

If the above approach is taken (change the buffering API to be more forgiving), 
then my patch below doesn't need to be applied.  On reflection, I think I 
prefer this approach, because the buf_shutdown() method is not intended to 
destroy the object - so the object should remain "valid", and therefore it 
doesn't seem unreasonable to me that repeated buf_shutdown() calls should 
silently succeed.



>>> <david.sainty@ird.govt.nz> 29/04/2002 22:52:45 >>>

>Submitter-Id:   net
>Originator:     David Sainty
>Confidential:  no
>Synopsis:      Server recurses and cores on unexpected client connection loss
>Severity:      serious
>Priority:      medium
>Category:      cvs
>Class:         sw-bug
>Release:       1.11.2
System: SunOS dotp01 5.8 Generic_108528-03 sun4u sparc SUNW,Ultra-5_10
Architecture: sun4


        CVS (questionably?) traps SIGPIPE and uses this to run various
        shutdown operations.  One of those operations is to call
        server_cleanup() [association added in server.c].  Another operation
        is to call main_cleanup() [association added in main.c].  However,
        main_cleanup() calls error(), which calls error_exit(), which calls

        The (amusing, but painful) sequence of events is:

        1. client drops connection unexpectedly
        2. server receives SIGPIPE
        3. server_cleanup() is called, calls
        4. main_cleanup() is called, server_cleanup() is called, calls
             stdio_buffer_shutdown(buf_from_net).  'stdin' is closed.
        5. stdio_buffer_shutdown() does an assert [this is also a bug, asserts
           can be compiled out] on a positive result from fstat('stdin').
        6. fstat() fails on closed file descriptor, assert generates a
        7. SIGABORT is trapped, and also calls server_cleanup() and
        8. Process repeats 4554 times, at which point the stack is exhausted.
           Process cores.

        It appears to be consistently repeatable simply by doing a 'cvs -d
        :pserver:user@host:/cvsroot/repository update -dP', and hitting ctrl-C
        in the middle of processing.

        The following patch avoids the core, and hasn't really been tested.
        However it looks closer to intention, I think that it can't really be
        any "worse" than current...

        Some points:

        - I only caught the cvs_outerr() change because it causes a core if
          omitted.  There may be other places where cvs is (erroneously)
          continuing to use buffers after they are potentially closed.

        - The assert() in buffer.c should be broken out to (at the VERY least)
          an if/abort.

        - Perhaps the SIGPIPE stuff could die entirely, it isn't the nicest
          way to handle a network connection shutdown cleanup :)

--- src/server.c.orig   Mon Apr 29 21:37:39 2002
+++ src/server.c        Mon Apr 29 22:43:07 2002
@@ -4885,11 +4885,17 @@
         * generated when the client shuts down its buffer.  Then, after we
         * have generated any final output, we shut down BUF_TO_NET.
-       status = buf_shutdown (buf_from_net);
-       if (status != 0)
+       if (buf_from_net != NULL)
-           error (0, status, "shutting down buffer from client");
+           /* Temporary used to protect against potential reentry */
+           struct buffer *tmp_buf_from_net = buf_from_net;
+           buf_from_net = NULL;
+           status = buf_shutdown (tmp_buf_from_net);
+           if (status != 0)
+           {
+               error (0, status, "shutting down buffer from client");
+           }
@@ -4898,7 +4904,13 @@
        if (buf_to_net != NULL)
            (void) buf_flush (buf_to_net, 1);
-           (void) buf_shutdown (buf_to_net);
+           {
+               /* Temporary used to protect against potential reentry */
+               struct buffer *tmp_buf_to_net = buf_to_net;
+               buf_to_net = NULL;
+               (void) buf_shutdown (tmp_buf_to_net);
+           }
@@ -4998,8 +5010,13 @@
     if (buf_to_net != NULL)
+       /* Temporary used to protect against potential reentry */
+       struct buffer *tmp_buf_to_net;
        (void) buf_flush (buf_to_net, 1);
-       (void) buf_shutdown (buf_to_net);
+       tmp_buf_to_net = buf_to_net;
+       buf_to_net = NULL;
+       (void) buf_shutdown (tmp_buf_to_net);
@@ -6484,8 +6501,13 @@
     if (error_use_protocol)
-       buf_output (saved_outerr, str, len);
-       buf_copy_lines (buf_to_net, saved_outerr, 'E');
+       /* We expect this channel to be available.  If it isn't, just
+           drop the error... */
+       if (buf_to_net != NULL)
+       {
+           buf_output (saved_outerr, str, len);
+           buf_copy_lines (buf_to_net, saved_outerr, 'E');
+       }
     else if (server_active)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]