Index: sys/netinet/ip_output.c =================================================================== --- sys/netinet/ip_output.c +++ sys/netinet/ip_output.c @@ -1226,7 +1226,10 @@ #if defined(IPSEC) || defined(IPSEC_SUPPORT) case IP_IPSEC_POLICY: if (IPSEC_ENABLED(ipv4)) { + INP_WLOCK(inp); error = IPSEC_PCBCTL(ipv4, inp, sopt); + if (!error) + INP_WUNLOCK(inp); break; } /* FALLTHROUGH */ @@ -1380,7 +1383,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 */ Index: sys/netinet6/ip6_output.c =================================================================== --- sys/netinet6/ip6_output.c +++ sys/netinet6/ip6_output.c @@ -1905,7 +1905,10 @@ #if defined(IPSEC) || defined(IPSEC_SUPPORT) case IPV6_IPSEC_POLICY: if (IPSEC_ENABLED(ipv6)) { + INP_WLOCK(in6p); error = IPSEC_PCBCTL(ipv6, in6p, sopt); + if (!error) + INP_WUNLOCK(in6p); break; } /* FALLTHROUGH */ @@ -2140,7 +2143,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 */ 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); } Index: sys/netipsec/ipsec_support.h =================================================================== --- sys/netipsec/ipsec_support.h +++ sys/netipsec/ipsec_support.h @@ -44,6 +44,14 @@ int ipsec_delete_pcbpolicy(struct inpcb *); int ipsec_copy_pcbpolicy(struct inpcb *, struct inpcb *); +/* + * The pcbctl function has the following locking characteristics: + * On setting values, the inp must already be wlocked. If an error is + * returned, the lock must be released. If there is no error returned, + * then the lock must be held. + * On getting values, the inp must already be wlocked. The lock must be + * released before returning. + */ struct ipsec_methods { int (*input)(struct mbuf *, int, int); int (*check_policy)(const struct mbuf *, struct inpcb *);