[PATCH] net/sched: em_meta: clamp shift to value width
From: Noah Weinberger
Date: Sun Nov 08 2026 - 17:37:35 EST
Hello,
em_meta_change() copies the user-supplied header without validating
hdr.shift, and meta_int_apply_extras() then executes
dst->value >>= v->hdr.shift on an unsigned long. A shift count of
BITS_PER_LONG or more (the field is a u8, so up to 255) is undefined
behaviour - on x86 the count is hardware-masked, so the practical
effect is a wrong match verdict or a UBSAN splat, not corruption.
act_pedit clamps its shift for exactly this class.
The patch clamps both values at change time, like act_pedit does.
Affected version/commit: mainline fa4ad798f6f681031775266044ba1b0cf3c670bd (v7.4-rc1, 2026-11-07), net/sched/em_meta.c:762-770 (meta_int_apply_extras), 906-941 (em_meta_change).
Longstanding code; the sibling fix pattern exists in act_pedit.
This was found during an assisted (AI-supported) static source review of
mainline fa4ad798f6f681031775266044ba1b0cf3c670bd (v7.4-rc1, 2026-11-07); every code path cited below was re-verified by the
reporter against that tree. The patch below has NOT been compiled and has
NOT been runtime tested, and no reproducer was produced - the triggering
conditions are described above instead. Please treat this as a report for
verification rather than a battle-tested fix.
Reported-by: Noah Weinberger <noahweinberger2003@xxxxxxxxx>
---
diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c
index 48bdd77..6681c67 100644
--- a/net/sched/em_meta.c
+++ b/net/sched/em_meta.c
@@ -936,6 +936,14 @@ static int em_meta_change(struct net *net, void *data, int len,
memcpy(&meta->lvalue.hdr, &hdr->left, sizeof(hdr->left));
memcpy(&meta->rvalue.hdr, &hdr->right, sizeof(hdr->right));
+ /* Keep the shift within the value width to avoid undefined
+ * behaviour in meta_int_apply_extras(), like act_pedit does.
+ */
+ if (meta->lvalue.hdr.shift > BITS_PER_LONG - 1)
+ meta->lvalue.hdr.shift = BITS_PER_LONG - 1;
+ if (meta->rvalue.hdr.shift > BITS_PER_LONG - 1)
+ meta->rvalue.hdr.shift = BITS_PER_LONG - 1;
+
if (!meta_is_supported(&meta->lvalue) ||
!meta_is_supported(&meta->rvalue)) {
err = -EOPNOTSUPP;