]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
netfilter: xt_multiport: validate range encoding in checkentry
authorRen Wei <n05ec@lzu.edu.cn>
Fri, 3 Apr 2026 15:52:52 +0000 (23:52 +0800)
committerFlorian Westphal <fw@strlen.de>
Wed, 8 Apr 2026 11:33:38 +0000 (13:33 +0200)
ports_match_v1() treats any non-zero pflags entry as the start of a
port range and unconditionally consumes the next ports[] element as
the range end.

The checkentry path currently validates protocol, flags and count, but
it does not validate the range encoding itself. As a result, malformed
rules can mark the last slot as a range start or place two range starts
back to back, leaving ports_match_v1() to step past the last valid
ports[] element while interpreting the rule.

Reject malformed multiport v1 rules in checkentry by validating that
each range start has a following element and that the following element
is not itself marked as another range start.

Fixes: a89ecb6a2ef7 ("[NETFILTER]: x_tables: unify IPv4/IPv6 multiport match")
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Co-developed-by: Yuan Tan <yuantan098@gmail.com>
Signed-off-by: Yuan Tan <yuantan098@gmail.com>
Suggested-by: Xin Liu <bird@lzu.edu.cn>
Tested-by: Yuhang Zheng <z1652074432@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
Signed-off-by: Florian Westphal <fw@strlen.de>
net/netfilter/xt_multiport.c

index 44a00f5acde8ae3a371fe0f8159d549b00c13cb7..a1691ff405d3c43b22d7f5d56d4b83dca5d7940f 100644 (file)
@@ -105,6 +105,28 @@ multiport_mt(const struct sk_buff *skb, struct xt_action_param *par)
        return ports_match_v1(multiinfo, ntohs(pptr[0]), ntohs(pptr[1]));
 }
 
+static bool
+multiport_valid_ranges(const struct xt_multiport_v1 *multiinfo)
+{
+       unsigned int i;
+
+       for (i = 0; i < multiinfo->count; i++) {
+               if (!multiinfo->pflags[i])
+                       continue;
+
+               if (++i >= multiinfo->count)
+                       return false;
+
+               if (multiinfo->pflags[i])
+                       return false;
+
+               if (multiinfo->ports[i - 1] > multiinfo->ports[i])
+                       return false;
+       }
+
+       return true;
+}
+
 static inline bool
 check(u_int16_t proto,
       u_int8_t ip_invflags,
@@ -127,8 +149,10 @@ static int multiport_mt_check(const struct xt_mtchk_param *par)
        const struct ipt_ip *ip = par->entryinfo;
        const struct xt_multiport_v1 *multiinfo = par->matchinfo;
 
-       return check(ip->proto, ip->invflags, multiinfo->flags,
-                    multiinfo->count) ? 0 : -EINVAL;
+       if (!check(ip->proto, ip->invflags, multiinfo->flags, multiinfo->count))
+               return -EINVAL;
+
+       return multiport_valid_ranges(multiinfo) ? 0 : -EINVAL;
 }
 
 static int multiport_mt6_check(const struct xt_mtchk_param *par)
@@ -136,8 +160,10 @@ static int multiport_mt6_check(const struct xt_mtchk_param *par)
        const struct ip6t_ip6 *ip = par->entryinfo;
        const struct xt_multiport_v1 *multiinfo = par->matchinfo;
 
-       return check(ip->proto, ip->invflags, multiinfo->flags,
-                    multiinfo->count) ? 0 : -EINVAL;
+       if (!check(ip->proto, ip->invflags, multiinfo->flags, multiinfo->count))
+               return -EINVAL;
+
+       return multiport_valid_ranges(multiinfo) ? 0 : -EINVAL;
 }
 
 static struct xt_match multiport_mt_reg[] __read_mostly = {