From nobody Tue Sep 29 16:00:06 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 4hvNDQ67Myz6tlXh for ; Tue, 29 Sep 2026 16:00:06 +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 4hvNDQ3WDCz4h0L for ; Tue, 29 Sep 2026 16:00:06 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1790697606; 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=+TdXFcvfGil9HCRRM3a1gmY4AADRxOCEykgEHAiioTQ=; b=L5LQyaDuPN6PiNDg3Y2+zCGbP05pDWkVQt+1l68FDWPqOJPQUFhvqfpVvbSZzQrA3KVYzN ad710/FAehaJsD0EIzZxMhjaIiLF+kWoXcHjoshhfXiO2X2PtMBk8zXGOnvppxmBcP4zZx WGUCfuH8/9dwjl8QKvL465oml56F/qZobjlNR1z95aUjuO7NQgq1BejGJWLXCGyBATnbbS wZHj5VAi4qcA9tQ3Rp2LO6hckYvxp+pO1B/iSLpRzZAh5M1/Y1FuQzeqE1gi05MDhMhFbO 3ncuugAYJaqT9F06vjvd5P+Z2l3rlhnYamrPGiKF9xoMyvbYlVB0aMsHqXIanA== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1790697606; b=SNbksKYMtkwUDjB0tIMFirM8TdXA7DYb1F4wiTycEvLgtYNV0NuFtTWUvKihQ+qY7zqPrN SNGoTgv5F5ZBGc5hFiTzjLfZu3EeLHuRkDhlUzm3SE5GJtilKYAJ1i5Mzhgt3XyzF6FxZ7 wqHFqOSUkeyHcuHavlrpBlJe1mG8FZHE5EdB+vVPDeGhA9zPK2g7PyyAKhMgmY9fyatKWC gkxn8bL2pFDPdIUbbp712OlSqQ52QpqapSE799H5MidujNCbeVj2cOh00kbW81zmhuMC9m A7zo1dRuDFvOvxZfQ5dxgkAHrjmt3qXqHCO8r8o2JTXKShthn/8Zeb7ei+V5jw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1790697606; 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=+TdXFcvfGil9HCRRM3a1gmY4AADRxOCEykgEHAiioTQ=; b=MKcMpRQSjnlz+6gJZ391CFg69+HGAzIzKJK0rbWMVzvHP93EPip2zNynzOlhhgaNyMsux4 tO7bpHdmHXUpqVMpbVc+i1pfc3pINhh8wBZ7EN9G8o+GbDgPp6+N5xMZEjtMsJ5T65FTB8 PC0kyOYCUX2jYDnE+F4HM7xUYj1G1ELkqUnuXOCGMQpLyvyi4n1zQSuYk8qv2EN9QXIdzI lHd7qUQ/Z0T+Ly3u0AdvPS3CW6zakX2bwz/b5FvVxvK+oiwKQwkjaYbF1JyEwbhvj8KBgO YeVaMNuwhHiXEhFdE3TTzoaknWtnFeWpUjWzh+JKCpPYGI27kji0cX4i8tGGAw== 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 4hvNDQ2DZyz1KPk for ; Tue, 29 Sep 2026 16:00:06 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 30270 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 29 Sep 2026 16:00:06 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: e2db1c9a88e8 - releng/15.1 - 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/releng/15.1 X-Git-Reftype: branch X-Git-Commit: e2db1c9a88e89e0ee5d2eed0acaebb9283d1294e Auto-Submitted: auto-generated Date: Tue, 29 Sep 2026 16:00:06 +0000 Message-Id: <6abbe086.30270.3bf6840c@gitrepo.freebsd.org> The branch releng/15.1 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=e2db1c9a88e89e0ee5d2eed0acaebb9283d1294e commit e2db1c9a88e89e0ee5d2eed0acaebb9283d1294e Author: Mark Johnston AuthorDate: 2026-09-28 14:43:23 +0000 Commit: Mark Johnston CommitDate: 2026-09-29 01:01:48 +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 e9ee752712f0..522e824a98d5 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 6e23525186ea..e4e6f3316dc0 100644 --- a/sys/kern/subr_capability.c +++ b/sys/kern/subr_capability.c @@ -314,6 +314,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 3847c4c73e75..a6811e2de9b6 100644 --- a/sys/sys/capsicum.h +++ b/sys/sys/capsicum.h @@ -344,6 +344,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 2f9dedec1905..b2c4be9874c1 100644 --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -245,6 +245,7 @@ bool filecaps_copy(const struct filecaps *src, struct filecaps *dst, void filecaps_move(struct filecaps *src, struct filecaps *dst); 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);