From nobody Tue Sep 29 15:56:22 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4hvN865Qfyz6tkkf for ; Tue, 29 Sep 2026 15:56:22 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hvN8626yJz4Vkx for ; Tue, 29 Sep 2026 15:56:22 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1790697382; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=qSQ6SWP0fgNp16LzajCgy6AjmBslsfQWNNQ6k69wUjs=; b=CL+YVUMAGKWzrjh7fdSCc55V/UXxXbZr7GtGrU4SagKug2R0CrICP0lAe6+kFTXXICLHvk cxktnQ12DAD3wjox5IZfkDka2N9qKoqMT3SM27JDN7DeQGBBBYveg6+U6ixuBB3TfdwCn6 hxnxeOP1V3DQYjLq2W4tj2TlccRUdlmxTULyK3d5F/20mcFvXAkFaMQav1dp8ksBv1AQYF fTRhwaCiYFIJMnOAY/uSMH5PYm2KuLKzQg0Jsv3osYVlOiIpa+4KnHFjtyKpPe9Oop07zl J719qsW7hr4TAIcnw5nie+8L6UfJ8Kmsq+Wz3TEhDKCUthC5OmMj9RCkEnl1Yw== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1790697382; b=HNZaLCB8Q9gRfwYZnbLXYlmkeKG4z9X8Vx2IqXb3CPqO9B6XMdPDmdY/EvNwNWvyjd4Svv HgNWFYZI5IlsqLY0B1+y81ypvsljLAYQaF6nE6nw9WHxX91nwOGbYJU/zOuntDk38uJ05x HER0oP3zpSDS16MHXbZoXTDK4dKDkw0iqwfsKUzSAFdIosn9SpbYFz/3N4QQhBhf4X37lm SIfhfUO0KDhE++arFkA+z/kpcLMlHTf8xtxgp/61V6zff8mbQiuBBHVlvWukBtaS/7sog+ tUw8/HSonNMAxYBHxQA/8O45sa/BecN7CKNGi8ZVkPfrnZ31MQfQSv2Mz/O9iA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1790697382; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=qSQ6SWP0fgNp16LzajCgy6AjmBslsfQWNNQ6k69wUjs=; b=A38J1QWXigPhqpLi8DoUX2E26+lk6BsJGLjBeEsqXLmyuKfNtaANuLa+oPi0r2I7dAbdFm 9BpFrytb5sy6YfaoyVGnwZTQX7qTeDBKyP0FX/+ij36Z1k9Da5Kvc8dLi6s1e3itWf4FyU IxFzoMYEPbpUAhKTCB9PPPtwr3y1rXhzKR2OGYqQ4ZbfuqB5CJMVVXvtdm0gUaR7qYMrGr vErVg935ndFMC3XMnI6d3ORiTUxGBbdw05dD2Xe1Y7WsXzlYFMwvxBDNWPzHR/ApCo7B3I CYAluN5vyvgy8kP2t9HlDjPDdZmz/1iOQRaVxN4AHMrp2ocuG2G+74AMh81Slw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hvN860Ymfz1KNG for ; Tue, 29 Sep 2026 15:56:22 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 26260 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 29 Sep 2026 15:56:22 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: 4536e595eada - stable/15 - file: Add filecaps_intersect() and cap_rights_intersect() List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: 4536e595eada166687db8f911ff142e36b02205c Auto-Submitted: auto-generated Date: Tue, 29 Sep 2026 15:56:22 +0000 Message-Id: <6abbdfa6.26260.6ae9b8b6@gitrepo.freebsd.org> The branch stable/15 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=4536e595eada166687db8f911ff142e36b02205c commit 4536e595eada166687db8f911ff142e36b02205c Author: Mark Johnston AuthorDate: 2026-09-28 14:43:23 +0000 Commit: Mark Johnston CommitDate: 2026-09-29 15:56:01 +0000 file: Add filecaps_intersect() and cap_rights_intersect() These routines let one compute the intersection of two sets of filecaps or capability rights, just as filecaps_merge() and cap_rights_merge() compute the union. This will be useful in an upcoming patch. filecaps_intersect() is complex due to the need to merge sets of ioctls. For now this is implemented with a dumb nested loop on the basis that ioctl lists are typically short enough that this is fine. It may be better to instead sort the two lists first and step through them together. No functional change intended. Approved by: so Security: FreeBSD-SA-26:66.jail Reviewed by: kib Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D59885 --- lib/libc/capability/cap_rights_init.3 | 16 ++++++++++-- sys/kern/kern_descrip.c | 49 +++++++++++++++++++++++++++++++++++ sys/kern/subr_capability.c | 23 ++++++++++++++++ sys/sys/capsicum.h | 1 + sys/sys/filedesc.h | 1 + 5 files changed, 88 insertions(+), 2 deletions(-) diff --git a/lib/libc/capability/cap_rights_init.3 b/lib/libc/capability/cap_rights_init.3 index 98b50f653f2c..1dbb76686283 100644 --- a/lib/libc/capability/cap_rights_init.3 +++ b/lib/libc/capability/cap_rights_init.3 @@ -35,6 +35,7 @@ .Nm cap_rights_is_set , .Nm cap_rights_is_empty , .Nm cap_rights_is_valid , +.Nm cap_rights_intersect , .Nm cap_rights_merge , .Nm cap_rights_remove , .Nm cap_rights_contains @@ -56,6 +57,8 @@ .Ft bool .Fn cap_rights_is_valid "const cap_rights_t *rights" .Ft cap_rights_t * +.Fn cap_rights_intersect "cap_rights_t *dst" "const cap_rights_t *src" +.Ft cap_rights_t * .Fn cap_rights_merge "cap_rights_t *dst" "const cap_rights_t *src" .Ft cap_rights_t * .Fn cap_rights_remove "cap_rights_t *dst" "const cap_rights_t *src" @@ -133,6 +136,14 @@ function verifies if the given structure is valid. .Pp The +.Fn cap_rights_intersect +function clears all capability rights from the +.Fa dst +structure that are not present in the +.Fa src +structure, leaving only the rights common to both. +.Pp +The .Fn cap_rights_merge function merges all capability rights present in the .Fa src @@ -173,9 +184,10 @@ structure given in the argument. .Pp The -.Fn cap_rights_merge -and +.Fn cap_rights_merge , .Fn cap_rights_remove +and +.Fn cap_rights_intersect functions return pointer to the .Vt cap_rights_t structure given in the diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index ba34cc4dfef4..90e0a5c8d2e0 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -1945,6 +1945,55 @@ filecaps_full(const struct filecaps *fcaps) fcaps->fc_fcntls == CAP_FCNTL_ALL && fcaps->fc_nioctls == -1); } +/* + * Find the intersection of two filecaps structures and store the result in the + * first structure. This is a destructive operation on the src structure. + */ +void +filecaps_intersect(struct filecaps *src, struct filecaps *dst) +{ + + cap_rights_intersect(&dst->fc_rights, &src->fc_rights); + dst->fc_fcntls &= src->fc_fcntls; + if (dst->fc_nioctls == -1) { + dst->fc_ioctls = src->fc_ioctls; + dst->fc_nioctls = src->fc_nioctls; + src->fc_ioctls = NULL; + } else if (src->fc_nioctls != -1) { + int count; + + /* + * ioctl lists are usually short, so this dumb merge is fine. + * We could alternately sort both lists and walk them in + * parallel. + */ + count = 0; + for (int i = 0; i < dst->fc_nioctls; i++) { + bool found; + + found = false; + for (int j = 0; j < src->fc_nioctls; j++) { + if (dst->fc_ioctls[i] == src->fc_ioctls[j]) { + count++; + found = true; + break; + } + } + if (!found) { + if (i != dst->fc_nioctls - 1) + dst->fc_ioctls[i] = + dst->fc_ioctls[dst->fc_nioctls - 1]; + dst->fc_nioctls--; + i--; + } + } + dst->fc_nioctls = count; + } + if (dst->fc_nioctls == 0) + filecaps_free_ioctl(dst); + filecaps_free(src); +} + static u_long * filecaps_free_prep(struct filecaps *fcaps) { diff --git a/sys/kern/subr_capability.c b/sys/kern/subr_capability.c index f5242216a2a2..72636980b1b1 100644 --- a/sys/kern/subr_capability.c +++ b/sys/kern/subr_capability.c @@ -316,6 +316,29 @@ cap_rights_is_valid(const cap_rights_t *rights) return (true); } +cap_rights_t * +cap_rights_intersect(cap_rights_t *dst, const cap_rights_t *src) +{ + unsigned int i, n; + + assert(CAPVER(dst) == CAP_RIGHTS_VERSION_00); + assert(CAPVER(src) == CAP_RIGHTS_VERSION_00); + assert(CAPVER(dst) == CAPVER(src)); + assert(cap_rights_is_valid(src)); + assert(cap_rights_is_valid(dst)); + + n = CAPARSIZE(dst); + assert(n >= CAPARSIZE_MIN && n <= CAPARSIZE_MAX); + + for (i = 0; i < n; i++) + dst->cr_rights[i] &= src->cr_rights[i] | ~0x01FFFFFFFFFFFFFFULL; + + assert(cap_rights_is_valid(src)); + assert(cap_rights_is_valid(dst)); + + return (dst); +} + cap_rights_t * cap_rights_merge(cap_rights_t *dst, const cap_rights_t *src) { diff --git a/sys/sys/capsicum.h b/sys/sys/capsicum.h index d912d10bc4e2..03173b02375d 100644 --- a/sys/sys/capsicum.h +++ b/sys/sys/capsicum.h @@ -391,6 +391,7 @@ bool __cap_rights_is_set(const cap_rights_t *rights, ...); bool cap_rights_is_empty(const cap_rights_t *rights); bool cap_rights_is_valid(const cap_rights_t *rights); +cap_rights_t *cap_rights_intersect(cap_rights_t *dst, const cap_rights_t *src); cap_rights_t *cap_rights_merge(cap_rights_t *dst, const cap_rights_t *src); cap_rights_t *cap_rights_remove(cap_rights_t *dst, const cap_rights_t *src); diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h index 0f878e530436..a2bb6c671dbb 100644 --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -246,6 +246,7 @@ void filecaps_move(struct filecaps *src, struct filecaps *dst); void filecaps_fill(struct filecaps *fcaps); void filecaps_free(struct filecaps *fcaps); bool filecaps_full(const struct filecaps *fcaps); +void filecaps_intersect(struct filecaps *src, struct filecaps *dst); int closef(struct file *fp, struct thread *td); void closef_nothread(struct file *fp);