Merge branch 'net-fix-potential-crash-in-net-sched-cls_u32-c'

Eric Dumazet says:

====================
net: fix potential crash in net/sched/cls_u32.c

GangMin Kim provided a report and a repro fooling u32_classify().

Add skb_header_pointer_careful() variant of skb_header_pointer()
and use it in net/sched/cls_u32.c.

Later we can also use it in net/sched/act_pedit.c
====================

Link: https://patch.msgid.link/20260128141539.3404400-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-01-29 18:25:24 -08:00
commit de5720f91b
2 changed files with 18 additions and 7 deletions

View File

@ -4301,6 +4301,18 @@ skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer)
skb_headlen(skb), buffer);
}
/* Variant of skb_header_pointer() where @offset is user-controlled
* and potentially negative.
*/
static inline void * __must_check
skb_header_pointer_careful(const struct sk_buff *skb, int offset,
int len, void *buffer)
{
if (unlikely(offset < 0 && -offset > skb_headroom(skb)))
return NULL;
return skb_header_pointer(skb, offset, len, buffer);
}
static inline void * __must_check
skb_pointer_if_linear(const struct sk_buff *skb, int offset, int len)
{

View File

@ -161,10 +161,8 @@ TC_INDIRECT_SCOPE int u32_classify(struct sk_buff *skb,
int toff = off + key->off + (off2 & key->offmask);
__be32 *data, hdata;
if (skb_headroom(skb) + toff > INT_MAX)
goto out;
data = skb_header_pointer(skb, toff, 4, &hdata);
data = skb_header_pointer_careful(skb, toff, 4,
&hdata);
if (!data)
goto out;
if ((*data ^ key->val) & key->mask) {
@ -214,8 +212,9 @@ TC_INDIRECT_SCOPE int u32_classify(struct sk_buff *skb,
if (ht->divisor) {
__be32 *data, hdata;
data = skb_header_pointer(skb, off + n->sel.hoff, 4,
&hdata);
data = skb_header_pointer_careful(skb,
off + n->sel.hoff,
4, &hdata);
if (!data)
goto out;
sel = ht->divisor & u32_hash_fold(*data, &n->sel,
@ -229,7 +228,7 @@ TC_INDIRECT_SCOPE int u32_classify(struct sk_buff *skb,
if (n->sel.flags & TC_U32_VAROFFSET) {
__be16 *data, hdata;
data = skb_header_pointer(skb,
data = skb_header_pointer_careful(skb,
off + n->sel.offoff,
2, &hdata);
if (!data)