git: 91d3f40d93e3 - stable/13 - libsysdecode: Fix decoding of Capsicum rights

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Fri, 22 Apr 2022 15:25:04 UTC
The branch stable/13 has been updated by markj:

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

commit 91d3f40d93e3cda1a04eb0b36158a74c67c62e37
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2022-04-13 14:47:08 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2022-04-22 14:36:22 +0000

    libsysdecode: Fix decoding of Capsicum rights
    
    Capsicum rights are a bit tricky since some of them are subsets of
    others, and one can have rights R1 and R2 such that R1 is a subset of
    R2, but there is no collection of named rights whose union is R2.  So,
    they don't behave like most other flag sets.  sysdecode_cap_rights(3)
    does not handle this properly and so can emit misleading decodings.
    
    Try to fix all of these problems:
    - Include composite rights in the caprights table.
    - Use a constructor to sort the caprights table such that "larger"
      rights appear first and thus are matched first.
    - Don't print rights that are a subset of rights already printed, so as
      to minimize the length of the output.
    - Print a trailing message if some of the specific rights are not
      matched by the table.
    
    PR:             263165
    Reviewed by:    pauamma_gundo.com (doc), jhb, emaste
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit 869199d9922c7dee92c1c24f95b90f1d1319433e)
---
 lib/libsysdecode/flags.c                | 53 +++++++++++++++++++++++++++++++--
 lib/libsysdecode/mktables               |  2 +-
 lib/libsysdecode/sysdecode_cap_rights.3 | 23 +++++++++++++-
 3 files changed, 73 insertions(+), 5 deletions(-)

diff --git a/lib/libsysdecode/flags.c b/lib/libsysdecode/flags.c
index 35bce1ff77f9..f02c8dd8b339 100644
--- a/lib/libsysdecode/flags.c
+++ b/lib/libsysdecode/flags.c
@@ -1171,7 +1171,8 @@ sysdecode_umtx_rwlock_flags(FILE *fp, u_long flags, u_long *rem)
 void
 sysdecode_cap_rights(FILE *fp, cap_rights_t *rightsp)
 {
-	struct name_table *t;
+	cap_rights_t diff, sum, zero;
+	const struct name_table *t;
 	int i;
 	bool comma;
 
@@ -1181,13 +1182,59 @@ sysdecode_cap_rights(FILE *fp, cap_rights_t *rightsp)
 			return;
 		}
 	}
-	comma = false;
-	for (t = caprights; t->str != NULL; t++) {
+	cap_rights_init(&sum);
+	diff = *rightsp;
+	for (t = caprights, comma = false; t->str != NULL; t++) {
 		if (cap_rights_is_set(rightsp, t->val)) {
+			cap_rights_clear(&diff, t->val);
+			if (cap_rights_is_set(&sum, t->val)) {
+				/* Don't print redundant rights. */
+				continue;
+			}
+			cap_rights_set(&sum, t->val);
+
 			fprintf(fp, "%s%s", comma ? "," : "", t->str);
 			comma = true;
 		}
 	}
+	if (!comma)
+		fprintf(fp, "CAP_NONE");
+
+	/*
+	 * Provide a breadcrumb if some of the provided rights are not included
+	 * in the table, likely due to a bug in the mktables script.
+	 */
+	CAP_NONE(&zero);
+	if (!cap_rights_contains(&zero, &diff))
+		fprintf(fp, ",unknown rights");
+}
+
+/*
+ * Pre-sort the set of rights, which has a partial ordering defined by the
+ * subset relation.  This lets sysdecode_cap_rights() print a list of minimal
+ * length with a single pass over the "caprights" table.
+ */
+static void __attribute__((constructor))
+sysdecode_cap_rights_init(void)
+{
+	cap_rights_t tr, qr;
+	struct name_table *t, *q, tmp;
+	bool swapped;
+
+	do {
+		for (t = caprights, swapped = false; t->str != NULL; t++) {
+			cap_rights_init(&tr, t->val);
+			for (q = t + 1; q->str != NULL; q++) {
+				cap_rights_init(&qr, q->val);
+				if (cap_rights_contains(&qr, &tr)) {
+					tmp = *t;
+					*t = *q;
+					*q = tmp;
+					swapped = true;
+				}
+			}
+		}
+	} while (swapped);
 }
 
 static struct name_table cmsgtypeip[] = {
diff --git a/lib/libsysdecode/mktables b/lib/libsysdecode/mktables
index c9c6830b04aa..5003be201fbd 100644
--- a/lib/libsysdecode/mktables
+++ b/lib/libsysdecode/mktables
@@ -159,7 +159,7 @@ gen_table "msgflags"        "MSG_[A-Z_]+[[:space:]]+0x[0-9]+"              "sys/
 gen_table "sigcode"         "SI_[A-Z]+[[:space:]]+0(x[0-9abcdef]+)?"       "sys/signal.h"
 gen_table "umtxcvwaitflags" "CVWAIT_[A-Z_]+[[:space:]]+0x[0-9]+"           "sys/umtx.h"
 gen_table "umtxrwlockflags" "URWLOCK_PREFER_READER[[:space:]]+0x[0-9]+"    "sys/umtx.h"
-gen_table "caprights"       "CAP_[A-Z_]+[[:space:]]+CAPRIGHT\([0-9],[[:space:]]+0x[0-9]{16}ULL\)"   "sys/capsicum.h"
+gen_table "caprights"       "CAP_[A-Z_]+[[:space:]]+((CAPRIGHT\([0-9],[[:space:]]+0x[0-9]{16}ULL\))|(\(CAP_[A-Z_]+[[:space:]]*\|.*\)))"   "sys/capsicum.h"
 gen_table "sctpprpolicy"    "SCTP_PR_SCTP_[A-Z_]+[[:space:]]+0x[0-9]+"     "netinet/sctp_uio.h" "SCTP_PR_SCTP_ALL"
 gen_table "cmsgtypesocket"  "SCM_[A-Z_]+[[:space:]]+0x[0-9]+"              "sys/socket.h"
 if [ -e "${include_dir}/x86/sysarch.h" ]; then
diff --git a/lib/libsysdecode/sysdecode_cap_rights.3 b/lib/libsysdecode/sysdecode_cap_rights.3
index 674e12ad3a01..e165e3d43938 100644
--- a/lib/libsysdecode/sysdecode_cap_rights.3
+++ b/lib/libsysdecode/sysdecode_cap_rights.3
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd November 24, 2017
+.Dd April 11, 2022
 .Dt sysdecode_cap_rights 3
 .Os
 .Sh NAME
@@ -46,5 +46,26 @@ function outputs a comma-separated list of capability rights at
 .Fa rightsp
 to the stream
 .Fa fp .
+.Pp
+Note that some capability rights are supersets of others; for example,
+.Dv CAP_PREAD
+is the union of
+.Dv CAP_READ
+and
+.Dv CAP_SEEK .
+.Fn sysdecode_cap_rights
+emits a minimal list of rights whose union is equal to
+.Fa *rightsp .
+For example, if both
+.Dv CAP_READ
+and
+.Dv CAP_SEEK
+are set in
+.Fa *rightsp ,
+then
+.Fn sysdecode_cap_rights
+will include only
+.Dv CAP_PREAD
+in the output list.
 .Sh SEE ALSO
 .Xr sysdecode 3