git: 89ddfbbac84c - main - jail: Fix regression panic from eb8dcdeac22d

From: Zhenlei Huang <zlei_at_FreeBSD.org>
Date: Fri, 13 Jan 2023 10:46:09 UTC
The branch main has been updated by zlei:

URL: https://cgit.FreeBSD.org/src/commit/?id=89ddfbbac84cb923e41782c014dc581352e498a9

commit 89ddfbbac84cb923e41782c014dc581352e498a9
Author:     Zhenlei Huang <zlei@FreeBSD.org>
AuthorDate: 2023-01-13 10:15:06 +0000
Commit:     Zhenlei Huang <zlei@FreeBSD.org>
CommitDate: 2023-01-13 10:45:14 +0000

    jail: Fix regression panic from eb8dcdeac22d
    
    And possibly infinite loop calling prison_ip_restrict() in
    kern_jail_set() [2].
    
    [1] It is possible that prisons do not have any IPv4 or IPv6 addresses.
    [2] If prison_ip_restrict() is not provided with prison_ip, when it
        allocates prison_ip successfully, then it should return false to
        indicate not redo prison_ip_restrict() later.
    
    Reviewed by:    glebius
    Approved by:    kp (mentor)
    Fixes:  eb8dcdeac22d jail: network epoch protection for IP address lists
    Differential Revision:  https://reviews.freebsd.org/D37906
---
 sys/kern/kern_jail.c | 55 +++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 39 insertions(+), 16 deletions(-)

diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
index c8ae362c652c..e9fc8ddae144 100644
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -777,7 +777,7 @@ prison_ip_set(struct prison *pr, const pr_family_t af, struct prison_ip *new)
 
 /*
  * Restrict a prison's IP address list with its parent's, possibly replacing
- * it.  Return true if the replacement buffer was used (or would have been).
+ * it.  Return true if the replacement buffer was used (or should redo).
  * kern_jail_set() helper.
  */
 static bool
@@ -789,7 +789,7 @@ prison_ip_restrict(struct prison *pr, const pr_family_t af,
 	int (*const cmp)(const void *, const void *) = pr_families[af].cmp;
 	const size_t size = pr_families[af].size;
 	uint32_t ips;
-	bool alloced;
+	bool alloced, used;
 
 	mtx_assert(&pr->pr_mtx, MA_OWNED);
 
@@ -800,28 +800,44 @@ prison_ip_restrict(struct prison *pr, const pr_family_t af,
 	 * screw up sorting, and in case of IPv6 we can't even atomically write
 	 * one.
 	 */
-	ips = (pr->pr_flags & pr_families[af].ip_flag) ? pip->ips : ppip->ips;
-	if (ips == 0) {
-		prison_ip_set(pr, af, NULL);
+	if (ppip == NULL) {
+		if (pip != NULL)
+			prison_ip_set(pr, af, NULL);
 		return (false);
 	}
-	if (new == NULL) {
-		new = prison_ip_alloc(af, ips, M_NOWAIT);
-		if (new == NULL)
-			return (true);
-		alloced = true;
-	} else
-		alloced = false;
+
 	if (!(pr->pr_flags & pr_families[af].ip_flag)) {
+		if (new == NULL) {
+			new = prison_ip_alloc(af, ppip->ips, M_NOWAIT);
+			if (new == NULL)
+				return (true); /* redo */
+			used = false;
+		} else
+			used = true;
 		/* This has no user settings, so just copy the parent's list. */
-		bcopy(ppip + 1, new + 1, ips * size);
-	} else {
+		MPASS(new->ips == ppip->ips);
+		bcopy(ppip + 1, new + 1, ppip->ips * size);
+		prison_ip_set(pr, af, new);
+		return (used);
+	} else if (pip != NULL) {
 		/* Remove addresses that aren't in the parent. */
 		int i;
 
 		i = 0; /* index in pip */
 		ips = 0; /* index in new */
 
+		used = true;
+		if (new == NULL) {
+			new = prison_ip_alloc(af, pip->ips, M_NOWAIT);
+			if (new == NULL)
+				return (true); /* redo */
+			used = false;
+			alloced = true;
+		} else {
+			used = true;
+			alloced = false;
+		}
+
 		for (int pi = 0; pi < ppip->ips; pi++)
 			if (cmp(PR_IP(pip, 0), PR_IP(ppip, pi)) == 0) {
 				/* Found our primary address in parent. */
@@ -860,10 +876,17 @@ prison_ip_restrict(struct prison *pr, const pr_family_t af,
 			if (alloced)
 				prison_ip_free(new);
 			new = NULL;
+			used = false;
+		} else {
+			/* Shrink to real size */
+			KASSERT((new->ips >= ips),
+			    ("Out-of-bounds write to prison_ip %p", new));
+			new->ips = ips;
 		}
+		prison_ip_set(pr, af, new);
+		return (used);
 	}
-	prison_ip_set(pr, af, new);
-	return (new != NULL ? true : false);
+	return (false);
 }
 
 /*