jail: Fix regression panic from eb8dcdeac2

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:	eb8dcdeac2 jail: network epoch protection for IP address lists
Differential Revision:	https://reviews.freebsd.org/D37906
This commit is contained in:
Zhenlei Huang 2023-01-13 18:15:06 +08:00
parent ddbf879d79
commit 89ddfbbac8

View File

@ -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);
}
/*