git: 9f146a81d2b3 - main - dummymbuf: Validate syntax upon write to net.dummymbuf.rules sysctl

From: Igor Ostapenko <igoro_at_FreeBSD.org>
Date: Sat, 05 Oct 2024 09:51:51 UTC
The branch main has been updated by igoro:

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

commit 9f146a81d2b33cfed0d5a494e798100c4e4f2a72
Author:     Igor Ostapenko <igoro@FreeBSD.org>
AuthorDate: 2024-10-05 08:04:08 +0000
Commit:     Igor Ostapenko <igoro@FreeBSD.org>
CommitDate: 2024-10-05 08:04:08 +0000

    dummymbuf: Validate syntax upon write to net.dummymbuf.rules sysctl
    
    For now, opargs are not validated due to their runtime nature.
    
    Reviewed by:    kp
    Approved by:    kp (mentor)
    Differential Revision:  https://reviews.freebsd.org/D46496
---
 sys/net/dummymbuf.c | 61 ++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 49 insertions(+), 12 deletions(-)

diff --git a/sys/net/dummymbuf.c b/sys/net/dummymbuf.c
index d4ba00b13235..99b6e6944752 100644
--- a/sys/net/dummymbuf.c
+++ b/sys/net/dummymbuf.c
@@ -40,6 +40,8 @@
 #include <net/vnet.h>
 #include <net/pfil.h>
 
+static int validate_rules(const char *);
+
 /*
  * Separate sysctl sub-tree
  */
@@ -65,6 +67,7 @@ VNET_DEFINE_STATIC(struct sx,	dmb_rules_lock);
 #define DMB_RULES_SUNLOCK()	sx_sunlock(&V_dmb_rules_lock)
 #define DMB_RULES_XLOCK()	sx_xlock(&V_dmb_rules_lock)
 #define DMB_RULES_XUNLOCK()	sx_xunlock(&V_dmb_rules_lock)
+#define DMB_RULES_LOCK_ASSERT()	sx_assert(&V_dmb_rules_lock, SA_LOCKED)
 
 static int
 dmb_sysctl_handle_rules(SYSCTL_HANDLER_ARGS)
@@ -86,12 +89,14 @@ dmb_sysctl_handle_rules(SYSCTL_HANDLER_ARGS)
 	} else {
 		/* read and write */
 		DMB_RULES_XLOCK();
-		if (*rulesp == NULL) {
-			*rulesp = malloc(arg2, M_DUMMYMBUF_RULES,
-			    M_WAITOK | M_ZERO);
-		}
-		arg1 = *rulesp;
+		arg1 = malloc(arg2, M_DUMMYMBUF_RULES, M_WAITOK | M_ZERO);
 		error = sysctl_handle_string(oidp, arg1, arg2, req);
+		if (error == 0 && (error = validate_rules(arg1)) == 0) {
+			free(*rulesp, M_DUMMYMBUF_RULES);
+			*rulesp = arg1;
+			arg1 = NULL;
+		}
+		free(arg1, M_DUMMYMBUF_RULES);
 		DMB_RULES_XUNLOCK();
 	}
 
@@ -135,7 +140,13 @@ VNET_DEFINE_STATIC(pfil_hook_t,		dmb_pfil_ethernet_hook);
  * Logging
  */
 
-#define FEEDBACK(pfil_type, pfil_flags, ifp, rule, msg)			\
+#define FEEDBACK_RULE(rule, msg)					\
+	printf("dummymbuf: %s: %.*s\n",					\
+	    (msg),							\
+	    (rule).syntax_len, (rule).syntax_begin			\
+	)
+
+#define FEEDBACK_PFIL(pfil_type, pfil_flags, ifp, rule, msg)		\
 	printf("dummymbuf: %s %b %s: %s: %.*s\n",			\
 	    (pfil_type == PFIL_TYPE_IP4 ?	"PFIL_TYPE_IP4" :	\
 	     pfil_type == PFIL_TYPE_IP6 ?	"PFIL_TYPE_IP6" :	\
@@ -198,7 +209,7 @@ bad:
 }
 
 static bool
-read_rule(const char **cur, struct rule *rule)
+read_rule(const char **cur, struct rule *rule, bool *eof)
 {
 	// {inet | inet6 | ethernet} {in | out} <ifname> <opname>[ <opargs>];
 
@@ -276,11 +287,37 @@ read_rule(const char **cur, struct rule *rule)
 		return (false);
 	rule->opargs = *cur;
 
+	// the next rule & eof
 	*cur = delim + 1;
+	while (**cur == ' ')
+		(*cur)++;
+	*eof = strlen(*cur) == 0;
 
 	return (true);
 }
 
+static int
+validate_rules(const char *rules)
+{
+	const char *cursor = rules;
+	bool parsed;
+	struct rule rule;
+	bool eof = false;
+
+	DMB_RULES_LOCK_ASSERT();
+
+	while (!eof && (parsed = read_rule(&cursor, &rule, &eof))) {
+		/* noop */
+	}
+
+	if (!parsed) {
+		FEEDBACK_RULE(rule, "rule parsing failed");
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
 static pfil_return_t
 dmb_pfil_mbuf_chk(int pfil_type, struct mbuf **mp, struct ifnet *ifp,
     int flags, void *ruleset, void *unused)
@@ -289,26 +326,26 @@ dmb_pfil_mbuf_chk(int pfil_type, struct mbuf **mp, struct ifnet *ifp,
 	const char *cursor;
 	bool parsed;
 	struct rule rule;
+	bool eof = false;
 
 	DMB_RULES_SLOCK();
 	cursor = V_dmb_rules;
-	while ((parsed = read_rule(&cursor, &rule))) {
+	while (!eof && (parsed = read_rule(&cursor, &rule, &eof))) {
 		if (rule.pfil_type == pfil_type &&
 		    rule.pfil_dir == (flags & rule.pfil_dir)  &&
 		    strcmp(rule.ifname, ifp->if_xname) == 0) {
 			m = rule.op(m, &rule);
 			if (m == NULL) {
-				FEEDBACK(pfil_type, flags, ifp, rule,
+				FEEDBACK_PFIL(pfil_type, flags, ifp, rule,
 				    "mbuf operation failed");
 				break;
 			}
 			counter_u64_add(V_dmb_hits, 1);
 		}
-		if (strlen(cursor) == 0)
-			break;
 	}
 	if (!parsed) {
-		FEEDBACK(pfil_type, flags, ifp, rule, "rule parsing failed");
+		FEEDBACK_PFIL(pfil_type, flags, ifp, rule,
+		    "rule parsing failed");
 		m_freem(m);
 		m = NULL;
 	}