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: