Date: Sun, 12 Jan 2020 17:52:33 +0000 (UTC) From: Michael Tuexen <tuexen@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r356663 - in head/sys: netinet netinet6 Message-ID: <202001121752.00CHqXrL079401@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: tuexen Date: Sun Jan 12 17:52:32 2020 New Revision: 356663 URL: https://svnweb.freebsd.org/changeset/base/356663 Log: Fix race when accepting TCP connections. When expanding a SYN-cache entry to a socket/inp a two step approach was taken: 1) The local address was filled in, then the inp was added to the hash table. 2) The remote address was filled in and the inp was relocated in the hash table. Before the epoch changes, a write lock was held when this happens and the code looking up entries was holding a corresponding read lock. Since the read lock is gone away after the introduction of the epochs, the half populated inp was found during lookup. This resulted in processing TCP segments in the context of the wrong TCP connection. This patch changes the above procedure in a way that the inp is fully populated before inserted into the hash table. Thanks to Paul <devgs@ukr.net> for reporting the issue on the net@ mailing list and for testing the patch! Reviewed by: rrs@ MFC after: 1 week Sponsored by: Netflix, Inc. Differential Revision: https://reviews.freebsd.org/D22971 Modified: head/sys/netinet/in_pcb.c head/sys/netinet/in_pcb.h head/sys/netinet/tcp_syncache.c head/sys/netinet6/in6_pcb.c head/sys/netinet6/in6_pcb.h Modified: head/sys/netinet/in_pcb.c ============================================================================== --- head/sys/netinet/in_pcb.c Sun Jan 12 17:41:09 2020 (r356662) +++ head/sys/netinet/in_pcb.c Sun Jan 12 17:52:32 2020 (r356663) @@ -972,7 +972,7 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *n */ int in_pcbconnect_mbuf(struct inpcb *inp, struct sockaddr *nam, - struct ucred *cred, struct mbuf *m) + struct ucred *cred, struct mbuf *m, bool rehash) { u_short lport, fport; in_addr_t laddr, faddr; @@ -991,6 +991,8 @@ in_pcbconnect_mbuf(struct inpcb *inp, struct sockaddr /* Do the initial binding of the local address if required. */ if (inp->inp_laddr.s_addr == INADDR_ANY && inp->inp_lport == 0) { + KASSERT(rehash == true, + ("Rehashing required for unbound inps")); inp->inp_lport = lport; inp->inp_laddr.s_addr = laddr; if (in_pcbinshash(inp) != 0) { @@ -1005,7 +1007,11 @@ in_pcbconnect_mbuf(struct inpcb *inp, struct sockaddr inp->inp_laddr.s_addr = laddr; inp->inp_faddr.s_addr = faddr; inp->inp_fport = fport; - in_pcbrehash_mbuf(inp, m); + if (rehash) { + in_pcbrehash_mbuf(inp, m); + } else { + in_pcbinshash_mbuf(inp, m); + } if (anonport) inp->inp_flags |= INP_ANONPORT; @@ -1016,7 +1022,7 @@ int in_pcbconnect(struct inpcb *inp, struct sockaddr *nam, struct ucred *cred) { - return (in_pcbconnect_mbuf(inp, nam, cred, NULL)); + return (in_pcbconnect_mbuf(inp, nam, cred, NULL, true)); } /* @@ -2500,7 +2506,7 @@ in_pcblookup_mbuf(struct inpcbinfo *pcbinfo, struct in * Insert PCB onto various hash lists. */ static int -in_pcbinshash_internal(struct inpcb *inp, int do_pcbgroup_update) +in_pcbinshash_internal(struct inpcb *inp, struct mbuf *m) { struct inpcbhead *pcbhash; struct inpcbporthead *pcbporthash; @@ -2566,35 +2572,27 @@ in_pcbinshash_internal(struct inpcb *inp, int do_pcbgr CK_LIST_INSERT_HEAD(pcbhash, inp, inp_hash); inp->inp_flags |= INP_INHASHLIST; #ifdef PCBGROUP - if (do_pcbgroup_update) + if (m != NULL) { + in_pcbgroup_update_mbuf(inp, m); + } else { in_pcbgroup_update(inp); + } #endif return (0); } -/* - * For now, there are two public interfaces to insert an inpcb into the hash - * lists -- one that does update pcbgroups, and one that doesn't. The latter - * is used only in the TCP syncache, where in_pcbinshash is called before the - * full 4-tuple is set for the inpcb, and we don't want to install in the - * pcbgroup until later. - * - * XXXRW: This seems like a misfeature. in_pcbinshash should always update - * connection groups, and partially initialised inpcbs should not be exposed - * to either reservation hash tables or pcbgroups. - */ int in_pcbinshash(struct inpcb *inp) { - return (in_pcbinshash_internal(inp, 1)); + return (in_pcbinshash_internal(inp, NULL)); } int -in_pcbinshash_nopcbgroup(struct inpcb *inp) +in_pcbinshash_mbuf(struct inpcb *inp, struct mbuf *m) { - return (in_pcbinshash_internal(inp, 0)); + return (in_pcbinshash_internal(inp, m)); } /* Modified: head/sys/netinet/in_pcb.h ============================================================================== --- head/sys/netinet/in_pcb.h Sun Jan 12 17:41:09 2020 (r356662) +++ head/sys/netinet/in_pcb.h Sun Jan 12 17:52:32 2020 (r356663) @@ -832,7 +832,7 @@ int in_pcbbind_setup(struct inpcb *, struct sockaddr * u_short *, struct ucred *); int in_pcbconnect(struct inpcb *, struct sockaddr *, struct ucred *); int in_pcbconnect_mbuf(struct inpcb *, struct sockaddr *, struct ucred *, - struct mbuf *); + struct mbuf *, bool); int in_pcbconnect_setup(struct inpcb *, struct sockaddr *, in_addr_t *, u_short *, in_addr_t *, u_short *, struct inpcb **, struct ucred *); @@ -841,7 +841,7 @@ void in_pcbdisconnect(struct inpcb *); void in_pcbdrop(struct inpcb *); void in_pcbfree(struct inpcb *); int in_pcbinshash(struct inpcb *); -int in_pcbinshash_nopcbgroup(struct inpcb *); +int in_pcbinshash_mbuf(struct inpcb *, struct mbuf *); int in_pcbladdr(struct inpcb *, struct in_addr *, struct in_addr *, struct ucred *); struct inpcb * Modified: head/sys/netinet/tcp_syncache.c ============================================================================== --- head/sys/netinet/tcp_syncache.c Sun Jan 12 17:41:09 2020 (r356662) +++ head/sys/netinet/tcp_syncache.c Sun Jan 12 17:52:32 2020 (r356663) @@ -841,34 +841,8 @@ syncache_socket(struct syncache *sc, struct socket *ls #endif } - /* - * Install in the reservation hash table for now, but don't yet - * install a connection group since the full 4-tuple isn't yet - * configured. - */ inp->inp_lport = sc->sc_inc.inc_lport; - if ((error = in_pcbinshash_nopcbgroup(inp)) != 0) { - /* - * Undo the assignments above if we failed to - * put the PCB on the hash lists. - */ #ifdef INET6 - if (sc->sc_inc.inc_flags & INC_ISIPV6) - inp->in6p_laddr = in6addr_any; - else -#endif - inp->inp_laddr.s_addr = INADDR_ANY; - inp->inp_lport = 0; - if ((s = tcp_log_addrs(&sc->sc_inc, NULL, NULL, NULL))) { - log(LOG_DEBUG, "%s; %s: in_pcbinshash failed " - "with error %i\n", - s, __func__, error); - free(s, M_TCPLOG); - } - INP_HASH_WUNLOCK(&V_tcbinfo); - goto abort; - } -#ifdef INET6 if (inp->inp_vflag & INP_IPV6PROTO) { struct inpcb *oinp = sotoinpcb(lso); @@ -900,7 +874,7 @@ syncache_socket(struct syncache *sc, struct socket *ls if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) inp->in6p_laddr = sc->sc_inc.inc6_laddr; if ((error = in6_pcbconnect_mbuf(inp, (struct sockaddr *)&sin6, - thread0.td_ucred, m)) != 0) { + thread0.td_ucred, m, false)) != 0) { inp->in6p_laddr = laddr6; if ((s = tcp_log_addrs(&sc->sc_inc, NULL, NULL, NULL))) { log(LOG_DEBUG, "%s; %s: in6_pcbconnect failed " @@ -940,7 +914,7 @@ syncache_socket(struct syncache *sc, struct socket *ls if (inp->inp_laddr.s_addr == INADDR_ANY) inp->inp_laddr = sc->sc_inc.inc_laddr; if ((error = in_pcbconnect_mbuf(inp, (struct sockaddr *)&sin, - thread0.td_ucred, m)) != 0) { + thread0.td_ucred, m, false)) != 0) { inp->inp_laddr = laddr; if ((s = tcp_log_addrs(&sc->sc_inc, NULL, NULL, NULL))) { log(LOG_DEBUG, "%s; %s: in_pcbconnect failed " Modified: head/sys/netinet6/in6_pcb.c ============================================================================== --- head/sys/netinet6/in6_pcb.c Sun Jan 12 17:41:09 2020 (r356662) +++ head/sys/netinet6/in6_pcb.c Sun Jan 12 17:52:32 2020 (r356663) @@ -411,7 +411,7 @@ in6_pcbladdr(struct inpcb *inp, struct sockaddr *nam, */ int in6_pcbconnect_mbuf(struct inpcb *inp, struct sockaddr *nam, - struct ucred *cred, struct mbuf *m) + struct ucred *cred, struct mbuf *m, bool rehash) { struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; @@ -437,6 +437,8 @@ in6_pcbconnect_mbuf(struct inpcb *inp, struct sockaddr } if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) { if (inp->inp_lport == 0) { + KASSERT(rehash == true, + ("Rehashing required for unbound inps")); error = in6_pcbbind(inp, (struct sockaddr *)0, cred); if (error) return (error); @@ -451,7 +453,11 @@ in6_pcbconnect_mbuf(struct inpcb *inp, struct sockaddr inp->inp_flow |= (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK); - in_pcbrehash_mbuf(inp, m); + if (rehash) { + in_pcbrehash_mbuf(inp, m); + } else { + in_pcbinshash_mbuf(inp, m); + } return (0); } @@ -460,7 +466,7 @@ int in6_pcbconnect(struct inpcb *inp, struct sockaddr *nam, struct ucred *cred) { - return (in6_pcbconnect_mbuf(inp, nam, cred, NULL)); + return (in6_pcbconnect_mbuf(inp, nam, cred, NULL, true)); } void Modified: head/sys/netinet6/in6_pcb.h ============================================================================== --- head/sys/netinet6/in6_pcb.h Sun Jan 12 17:41:09 2020 (r356662) +++ head/sys/netinet6/in6_pcb.h Sun Jan 12 17:52:32 2020 (r356663) @@ -86,7 +86,7 @@ void in6_losing(struct inpcb *); int in6_pcbbind(struct inpcb *, struct sockaddr *, struct ucred *); int in6_pcbconnect(struct inpcb *, struct sockaddr *, struct ucred *); int in6_pcbconnect_mbuf(struct inpcb *, struct sockaddr *, - struct ucred *, struct mbuf *); + struct ucred *, struct mbuf *, bool); void in6_pcbdisconnect(struct inpcb *); struct inpcb * in6_pcblookup_local(struct inpcbinfo *,
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202001121752.00CHqXrL079401>