Subject: bin/24253: broken pipe race condition in CVS pserver
To: None <gnats-bugs@gnats.netbsd.org>
From: None <jesse_off@stchome.com>
List: netbsd-bugs
Date: 01/26/2004 11:05:51
>Number: 24253
>Category: bin
>Synopsis: broken pipe race condition in CVS pserver
>Confidential: no
>Severity: serious
>Priority: high
>Responsible: bin-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Mon Jan 26 18:06:00 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator:
>Release: NetBSD 1.6.2_RC4
>Organization:
Scientific Technologies Corporation
>Environment:
System: NetBSD angel 1.6.2_RC4 NetBSD 1.6.2_RC4 (DELL600SC) #13: Tue Jan 13 17:33:29 MST 2004 root@angel:/usr/src/sys/arch/i386/compile/DELL600SC i386
Architecture: i386
Machine: i386
>Description:
There is a race condition in the CVS pserver code that
causes CVS ops to abort with the "broken pipe" error message.
What happens is the parent CVS process tries to tell the
child to stop by writing a "S" to its flow control pipe.
However, the child may already be dead and its read pipe
is therefore closed. This happens when there is enough
outstanding data in the kernel socket buffers not yet read
by the parent process when the child exits. The master
reads this data in and tries to send a message on its
flowcontrol pipe to stop, but its flowcontrol pipe is
already closed and the kernel sends a SIGPIPE to the parent
PID.
>How-To-Repeat:
Very difficult, happened to have the right set of conditions for
this to appear constantly last week.
>Fix:
Don't let child process exit until parent has noticed output is done
and closed the flowcontrol pipe. Patch below...
Index: server.c
===================================================================
RCS file: /cvsroot/src/gnu/dist/cvs/src/server.c,v
retrieving revision 1.6.2.3
diff -u -r1.6.2.3 server.c
--- server.c 2003/12/17 17:41:02 1.6.2.3
+++ server.c 2004/01/26 17:50:44
@@ -2299,8 +2299,28 @@
# endif /* SERVER_LO_WATER */
static int set_nonblock_fd PROTO((int));
+static int unset_nonblock_fd PROTO((int));
/*
+ * Set fd to blocking I/O. Returns 0 for success or errno
+ * code.
+ */
+
+static int
+unset_nonblock_fd (fd)
+ int fd;
+{
+ int flags;
+
+ flags = fcntl (fd, F_GETFL, 0);
+ if (flags < 0)
+ return errno;
+ if (fcntl (fd, F_SETFL, flags | ~O_NONBLOCK) < 0)
+ return errno;
+ return 0;
+}
+
+/*
* Set buffer BUF to non-blocking I/O. Returns 0 for success or errno
* code.
*/
@@ -2807,6 +2827,19 @@
* the parent.
*/
buf_free (protocol);
+
+ close (STDIN_FILENO);
+ close (STDERR_FILENO);
+ close (STDOUT_FILENO);
+ close (protocol_pipe[1]);
+#ifdef SERVER_FLOWCONTROL
+ if (unset_nonblock_fd (flowcontrol_pipe[0]) == 0)
+ {
+ char junk;
+ while (read (flowcontrol_pipe[0], &junk, 1) != 0);
+ }
+#endif
+
exit (exitstatus);
}
>Release-Note:
>Audit-Trail:
>Unformatted: