From: Eric Dumazet <edumazet@google.com> mainline inclusion from mainline-v7.1-rc1 commit 7fb4c19670110f052c04e1ec1d2b953b9f4f57e4 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15794 CVE: CVE-2026-53091 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Most ndo_start_xmit() methods expects headers of gso packets to be already in skb->head. net/core/tso.c users are particularly at risk, because tso_build_hdr() does a memcpy(hdr, skb->data, hdr_len); qdisc_pkt_len_segs_init() already does a dissection of gso packets. Use pskb_may_pull() instead of skb_header_pointer() to make sure drivers do not have to reimplement this. Some malicious packets could be fed, detect them so that we can drop them sooner with a new SKB_DROP_REASON_SKB_BAD_GSO drop_reason. Fixes: e876f208af18 ("net: Add a software TSO helper API") Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: Joe Damato <joe@dama.to> Link: https://patch.msgid.link/20260403221540.3297753-3-edumazet@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Conflicts: include/net/dropreason-core.h net/core/dev.c [commit c504e5c2f964 ("net: skb: introduce kfree_skb_reason()") introduced skb_drop_reason enum and kfree_skb_reason(); later commit ff8372a467fa ("net: skb: move enum skb_drop_reason to standalone header file") and 5b8285cca6fe ("net: move dropreason.h to dropreason-core.h") moved it to include/net/dropreason-core.h. These are not present in OLK-5.10, so the SKB_DROP_REASON_SKB_BAD_GSO drop reason is omitted and the malicious GSO packet is dropped using kfree_skb() and atomic_long_inc(&dev->tx_dropped) instead. commit 874c1928d372 ("net_sched: initialize qdisc_skb_cb(skb)->pkt_segs in qdisc_pkt_len_init()") renamed qdisc_pkt_len_init() to qdisc_pkt_len_segs_init() and added pkt_segs initialization; this is not backported to OLK-5.10, so the fix is applied to the existing qdisc_pkt_len_init() function and the function returns -EINVAL on error. commit 625788b58445 ("net: add per-cpu storage and net->core_stats") introduced dev_core_stats_tx_dropped_inc(), which is not present in OLK-5.10; the existing atomic_long_inc(&dev->tx_dropped) is used instead.] Signed-off-by: Dong Chenchen <dongchenchen2@huawei.com> --- net/core/dev.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index d6dc66b2960d..8d2a00851eb7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3748,7 +3748,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d } EXPORT_SYMBOL_GPL(validate_xmit_skb_list); -static void qdisc_pkt_len_init(struct sk_buff *skb) +static int qdisc_pkt_len_init(struct sk_buff *skb) { const struct skb_shared_info *shinfo = skb_shinfo(skb); @@ -3758,7 +3758,7 @@ static void qdisc_pkt_len_init(struct sk_buff *skb) * we add to pkt_len the headers size of all segments */ if (shinfo->gso_size && skb_transport_header_was_set(skb)) { - unsigned int hdr_len; + unsigned int hdr_len, tlen; u16 gso_segs = shinfo->gso_segs; /* mac layer + network layer */ @@ -3767,30 +3767,36 @@ static void qdisc_pkt_len_init(struct sk_buff *skb) /* + transport layer */ if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) { const struct tcphdr *th; - struct tcphdr _tcphdr; - th = skb_header_pointer(skb, skb_transport_offset(skb), - sizeof(_tcphdr), &_tcphdr); - if (likely(th)) - hdr_len += __tcp_hdrlen(th); - } else if (shinfo->gso_type & SKB_GSO_UDP_L4) { - struct udphdr _udphdr; + if (!pskb_may_pull(skb, hdr_len + sizeof(struct tcphdr))) + return -EINVAL; - if (skb_header_pointer(skb, skb_transport_offset(skb), - sizeof(_udphdr), &_udphdr)) - hdr_len += sizeof(struct udphdr); + th = (const struct tcphdr *)(skb->data + hdr_len); + tlen = __tcp_hdrlen(th); + if (tlen < sizeof(*th)) + return -EINVAL; + hdr_len += tlen; + if (!pskb_may_pull(skb, hdr_len)) + return -EINVAL; + } else if (shinfo->gso_type & SKB_GSO_UDP_L4) { + if (!pskb_may_pull(skb, hdr_len + sizeof(struct udphdr))) + return -EINVAL; + hdr_len += sizeof(struct udphdr); } + /* prior pskb_may_pull() might have changed skb->head. */ + shinfo = skb_shinfo(skb); if (unlikely(shinfo->gso_type & SKB_GSO_DODGY)) { int payload = skb->len - hdr_len; /* Malicious packet. */ if (payload <= 0) - return; + return -EINVAL; gso_segs = DIV_ROUND_UP(payload, shinfo->gso_size); } qdisc_skb_cb(skb)->pkt_len += (gso_segs - 1) * hdr_len; } + return 0; } static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, @@ -4145,7 +4151,12 @@ static int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) skb_update_prio(skb); - qdisc_pkt_len_init(skb); + if (unlikely(qdisc_pkt_len_init(skb))) { + atomic_long_inc(&dev->tx_dropped); + kfree_skb(skb); + rc = -EINVAL; + goto out; + } #ifdef CONFIG_NET_CLS_ACT skb->tc_at_ingress = 0; # ifdef CONFIG_NET_EGRESS -- 2.43.0