ipsec: add a lock encompassing SPI allocation

SPIs get allocated and inserted in separate steps. Prior to the change
there was nothing preventing 2 differnet threads from ending up with the
same one.

PR:		258849
Reported by:	Herbie.Robinson@stratus.com
Reviewed by:	ae
Sponsored by:	Rubicon Communications, LLC ("Netgate")
Differential Revision:	https://reviews.freebsd.org/D32826
This commit is contained in:
Mateusz Guzik 2021-11-03 14:10:34 +01:00
parent 626bd0970a
commit 10ea195fa2

View File

@ -216,6 +216,13 @@ VNET_DEFINE_STATIC(struct mtx *, spdcache_lock);
#define SPDCACHE_LOCK(a) mtx_lock(&V_spdcache_lock[a]); #define SPDCACHE_LOCK(a) mtx_lock(&V_spdcache_lock[a]);
#define SPDCACHE_UNLOCK(a) mtx_unlock(&V_spdcache_lock[a]); #define SPDCACHE_UNLOCK(a) mtx_unlock(&V_spdcache_lock[a]);
static struct sx spi_alloc_lock;
#define SPI_ALLOC_LOCK_INIT() sx_init(&spi_alloc_lock, "spialloc")
#define SPI_ALLOC_LOCK_DESTROY() sx_destroy(&spi_alloc_lock)
#define SPI_ALLOC_LOCK() sx_xlock(&spi_alloc_lock)
#define SPI_ALLOC_UNLOCK() sx_unlock(&spi_alloc_lock)
#define SPI_ALLOC_LOCK_ASSERT() sx_assert(&spi_alloc_lock, SA_XLOCKED)
/* SAD */ /* SAD */
TAILQ_HEAD(secashead_queue, secashead); TAILQ_HEAD(secashead_queue, secashead);
LIST_HEAD(secashead_list, secashead); LIST_HEAD(secashead_list, secashead);
@ -4902,6 +4909,7 @@ key_getspi(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx); KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
/* SPI allocation */ /* SPI allocation */
SPI_ALLOC_LOCK();
spi = key_do_getnewspi( spi = key_do_getnewspi(
(struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE], &saidx); (struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE], &saidx);
if (spi == 0) { if (spi == 0) {
@ -4909,10 +4917,12 @@ key_getspi(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
* Requested SPI or SPI range is not available or * Requested SPI or SPI range is not available or
* already used. * already used.
*/ */
SPI_ALLOC_UNLOCK();
error = EEXIST; error = EEXIST;
goto fail; goto fail;
} }
sav = key_newsav(mhp, &saidx, spi, &error); sav = key_newsav(mhp, &saidx, spi, &error);
SPI_ALLOC_UNLOCK();
if (sav == NULL) if (sav == NULL)
goto fail; goto fail;
@ -5021,6 +5031,8 @@ key_do_getnewspi(struct sadb_spirange *spirange, struct secasindex *saidx)
uint32_t min, max, newspi, t; uint32_t min, max, newspi, t;
int tries, limit; int tries, limit;
SPI_ALLOC_LOCK_ASSERT();
/* set spi range to allocate */ /* set spi range to allocate */
if (spirange != NULL) { if (spirange != NULL) {
min = spirange->sadb_spirange_min; min = spirange->sadb_spirange_min;
@ -5629,9 +5641,11 @@ key_add(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
* secasindex. * secasindex.
* XXXAE: IPComp seems also doesn't use SPI. * XXXAE: IPComp seems also doesn't use SPI.
*/ */
SPI_ALLOC_LOCK();
if (proto == IPPROTO_TCP) { if (proto == IPPROTO_TCP) {
sav = key_getsav_tcpmd5(&saidx, &spi); sav = key_getsav_tcpmd5(&saidx, &spi);
if (sav == NULL && spi == 0) { if (sav == NULL && spi == 0) {
SPI_ALLOC_UNLOCK();
/* Failed to allocate SPI */ /* Failed to allocate SPI */
ipseclog((LOG_DEBUG, "%s: SA already exists.\n", ipseclog((LOG_DEBUG, "%s: SA already exists.\n",
__func__)); __func__));
@ -5643,12 +5657,14 @@ key_add(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
sav = key_getsavbyspi(spi); sav = key_getsavbyspi(spi);
} }
if (sav != NULL) { if (sav != NULL) {
SPI_ALLOC_UNLOCK();
key_freesav(&sav); key_freesav(&sav);
ipseclog((LOG_DEBUG, "%s: SA already exists.\n", __func__)); ipseclog((LOG_DEBUG, "%s: SA already exists.\n", __func__));
return key_senderror(so, m, EEXIST); return key_senderror(so, m, EEXIST);
} }
sav = key_newsav(mhp, &saidx, spi, &error); sav = key_newsav(mhp, &saidx, spi, &error);
SPI_ALLOC_UNLOCK();
if (sav == NULL) if (sav == NULL)
return key_senderror(so, m, error); return key_senderror(so, m, error);
KEYDBG(KEY_STAMP, KEYDBG(KEY_STAMP,
@ -6023,10 +6039,12 @@ key_delete(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
return (key_senderror(so, m, EINVAL)); return (key_senderror(so, m, EINVAL));
} }
sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA]; sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA];
SPI_ALLOC_LOCK();
if (proto == IPPROTO_TCP) if (proto == IPPROTO_TCP)
sav = key_getsav_tcpmd5(&saidx, NULL); sav = key_getsav_tcpmd5(&saidx, NULL);
else else
sav = key_getsavbyspi(sa0->sadb_sa_spi); sav = key_getsavbyspi(sa0->sadb_sa_spi);
SPI_ALLOC_UNLOCK();
if (sav == NULL) { if (sav == NULL) {
ipseclog((LOG_DEBUG, "%s: no SA found for SPI %u.\n", ipseclog((LOG_DEBUG, "%s: no SA found for SPI %u.\n",
__func__, ntohl(sa0->sadb_sa_spi))); __func__, ntohl(sa0->sadb_sa_spi)));
@ -6235,10 +6253,12 @@ key_get(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
} }
KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx); KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
SPI_ALLOC_LOCK();
if (proto == IPPROTO_TCP) if (proto == IPPROTO_TCP)
sav = key_getsav_tcpmd5(&saidx, NULL); sav = key_getsav_tcpmd5(&saidx, NULL);
else else
sav = key_getsavbyspi(sa0->sadb_sa_spi); sav = key_getsavbyspi(sa0->sadb_sa_spi);
SPI_ALLOC_UNLOCK();
if (sav == NULL) { if (sav == NULL) {
ipseclog((LOG_DEBUG, "%s: no SA found.\n", __func__)); ipseclog((LOG_DEBUG, "%s: no SA found.\n", __func__));
return key_senderror(so, m, ESRCH); return key_senderror(so, m, ESRCH);
@ -8318,6 +8338,7 @@ key_init(void)
SAHTREE_LOCK_INIT(); SAHTREE_LOCK_INIT();
ACQ_LOCK_INIT(); ACQ_LOCK_INIT();
SPACQ_LOCK_INIT(); SPACQ_LOCK_INIT();
SPI_ALLOC_LOCK_INIT();
#ifndef IPSEC_DEBUG2 #ifndef IPSEC_DEBUG2
callout_init(&key_timer, 1); callout_init(&key_timer, 1);
@ -8442,6 +8463,7 @@ key_destroy(void)
SAHTREE_LOCK_DESTROY(); SAHTREE_LOCK_DESTROY();
ACQ_LOCK_DESTROY(); ACQ_LOCK_DESTROY();
SPACQ_LOCK_DESTROY(); SPACQ_LOCK_DESTROY();
SPI_ALLOC_LOCK_DESTROY();
} }
#endif #endif