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: