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