Subject: kern/2794: Userland can hang system by setting socket buffers to 0
To: None <gnats-bugs@gnats.netbsd.org>
From: None <cyber@dis.org>
List: netbsd-bugs
Date: 10/01/1996 10:06:23
>Number:         2794
>Category:       kern
>Synopsis:       Userland can hang system by setting socket buffers to 0
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Oct  1 10:20:01 1996
>Last-Modified:
>Originator:     Erik Berls
>Organization:
>Release:        1.2_BETA
>Environment:
NetBSD dysphasia 1.2_BETA NetBSD 1.2_BETA (Dysphasia) #1: Sat Aug  3 13:02:07 
PDT 1996     cyber@dysphasia:/h/a/src/sys/arch/i386/compile/Dysphasia i386

>Description:
	By setting the buffer on both sides of a unix domain socket to
	zero after making the connection will cause NetBSD to hang.
	The system is still pingable.
	This problem exists on other hardware/OS combinations, I first found
	it on a Alpha/OSF box (4.0).  Under the Alpha the machine was not
	even pingable. Under SunOS the processes just deadlock.

	Appologies for using such an out of date machine for the NetBSD test.

	Source code following is from UNIX Network Programming with
	additions to shorten the bulk and the offending code between
	#ifndef DONT_CRASH.
>How-To-Repeat:

--cut here to damage your monitor--
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	client.c
#	server.c
#	supp.c
#	unix.h
#
echo x - client.c
sed 's/^X//' >client.c << 'END-of-client.c'
X/*
X * Example of client using UNIX domain stream protocol.
X */
X/* NOTE: this code from 'UNIX Network Programming' W. Richard Stevens 1990 */
X/* client.c */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <sys/un.h>
X
X#define UNIXSTR_PATH	"./s.unixstr"
X#define UNIXDG_PATH		"./s.unixdg"
X
X#define UNIXDG_TMP		"/tmp/db.XXXXXX"
X
Xchar *pname;
X
Xmain(argc, argv)
Xint		argc;
Xchar	*argv[];
X{
X	int					sockfd, servlen;
X	struct sockaddr_un	serv_addr;
X#ifndef DONT_CRASH
X	int optval, optlen;
X#endif
X
X	pname = argv[0];
X
X	/*
X	 * Fill in the structure "serv_addr" with the address of the
X	 * server that we want to send to.
X	 */
X
X	bzero((char *) &serv_addr, sizeof(serv_addr));
X	serv_addr.sun_family = AF_UNIX;
X	strcpy(serv_addr.sun_path, UNIXSTR_PATH);
X	servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);
X
X	/*
X	 * Open a socket (an UNIX doamin stream socket).
X	 */
X
X	if ( (sockfd = socket(AF_UNIX, SOCK_STREAM, 0 )) < 0) {
X		perror("client: can't open stream socket");
X		exit(1);
X	}
X
X	/*
X	 * Connect to the server.
X	 */
X
X	if (connect(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0) {
X		perror("client: can't connect to server");
X		exit(1);
X	}
X
X#ifndef DONT_CRASH
X	optval = 0; optlen = sizeof(optval);
X	setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &optval, optlen);
X	setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &optval, optlen);
X#endif
X
X	str_cli(stdin, sockfd);		/* do it all */
X
X	close(sockfd);
X	exit(0);
X}
X
END-of-client.c
echo x - server.c
sed 's/^X//' >server.c << 'END-of-server.c'
X/*
X * Example of server using UNIX domain stream protocol.
X */
X/* NOTE: this code from 'UNIX Network Programming' W. Richard Stevens 1990 */
X/* server.c */
X
X#include "unix.h"
X
X#define MAXLINE 512
X
Xstr_echo(sockfd)
Xint	sockfd;
X{
X	int n;
X	char line[MAXLINE];
X
X	for ( ; ; ) {
X		n = readline(sockfd, line, MAXLINE);
X		if (n == 0)
X			return;
X		else if (n < 0 ) {
X			perror("str_echo: readline error");
X			exit(1);
X		}
X
X		if (writen(sockfd, line, n) != n) {
X			perror("str_echo: writen error");
X			exit(1);
X		}
X	}
X}
X
X
Xmain(argc, argv)
Xint		argc;
Xchar	*argv[];
X{
X	int	sockfd, newsockfd, clilen, childpid, servlen;
X	struct sockaddr_un	cli_addr, serv_addr;
X#ifndef DONT_CRASH
X	int optval, optlen;
X#endif
X
X	pname = argv[0];
X
X	/*
X	 * Open a socket (a UNIX domain stream socket).
X	 */
X
X	if ( (sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0 ) {
X		perror("server: can't open stream socket");
X		exit(1);
X	}
X
X	/*
X	 * Bind our local address so that the client can send to us.
X	 */
X
X	bzero((char *) &serv_addr, sizeof(serv_addr));
X	serv_addr.sun_family = AF_UNIX;
X	strcpy(serv_addr.sun_path, UNIXSTR_PATH);
X	servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);
X
X	if (bind(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0) {
X		perror("server: can't bind local address");
X		exit(1);
X	}
X
X	listen(sockfd, 5);
X
X	for ( ; ; ) {
X		/*
X		 * Wait for a connection for a client process.
X		 * This is an example of a concurrent server.
X		 */
X
X		 clilen = sizeof(cli_addr);
X		 newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
X		 if (newsockfd < 0 ) {
X			perror("server: accept error");
X			exit(1);
X		}
X
X#ifndef DONT_CRASH
X	optval = 0; optlen = sizeof(optval);
X	setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &optval, optlen);
X	setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &optval, optlen);
X#endif
X		if ( (childpid = fork() ) < 0) {
X			perror("server: fork error");
X			exit(1);
X		}
X
X		else if (childpid == 0) {	/* child process */
X			close(sockfd);			/* close original socket */
X			str_echo(newsockfd);	/* process the request */
X			exit(0);
X		}
X
X		close (newsockfd);			/* parent process */
X	}
X}
X
END-of-server.c
echo x - supp.c
sed 's/^X//' >supp.c << 'END-of-supp.c'
X/* NOTE: this code from 'UNIX Network Programming' W. Richard Stevens 1990 */
X/* supp.c */
X
X#include <stdio.h>
X
X#define MAXLINE 512
X
Xint
Xwriten(fd, ptr, nbytes)
Xregister int fd;
Xregister char *ptr;
Xregister int nbytes;
X{
X	int	nleft, nwritten;
X
X	nleft = nbytes;
X	while (nleft > 0) {
X		nwritten = write(fd, ptr, nleft);
X		if (nwritten <= 0)
X			return(nwritten);	/* error*/
X		nleft -= nwritten;
X		ptr   += nwritten;
X	}
X	return(nbytes-nleft);
X}
X
X
Xint
Xreadline(fd, ptr, maxlen)
Xregister int fd;
Xregister char *ptr;
Xregister int maxlen;
X{
X	int n, rc;
X	char c;
X
X	for(n =1 ; n < maxlen; n++) {
X		if ( (rc = read(fd, &c, 1)) == 1) {
X				*ptr++ = c;
X				if (c == '\n')
X					break;
X		} else if (rc == 0 ) {
X			if (n == 1 )
X				return(0);	/* EOF, no data read */
X			else
X				break;		/* EOF, some data was read */
X		} else
X			return(-1);		/* error */
X	}
X
X	*ptr = 0;
X	return(n);
X}
X
Xstr_cli(fp, sockfd)
Xregister FILE *fp;
Xregister int  sockfd;
X{
X	int	n;
X	char sendline[MAXLINE], recvline[MAXLINE + 1];
X
X	while (fgets(sendline, MAXLINE, fp) != NULL) {
X		n = strlen(sendline);
X		if (writen(sockfd, sendline, n) != n)
X		{
X			perror("str_cli: writen error on socket");
X			exit(1);
X		}
X
X		/*
X		 * Now read a line from the socket and write it to 
X		 * our standard output.
X		 */
X
X		n = readline(sockfd, recvline, MAXLINE);
X		if (n < 0) {
X			perror("str_cli: readline error");
X			exit(1);
X		}
X
X		fputs(recvline, stdout);
X	}
X
X	if (ferror(fp)) {
X		perror("str_cli: error reading file");
X		exit(1);
X	}
X}
X
X
END-of-supp.c
echo x - unix.h
sed 's/^X//' >unix.h << 'END-of-unix.h'
X/*
X * Definitions for UNIX domain stream and datagram client/server programs.
X */
X/* NOTE: this code from 'UNIX Network Programming' W. Richard Stevens 1990 */
X/* unix.h */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <sys/un.h>
X
X#define UNIXSTR_PATH	"./s.unixstr"
X#define UNIXDG_PATH		"./s.unixdg"
X
X#define UNIXDG_TMP		"/tmp/db.XXXXXX"
X
Xchar *pname;
X
END-of-unix.h
exit
--cut here to damage your monitor--

>Fix:
	Dont know yet, will look into it after the project that i found
	it in goes into production. (ie: next week)
	Check for bad values?
	Avoid doing buffering?

>Audit-Trail:
>Unformatted: