netfilter: xt_multiport: validate range encoding in checkentry

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: a89ecb6a2e ("[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>
This commit is contained in:
Ren Wei 2026-04-03 23:52:52 +08:00 committed by Florian Westphal
parent 1f3083aec8
commit ff64c5bfef

View 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 = {