From owner-svn-src-all@FreeBSD.ORG Sun Nov 6 10:47:20 2011 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id E6ACD106564A; Sun, 6 Nov 2011 10:47:20 +0000 (UTC) (envelope-from trociny@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id D65D58FC0C; Sun, 6 Nov 2011 10:47:20 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id pA6AlKRp017574; Sun, 6 Nov 2011 10:47:20 GMT (envelope-from trociny@svn.freebsd.org) Received: (from trociny@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id pA6AlKnc017568; Sun, 6 Nov 2011 10:47:20 GMT (envelope-from trociny@svn.freebsd.org) Message-Id: <201111061047.pA6AlKnc017568@svn.freebsd.org> From: Mikolaj Golub Date: Sun, 6 Nov 2011 10:47:20 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r227207 - in head/sys: netinet netinet6 X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 06 Nov 2011 10:47:21 -0000 Author: trociny Date: Sun Nov 6 10:47:20 2011 New Revision: 227207 URL: http://svn.freebsd.org/changeset/base/227207 Log: Cache SO_REUSEPORT socket option in inpcb-layer in order to avoid inp_socket->so_options dereference when we may not acquire the lock on the inpcb. This fixes the crash due to NULL pointer dereference in in_pcbbind_setup() when inp_socket->so_options in a pcb returned by in_pcblookup_local() was checked. Reported by: dave jones , Arnaud Lacombe Suggested by: rwatson Glanced by: rwatson Tested by: dave jones Modified: head/sys/netinet/in_pcb.c head/sys/netinet/in_pcb.h head/sys/netinet/ip_output.c head/sys/netinet6/in6_pcb.c head/sys/netinet6/ip6_output.c Modified: head/sys/netinet/in_pcb.c ============================================================================== --- head/sys/netinet/in_pcb.c Sun Nov 6 09:29:52 2011 (r227206) +++ head/sys/netinet/in_pcb.c Sun Nov 6 10:47:20 2011 (r227207) @@ -575,8 +575,7 @@ in_pcbbind_setup(struct inpcb *inp, stru ntohl(t->inp_faddr.s_addr) == INADDR_ANY) && (ntohl(sin->sin_addr.s_addr) != INADDR_ANY || ntohl(t->inp_laddr.s_addr) != INADDR_ANY || - (t->inp_socket->so_options & - SO_REUSEPORT) == 0) && + (t->inp_flags2 & INP_REUSEPORT) == 0) && (inp->inp_cred->cr_uid != t->inp_cred->cr_uid)) return (EADDRINUSE); @@ -594,15 +593,15 @@ in_pcbbind_setup(struct inpcb *inp, stru if (tw == NULL || (reuseport & tw->tw_so_options) == 0) return (EADDRINUSE); - } else if (t && - (reuseport & t->inp_socket->so_options) == 0) { + } else if (t && (reuseport == 0 || + (t->inp_flags2 & INP_REUSEPORT) == 0)) { #ifdef INET6 if (ntohl(sin->sin_addr.s_addr) != INADDR_ANY || ntohl(t->inp_laddr.s_addr) != INADDR_ANY || - INP_SOCKAF(so) == - INP_SOCKAF(t->inp_socket)) + (inp->inp_vflag & INP_IPV6PROTO) == 0 || + (t->inp_vflag & INP_IPV6PROTO) == 0) #endif return (EADDRINUSE); } Modified: head/sys/netinet/in_pcb.h ============================================================================== --- head/sys/netinet/in_pcb.h Sun Nov 6 09:29:52 2011 (r227206) +++ head/sys/netinet/in_pcb.h Sun Nov 6 10:47:20 2011 (r227207) @@ -540,6 +540,7 @@ void inp_4tuple_get(struct inpcb *inp, #define INP_LLE_VALID 0x00000001 /* cached lle is valid */ #define INP_RT_VALID 0x00000002 /* cached rtentry is valid */ #define INP_PCBGROUPWILD 0x00000004 /* in pcbgroup wildcard list */ +#define INP_REUSEPORT 0x00000008 /* SO_REUSEPORT option is set */ /* * Flags passed to in_pcblookup*() functions. Modified: head/sys/netinet/ip_output.c ============================================================================== --- head/sys/netinet/ip_output.c Sun Nov 6 09:29:52 2011 (r227206) +++ head/sys/netinet/ip_output.c Sun Nov 6 10:47:20 2011 (r227207) @@ -895,12 +895,43 @@ ip_ctloutput(struct socket *so, struct s error = optval = 0; if (sopt->sopt_level != IPPROTO_IP) { - if ((sopt->sopt_level == SOL_SOCKET) && - (sopt->sopt_name == SO_SETFIB)) { - inp->inp_inc.inc_fibnum = so->so_fibnum; - return (0); + error = EINVAL; + + if (sopt->sopt_level == SOL_SOCKET && + sopt->sopt_dir == SOPT_SET) { + switch (sopt->sopt_name) { + case SO_REUSEADDR: + INP_WLOCK(inp); + if (IN_MULTICAST(ntohl(inp->inp_laddr.s_addr))) { + if ((so->so_options & + (SO_REUSEADDR | SO_REUSEPORT)) != 0) + inp->inp_flags2 |= INP_REUSEPORT; + else + inp->inp_flags2 &= ~INP_REUSEPORT; + } + INP_WUNLOCK(inp); + error = 0; + break; + case SO_REUSEPORT: + INP_WLOCK(inp); + if ((so->so_options & SO_REUSEPORT) != 0) + inp->inp_flags2 |= INP_REUSEPORT; + else + inp->inp_flags2 &= ~INP_REUSEPORT; + INP_WUNLOCK(inp); + error = 0; + break; + case SO_SETFIB: + INP_WLOCK(inp); + inp->inp_inc.inc_fibnum = so->so_fibnum; + INP_WUNLOCK(inp); + error = 0; + break; + default: + break; + } } - return (EINVAL); + return (error); } switch (sopt->sopt_dir) { Modified: head/sys/netinet6/in6_pcb.c ============================================================================== --- head/sys/netinet6/in6_pcb.c Sun Nov 6 09:29:52 2011 (r227206) +++ head/sys/netinet6/in6_pcb.c Sun Nov 6 10:47:20 2011 (r227207) @@ -207,8 +207,8 @@ in6_pcbbind(register struct inpcb *inp, IN6_IS_ADDR_UNSPECIFIED(&t->in6p_faddr)) && (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || - (t->inp_socket->so_options & SO_REUSEPORT) - == 0) && (inp->inp_cred->cr_uid != + (t->inp_flags2 & INP_REUSEPORT) == 0) && + (inp->inp_cred->cr_uid != t->inp_cred->cr_uid)) return (EADDRINUSE); #ifdef INET @@ -267,12 +267,10 @@ in6_pcbbind(register struct inpcb *inp, INP_IPV6PROTO) == (t->inp_vflag & INP_IPV6PROTO)))) return (EADDRINUSE); - } - else if (t && - (reuseport & t->inp_socket->so_options) - == 0 && (ntohl(t->inp_laddr.s_addr) != - INADDR_ANY || INP_SOCKAF(so) == - INP_SOCKAF(t->inp_socket))) + } else if (t && (reuseport == 0 || + (t->inp_flags2 & INP_REUSEPORT) == 0) && + (ntohl(t->inp_laddr.s_addr) != INADDR_ANY || + (t->inp_vflag & INP_IPV6PROTO) == 0)) return (EADDRINUSE); } #endif Modified: head/sys/netinet6/ip6_output.c ============================================================================== --- head/sys/netinet6/ip6_output.c Sun Nov 6 09:29:52 2011 (r227206) +++ head/sys/netinet6/ip6_output.c Sun Nov 6 10:47:20 2011 (r227207) @@ -1421,7 +1421,38 @@ ip6_ctloutput(struct socket *so, struct optval = 0; uproto = (int)so->so_proto->pr_protocol; - if (level == IPPROTO_IPV6) { + if (level != IPPROTO_IPV6) { + error = EINVAL; + + if (sopt->sopt_level == SOL_SOCKET && + sopt->sopt_dir == SOPT_SET) { + switch (sopt->sopt_name) { + case SO_REUSEADDR: + INP_WLOCK(in6p); + if (IN_MULTICAST(ntohl(in6p->inp_laddr.s_addr))) { + if ((so->so_options & + (SO_REUSEADDR | SO_REUSEPORT)) != 0) + in6p->inp_flags2 |= INP_REUSEPORT; + else + in6p->inp_flags2 &= ~INP_REUSEPORT; + } + INP_WUNLOCK(in6p); + error = 0; + break; + case SO_REUSEPORT: + INP_WLOCK(in6p); + if ((so->so_options & SO_REUSEPORT) != 0) + in6p->inp_flags2 |= INP_REUSEPORT; + else + in6p->inp_flags2 &= ~INP_REUSEPORT; + INP_WUNLOCK(in6p); + error = 0; + break; + default: + break; + } + } + } else { /* level == IPPROTO_IPV6 */ switch (op) { case SOPT_SET: @@ -2044,8 +2075,6 @@ do { \ } break; } - } else { /* level != IPPROTO_IPV6 */ - error = EINVAL; } return (error); }