loverso@encore.UUCP (John LoVerso) (10/09/87)
Index: sys/uipc_socket2 4.3BSD +FIX Description: Someone is listen()ing on a TCP port. An incoming connection comes, tcp_input() accepts it and calls sonewconn() with the listening socket. A new socket is allocated (in an mbuf) and linked in with the existing one. tcp_usrreq() is called with PRU_ATTACH to attach the new socket with TCP. This calls tcp_attach(), which in turn calls in_pcballoc() to allocate an internet control block (in an mbuf). tcp_attach() then calls tcp_newtcpcb(), to allocate a TCP control block in an mbuf. This mget() fails because you're out of mbufs (possibly short term). Back in tcp_attach(), it does an in_pcbdetach() which calls sofree() on your new socket, which calls soqremque() on the new socket (zapping its so_head pointer), and then exits. When we get all the way back to sonewconn(), it will again call soqremque() on the new socket! The so_head pointer is already 0 and if you're not on a VAX, dereferencing off it will cause bad things to occur. This problem is especially significant in the case of the Annex, which allocates an mbuf pool at boot time that never grows. Although I haven't verified the fact, most likely this bug is also in 4.2BSD. Repeat-By: Difficult to reproduce as it depends upon a specific mget failing. Easy to way to cheat is to add a global variable tcp_input() thats set just before the call to sonewconn(). tcp_newtcpcb will fake the failing of the mget when this variable is set. Just add the panic from below. Boot this on an idle machine and telnet into it. Fix: Note: the additional panic() isn't really necessary. *** uipc_socket2.c_orig Mon Oct 5 16:39:31 1987 --- uipc_socket2.c Mon Oct 5 17:26:31 1987 *************** *** 212,218 **** soqinsque(head, so, 0); if ((*so->so_proto->pr_usrreq)(so, PRU_ATTACH, (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)) { ! (void) soqremque(so, 0); (void) m_free(m); goto bad; } --- 212,219 ---- soqinsque(head, so, 0); if ((*so->so_proto->pr_usrreq)(so, PRU_ATTACH, (struct mbuf *)0, (struct mbuf *)0, (struct mbuf *)0)) { ! if (so->so_head) /* may already have happened */ ! (void) soqremque(so, 0); (void) m_free(m); goto bad; } *************** *** 245,250 **** --- 246,253 ---- register struct socket *head, *prev, *next; head = so->so_head; + if (head == 0) + panic("soqremque"); prev = head; for (;;) { next = q ? prev->so_q : prev->so_q0;