mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-20 11:11:24 +00:00
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 <s.dave.jones@gmail.com>, Arnaud Lacombe <lacombar@gmail.com> Suggested by: rwatson Glanced by: rwatson Tested by: dave jones <s.dave.jones@gmail.com>
This commit is contained in:
parent
29381b363b
commit
fc06cd427e
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=227207
@ -575,8 +575,7 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
|
||||
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, struct sockaddr *nam, in_addr_t *laddrp,
|
||||
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);
|
||||
}
|
||||
|
@ -540,6 +540,7 @@ void inp_4tuple_get(struct inpcb *inp, uint32_t *laddr, uint16_t *lp,
|
||||
#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.
|
||||
|
@ -895,12 +895,43 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt)
|
||||
|
||||
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) {
|
||||
|
@ -207,8 +207,8 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam,
|
||||
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, struct sockaddr *nam,
|
||||
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
|
||||
|
@ -1421,7 +1421,38 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt)
|
||||
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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user