From: Bruce Richardson <[email protected]>

The gso library never checks that the buffers used for the packet
headers are actually big enough to handle those headers, leading to a
memcpy overflow if the allocated mbuf size is too small. Add some
appropriate checks.

CVE-2026-100236
Bugzilla ID: 2047

Fixes: 119583797b6a ("gso: support TCP/IPv4 GSO")
Cc: [email protected]

Reported-by: Alexandru-Ionut Jipa <[email protected]>
Acked-by: Maxime Coquelin <[email protected]>
Signed-off-by: Bruce Richardson <[email protected]>
---
 .mailmap                  |  1 +
 lib/gso/gso_common.c      | 12 ++++++++++++
 lib/gso/gso_tcp4.c        |  5 ++---
 lib/gso/gso_tunnel_tcp4.c |  6 +++---
 lib/gso/gso_tunnel_udp4.c |  4 ++--
 lib/gso/gso_udp4.c        |  5 ++---
 6 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/.mailmap b/.mailmap
index 2e348c3bce..38d89d9e87 100644
--- a/.mailmap
+++ b/.mailmap
@@ -71,6 +71,7 @@ Alexander Skorichenko <[email protected]>
 Alexander Solganik <[email protected]>
 Alexander V Gutkin <[email protected]>
 Alexandre Ferrieux <[email protected]>
+Alexandru-Ionut Jipa <[email protected]>
 Alexey Kardashevskiy <[email protected]>
 Alexey Simakov <[email protected]>
 Alfredo Cardigliano <[email protected]>
diff --git a/lib/gso/gso_common.c b/lib/gso/gso_common.c
index 378bf2c6aa..481c3b3beb 100644
--- a/lib/gso/gso_common.c
+++ b/lib/gso/gso_common.c
@@ -50,8 +50,20 @@ gso_do_segment(struct rte_mbuf *pkt,
        struct rte_mbuf *hdr_segment, *pyld_segment, *prev_segment;
        uint16_t pkt_in_data_pos, segment_bytes_remaining;
        uint16_t pyld_len, nb_segs;
+       uint16_t direct_buf_size, direct_tailroom;
        bool more_in_pkt, more_out_segs;
 
+       /*
+        * The header must be fully contained in pkt's first segment and must 
fit
+        * in a freshly allocated direct_pool mbuf.
+        */
+       direct_buf_size = rte_pktmbuf_data_room_size(direct_pool);
+       if (unlikely(direct_buf_size <= (uint16_t)RTE_PKTMBUF_HEADROOM))
+               return -EINVAL;
+       direct_tailroom = direct_buf_size - RTE_PKTMBUF_HEADROOM;
+       if (unlikely(pkt_hdr_offset > pkt->data_len || pkt_hdr_offset > 
direct_tailroom))
+               return -EINVAL;
+
        pkt_in = pkt;
        nb_segs = 0;
        more_in_pkt = 1;
diff --git a/lib/gso/gso_tcp4.c b/lib/gso/gso_tcp4.c
index e2ae4aaf6c..d086106355 100644
--- a/lib/gso/gso_tcp4.c
+++ b/lib/gso/gso_tcp4.c
@@ -53,11 +53,10 @@ gso_tcp4_segment(struct rte_mbuf *pkt,
                return 0;
        }
 
-       /* Don't process the packet without data */
+       /* Don't process the packet without data or hdr larger than seg size */
        hdr_offset = pkt->l2_len + pkt->l3_len + pkt->l4_len;
-       if (unlikely(hdr_offset >= pkt->pkt_len)) {
+       if (unlikely(hdr_offset >= pkt->pkt_len || hdr_offset >= gso_size))
                return 0;
-       }
 
        pyld_unit_size = gso_size - hdr_offset;
 
diff --git a/lib/gso/gso_tunnel_tcp4.c b/lib/gso/gso_tunnel_tcp4.c
index 3a9159774b..f0d1a6efd6 100644
--- a/lib/gso/gso_tunnel_tcp4.c
+++ b/lib/gso/gso_tunnel_tcp4.c
@@ -77,10 +77,10 @@ gso_tunnel_tcp4_segment(struct rte_mbuf *pkt,
        }
 
        hdr_offset += pkt->l3_len + pkt->l4_len;
-       /* Don't process the packet without data */
-       if (hdr_offset >= pkt->pkt_len) {
+       /* Don't process the packet without data or hdr larger than seg size */
+       if (hdr_offset >= pkt->pkt_len || hdr_offset >= gso_size)
                return 0;
-       }
+
        pyld_unit_size = gso_size - hdr_offset;
 
        /* Segment the payload */
diff --git a/lib/gso/gso_tunnel_udp4.c b/lib/gso/gso_tunnel_udp4.c
index 4fb275484c..43e4103bc7 100644
--- a/lib/gso/gso_tunnel_udp4.c
+++ b/lib/gso/gso_tunnel_udp4.c
@@ -78,8 +78,8 @@ gso_tunnel_udp4_segment(struct rte_mbuf *pkt,
                return 0;
 
        hdr_offset += pkt->l3_len;
-       /* Don't process the packet without data */
-       if ((hdr_offset + pkt->l4_len) >= pkt->pkt_len)
+       /* Don't process the packet without data or hdrs larger than seg size */
+       if ((hdr_offset + pkt->l4_len) >= pkt->pkt_len || hdr_offset >= 
gso_size)
                return 0;
 
        /* pyld_unit_size must be a multiple of 8 because frag_off
diff --git a/lib/gso/gso_udp4.c b/lib/gso/gso_udp4.c
index 5d0186aa24..e2ec84759d 100644
--- a/lib/gso/gso_udp4.c
+++ b/lib/gso/gso_udp4.c
@@ -62,10 +62,9 @@ gso_udp4_segment(struct rte_mbuf *pkt,
         */
        hdr_offset = pkt->l2_len + pkt->l3_len;
 
-       /* Don't process the packet without data. */
-       if (unlikely(hdr_offset + pkt->l4_len >= pkt->pkt_len)) {
+       /* Don't process the packet without data or header larger than seg 
size. */
+       if (unlikely(hdr_offset + pkt->l4_len >= pkt->pkt_len || hdr_offset >= 
gso_size))
                return 0;
-       }
 
        /* pyld_unit_size must be a multiple of 8 because frag_off
         * uses 8 bytes as unit.
-- 
2.56.0

Reply via email to