git: b2a270da9fbf - main - libjail: fix fetching mac.label for multiple jails

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Wed, 09 Sep 2026 21:24:34 UTC
The branch main has been updated by kevans:

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

commit b2a270da9fbf0fb58fb7c1bf1006ae675e14a5e3
Author:     Kyle Evans <kevans@FreeBSD.org>
AuthorDate: 2026-09-09 21:24:18 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2026-09-09 21:24:18 +0000

    libjail: fix fetching mac.label for multiple jails
    
    When doing a basic `jls -n`, jls(8) will jailparam_get() the mac.label
    for every jail on the system using the same set of jailparams, and thus
    the same jp_value.  We only init the mac_t the first time, so the first
    jail would populate it with `?` from /etc/mac.conf and the resulting
    jail_get(2) would clobber it with the empty string, then a second jail
    would try to pass the empty string to the kernel and fail because it
    must have a non-zero length.
    
    Fix it by invoking jps_get() every time.  Drop some comments to note
    that jps_get() will be invoked with zero || garbage from previous call,
    and be sure that we don't leak our previous mac_t.  There aren't any
    other jps_get implementations at this time, so this shouldn't cause any
    unexpected problems.
    
    Reported by:    ivy
    Reviewed by:    jamie
    Differential Revision:  https://reviews.freebsd.org/D57280
---
 lib/libjail/jail.c                     | 31 +++++++++++++++++++++++++------
 usr.sbin/jail/tests/jail_basic_test.sh |  5 ++++-
 2 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/lib/libjail/jail.c b/lib/libjail/jail.c
index 95ba336b62ce..c180b8533e97 100644
--- a/lib/libjail/jail.c
+++ b/lib/libjail/jail.c
@@ -746,13 +746,23 @@ jailparam_get(struct jailparam *jp, unsigned njp, int flags)
 			jiov[i].iov_base = jp[j].jp_name;
 			jiov[i].iov_len = strlen(jp[j].jp_name) + 1;
 			i++;
-			if (jp[j].jp_value == NULL &&
-			    !(jp[j].jp_flags & JP_RAWVALUE)) {
-				jp[j].jp_value = malloc(jp[j].jp_valuelen);
+
+			/*
+			 * We give structured types' jps_get() implementations
+			 * a chance to initialize the value.  We'll guarantee
+			 * that the initial value is zeroed out, but they should
+			 * assume on every call that the value may be populated
+			 * by a subsequent jailparam_get().
+			 */
+			if (!(jp[j].jp_flags & JP_RAWVALUE)) {
 				if (jp[j].jp_value == NULL) {
-					strerror_r(errno, jail_errmsg,
-					    JAIL_ERRMSGLEN);
-					return (-1);
+					jp[j].jp_value = calloc(1,
+					    jp[j].jp_valuelen);
+					if (jp[j].jp_value == NULL) {
+						strerror_r(errno, jail_errmsg,
+						    JAIL_ERRMSGLEN);
+						return (-1);
+					}
 				}
 
 				/*
@@ -1435,6 +1445,15 @@ jps_get_mac_label(struct jailparam *jp, struct iovec *jiov)
 	mac_t *pmac = jp->jp_value;
 	int error;
 
+	/*
+	 * Our value is only allocated once and may be reused for many
+	 * jailparam_get() calls; avoid leaking.
+	 */
+	if (*pmac != NULL) {
+		mac_free(*pmac);
+		*pmac = NULL;
+	}
+
 	error = mac_prepare_type(pmac, "jail");
 	if (error != 0 && errno == ENOENT) {
 		/*
diff --git a/usr.sbin/jail/tests/jail_basic_test.sh b/usr.sbin/jail/tests/jail_basic_test.sh
index cb50a94a45e8..c662e44f1a16 100755
--- a/usr.sbin/jail/tests/jail_basic_test.sh
+++ b/usr.sbin/jail/tests/jail_basic_test.sh
@@ -35,6 +35,9 @@ basic_body()
 {
 	# Create the jail
 	atf_check -s exit:0 -o ignore jail -c name=basejail persist ip4.addr=192.0.1.1
+	# Create a second jail to test `jls -n` in the process for breakage when
+	# handling some of the non-trivial parameter types.
+	atf_check -s exit:0 -o ignore jail -c name=basejail2 persist ip4.addr=192.0.1.2
 	# Check output of jls
 	atf_check -s exit:0 -o ignore jls
 	atf_check -s exit:0 -o ignore jls -v
@@ -52,7 +55,7 @@ basic_body()
 
 basic_cleanup()
 {
-	jail -r basejail
+	jail -r basejail basejail2
 }
 
 atf_test_case "list" "cleanup"