git: dfc979fb8e21 - main - file: Add filecaps_intersect() and cap_rights_intersect()
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 29 Sep 2026 15:55:47 UTC
The branch main has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=dfc979fb8e210b565c4e291b6e67205733b6e663
commit dfc979fb8e210b565c4e291b6e67205733b6e663
Author: Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-09-28 14:43:23 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-09-29 15:54:16 +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 b169a3a677c3..80735e0b4c0c 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);