Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F163566850
D12586.id34349.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
24 KB
Referenced Files
None
Subscribers
None
D12586.id34349.diff
View Options
Index: sys/netinet/ip_output.c
===================================================================
--- sys/netinet/ip_output.c
+++ sys/netinet/ip_output.c
@@ -955,13 +955,25 @@
int
ip_ctloutput(struct socket *so, struct sockopt *sopt)
{
- struct inpcb *inp = sotoinpcb(so);
+ struct inpcb *inp;
+ struct mbuf *options;
int error, optval;
+ bool rlock = false;
+ bool wlock = false;
#ifdef RSS
uint32_t rss_bucket;
int retval;
#endif
+ SOCK_LOCK(so);
+ inp = sotoinpcb(so);
+ if (inp == NULL) {
+ SOCK_UNLOCK(so);
+ return ENOTCONN;
+ }
+ in_pcbref(inp);
+ SOCK_UNLOCK(so);
+
error = optval = 0;
if (sopt->sopt_level != IPPROTO_IP) {
error = EINVAL;
@@ -971,33 +983,33 @@
switch (sopt->sopt_name) {
case SO_REUSEADDR:
INP_WLOCK(inp);
+ wlock = true;
if ((so->so_options & SO_REUSEADDR) != 0)
inp->inp_flags2 |= INP_REUSEADDR;
else
inp->inp_flags2 &= ~INP_REUSEADDR;
- INP_WUNLOCK(inp);
error = 0;
break;
case SO_REUSEPORT:
INP_WLOCK(inp);
+ wlock = true;
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);
+ wlock = true;
inp->inp_inc.inc_fibnum = so->so_fibnum;
- INP_WUNLOCK(inp);
error = 0;
break;
case SO_MAX_PACING_RATE:
#ifdef RATELIMIT
INP_WLOCK(inp);
+ wlock = true;
inp->inp_flags2 |= INP_RATE_LIMIT_CHANGED;
- INP_WUNLOCK(inp);
error = 0;
#else
error = EOPNOTSUPP;
@@ -1007,6 +1019,10 @@
break;
}
}
+ if (!wlock)
+ INP_WLOCK(inp);
+ if (!in_pcbrele_wlocked(inp))
+ INP_WUNLOCK(inp);
return (error);
}
@@ -1036,9 +1052,9 @@
break;
}
INP_WLOCK(inp);
+ wlock = true;
error = ip_pcbopts(inp, sopt->sopt_name, m);
- INP_WUNLOCK(inp);
- return (error);
+ break;
}
case IP_BINDANY:
@@ -1092,20 +1108,20 @@
#define OPTSET(bit) do { \
INP_WLOCK(inp); \
+ wlock = true; \
if (optval) \
inp->inp_flags |= bit; \
else \
inp->inp_flags &= ~bit; \
- INP_WUNLOCK(inp); \
} while (0)
#define OPTSET2(bit, val) do { \
INP_WLOCK(inp); \
+ wlock = true; \
if (val) \
inp->inp_flags2 |= bit; \
else \
inp->inp_flags2 &= ~bit; \
- INP_WUNLOCK(inp); \
} while (0)
case IP_RECVOPTS:
@@ -1200,6 +1216,7 @@
break;
INP_WLOCK(inp);
+ wlock = true;
switch (optval) {
case IP_PORTRANGE_DEFAULT:
inp->inp_flags &= ~(INP_LOWPORT);
@@ -1220,13 +1237,16 @@
error = EINVAL;
break;
}
- INP_WUNLOCK(inp);
break;
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
case IP_IPSEC_POLICY:
if (IPSEC_ENABLED(ipv4)) {
+ INP_WLOCK(inp);
+ wlock = true;
error = IPSEC_PCBCTL(ipv4, inp, sopt);
+ if (error)
+ wlock = false;
break;
}
/* FALLTHROUGH */
@@ -1242,12 +1262,18 @@
switch (sopt->sopt_name) {
case IP_OPTIONS:
case IP_RETOPTS:
- if (inp->inp_options)
+ if (inp->inp_options) {
+ options = malloc(sopt->sopt_valsize, M_TEMP, M_WAITOK|M_ZERO);
+ INP_RLOCK(inp);
+ bcopy(inp->inp_options, options, min(inp->inp_options->m_len, sopt->sopt_valsize));
+ if (!in_pcbrele_rlocked(inp))
+ INP_RUNLOCK(inp);
error = sooptcopyout(sopt,
- mtod(inp->inp_options,
+ mtod(options,
char *),
- inp->inp_options->m_len);
- else
+ options->m_len);
+ return (error);
+ } else
sopt->sopt_valsize = 0;
break;
@@ -1315,6 +1341,8 @@
break;
case IP_PORTRANGE:
+ INP_RLOCK(inp);
+ rlock = true;
if (inp->inp_flags & INP_HIGHPORT)
optval = IP_PORTRANGE_HIGH;
else if (inp->inp_flags & INP_LOWPORT)
@@ -1346,6 +1374,8 @@
break;
#ifdef RSS
case IP_RSSBUCKETID:
+ INP_RLOCK(inp);
+ rlock = true;
retval = rss_hash2bucket(inp->inp_flowid,
inp->inp_flowtype,
&rss_bucket);
@@ -1362,8 +1392,12 @@
optval = OPTBIT2(INP_BINDMULTI);
break;
}
+ if (!rlock)
+ INP_RLOCK(inp);
+ if (!in_pcbrele_rlocked(inp))
+ INP_RUNLOCK(inp);
error = sooptcopyout(sopt, &optval, sizeof optval);
- break;
+ return (error);
/*
* Multicast socket options are processed by the in_mcast
@@ -1380,7 +1414,9 @@
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
case IP_IPSEC_POLICY:
if (IPSEC_ENABLED(ipv4)) {
+ INP_WLOCK(inp);
error = IPSEC_PCBCTL(ipv4, inp, sopt);
+ INP_WUNLOCK(inp);
break;
}
/* FALLTHROUGH */
@@ -1392,6 +1428,14 @@
}
break;
}
+ if (wlock && !in_pcbrele_wlocked(inp))
+ INP_WUNLOCK(inp);
+ else {
+ if (!rlock)
+ INP_RLOCK(inp);
+ if (!in_pcbrele_rlocked(inp))
+ INP_RUNLOCK(inp);
+ }
return (error);
}
Index: sys/netinet/raw_ip.c
===================================================================
--- sys/netinet/raw_ip.c
+++ sys/netinet/raw_ip.c
@@ -563,19 +563,30 @@
int
rip_ctloutput(struct socket *so, struct sockopt *sopt)
{
- struct inpcb *inp = sotoinpcb(so);
- int error, optval;
+ struct inpcb *inp;
+ int optval;
+ int error = 0;
+ bool wlock = false;
+
+ SOCK_LOCK(so);
+ inp = sotoinpcb(so);
+ if (inp == NULL) {
+ SOCK_UNLOCK(so);
+ return ENOTCONN;
+ }
+ in_pcbref(inp);
+ SOCK_UNLOCK(so);
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);
+ goto done;
}
- return (EINVAL);
+ error = EINVAL;
+ goto done;
}
- error = 0;
switch (sopt->sopt_dir) {
case SOPT_GET:
switch (sopt->sopt_name) {
@@ -619,7 +630,7 @@
case MRT_DEL_BW_UPCALL:
error = priv_check(curthread, PRIV_NETINET_MROUTE);
if (error != 0)
- return (error);
+ break;
error = ip_mrouter_get ? ip_mrouter_get(so, sopt) :
EOPNOTSUPP;
break;
@@ -637,6 +648,8 @@
sizeof optval);
if (error)
break;
+ INP_WLOCK(inp);
+ wlock = true;
if (optval)
inp->inp_flags |= INP_HDRINCL;
else
@@ -673,14 +686,14 @@
case IP_RSVP_ON:
error = priv_check(curthread, PRIV_NETINET_MROUTE);
if (error != 0)
- return (error);
+ break;
error = ip_rsvp_init(so);
break;
case IP_RSVP_OFF:
error = priv_check(curthread, PRIV_NETINET_MROUTE);
if (error != 0)
- return (error);
+ break;
error = ip_rsvp_done();
break;
@@ -688,7 +701,7 @@
case IP_RSVP_VIF_OFF:
error = priv_check(curthread, PRIV_NETINET_MROUTE);
if (error != 0)
- return (error);
+ break;
error = ip_rsvp_vif ?
ip_rsvp_vif(so, sopt) : EINVAL;
break;
@@ -707,7 +720,7 @@
case MRT_DEL_BW_UPCALL:
error = priv_check(curthread, PRIV_NETINET_MROUTE);
if (error != 0)
- return (error);
+ break;
error = ip_mrouter_set ? ip_mrouter_set(so, sopt) :
EOPNOTSUPP;
break;
@@ -719,6 +732,12 @@
break;
}
+done:
+ if (!wlock)
+ INP_WLOCK(inp);
+ if (!in_pcbrele_wlocked(inp))
+ INP_WUNLOCK(inp);
+
return (error);
}
Index: sys/netinet6/ip6_output.c
===================================================================
--- sys/netinet6/ip6_output.c
+++ sys/netinet6/ip6_output.c
@@ -133,7 +133,7 @@
struct ucred *, int);
static int ip6_pcbopts(struct ip6_pktopts **, struct mbuf *,
struct socket *, struct sockopt *);
-static int ip6_getpcbopt(struct ip6_pktopts *, int, struct sockopt *);
+static int ip6_getpcbopt(struct inpcb *, int, struct sockopt *);
static int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *,
struct ucred *, int, int, int);
@@ -1400,11 +1400,13 @@
{
int optdatalen, uproto;
void *optdata;
- struct inpcb *in6p = sotoinpcb(so);
+ struct inpcb *in6p;
int error, optval;
int level, op, optname;
int optlen;
struct thread *td;
+ bool rlock = false;
+ bool wlock = false;
#ifdef RSS
uint32_t rss_bucket;
int retval;
@@ -1428,6 +1430,15 @@
optval = 0;
uproto = (int)so->so_proto->pr_protocol;
+ SOCK_LOCK(so);
+ in6p = sotoinpcb(so);
+ if (in6p == NULL) {
+ SOCK_UNLOCK(so);
+ return ENOTCONN;
+ }
+ in_pcbref(in6p);
+ SOCK_UNLOCK(so);
+
if (level != IPPROTO_IPV6) {
error = EINVAL;
@@ -1436,33 +1447,33 @@
switch (sopt->sopt_name) {
case SO_REUSEADDR:
INP_WLOCK(in6p);
+ wlock = true;
if ((so->so_options & SO_REUSEADDR) != 0)
in6p->inp_flags2 |= INP_REUSEADDR;
else
in6p->inp_flags2 &= ~INP_REUSEADDR;
- INP_WUNLOCK(in6p);
error = 0;
break;
case SO_REUSEPORT:
INP_WLOCK(in6p);
+ wlock = true;
if ((so->so_options & SO_REUSEPORT) != 0)
in6p->inp_flags2 |= INP_REUSEPORT;
else
in6p->inp_flags2 &= ~INP_REUSEPORT;
- INP_WUNLOCK(in6p);
error = 0;
break;
case SO_SETFIB:
INP_WLOCK(in6p);
+ wlock = true;
in6p->inp_inc.inc_fibnum = so->so_fibnum;
- INP_WUNLOCK(in6p);
error = 0;
break;
case SO_MAX_PACING_RATE:
#ifdef RATELIMIT
INP_WLOCK(in6p);
+ wlock = true;
in6p->inp_flags2 |= INP_RATE_LIMIT_CHANGED;
- INP_WUNLOCK(in6p);
error = 0;
#else
error = EOPNOTSUPP;
@@ -1496,6 +1507,8 @@
error = soopt_mcopyin(sopt, m); /* XXX */
if (error != 0)
break;
+ INP_WLOCK(in6p);
+ wlock = true;
error = ip6_pcbopts(&in6p->in6p_outputopts,
m, so, sopt);
m_freem(m); /* XXX */
@@ -1567,6 +1580,8 @@
error = EINVAL;
else {
/* -1 = kernel default */
+ INP_WLOCK(in6p);
+ wlock = true;
in6p->in6p_hops = optval;
if ((in6p->inp_vflag &
INP_IPV4) != 0)
@@ -1575,42 +1590,58 @@
break;
#define OPTSET(bit) \
do { \
- INP_WLOCK(in6p); \
+ if (!wlock) { \
+ INP_WLOCK(in6p); \
+ wlock = true; \
+ } \
if (optval) \
in6p->inp_flags |= (bit); \
else \
in6p->inp_flags &= ~(bit); \
- INP_WUNLOCK(in6p); \
} while (/*CONSTCOND*/ 0)
#define OPTSET2292(bit) \
do { \
- INP_WLOCK(in6p); \
+ if (!wlock) { \
+ INP_WLOCK(in6p); \
+ wlock = true; \
+ } \
in6p->inp_flags |= IN6P_RFC2292; \
if (optval) \
in6p->inp_flags |= (bit); \
else \
in6p->inp_flags &= ~(bit); \
- INP_WUNLOCK(in6p); \
} while (/*CONSTCOND*/ 0)
#define OPTBIT(bit) (in6p->inp_flags & (bit) ? 1 : 0)
#define OPTSET2(bit, val) do { \
- INP_WLOCK(in6p); \
+ if (!wlock) { \
+ INP_WLOCK(in6p); \
+ wlock = true; \
+ } \
if (val) \
in6p->inp_flags2 |= bit; \
else \
in6p->inp_flags2 &= ~bit; \
- INP_WUNLOCK(in6p); \
} while (0)
#define OPTBIT2(bit) (in6p->inp_flags2 & (bit) ? 1 : 0)
+#define OPTSET2292_EXCLUSIVE(bit) \
+do { \
+ if (!wlock) { \
+ INP_WLOCK(in6p); \
+ wlock = true; \
+ } \
+ if (OPTBIT(IN6P_RFC2292)) { \
+ error = EINVAL; \
+ } else { \
+ if (optval) \
+ in6p->inp_flags |= (bit); \
+ else \
+ in6p->inp_flags &= ~(bit); \
+ } \
+} while (/*CONSTCOND*/ 0)
case IPV6_RECVPKTINFO:
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_PKTINFO);
+ OPTSET2292_EXCLUSIVE(IN6P_PKTINFO);
break;
case IPV6_HOPLIMIT:
@@ -1622,6 +1653,8 @@
error = EINVAL;
break;
}
+ INP_WLOCK(in6p);
+ wlock = true;
optp = &in6p->in6p_outputopts;
error = ip6_pcbopt(IPV6_HOPLIMIT,
(u_char *)&optval, sizeof(optval),
@@ -1631,48 +1664,23 @@
}
case IPV6_RECVHOPLIMIT:
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_HOPLIMIT);
+ OPTSET2292_EXCLUSIVE(IN6P_HOPLIMIT);
break;
case IPV6_RECVHOPOPTS:
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_HOPOPTS);
+ OPTSET2292_EXCLUSIVE(IN6P_HOPOPTS);
break;
case IPV6_RECVDSTOPTS:
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_DSTOPTS);
+ OPTSET2292_EXCLUSIVE(IN6P_DSTOPTS);
break;
case IPV6_RECVRTHDRDSTOPTS:
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_RTHDRDSTOPTS);
+ OPTSET2292_EXCLUSIVE(IN6P_RTHDRDSTOPTS);
break;
case IPV6_RECVRTHDR:
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_RTHDR);
+ OPTSET2292_EXCLUSIVE(IN6P_RTHDR);
break;
case IPV6_RECVPATHMTU:
@@ -1702,12 +1710,15 @@
* available only prior to bind(2).
* see ipng mailing list, Jun 22 2001.
*/
+ INP_WLOCK(in6p);
+ wlock = true;
if (in6p->inp_lport ||
!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) {
error = EINVAL;
break;
}
OPTSET(IN6P_IPV6_V6ONLY);
+ /* optclear vflag */
if (optval)
in6p->inp_vflag &= ~INP_IPV4;
else
@@ -1715,11 +1726,7 @@
break;
case IPV6_RECVTCLASS:
/* cannot mix with RFC2292 XXX */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
- OPTSET(IN6P_TCLASS);
+ OPTSET2292_EXCLUSIVE(IN6P_TCLASS);
break;
case IPV6_AUTOFLOWLABEL:
OPTSET(IN6P_AUTOFLOWLABEL);
@@ -1739,8 +1746,8 @@
case IPV6_RSS_LISTEN_BUCKET:
if ((optval >= 0) &&
(optval < rss_getnumbuckets())) {
- in6p->inp_rss_listen_bucket = optval;
OPTSET2(INP_RSS_BUCKET_SET, 1);
+ in6p->inp_rss_listen_bucket = optval;
} else {
error = EINVAL;
}
@@ -1763,6 +1770,8 @@
break;
{
struct ip6_pktopts **optp;
+ INP_WLOCK(in6p);
+ wlock = true;
optp = &in6p->in6p_outputopts;
error = ip6_pcbopt(optname,
(u_char *)&optval, sizeof(optval),
@@ -1801,7 +1810,7 @@
error = priv_check(td,
PRIV_NETINET_SETHDROPTS);
if (error)
- return (error);
+ break;
}
OPTSET2292(IN6P_HOPOPTS);
break;
@@ -1810,7 +1819,7 @@
error = priv_check(td,
PRIV_NETINET_SETHDROPTS);
if (error)
- return (error);
+ break;
}
OPTSET2292(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */
break;
@@ -1832,12 +1841,6 @@
int optlen;
struct ip6_pktopts **optp;
- /* cannot mix with RFC2292 */
- if (OPTBIT(IN6P_RFC2292)) {
- error = EINVAL;
- break;
- }
-
/*
* We only ensure valsize is not too large
* here. Further validation will be done
@@ -1847,6 +1850,14 @@
sizeof(optbuf_storage), 0);
if (error)
break;
+
+ INP_WLOCK(in6p);
+ wlock = true;
+ /* cannot mix with RFC2292 */
+ if (OPTBIT(IN6P_RFC2292)) {
+ error = EINVAL;
+ break;
+ }
optlen = sopt->sopt_valsize;
optbuf = optbuf_storage;
optp = &in6p->in6p_outputopts;
@@ -1879,6 +1890,7 @@
break;
INP_WLOCK(in6p);
+ wlock = true;
switch (optval) {
case IPV6_PORTRANGE_DEFAULT:
in6p->inp_flags &= ~(INP_LOWPORT);
@@ -1899,13 +1911,16 @@
error = EINVAL;
break;
}
- INP_WUNLOCK(in6p);
break;
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
case IPV6_IPSEC_POLICY:
if (IPSEC_ENABLED(ipv6)) {
+ INP_WLOCK(in6p);
+ wlock = true;
error = IPSEC_PCBCTL(ipv6, in6p, sopt);
+ if (error)
+ wlock = false;
break;
}
/* FALLTHROUGH */
@@ -2037,6 +2052,8 @@
break;
#ifdef RSS
case IPV6_RSSBUCKETID:
+ INP_RLOCK(in6p);
+ rlock = true;
retval =
rss_hash2bucket(in6p->inp_flowid,
in6p->inp_flowtype,
@@ -2059,24 +2076,36 @@
}
if (error)
break;
+ if (!rlock)
+ INP_RLOCK(in6p);
+ if (!in_pcbrele_rlocked(in6p))
+ INP_RUNLOCK(in6p);
error = sooptcopyout(sopt, &optval,
sizeof optval);
- break;
+ return (error);
case IPV6_PATHMTU:
{
u_long pmtu = 0;
struct ip6_mtuinfo mtuinfo;
+ struct in6_addr addr;
- if (!(so->so_state & SS_ISCONNECTED))
- return (ENOTCONN);
+ if (!(so->so_state & SS_ISCONNECTED)) {
+ error = ENOTCONN;
+ break;
+ }
/*
* XXX: we dot not consider the case of source
* routing, or optional information to specify
* the outgoing interface.
+ * Copy faddr out of in6p to avoid holding lock
+ * on inp during route lookup.
*/
+ INP_RLOCK(in6p);
+ bcopy(&in6p->in6p_faddr, &addr, sizeof(addr));
+ INP_RUNLOCK(in6p);
error = ip6_getpmtu_ctl(so->so_fibnum,
- &in6p->in6p_faddr, &pmtu);
+ &addr, &pmtu);
if (error)
break;
if (pmtu > IPV6_MAXPACKET)
@@ -2126,8 +2155,7 @@
case IPV6_DONTFRAG:
case IPV6_USE_MIN_MTU:
case IPV6_PREFER_TEMPADDR:
- error = ip6_getpcbopt(in6p->in6p_outputopts,
- optname, sopt);
+ error = ip6_getpcbopt(in6p, optname, sopt);
break;
case IPV6_MULTICAST_IF:
@@ -2140,7 +2168,9 @@
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
case IPV6_IPSEC_POLICY:
if (IPSEC_ENABLED(ipv6)) {
+ INP_RLOCK(in6p);
error = IPSEC_PCBCTL(ipv6, in6p, sopt);
+ INP_RUNLOCK(in6p);
break;
}
/* FALLTHROUGH */
@@ -2152,6 +2182,14 @@
break;
}
}
+ if (wlock && !in_pcbrele_wlocked(in6p))
+ INP_WUNLOCK(in6p);
+ else {
+ if (!rlock)
+ INP_RLOCK(in6p);
+ if (!in_pcbrele_rlocked(in6p))
+ INP_RUNLOCK(in6p);
+ }
return (error);
}
@@ -2160,7 +2198,7 @@
{
int error = 0, optval, optlen;
const int icmp6off = offsetof(struct icmp6_hdr, icmp6_cksum);
- struct inpcb *in6p = sotoinpcb(so);
+ struct inpcb *in6p;
int level, op, optname;
level = sopt->sopt_level;
@@ -2172,6 +2210,15 @@
return (EINVAL);
}
+ SOCK_LOCK(so);
+ in6p = sotoinpcb(so);
+ if (in6p == NULL) {
+ SOCK_UNLOCK(so);
+ return ENOTCONN;
+ }
+ in_pcbref(in6p);
+ SOCK_UNLOCK(so);
+
switch (optname) {
case IPV6_CHECKSUM:
/*
@@ -2223,6 +2270,10 @@
break;
}
+ INP_RLOCK(in6p);
+ if (!in_pcbrele_rlocked(in6p))
+ INP_RUNLOCK(in6p);
+
return (error);
}
@@ -2238,6 +2289,8 @@
int error = 0;
struct thread *td = sopt->sopt_td;
+ INP_WLOCK_ASSERT(sotoinpcb(so));
+
/* turn off any old options. */
if (opt) {
#ifdef DIAGNOSTIC
@@ -2302,17 +2355,50 @@
return (ip6_setpktopt(optname, buf, len, opt, cred, 1, 0, uproto));
}
+#define GET_PKTOPT_VAR(field, lenexpr) do { \
+ if (pktopt && pktopt->field) { \
+ INP_RUNLOCK(in6p); \
+ optdata = malloc(sopt->sopt_valsize, M_TEMP, M_WAITOK); \
+ malloc_optdata = true; \
+ INP_RLOCK(in6p); \
+ if (in6p->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { \
+ INP_RUNLOCK(in6p); \
+ free(optdata, M_TEMP); \
+ return (ECONNRESET); \
+ } \
+ pktopt = in6p->in6p_outputopts; \
+ if (pktopt && pktopt->field) { \
+ optdatalen = min(lenexpr, sopt->sopt_valsize); \
+ bcopy(&pktopt->field, optdata, optdatalen); \
+ } else { \
+ free(optdata, M_TEMP); \
+ optdata = NULL; \
+ malloc_optdata = false; \
+ } \
+ } \
+} while(0)
+
+#define GET_PKTOPT_EXT_HDR(field) GET_PKTOPT_VAR(field, \
+ (((struct ip6_ext *)pktopt->field)->ip6e_len + 1) << 3)
+
+#define GET_PKTOPT_SOCKADDR(field) GET_PKTOPT_VAR(field, \
+ pktopt->field->sa_len)
+
static int
-ip6_getpcbopt(struct ip6_pktopts *pktopt, int optname, struct sockopt *sopt)
+ip6_getpcbopt(struct inpcb *in6p, int optname, struct sockopt *sopt)
{
void *optdata = NULL;
+ bool malloc_optdata = false;
int optdatalen = 0;
- struct ip6_ext *ip6e;
int error = 0;
struct in6_pktinfo null_pktinfo;
int deftclass = 0, on;
int defminmtu = IP6PO_MINMTU_MCASTONLY;
int defpreftemp = IP6PO_TEMPADDR_SYSTEM;
+ struct ip6_pktopts *pktopt;
+
+ INP_RLOCK(in6p);
+ pktopt = in6p->in6p_outputopts;
switch (optname) {
case IPV6_PKTINFO:
@@ -2329,50 +2415,29 @@
break;
case IPV6_TCLASS:
if (pktopt && pktopt->ip6po_tclass >= 0)
- optdata = (void *)&pktopt->ip6po_tclass;
- else
- optdata = (void *)&deftclass;
+ deftclass = pktopt->ip6po_tclass;
+ optdata = (void *)&deftclass;
optdatalen = sizeof(int);
break;
case IPV6_HOPOPTS:
- if (pktopt && pktopt->ip6po_hbh) {
- optdata = (void *)pktopt->ip6po_hbh;
- ip6e = (struct ip6_ext *)pktopt->ip6po_hbh;
- optdatalen = (ip6e->ip6e_len + 1) << 3;
- }
+ GET_PKTOPT_EXT_HDR(ip6po_hbh);
break;
case IPV6_RTHDR:
- if (pktopt && pktopt->ip6po_rthdr) {
- optdata = (void *)pktopt->ip6po_rthdr;
- ip6e = (struct ip6_ext *)pktopt->ip6po_rthdr;
- optdatalen = (ip6e->ip6e_len + 1) << 3;
- }
+ GET_PKTOPT_EXT_HDR(ip6po_rthdr);
break;
case IPV6_RTHDRDSTOPTS:
- if (pktopt && pktopt->ip6po_dest1) {
- optdata = (void *)pktopt->ip6po_dest1;
- ip6e = (struct ip6_ext *)pktopt->ip6po_dest1;
- optdatalen = (ip6e->ip6e_len + 1) << 3;
- }
+ GET_PKTOPT_EXT_HDR(ip6po_dest1);
break;
case IPV6_DSTOPTS:
- if (pktopt && pktopt->ip6po_dest2) {
- optdata = (void *)pktopt->ip6po_dest2;
- ip6e = (struct ip6_ext *)pktopt->ip6po_dest2;
- optdatalen = (ip6e->ip6e_len + 1) << 3;
- }
+ GET_PKTOPT_EXT_HDR(ip6po_dest2);
break;
case IPV6_NEXTHOP:
- if (pktopt && pktopt->ip6po_nexthop) {
- optdata = (void *)pktopt->ip6po_nexthop;
- optdatalen = pktopt->ip6po_nexthop->sa_len;
- }
+ GET_PKTOPT_SOCKADDR(ip6po_nexthop);
break;
case IPV6_USE_MIN_MTU:
if (pktopt)
- optdata = (void *)&pktopt->ip6po_minmtu;
- else
- optdata = (void *)&defminmtu;
+ defminmtu = pktopt->ip6po_minmtu;
+ optdata = (void *)&defminmtu;
optdatalen = sizeof(int);
break;
case IPV6_DONTFRAG:
@@ -2385,19 +2450,22 @@
break;
case IPV6_PREFER_TEMPADDR:
if (pktopt)
- optdata = (void *)&pktopt->ip6po_prefer_tempaddr;
- else
- optdata = (void *)&defpreftemp;
+ defpreftemp = pktopt->ip6po_prefer_tempaddr;
+ optdata = (void *)&defpreftemp;
optdatalen = sizeof(int);
break;
default: /* should not happen */
#ifdef DIAGNOSTIC
panic("ip6_getpcbopt: unexpected option\n");
#endif
+ INP_RUNLOCK(in6p);
return (ENOPROTOOPT);
}
+ INP_RUNLOCK(in6p);
error = sooptcopyout(sopt, optdata, optdatalen);
+ if (malloc_optdata)
+ free(optdata, M_TEMP);
return (error);
}
@@ -3082,6 +3150,8 @@
{
int len;
+ INP_WLOCK_ASSERT(in6p);
+
if (!in6p->in6p_outputopts)
return 0;
Index: sys/netipsec/ipsec_pcb.c
===================================================================
--- sys/netipsec/ipsec_pcb.c
+++ sys/netipsec/ipsec_pcb.c
@@ -276,6 +276,8 @@
struct secpolicy **spp, *newsp;
int error, flags;
+ INP_WLOCK_ASSERT(inp);
+
xpl = (struct sadb_x_policy *)request;
/* Select direction. */
switch (xpl->sadb_x_policy_dir) {
@@ -332,7 +334,6 @@
return (EINVAL);
}
- INP_WLOCK(inp);
if (xpl->sadb_x_policy_dir == IPSEC_DIR_INBOUND) {
spp = &inp->inp_sp->sp_in;
flags = INP_INBOUND_POLICY;
@@ -352,7 +353,6 @@
inp->inp_sp->flags |= flags;
KEYDBG(IPSEC_DUMP, kdebug_secpolicy(newsp));
}
- INP_WUNLOCK(inp);
return (0);
}
@@ -365,7 +365,7 @@
xpl = (struct sadb_x_policy *)request;
- INP_RLOCK(inp);
+ INP_WLOCK_ASSERT(inp);
flags = inp->inp_sp->flags;
/* Select direction. */
switch (xpl->sadb_x_policy_dir) {
@@ -378,7 +378,6 @@
flags &= INP_OUTBOUND_POLICY;
break;
default:
- INP_RUNLOCK(inp);
ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__,
xpl->sadb_x_policy_dir));
return (EINVAL);
@@ -386,7 +385,6 @@
if (flags == 0) {
/* Return ENTRUST policy */
- INP_RUNLOCK(inp);
xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
xpl->sadb_x_policy_type = IPSEC_POLICY_ENTRUST;
xpl->sadb_x_policy_id = 0;
@@ -400,7 +398,6 @@
("sp is NULL, but flags is 0x%04x", inp->inp_sp->flags));
key_addref(sp);
- INP_RUNLOCK(inp);
error = key_sp2msg(sp, request, len);
key_freesp(&sp);
if (error == EINVAL)
@@ -421,30 +418,43 @@
size_t optlen;
int error;
- if (inp->inp_sp == NULL)
+ INP_WLOCK_ASSERT(inp);
+
+ if (inp->inp_sp == NULL) {
+ INP_WUNLOCK(inp);
return (ENOPROTOOPT);
+ }
/* Limit maximum request size to PAGE_SIZE */
optlen = sopt->sopt_valsize;
- if (optlen < sizeof(struct sadb_x_policy) || optlen > PAGE_SIZE)
+ if (optlen < sizeof(struct sadb_x_policy) || optlen > PAGE_SIZE) {
+ INP_WUNLOCK(inp);
return (EINVAL);
+ }
optdata = malloc(optlen, M_TEMP, sopt->sopt_td ? M_WAITOK: M_NOWAIT);
- if (optdata == NULL)
+ if (optdata == NULL) {
+ INP_WUNLOCK(inp);
return (ENOBUFS);
+ }
/*
* We need a hint from the user, what policy is requested - input
* or output? User should specify it in the buffer, even for
* setsockopt().
*/
+ INP_WUNLOCK(inp);
error = sooptcopyin(sopt, optdata, optlen, optlen);
+ INP_WLOCK(inp);
if (error == 0) {
- if (sopt->sopt_dir == SOPT_SET)
+ if (sopt->sopt_dir == SOPT_SET) {
error = ipsec_set_pcbpolicy(inp,
sopt->sopt_td ? sopt->sopt_td->td_ucred: NULL,
optdata, optlen);
- else {
+ if (error != 0)
+ INP_WUNLOCK(inp);
+ } else {
error = ipsec_get_pcbpolicy(inp, optdata, &optlen);
+ INP_WUNLOCK(inp);
if (error == 0)
error = sooptcopyout(sopt, optdata, optlen);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Jul 25, 1:50 PM (13 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35475498
Default Alt Text
D12586.id34349.diff (24 KB)
Attached To
Mode
D12586: improve inp locking in getsockopt, setsockopt, and related functions
Attached
Detach File
Event Timeline
Log In to Comment