Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 13 Aug 2021 11:16:18 GMT
From:      Wojciech Macek <wma@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 4920e38fecc3 - main - ipsec: fix race condition in key.c
Message-ID:  <202108131116.17DBGI5Q074065@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by wma:

URL: https://cgit.FreeBSD.org/src/commit/?id=4920e38fecc3d0274b03ae7151153e9d6b9eb526

commit 4920e38fecc3d0274b03ae7151153e9d6b9eb526
Author:     Wojciech Macek <wma@FreeBSD.org>
AuthorDate: 2021-08-13 10:52:38 +0000
Commit:     Wojciech Macek <wma@FreeBSD.org>
CommitDate: 2021-08-13 10:52:38 +0000

    ipsec: fix race condition in key.c
    
    Small patch that fixes a race condition in sys/netipsec/key.c
    
    Obtained from:          Stormshield
    Differential revision:  https://reviews.freebsd.org/D31271
---
 sys/netipsec/key.c | 53 +++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 37 insertions(+), 16 deletions(-)

diff --git a/sys/netipsec/key.c b/sys/netipsec/key.c
index 3ea3a81b90c3..72c598586d8e 100644
--- a/sys/netipsec/key.c
+++ b/sys/netipsec/key.c
@@ -616,6 +616,7 @@ static struct callout key_timer;
 #endif
 
 static void key_unlink(struct secpolicy *);
+static void key_detach(struct secpolicy *);
 static struct secpolicy *key_do_allocsp(struct secpolicyindex *spidx, u_int dir);
 static struct secpolicy *key_getsp(struct secpolicyindex *);
 static struct secpolicy *key_getspbyid(u_int32_t);
@@ -1200,18 +1201,26 @@ key_freesp(struct secpolicy **spp)
 static void
 key_unlink(struct secpolicy *sp)
 {
+	SPTREE_WLOCK();
+	key_detach(sp);
+	SPTREE_WUNLOCK();
+	if (SPDCACHE_ENABLED())
+		spdcache_clear();
+	key_freesp(&sp);
+}
 
+static void
+key_detach(struct secpolicy *sp)
+{
 	IPSEC_ASSERT(sp->spidx.dir == IPSEC_DIR_INBOUND ||
 	    sp->spidx.dir == IPSEC_DIR_OUTBOUND,
 	    ("invalid direction %u", sp->spidx.dir));
-	SPTREE_UNLOCK_ASSERT();
+	SPTREE_WLOCK_ASSERT();
 
 	KEYDBG(KEY_STAMP,
 	    printf("%s: SP(%p)\n", __func__, sp));
-	SPTREE_WLOCK();
 	if (sp->state != IPSEC_SPSTATE_ALIVE) {
 		/* SP is already unlinked */
-		SPTREE_WUNLOCK();
 		return;
 	}
 	sp->state = IPSEC_SPSTATE_DEAD;
@@ -1219,10 +1228,6 @@ key_unlink(struct secpolicy *sp)
 	V_spd_size--;
 	LIST_REMOVE(sp, idhash);
 	V_sp_genid++;
-	SPTREE_WUNLOCK();
-	if (SPDCACHE_ENABLED())
-		spdcache_clear();
-	key_freesp(&sp);
 }
 
 /*
@@ -1941,7 +1946,7 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
 	struct sadb_address *src0, *dst0;
 	struct sadb_x_policy *xpl0, *xpl;
 	struct sadb_lifetime *lft = NULL;
-	struct secpolicy *newsp;
+	struct secpolicy *newsp, *oldsp;
 	int error;
 
 	IPSEC_ASSERT(so != NULL, ("null socket"));
@@ -2019,15 +2024,13 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
 	                src0->sadb_address_proto,
 	                &spidx);
 	/* Checking there is SP already or not. */
-	newsp = key_getsp(&spidx);
-	if (newsp != NULL) {
+	oldsp = key_getsp(&spidx);
+	if (oldsp != NULL) {
 		if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) {
 			KEYDBG(KEY_STAMP,
 			    printf("%s: unlink SP(%p) for SPDUPDATE\n",
-				__func__, newsp));
-			KEYDBG(KEY_DATA, kdebug_secpolicy(newsp));
-			key_unlink(newsp);
-			key_freesp(&newsp);
+				__func__, oldsp));
+			KEYDBG(KEY_DATA, kdebug_secpolicy(oldsp));
 		} else {
 			key_freesp(&newsp);
 			ipseclog((LOG_DEBUG,
@@ -2038,6 +2041,10 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
 
 	/* allocate new SP entry */
 	if ((newsp = key_msg2sp(xpl0, PFKEY_EXTLEN(xpl0), &error)) == NULL) {
+		if (oldsp != NULL) {
+			key_unlink(oldsp);
+			key_freesp(&oldsp); /* second for our reference */
+		}
 		return key_senderror(so, m, error);
 	}
 
@@ -2046,18 +2053,32 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
 	newsp->validtime = lft ? lft->sadb_lifetime_usetime : 0;
 	bcopy(&spidx, &newsp->spidx, sizeof(spidx));
 
-	/* XXXAE: there is race between key_getsp() and key_insertsp() */
 	SPTREE_WLOCK();
 	if ((newsp->id = key_getnewspid()) == 0) {
+		if (oldsp != NULL)
+			key_detach(oldsp);
 		SPTREE_WUNLOCK();
+		if (oldsp != NULL) {
+			key_freesp(&oldsp); /* first for key_detach */
+			IPSEC_ASSERT(oldsp != NULL, ("null oldsp: refcount bug"));
+			key_freesp(&oldsp); /* second for our reference */
+			if (SPDCACHE_ENABLED()) /* refresh cache because of key_detach */
+				spdcache_clear();
+		}
 		key_freesp(&newsp);
 		return key_senderror(so, m, ENOBUFS);
 	}
+	if (oldsp != NULL)
+		key_detach(oldsp);
 	key_insertsp(newsp);
 	SPTREE_WUNLOCK();
+	if (oldsp != NULL) {
+		key_freesp(&oldsp); /* first for key_detach */
+		IPSEC_ASSERT(oldsp != NULL, ("null oldsp: refcount bug"));
+		key_freesp(&oldsp); /* second for our reference */
+	}
 	if (SPDCACHE_ENABLED())
 		spdcache_clear();
-
 	KEYDBG(KEY_STAMP,
 	    printf("%s: SP(%p)\n", __func__, newsp));
 	KEYDBG(KEY_DATA, kdebug_secpolicy(newsp));



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202108131116.17DBGI5Q074065>