Index: ip_output.c =================================================================== RCS file: /data/fbsd-cvs/ncvs/src/sys/netinet/ip_output.c,v retrieving revision 1.242.2.16 diff -u -r1.242.2.16 ip_output.c --- ip_output.c 24 Oct 2006 13:23:03 -0000 1.242.2.16 +++ ip_output.c 24 Nov 2006 15:47:13 -0000 @@ -1148,15 +1148,29 @@ /* * IP socket option processing. + * + * There are two versions of this call in order to work around a race + * condition in TCP in FreeBSD 6.x. In the TCP implementation, so->so_pcb + * can become NULL if the pcb or pcbinfo lock isn't held. However, when + * entering ip_ctloutput(), neither lock is held, and finding the pointer to + * either lock requires follow so->so_pcb, which may be NULL. + * ip_ctloutput_pcbinfo() accepts the pcbinfo pointer so that the lock can be + * safely acquired. This is not required in FreeBSD 7.x because the + * invariants on so->so_pcb are much stronger, so it cannot become NULL + * while the socket is in use. */ int -ip_ctloutput(so, sopt) +ip_ctloutput_pcbinfo(so, sopt, pcbinfo) struct socket *so; struct sockopt *sopt; + struct inpcbinfo *pcbinfo; { struct inpcb *inp = sotoinpcb(so); int error, optval; + if (pcbinfo == NULL) + pcbinfo = inp->inp_pcbinfo; + error = optval = 0; if (sopt->sopt_level != IPPROTO_IP) { return (EINVAL); @@ -1190,12 +1204,15 @@ m_free(m); break; } + INP_INFO_WLOCK(pcbinfo); if (so->so_pcb == NULL) { + INP_INFO_WUNLOCK(pcbinfo); m_free(m); error = EINVAL; break; } INP_LOCK(inp); + INP_INFO_WUNLOCK(pcbinfo); error = ip_pcbopts(inp, sopt->sopt_name, m); INP_UNLOCK(inp); return (error); @@ -1217,10 +1234,14 @@ if (error) break; + INP_INFO_WLOCK(pcbinfo); if (so->so_pcb == NULL) { + INP_INFO_WUNLOCK(pcbinfo); error = EINVAL; break; } + INP_LOCK(inp); + INP_INFO_WUNLOCK(pcbinfo); switch (sopt->sopt_name) { case IP_TOS: inp->inp_ip_tos = optval; @@ -1277,6 +1298,7 @@ OPTSET(INP_DONTFRAG); break; } + INP_UNLOCK(inp); break; #undef OPTSET @@ -1295,11 +1317,13 @@ if (error) break; + INP_INFO_WLOCK(pcbinfo); if (so->so_pcb == NULL) { error = EINVAL; break; } INP_LOCK(inp); + INP_INFO_WUNLOCK(pcbinfo); switch (optval) { case IP_PORTRANGE_DEFAULT: inp->inp_flags &= ~(INP_LOWPORT); @@ -1480,6 +1504,15 @@ return (error); } +int +ip_ctloutput(so, sopt) + struct socket *so; + struct sockopt *sopt; +{ + + return (ip_ctloutput_pcbinfo(so, sopt, NULL)); +} + /* * Set up IP options in pcb for insertion in output packets. * Store in mbuf with pointer in pcbopt, adding pseudo-option Index: ip_var.h =================================================================== RCS file: /data/fbsd-cvs/ncvs/src/sys/netinet/ip_var.h,v retrieving revision 1.95 diff -u -r1.95 ip_var.h --- ip_var.h 2 Jul 2005 23:13:31 -0000 1.95 +++ ip_var.h 24 Nov 2006 15:32:53 -0000 @@ -144,6 +144,7 @@ struct ip; struct inpcb; +struct inpcbinfo; struct route; struct sockopt; @@ -164,6 +165,8 @@ extern struct pr_usrreqs rip_usrreqs; int ip_ctloutput(struct socket *, struct sockopt *sopt); +int ip_ctloutput_pcbinfo(struct socket *, struct sockopt *sopt, + struct inpcbinfo *pcbinfo); void ip_drain(void); void ip_fini(void *xtp); int ip_fragment(struct ip *ip, struct mbuf **m_frag, int mtu, Index: tcp_usrreq.c =================================================================== RCS file: /data/fbsd-cvs/ncvs/src/sys/netinet/tcp_usrreq.c,v retrieving revision 1.124.2.3 diff -u -r1.124.2.3 tcp_usrreq.c --- tcp_usrreq.c 27 Sep 2006 09:24:44 -0000 1.124.2.3 +++ tcp_usrreq.c 24 Nov 2006 14:59:41 -0000 @@ -1035,7 +1035,7 @@ error = ip6_ctloutput(so, sopt); else #endif /* INET6 */ - error = ip_ctloutput(so, sopt); + error = ip_ctloutput_pcbinfo(so, sopt, &tcbinfo); return (error); } tp = intotcpcb(inp);