misc/112554: unp_gc is overly agressive and removes valid file descriptors in transit

Spencer Minear minear at securecomputing.com
Wed May 9 21:10:06 UTC 2007


>Number:         112554
>Category:       misc
>Synopsis:       unp_gc is overly agressive and removes valid file descriptors in transit
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed May 09 21:10:05 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator:     Spencer  Minear
>Release:        Our modified 6.0 system.  But the problem still exist in current
>Organization:
Secure Computing Corp
>Environment:
Any 6.0 system will show the problem.
>Description:
The unp_gc function within kern/unpc_usrreq.c is suppose to cleanup file descriptors that were passed in a message when the message contianing the file reference is NO longer accessible.  HOWEVER the code is abit aggressive and will remove file descriptors that are in a message buffer waiting on a socket that has yet to be accepted.

>How-To-Repeat:
The short summary is that it is easiest to show if you have client/server that communicate via a file descriptor sent over a socket.  Then run one client server pair that ignores errors and just keeps on running to keep unp_gc working.  Then run a separate client/server pair that sets up the listen, but is SLOW to do the accept so that the client socket sits in the message buffer for a long time.  unp_gc will screw up this socket nearly every time.

README, Makefile and Test program code follow:

README file
===========================================================

The test program in this directory was created to show that file
descriptors passed in a socket can, in some cases get lost.  The test
evolved as the problem was being investigated.  The test was modified
so that it can be run in two sets of clinet/server instances.  The
first client/server pair do trigger the problem, but in its current
form running in this form will not print out any notification that the
problem occured.  It is used just to setup background activity that
will result in the second pair failing on the first attempted exchange.

Program Operation Overview:

  The single code file is compiled to a test program named
  sockpair_test.  The program can be run as a server or as a client.
  The server creates a unix socket in the current working directory
  and enters a loop to listen for new service connections.  Once
  connection is received and a new socket is obtained via an accept
  operation, The server will fork a child instance of it self which
  will select on the new socket and when ready it does a rcvmsg on the
  socket and expects to receive a yet another new socket which the
  client passed in the first message.  Once received the first socket
  is closed and a message is sent on the second, and then a return
  message is read from that socket and the clinet closes this
  connection and exits.

  The client side enters a loop which creates a socket which it uses
  to connect to the server, then it creates a socket pair.  It keeps
  one half of the socket pair and sends the file descriptor for the
  other half in a message over connection socket.  Once the second
  file descriptor is sent, the client close its end of the transmitted
  file descriptor, and the connection socket.  It waits for a mesasge
  to be received on it's end of the socket pair and once received
  sends its reply and repeats process.

To build the test:

  1. Extract the tarball containing the test source, the makefile and
     this information file. 

  2. run "make link" in directory.

To run the test establish 4 connections to the system.

  In the first two connection run the following commands.

    ./server_side
    ./client_side

  Once these report that they are running then run the following two
  commands in the other two connections.  NOTE:  you need to run the
  second command wihtin a second or two of getting the first running.

    ./server_side -T
    ./client_side -T

  After a short delay, about 5 seconds max, the following error messages
  should appear be observed.

    sever_side

      Starting server side
      New fd 5 recvmsged from new_socket
      pid <num> read select on <fd> already says ready.
      w_read: read 0 bytes.
      w_read error from recvmsg: Connection reset by peer
      Server side socket select timed out

    client_side

      Starting client side
      client: sendmsg() failed, 55, No buffer space available

Problem Explantion.

  The problem is while the client provided file descriptor for the
  second half of socket pair is sitting in the socket queue waiting to
  be "received" by the server, the unp_gc function in
  sys/kern/unp_usrreq.c may see it and think that it is no longer
  accessible and closes the remain view of the file.  Once the server
  gets to using the socket it finds that it is closed for reading.  It
  can send the initial message, but when it tries to read from it it
  receives a "Connection reset by peer" error.

The Fix

  The fix is to add logic into the unp_gc function that takes into
  account the fact that the socket containing the file reference may
  in a listen state and in that case it should be be removed.

Makefile
==================================================================

PROG	= sockpair_test
CLEANFILES += server_side client_side

CFLAGS += -g
LDFLAGS += -static
LDADD+=

NO_MAN=

link: sockpair_test
	if test -f server_side; then \
	    rm server_side; \
	fi
	if test -f client_side; then \
	    rm client_side; \
	fi
	ln sockpair_test server_side 
	ln sockpair_test client_side 

.include <bsd.prog.mk>

Code file
=========================================

/*---------------------------------------------------------------------------

   Test program to demonstrate the lose of file descriptors passed through a
   socket.  

   This single program can be used in 4 modes.  The first two area used to
   generate enough activity which should result 3rd and 4th modes will
   detect the failure on the first server client exchange.

      1. A quiet Server side test driver used to generate enough traffice to 
         trigger the problem for test modes 3 and 4.

      2. A quiet Client side program which talkes to the quiet server.

      3. A server side that delays to help trigger an immediate failure 
         and out error messages to report when the problem is detected.
	 Also times out after the clinet goes away for 15 seconds.

      4. A client side that reports errors when the problem is detected and
         exits immediately after the first errro.

   Processing over view:

	The server creates a unix socket in the current working directory
	and enters a loop to listen for new service connections.  Once
	connection is received and a new socket is obtained via an accept
	operation, The server will fork a child instance of it self which
	will select on the new socket and when ready it does a rcvmsg on the
	socket and expects to receive a yet another new socket which the
	client passed in the first message.  Once received the first socket
	is closed and a message is sent on the second, and then a return
	message is read from that socket and the clinet closes this
	connection and exits.

	The client side enters a loop which creates a socket which it uses
	to connect to the server, then it creates a socket pair.  It keeps
	one half of the socket pair and sends the file descriptor for the
	other half in a message over connection socket.  Once the second
	file descriptor is sent, the client close its end of the transmitted
	file descriptor, and the connection socket.  It waits for a mesasge
	to be received on it's end of the socket pair and once received
	sends its reply and repeats process.

 ---------------------------------------------------------------------------*/

#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>

void do_parent(void);
void do_child(void);

typedef struct mymsg_s {

    u_int32_t	msg_magic;
    u_int32_t	msg_cnt;
    char	msg_text[40];
} mymsg_t;	

#define MSG_MAGIC	0x01020304

char *serv_sockets[2] = {
    "./TESTSERVSOCK1",		/* Used for the first quite mode operation */
    "./TESTSERVSOCK2"		/* Used by the problem reporting operational
				 * mode */
};

int opt_daemon = 0;	/* Indicates we are running as a sever */
int opt_client = 1;     /* Indicates we are running as a client */
int opt_instance = 0;   /* 0 indicates quite background mode
			 * 1 indicates the report problem mode */
int opt_verbose = 0;    /* Can be used to have the quite mode print 
			   out error messages.  On by default for
			   opt_instance 1 */

/***************************************************************************
 *
 * Forward declarations of local functions
 *
 ***************************************************************************/
static void sig_child(int sig);
static void usage(char *prog);
void do_server(int dinst);
void do_client(int cinst);
int get_sock(int rsock);
void saction(int sock);
int s_write_msg(int sock, char mkey, char *msg);
int s_read_msg(int, char *msg);
int c_write_msg(int sock, char mkey, char *msg);
int c_read_msg(int, char *msg);
void caction(int sock1, int *sockp_p);

/*---------------------------------------------------------------------------

    Name		: Main

    Function Description: 

	Main entry, detects executaion name and processes arguments
	to set up option global values and starts the appropriate processing/

 ---------------------------------------------------------------------------*/
int
main(int argc, char ** argv)
{
    pid_t             pid;
    char   ch;
    
    if (strstr(argv[0], "server_side") != NULL) {
	printf("Starting server side\n");
	opt_daemon = 1;
	opt_client = 0;
    } else {
	printf("Starting client side\n");
	opt_daemon = 0;
	opt_client = 1;
    }

    while ((ch = getopt(argc, argv, "hTdcv")) != EOF) {

	switch(ch) {

	case 'T':
	    opt_instance = 1;
	    opt_verbose = 1;
	    break;
	case 'd':
	    opt_daemon = 1;
	    opt_client = 0;
	    break;
	case 'c':
	    opt_client = 1;
	    opt_daemon = 0;
	    break;
	case 'h':
	    usage(argv[0]);
	    exit(0);
	    break;
	case 'v':
	    opt_verbose = 1;
	    break;
	default:
	    usage(argv[0]);
	    exit(1);
	    break;
	}
    }
    
    if (opt_daemon) {
	do_server(opt_instance);
    }

    if (opt_client) {
	do_client(opt_instance);
    }
}

/*************************************************************************
 *
 * Local Functions
 *
 *************************************************************************/

/*---------------------------------------------------------------------------

    Name		: sig_child

    Function Description: 

	Clean up child processes to keep the system clean.

    Arguments		:

        sig - The signal value being responded to.  Should be SIGCHLD

 ---------------------------------------------------------------------------*/
static void
sig_child(int sig)
{
    int child_status;

    /* Reap _all_ dead children */
    if (sig != SIGCHLD) {
	fprintf(stderr, "Invalid signal %d seen in sig_child()\n", sig);
	exit(1);
    }
    while ( wait3 ( &child_status, WNOHANG, 0 ) > 0 );
}

/*---------------------------------------------------------------------------

    Name		: usage

    Function Description: 

	Print out a short usage summary

    Arguments		:

        prog:	Name used to start the program.

 ---------------------------------------------------------------------------*/
static void
usage(char * prog)
{
    fprintf(stderr, "%s: [-hbdciT] [-t delay_sec]\n");
    fprintf(stderr, "\t-h - print this message\n");
    fprintf(stderr, "\t-T - Run expecting to fail on first try\n");
    fprintf(stderr, "\t-d - Run as the daemon (use server_side link)\n");
    fprintf(stderr, "\t-c - Run as the client (use client_sice link)\n");
    fprintf(stderr, "\t-v - Do more verbose output.  Default with -T\n");
}


/*************************************************************************
 *
 * Server Side Functions
 *
 *************************************************************************/

#define SUN_PATH_SIZE (sizeof(proxy_addr.sun_path))

/*---------------------------------------------------------------------------

    Name		: do_server

    Function Description: 

	Setup the daemon service socket and enter the service loop which
	waits for a new connection socket.  When new connection is received
	fork a child process to carry out the interaction with the client.

    Arguments		:

	inst	- 0 to indicate running in quiet mode, 
		  1 to indicate running in report error mode.

 ---------------------------------------------------------------------------*/
void
do_server(int inst)
{
    int rsock;
    struct	sockaddr_un	proxy_addr;
    int proxy_len;
    int new_socket;
    int pid;
    struct stat sb;

    if ( signal(SIGCHLD, sig_child) == SIG_ERR) {
	printf("failed to set sig child handler\n");
	exit(1);
    }

restart:
    if (stat(serv_sockets[inst], &sb) == 0) {
	unlink(serv_sockets[inst]);
    }

    /* build the service socket */
    rsock = socket(AF_UNIX, SOCK_STREAM, 0);
    fcntl(rsock, F_SETFL, FNDELAY);

    bzero ( (char *) &proxy_addr , sizeof(proxy_addr) );
    proxy_addr.sun_family = AF_UNIX;
    strcpy ( proxy_addr.sun_path , serv_sockets[inst]);
    proxy_len = (sizeof(proxy_addr.sun_family) 
		 + 1 + strlen(proxy_addr.sun_path));

    bind (rsock, (struct sockaddr *)&proxy_addr, proxy_len );
    listen(rsock, 5);
	
    do {
	if (inst > 0) {
	    sleep(5);
	}
 	if((new_socket = get_sock(rsock)) != -1) {

	    switch(pid = fork()) {
	    case 0:
		saction(new_socket);
		exit(0);
		break;
	    default:
		close(new_socket);
	    }
	} else {
	    printf("No new socket received\n");
	    close(rsock);
	    goto restart;
	}
    } while(1);
}

/*---------------------------------------------------------------------------

    Name		: get_sock

    Function Description: 

	Wait for new connection and when available accept the new connection
	and return the file descriptor to the caller.

    Return Value	: 

	file descriptor for the accepted connection socket
    
    Arguments		:

        rsock	- File descriptor for the server service socket.

 ---------------------------------------------------------------------------*/
int
get_sock(int rsock)
{

    int			ns;	 /* New socket from accept()	*/
    struct sockaddr_un  ns_addr; /* Address provided with the acept */
    int		        ns_addr_len;       
    int			sres;

    struct timeval	timeout;	/* Time till select times out	*/
    fd_set		read_ready;

    ns_addr_len = sizeof(ns_addr);

    timeout.tv_sec  = 15;
    timeout.tv_usec = 0;

reselect:
    FD_ZERO ( &read_ready );
    FD_SET  ( rsock, &read_ready );

    if ((ns = select(rsock+1, &read_ready, NULL, NULL, &timeout)) == -1 ) {

	if (errno == EINTR) {
	    goto reselect;
	}
	printf("Server side socket select error, %d %s\n",
	       errno, strerror(errno));
	exit(1);
    } else if (ns == 0) {
	printf("Server side socket select timed out\n");
	if (opt_instance > 0) {
	    exit(1);
	}
	goto reselect;
    }

    if (!FD_ISSET(rsock, &read_ready) ) {
	printf("No new connection waiting on 'rsock'\n");
         return ( -1 );
    }

    ns = accept(rsock, (struct sockaddr *)&ns_addr, &ns_addr_len);

    if ( ns < 0 ) {
	printf("Error accept()ing on rsock\n");
        return ( -1 );
    }

    return(ns);
}

/*---------------------------------------------------------------------------

    Name		: saction

    Function Description: 

	Wait on the connection socket to receive the first client message
	which conntains the file descriptor for the per connection
	communication socket.  Close the connection socket once
	the messge is received.

	Select on the communication socket with zero timeout.  This isn't
	really necessary but when it comes back with an indication that
	data is read to read we have a hint that the problem has occurred
	since the client has not yet sent data.

	Write the server to client message and wait for the client
	response message.

    Arguments		:

        nsock	- File descriptor for the newly accepted connection socket

 ---------------------------------------------------------------------------*/
void
saction(int nsoc)
{
    int			asoc;
    char		msg_buf[80];
    struct msghdr	msg;
    struct {
	struct cmsghdr	hdr;
	int		fd;
    }			control;
    int			bytes_read;	/* # of bytes in recvmsg()	*/
    fd_set		read_ready;
    struct timeval	timeout;
    int			sres;

    msg.msg_name = (caddr_t) 0;
    msg.msg_namelen = 0;
    msg.msg_iov = (struct iovec *)0;
    msg.msg_iovlen = 0;
    msg.msg_control = (caddr_t) &control;
    msg.msg_controllen = sizeof(control);
    msg.msg_flags = 0;

    timeout.tv_sec = 100;
    timeout.tv_usec= 0;

    FD_ZERO(&read_ready);
    FD_SET(nsoc, &read_ready);

    sres = select(nsoc + 1, &read_ready, (fd_set *)NULL, (fd_set *)NULL,
		&timeout);

    if (sres < 0 ) {
	printf("Error selecting on accepted socket\n");
        close (nsoc);
        return;
    }

    if (!FD_ISSET(nsoc,  &read_ready)) {
	printf("Select() reports no data on new_socket\n");
        close  (nsoc);
        return;
    }

    if ((bytes_read = recvmsg(nsoc, &msg, 0) ) < 0) {

	printf("Error recvmsg()ing new_socket\n");
        close ( nsoc );
        return;
    }

    asoc = control.fd;

    if (opt_verbose) {
	printf( "New fd %d recvmsged from new_socket\n", asoc);
    }
    close(nsoc);

    FD_ZERO(&read_ready);
    FD_SET(asoc, &read_ready);
    timeout.tv_sec = 0;
    timeout.tv_usec= 0;

    /* This select would not expect not normally expect to to see available data */
    sres = select(asoc + 1, &read_ready, (fd_set *)NULL, (fd_set *)NULL,
		  &timeout);
    if (opt_verbose && (sres > 0)) {
	printf("pid %d, read select on %d already says ready\n",
	       getpid(), asoc);
    }
    
    if (s_write_msg(asoc, 'a', "msg1") < 0) {
	if (opt_verbose) {
	    printf("Warder write msg 1 failed: %s\n", strerror(errno));
	}
	close(asoc);
	return;
    }

    bzero(msg_buf, 80);
    if (s_read_msg(asoc, msg_buf) < 0) {
	close(asoc);
	if (opt_instance != 0) {
	    exit(1);
	}
	return;
    }

    close(asoc);
    return;
}

/*---------------------------------------------------------------------------

    Name		: s_write_msg

    Function Description: 

	Build and send a message to the client.

    Return Value	: 

	-1	- write failed
	1	- operation was successful
    
    Arguments		:

        soc	- file descriptor of the socket to write to
	msgkey  - First character of the message
	msg	- text of the messge.

 ---------------------------------------------------------------------------*/
int
s_write_msg(int soc, char msgkey, char *msg)
{

    char rmsg[80];

    sprintf(&rmsg[0], "%c%s\n", msgkey, msg);
    if (write(soc, rmsg, strlen(rmsg)) < 0) {
	return(-1);
    }
    return(1);
}

/*---------------------------------------------------------------------------

    Name		: s_read_msg

    Function Description: 

	Select on the socket waiting for data and when availabel 
	read it from the socket and save it in the msg buffer.

    Return Value	: 

	-1	- write failed
	1	- operation was successful
    
    
    Arguments		:

        soc	- file descriptor from which to read the client messge
	msg_buf - storage location where the message is placed.

 ---------------------------------------------------------------------------*/
int
s_read_msg(int soc, char *msg_buf)
{
    struct	timeval	timeout;
    fd_set	read_ready;
    int		rcnt;
    int	        scnt;
    struct msghdr	msg;
    struct {
	struct cmsghdr	hdr;
	int		fd;
    }			control;
    struct iovec	iov[1];
    int			bytes_read;	/* # of bytes in recvmsg()	*/

retry:

    bzero(msg_buf, 80);

    timeout.tv_sec = 100;
    timeout.tv_usec = 0;


    FD_ZERO ( &read_ready );
    FD_SET  (soc, &read_ready );

    if ((scnt = select( soc+1, &read_ready, NULL, NULL, &timeout)) == -1) {
	printf("select error in warder read\n");
        return (-1);
    } else if (scnt == 0) {
	printf("read message timed out select\n"); 
        return (-1);
    }

    if ( !FD_ISSET( soc, &read_ready )) {
	printf("soc %d is not read after select\n", soc); 
        return (-1);
    }

    bzero(&control, sizeof(control));

    iov[0].iov_base = msg_buf;
    iov[0].iov_len  = 80;
    msg.msg_name = (caddr_t) 0;
    msg.msg_namelen = 0;
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;
    msg.msg_control = (caddr_t) &control;
    msg.msg_controllen = sizeof(control);
    msg.msg_flags = 0;

    if ((rcnt = recvmsg(soc, &msg, 0)) < 0) {

	if (opt_verbose) {
	    printf("w_read: recvmsg error: %s\n", strerror(errno));
	}
        return (-1);

    } else if (rcnt == 0) {
	if (opt_verbose) {
	    printf("w_read: read 0 bytes\n");
	}
	sleep(1);
	goto retry;
    } else if (strcmp("bmsg2\n", msg_buf) != 0) {
	printf("w_read unexpected response len %d, '%s'\n", rcnt, msg);
	goto retry;
    }

    return(1);
}

/*************************************************************************
 *
 * Client Side Functions
 *
 *************************************************************************/
/*---------------------------------------------------------------------------

    Name		: do_client

    Function Description: 

	Enters a perpetual loop which creates the connection socket,
	connects to the server.  Once the connection is extablished
	carry out the client side connection actions and then close
	up the connection file descriptors and repeat the process.

    Arguments		:

	cinst	- 0 to indicate running in quiet mode, 
		  1 to indicate running in report error mode.

 ---------------------------------------------------------------------------*/
void
do_client(int cinst)
{
    int length;
    int r;
    struct sockaddr_un  peer;
    char* sockfilename;
    int sock1;
    int sockp[2];
    int cycle_cnt = 0;

    while (1) {
	if ((sock1 = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
	    printf("client socket failed\n");
	    exit(0);
	}

	memset(&peer, 0, sizeof(peer));
	peer.sun_family = AF_UNIX;

	strcpy( peer.sun_path, serv_sockets[cinst]);
	length = strlen( peer.sun_path) + sizeof (peer.sun_family) + 1;
retry:
	if (connect (sock1, (struct sockaddr*) & peer, length) < 0) {
	    printf("client connect() failed for %s: %s\n", 
		   serv_sockets[0], strerror(errno));
	    sleep(1);
	    goto retry;
	}
	fcntl(sock1, F_SETFD, 1); 

	caction(sock1, sockp);

	close(sock1);
	close(sockp[0]);
	close(sockp[1]);
	cycle_cnt++;

	if ((cycle_cnt % 20) == 0) {
	    sleep(5);
	}
    }
}

/*---------------------------------------------------------------------------

    Name		: caction

    Function Description: 

	Create a socket pair, send the second file descriptor to the
	server using the provides connection socket.  Close the 
	client file descriptor for the sent file descriptor.

	Wait for the server message and send the client response.

    Arguments		:

        sock1	- The file descriptor for the connection socket
	sockp   - Pointer to storage for socket pair file descriptors

 ---------------------------------------------------------------------------*/
void
caction(int sock1, int *sockp)
{
    struct msghdr	msg;
    struct {
	struct cmsghdr	hdr;
	int		fd;
    }			control;
    char buf[80];
    fd_set		read_ready;
    struct timeval	timeout;
    int			scnt;

    if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockp) < 0) {
	printf("client socket pair failed\n");
	exit(0);
    }

    fcntl(sockp[0], F_SETFL, O_NONBLOCK);
    fcntl(sockp[0], F_SETFD, 1);

    timeout.tv_sec = 0;
    timeout.tv_usec = 0;

    FD_ZERO ( &read_ready );
    FD_SET  ( sockp[1], &read_ready );

    if ((scnt =select(sockp[1] + 1, &read_ready, NULL, NULL, &timeout)) < 0) {
	printf("client: select on socket pair fd %d failed, %s\n",
	       sockp[1], strerror(errno));
    }

    if (scnt > 0) {
	printf("client: select on socket pair fd1 %d read before being sent\n",
	       sockp[1]);
    }

    control.hdr.cmsg_len = sizeof(control);
    control.hdr.cmsg_level = SOL_SOCKET;
    control.hdr.cmsg_type = SCM_RIGHTS;
    control.fd = sockp[1];

    msg.msg_name = (caddr_t)0;
    msg.msg_namelen = 0;
    msg.msg_iov = (struct iovec *)0;
    msg.msg_iovlen = 0;
    msg.msg_control = (caddr_t) &control;
    msg.msg_controllen = sizeof(control);
    msg.msg_flags = 0;

    if (sendmsg(sock1, &msg, 0) < 0) 
      {
	  printf("client: failed to send fd to server. rsock = %d.  mine = %d.  warder's = %d",
		 sock1, sockp[0], sockp[1]);
	close(sockp[0]);
	close(sockp[1]);
	exit(1);
    }

    close(sockp[1]);

    if (c_read_msg(sockp[0], buf) < 0) {
	exit(0);
    }
    if (c_write_msg(sockp[0], 'b', "msg2\n") < 0) {
	exit(0);
    }
}

/*---------------------------------------------------------------------------

    Name		: c_read_msg

    Function Description: 

	Wait on the socket for receipt of data, then read the server
	mesasge from the provided file descriptor and save the results
	in the message buffer.

    Return Value	: 

	-1	Operation failed
	1	Operaiton was successful
    
    Arguments		:

        soc	- File descriptor to read the server message from
	msg_buf - Place to hold the received message.

 ---------------------------------------------------------------------------*/
int
c_read_msg(int soc, char *msg_buf)
{
    struct iovec vec[2];
    char prefix;
#define BUFSIZE 8000
    char buffer[BUFSIZE];
    char *dyn_buf;
    int ready;
    int r;
    struct msghdr msg;
    fd_set	read_ready;
    struct timeval	timeout;

    timeout.tv_sec = 100;
    timeout.tv_usec = 0;

    FD_ZERO ( &read_ready );
    FD_SET  ( soc, &read_ready );

    if (select(soc + 1, &read_ready, NULL, NULL, &timeout) < 0) {
	printf("client read: select error on msg 1: %s\n", strerror(errno));
	return(-1);
    }

    vec[0].iov_base = & prefix;
    vec[0].iov_len = 1;
    vec[1].iov_base = & buffer[0];
    vec[1].iov_len = sizeof(buffer);

    msg.msg_iov = & vec[0];
    msg.msg_iovlen = 2;
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_control = NULL;
    msg.msg_controllen = 0;
    msg.msg_flags = 0;

    if ((r = recvmsg(soc, &msg, 0)) < 0) {
	printf("client recv msg 1 failed: %s\n", strerror(errno));
	return(-1);
    } else if (r == 0) {
	printf("clinet recved no data\n");
	return(-1);
    }
    memcpy(msg_buf, buffer, r - 1);
    return(1);
}

/*---------------------------------------------------------------------------

    Name		: c_write_msg

    Function Description: 

	Build and send the reply message to the server.  In the case
	that we are running in the detect problem mode we exit processing
	when the error is detected.

    Return Value	: 

	1	- Operation completed.
    
    Arguments		:

	sock	- File descriptor to which to write the message
	msgkey  - First character of the message
	smsg    - Test of the message.

 ---------------------------------------------------------------------------*/
int
c_write_msg(int sock, char msgkey, char *smsg)
{
    struct iovec vec[2];
    int count;
    struct msghdr msg;
    int total;
    int r;

    vec[0].iov_base = &msgkey;
    vec[0].iov_len = 1;
    total = vec[0].iov_len;
    count = 1;
  
    if (smsg) {
	vec[1].iov_base = (void*)smsg;
	vec[1].iov_len = strlen(smsg);
	total += vec[1].iov_len;
	count = 2;
    }

    msg.msg_iov = & vec[0];
    msg.msg_iovlen = count;
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_control = NULL;
    msg.msg_controllen = 0;
    msg.msg_flags = 0;

    errno = 0;
    r = sendmsg(sock, &msg, 0);
  
    if (r != total) {
	if (opt_verbose) {
	    printf("client: sendmsg() failed, %d, %s\n", errno, strerror(errno));
	}
	if (opt_instance == 1) {
	    exit(1);
	}
    }
    return(1);
}

>Fix:
Add code like the following to unp_gc.  NOTE: the version numebers do not match FreeBSD CVS, nor to the line nubmers match any freebsd release.  BUT, the context should point to the relevant location for a 6.0 copy of the file as well as current.

diff -c -r1.12 -r1.14
*** uipc_usrreq.c       9 Jan 2007 22:40:59 -0000       1.12
--- uipc_usrreq.c       13 Feb 2007 23:31:32 -0000      1.14
***************
*** 1675,1680 ****
--- 1675,1681 ----
  {
        struct file *fp, *nextfp;
        struct socket *so;
+       struct socket *soa;
        struct file **extra_ref, **fpp;
        int nunref, i;
        int nfiles_snap;
***************
*** 1762,1767 ****
--- 1765,1783 ----
                        SOCKBUF_LOCK(&so->so_rcv);
                        unp_scan(so->so_rcv.sb_mb, unp_mark);
                        SOCKBUF_UNLOCK(&so->so_rcv);
+                       /*
+                        * If socket is in listening state, then sockets
+                        * in its accept queue are accessible, and so
+                        * are any descriptors in those sockets' receive
+                        * queues.
+                        */
+                       ACCEPT_LOCK();
+                       TAILQ_FOREACH(soa, &so->so_comp, so_list) {
+                           SOCKBUF_LOCK(&soa->so_rcv);
+                           unp_scan(soa->so_rcv.sb_mb, unp_mark);
+                           SOCKBUF_UNLOCK(&soa->so_rcv);
+                       }
+                       ACCEPT_UNLOCK();
                }
        } while (unp_defer);
        sx_sunlock(&filelist_lock);

>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list