* [PATCH net v3 0/2] octeon_ep, octeon_ep_vf: fix skb frags overflow in the RX path
@ 2026-07-04 6:15 Maoyi Xie
2026-07-04 6:15 ` [PATCH net v3 1/2] octeon_ep: " Maoyi Xie
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Maoyi Xie @ 2026-07-04 6:15 UTC (permalink / raw)
To: Veerasenareddy Burru, Sathesh Edara
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Maciej Fijalkowski, netdev, linux-kernel
Both octeon_ep and octeon_ep_vf build an skb for a multi-buffer RX packet
by adding one fragment per buffer_size chunk of a device-reported length.
Neither bounds the count against MAX_SKB_FRAGS. A long packet yields about
18 fragments, one past the default MAX_SKB_FRAGS of 17, so
skb_add_rx_frag() writes past shinfo->frags[].
Each driver now checks the fragment count before it builds the skb and
drops a packet that would not fit.
v3:
- octeon_ep_vf: pull the drop drain into octep_vf_oq_drop_rx().
The overflow drop and the napi_build_skb failure path both use it.
Suggested by Maciej Fijalkowski.
- octeon_ep: add Maciej's Reviewed-by.
v1: https://lore.kernel.org/r/20260701112825.1653044-1-maoyixie.tju@gmail.com
v2: https://lore.kernel.org/r/20260702180518.2013324-1-maoyixie.tju@gmail.com
Maoyi Xie (2):
octeon_ep: fix skb frags overflow in the RX path
octeon_ep_vf: fix skb frags overflow in the RX path
.../net/ethernet/marvell/octeon_ep/octep_rx.c | 9 ++++
.../marvell/octeon_ep_vf/octep_vf_rx.c | 46 ++++++++++++-------
2 files changed, 39 insertions(+), 16 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH net v3 1/2] octeon_ep: fix skb frags overflow in the RX path
2026-07-04 6:15 [PATCH net v3 0/2] octeon_ep, octeon_ep_vf: fix skb frags overflow in the RX path Maoyi Xie
@ 2026-07-04 6:15 ` Maoyi Xie
2026-07-20 23:49 ` Jakub Kicinski
2026-07-04 6:15 ` [PATCH net v3 2/2] octeon_ep_vf: " Maoyi Xie
2026-07-20 23:50 ` [PATCH net v3 0/2] octeon_ep, " Jakub Kicinski
2 siblings, 1 reply; 7+ messages in thread
From: Maoyi Xie @ 2026-07-04 6:15 UTC (permalink / raw)
To: Veerasenareddy Burru, Sathesh Edara
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Maciej Fijalkowski, netdev, linux-kernel
__octep_oq_process_rx() builds an skb for a multi-buffer packet by adding
one fragment per buffer_size chunk:
data_len = buff_info->len - oq->max_single_buffer_size;
while (data_len) {
...
skb_add_rx_frag(skb, shinfo->nr_frags, buff_info->page, 0,
buff_info->len, buff_info->len);
...
}
buff_info->len comes from the device response header
(be64_to_cpu(resp_hw->length)). Nothing bounds the fragment count against
MAX_SKB_FRAGS. data_len can be close to 65535. buffer_size defaults to
about 3776 on 4K pages, so a full packet yields about 18 fragments. That
is one more than the default MAX_SKB_FRAGS of 17, so skb_add_rx_frag()
writes past shinfo->frags[].
The fragment count is now checked before build_skb(). A packet that needs
more fragments than the skb can hold is dropped. octep_oq_drop_rx()
consumes its descriptors like the build_skb failure path. The same class
was fixed in other RX paths, including commit 5ffcb7b890f6 ("net: atlantic:
fix fragment overflow handling in RX path") and commit f0813bcd2d9d ("net:
wwan: t7xx: fix potential skb->frags overflow in RX path").
Fixes: 37d79d059606 ("octeon_ep: add Tx/Rx processing and interrupt support")
Co-developed-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
Signed-off-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
---
drivers/net/ethernet/marvell/octeon_ep/octep_rx.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c
index e6ebc7e44a..bdbed58c7b 100644
--- a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c
+++ b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c
@@ -453,6 +453,15 @@ static int __octep_oq_process_rx(struct octep_device *oct,
octep_oq_next_pkt(oq, buff_info, &read_idx, &desc_used);
+ if (buff_info->len > oq->max_single_buffer_size) {
+ u16 data_len = buff_info->len - oq->max_single_buffer_size;
+
+ if (DIV_ROUND_UP(data_len, oq->buffer_size) > MAX_SKB_FRAGS) {
+ octep_oq_drop_rx(oq, buff_info, &read_idx, &desc_used);
+ continue;
+ }
+ }
+
skb = build_skb((void *)resp_hw, PAGE_SIZE);
if (!skb) {
octep_oq_drop_rx(oq, buff_info,
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH net v3 2/2] octeon_ep_vf: fix skb frags overflow in the RX path
2026-07-04 6:15 [PATCH net v3 0/2] octeon_ep, octeon_ep_vf: fix skb frags overflow in the RX path Maoyi Xie
2026-07-04 6:15 ` [PATCH net v3 1/2] octeon_ep: " Maoyi Xie
@ 2026-07-04 6:15 ` Maoyi Xie
2026-07-06 9:40 ` Maciej Fijalkowski
2026-07-20 23:49 ` Jakub Kicinski
2026-07-20 23:50 ` [PATCH net v3 0/2] octeon_ep, " Jakub Kicinski
2 siblings, 2 replies; 7+ messages in thread
From: Maoyi Xie @ 2026-07-04 6:15 UTC (permalink / raw)
To: Veerasenareddy Burru, Sathesh Edara
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Maciej Fijalkowski, netdev, linux-kernel
__octep_vf_oq_process_rx() has the same unbounded fragment loop as the PF
driver. buff_info->len comes from the device response header, and one
fragment is added per buffer_size chunk with no check against
MAX_SKB_FRAGS. A long packet yields about 18 fragments, one past the
default MAX_SKB_FRAGS of 17, so skb_add_rx_frag() writes past
shinfo->frags[].
The fragment count is now checked before napi_build_skb(). A packet that
needs more fragments than the skb can hold is dropped.
octep_vf_oq_drop_rx() drains its descriptors. The napi_build_skb()
failure path now uses the same helper.
Fixes: 1cd3b407977c ("octeon_ep_vf: add Tx/Rx processing and interrupt support")
Co-developed-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
Signed-off-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
---
.../marvell/octeon_ep_vf/octep_vf_rx.c | 46 ++++++++++++-------
1 file changed, 30 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
index d982474082..aa77b673ae 100644
--- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
+++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
@@ -357,6 +357,29 @@ static inline u32 octep_vf_oq_next_idx(struct octep_vf_oq *oq, u32 idx)
return (idx + 1 == oq->max_count) ? 0 : idx + 1;
}
+static void octep_vf_oq_drop_rx(struct octep_vf_oq *oq,
+ struct octep_vf_rx_buffer *buff_info,
+ u32 *read_idx, u32 *desc_used)
+{
+ u16 data_len = buff_info->len - oq->max_single_buffer_size;
+
+ (*desc_used)++;
+ *read_idx = octep_vf_oq_next_idx(oq, *read_idx);
+ while (data_len) {
+ dma_unmap_page(oq->dev, oq->desc_ring[*read_idx].buffer_ptr,
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ buff_info = (struct octep_vf_rx_buffer *)
+ &oq->buff_info[*read_idx];
+ buff_info->page = NULL;
+ if (data_len < oq->buffer_size)
+ data_len = 0;
+ else
+ data_len -= oq->buffer_size;
+ (*desc_used)++;
+ *read_idx = octep_vf_oq_next_idx(oq, *read_idx);
+ }
+}
+
/**
* __octep_vf_oq_process_rx() - Process hardware Rx queue and push to stack.
*
@@ -431,25 +454,16 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
struct skb_shared_info *shinfo;
u16 data_len;
+ data_len = buff_info->len - oq->max_single_buffer_size;
+ if (DIV_ROUND_UP(data_len, oq->buffer_size) > MAX_SKB_FRAGS) {
+ octep_vf_oq_drop_rx(oq, buff_info, &read_idx, &desc_used);
+ continue;
+ }
+
skb = napi_build_skb((void *)resp_hw, PAGE_SIZE);
if (!skb) {
oq->stats->alloc_failures++;
- desc_used++;
- read_idx = octep_vf_oq_next_idx(oq, read_idx);
- data_len = buff_info->len - oq->max_single_buffer_size;
- while (data_len) {
- dma_unmap_page(oq->dev, oq->desc_ring[read_idx].buffer_ptr,
- PAGE_SIZE, DMA_FROM_DEVICE);
- buff_info = (struct octep_vf_rx_buffer *)
- &oq->buff_info[read_idx];
- buff_info->page = NULL;
- if (data_len < oq->buffer_size)
- data_len = 0;
- else
- data_len -= oq->buffer_size;
- desc_used++;
- read_idx = octep_vf_oq_next_idx(oq, read_idx);
- }
+ octep_vf_oq_drop_rx(oq, buff_info, &read_idx, &desc_used);
continue;
}
rx_bytes += buff_info->len;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH net v3 2/2] octeon_ep_vf: fix skb frags overflow in the RX path
2026-07-04 6:15 ` [PATCH net v3 2/2] octeon_ep_vf: " Maoyi Xie
@ 2026-07-06 9:40 ` Maciej Fijalkowski
2026-07-20 23:49 ` Jakub Kicinski
1 sibling, 0 replies; 7+ messages in thread
From: Maciej Fijalkowski @ 2026-07-06 9:40 UTC (permalink / raw)
To: Maoyi Xie
Cc: Veerasenareddy Burru, Sathesh Edara, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, linux-kernel
On Sat, Jul 04, 2026 at 02:15:11PM +0800, Maoyi Xie wrote:
> __octep_vf_oq_process_rx() has the same unbounded fragment loop as the PF
> driver. buff_info->len comes from the device response header, and one
> fragment is added per buffer_size chunk with no check against
> MAX_SKB_FRAGS. A long packet yields about 18 fragments, one past the
> default MAX_SKB_FRAGS of 17, so skb_add_rx_frag() writes past
> shinfo->frags[].
>
> The fragment count is now checked before napi_build_skb(). A packet that
> needs more fragments than the skb can hold is dropped.
> octep_vf_oq_drop_rx() drains its descriptors. The napi_build_skb()
> failure path now uses the same helper.
>
> Fixes: 1cd3b407977c ("octeon_ep_vf: add Tx/Rx processing and interrupt support")
> Co-developed-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
> Signed-off-by: Kaixuan Li <kaixuan.li@ntu.edu.sg>
> Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
> ---
> .../marvell/octeon_ep_vf/octep_vf_rx.c | 46 ++++++++++++-------
> 1 file changed, 30 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
> index d982474082..aa77b673ae 100644
> --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
> +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
> @@ -357,6 +357,29 @@ static inline u32 octep_vf_oq_next_idx(struct octep_vf_oq *oq, u32 idx)
> return (idx + 1 == oq->max_count) ? 0 : idx + 1;
> }
>
> +static void octep_vf_oq_drop_rx(struct octep_vf_oq *oq,
> + struct octep_vf_rx_buffer *buff_info,
> + u32 *read_idx, u32 *desc_used)
> +{
> + u16 data_len = buff_info->len - oq->max_single_buffer_size;
> +
> + (*desc_used)++;
> + *read_idx = octep_vf_oq_next_idx(oq, *read_idx);
> + while (data_len) {
> + dma_unmap_page(oq->dev, oq->desc_ring[*read_idx].buffer_ptr,
> + PAGE_SIZE, DMA_FROM_DEVICE);
> + buff_info = (struct octep_vf_rx_buffer *)
> + &oq->buff_info[*read_idx];
> + buff_info->page = NULL;
> + if (data_len < oq->buffer_size)
> + data_len = 0;
> + else
> + data_len -= oq->buffer_size;
> + (*desc_used)++;
> + *read_idx = octep_vf_oq_next_idx(oq, *read_idx);
> + }
> +}
> +
> /**
> * __octep_vf_oq_process_rx() - Process hardware Rx queue and push to stack.
> *
> @@ -431,25 +454,16 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
> struct skb_shared_info *shinfo;
> u16 data_len;
>
> + data_len = buff_info->len - oq->max_single_buffer_size;
> + if (DIV_ROUND_UP(data_len, oq->buffer_size) > MAX_SKB_FRAGS) {
> + octep_vf_oq_drop_rx(oq, buff_info, &read_idx, &desc_used);
> + continue;
> + }
> +
> skb = napi_build_skb((void *)resp_hw, PAGE_SIZE);
> if (!skb) {
> oq->stats->alloc_failures++;
> - desc_used++;
> - read_idx = octep_vf_oq_next_idx(oq, read_idx);
> - data_len = buff_info->len - oq->max_single_buffer_size;
> - while (data_len) {
> - dma_unmap_page(oq->dev, oq->desc_ring[read_idx].buffer_ptr,
> - PAGE_SIZE, DMA_FROM_DEVICE);
> - buff_info = (struct octep_vf_rx_buffer *)
> - &oq->buff_info[read_idx];
> - buff_info->page = NULL;
> - if (data_len < oq->buffer_size)
> - data_len = 0;
> - else
> - data_len -= oq->buffer_size;
> - desc_used++;
> - read_idx = octep_vf_oq_next_idx(oq, read_idx);
> - }
> + octep_vf_oq_drop_rx(oq, buff_info, &read_idx, &desc_used);
> continue;
> }
> rx_bytes += buff_info->len;
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net v3 1/2] octeon_ep: fix skb frags overflow in the RX path
2026-07-04 6:15 ` [PATCH net v3 1/2] octeon_ep: " Maoyi Xie
@ 2026-07-20 23:49 ` Jakub Kicinski
0 siblings, 0 replies; 7+ messages in thread
From: Jakub Kicinski @ 2026-07-20 23:49 UTC (permalink / raw)
To: maoyixie.tju
Cc: Jakub Kicinski, vburru, sedara, andrew+netdev, davem, edumazet,
pabeni, maciej.fijalkowski, netdev, linux-kernel
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
octeon_ep: fix skb frags overflow in the RX path
This change adds a check in __octep_oq_process_rx() so that packets whose
fragment count would exceed MAX_SKB_FRAGS are dropped before build_skb()
via octep_oq_drop_rx(), mirroring the existing build_skb-failure path.
> diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c
> index e6ebc7e44a00..bdbed58c7b2a 100644
> --- a/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c
> +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_rx.c
> @@ -453,6 +453,15 @@ static int __octep_oq_process_rx(struct octep_device *oct,
>
> octep_oq_next_pkt(oq, buff_info, &read_idx, &desc_used);
>
> + if (buff_info->len > oq->max_single_buffer_size) {
> + u16 data_len = buff_info->len - oq->max_single_buffer_size;
> +
> + if (DIV_ROUND_UP(data_len, oq->buffer_size) > MAX_SKB_FRAGS) {
> + octep_oq_drop_rx(oq, buff_info, &read_idx, &desc_used);
> + continue;
> + }
> + }
> +
[High]
Before this new check runs, the outer octep_oq_next_pkt() has already
processed the head descriptor:
static void octep_oq_next_pkt(struct octep_oq *oq,
struct octep_rx_buffer *buff_info,
u32 *read_idx, u32 *desc_used)
{
dma_unmap_page(oq->dev, oq->desc_ring[*read_idx].buffer_ptr,
PAGE_SIZE, DMA_FROM_DEVICE);
buff_info->page = NULL;
...
}
At that point the head page has refcount 1 from dev_alloc_page() and no
struct sk_buff owns it yet, because build_skb() has not been called. resp_hw
is only a virtual pointer, so nothing else holds a reference to the page.
When this new path takes the continue via octep_oq_drop_rx(), the head
page is never freed — octep_oq_drop_rx() only walks fragment descriptors.
Does this drop path leak the head Rx page? The same question applies to
the pre-existing build_skb() failure path just below, but the new path
is device-triggerable in normal operation (packets larger than ~66 KB on
4 KB pages, which the commit message notes is reachable with data_len
close to 65535).
[High]
octep_oq_drop_rx() also appears to reuse the same head buff_info pointer
for every fragment descriptor:
static void octep_oq_drop_rx(struct octep_oq *oq,
struct octep_rx_buffer *buff_info,
u32 *read_idx, u32 *desc_used)
{
int data_len = buff_info->len - oq->max_single_buffer_size;
while (data_len > 0) {
octep_oq_next_pkt(oq, buff_info, read_idx, desc_used);
data_len -= oq->buffer_size;
}
}
The passed-in buff_info is the head slot's buff_info, not
&oq->buff_info[*read_idx] for each fragment. As a result
octep_oq_next_pkt() only clears the head slot's page pointer and never
NULLs the fragment slots' oq->buff_info[i].page.
Does this cause the fragment pages to be leaked on the next
octep_oq_refill(), where those non-NULL page pointers get overwritten
with a fresh dev_alloc_page() reference?
And if octep_oq_free_ring_buffers() runs before refill (netdev close or
reset), does it observe those stale page pointers and call
dma_unmap_page() on descriptor addresses that octep_oq_next_pkt() has
already unmapped inside drop_rx?
This defect predates this patch (it appears to come from the commit
that introduced octep_oq_drop_rx()), but this patch adds a new
device-triggerable call site (fragment count above MAX_SKB_FRAGS) that
would exercise it under normal jumbo Rx.
[Medium]
The new drop path silently discards the packet without updating any
counter:
> + if (DIV_ROUND_UP(data_len, oq->buffer_size) > MAX_SKB_FRAGS) {
> + octep_oq_drop_rx(oq, buff_info, &read_idx, &desc_used);
> + continue;
> + }
The commit message states:
octep_oq_drop_rx() consumes its descriptors like the build_skb failure
path.
The sibling build_skb() failure path increments oq->stats->alloc_failures,
but this new path does not, and ndo_get_stats64 (octep_get_stats64) is
not updated to bump rx_dropped either.
Should this drop bump a counter visible in ip -s link show or ethtool -S
so the loss is observable?
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net v3 2/2] octeon_ep_vf: fix skb frags overflow in the RX path
2026-07-04 6:15 ` [PATCH net v3 2/2] octeon_ep_vf: " Maoyi Xie
2026-07-06 9:40 ` Maciej Fijalkowski
@ 2026-07-20 23:49 ` Jakub Kicinski
1 sibling, 0 replies; 7+ messages in thread
From: Jakub Kicinski @ 2026-07-20 23:49 UTC (permalink / raw)
To: maoyixie.tju
Cc: Jakub Kicinski, vburru, sedara, andrew+netdev, davem, edumazet,
pabeni, maciej.fijalkowski, netdev, linux-kernel
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
octeon_ep_vf: fix skb frags overflow in the RX path
This patch adds a MAX_SKB_FRAGS check in __octep_vf_oq_process_rx() so
packets that would require more fragments than the skb can hold are
dropped, and factors the descriptor-drain loop into a new
octep_vf_oq_drop_rx() helper that is shared with the napi_build_skb()
failure path.
> diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
> index d98247408242..aa77b673ae3e 100644
> --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
> +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
> @@ -357,6 +357,29 @@ static inline u32 octep_vf_oq_next_idx(struct octep_vf_oq *oq, u32 idx)
> return (idx + 1 == oq->max_count) ? 0 : idx + 1;
> }
>
> +static void octep_vf_oq_drop_rx(struct octep_vf_oq *oq,
> + struct octep_vf_rx_buffer *buff_info,
> + u32 *read_idx, u32 *desc_used)
> +{
> + u16 data_len = buff_info->len - oq->max_single_buffer_size;
> +
> + (*desc_used)++;
> + *read_idx = octep_vf_oq_next_idx(oq, *read_idx);
> + while (data_len) {
> + dma_unmap_page(oq->dev, oq->desc_ring[*read_idx].buffer_ptr,
> + PAGE_SIZE, DMA_FROM_DEVICE);
> + buff_info = (struct octep_vf_rx_buffer *)
> + &oq->buff_info[*read_idx];
> + buff_info->page = NULL;
> + if (data_len < oq->buffer_size)
> + data_len = 0;
> + else
> + data_len -= oq->buffer_size;
> + (*desc_used)++;
> + *read_idx = octep_vf_oq_next_idx(oq, *read_idx);
> + }
> +}
[High]
Does octep_vf_oq_drop_rx() leak the pages backing the dropped
descriptors?
The helper dma_unmap_page()s each fragment descriptor and then just sets
buff_info->page = NULL, without ever calling put_page() on the page that
was allocated by dev_alloc_page() in octep_vf_oq_fill_ring_buffers() or
octep_vf_oq_refill().
Compare with octep_vf_oq_free_ring_buffers() in the same file, which
appears to be the intended release pattern:
for (i = 0; i < oq->max_count; i++) {
if (oq->buff_info[i].page) {
dma_unmap_page(oq->dev, desc_ring[i].buffer_ptr,
PAGE_SIZE, DMA_FROM_DEVICE);
put_page(oq->buff_info[i].page);
oq->buff_info[i].page = NULL;
desc_ring[i].buffer_ptr = 0;
}
}
After octep_vf_oq_drop_rx() returns, octep_vf_oq_refill() will overwrite
buff_info[refill_idx].page with a freshly allocated page from
dev_alloc_page(), so the previously-mapped page pointer is lost without
a matching put_page(), isn't it?
The head descriptor of the dropped packet looks affected too. The caller
in __octep_vf_oq_process_rx() has already stashed the head page's kernel
address into resp_hw and cleared buff_info->page:
resp_hw = page_address(buff_info->page);
buff_info->page = NULL;
On the two paths that now call octep_vf_oq_drop_rx() (the new
MAX_SKB_FRAGS-exceeded branch and the napi_build_skb() failure branch),
napi_build_skb() is either never called or returned NULL, so no skb has
taken ownership of resp_hw. Is the head page also leaked in these cases?
> @@ -431,25 +454,16 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
> struct skb_shared_info *shinfo;
> u16 data_len;
>
> + data_len = buff_info->len - oq->max_single_buffer_size;
> + if (DIV_ROUND_UP(data_len, oq->buffer_size) > MAX_SKB_FRAGS) {
> + octep_vf_oq_drop_rx(oq, buff_info, &read_idx, &desc_used);
> + continue;
> + }
> +
> skb = napi_build_skb((void *)resp_hw, PAGE_SIZE);
> if (!skb) {
> oq->stats->alloc_failures++;
> - desc_used++;
> - read_idx = octep_vf_oq_next_idx(oq, read_idx);
> - data_len = buff_info->len - oq->max_single_buffer_size;
> - while (data_len) {
> - dma_unmap_page(oq->dev, oq->desc_ring[read_idx].buffer_ptr,
> - PAGE_SIZE, DMA_FROM_DEVICE);
> - buff_info = (struct octep_vf_rx_buffer *)
> - &oq->buff_info[read_idx];
> - buff_info->page = NULL;
> - if (data_len < oq->buffer_size)
> - data_len = 0;
> - else
> - data_len -= oq->buffer_size;
> - desc_used++;
> - read_idx = octep_vf_oq_next_idx(oq, read_idx);
> - }
> + octep_vf_oq_drop_rx(oq, buff_info, &read_idx, &desc_used);
> continue;
> }
[High]
The napi_build_skb() failure path had the same missing put_page() before
this patch, so that part is a pre-existing issue.
The newly added MAX_SKB_FRAGS check reaches octep_vf_oq_drop_rx() on a
device-triggered path (buff_info->len comes from the response header),
so a misbehaving or malicious device that repeatedly sends packets whose
fragment count exceeds MAX_SKB_FRAGS would drop roughly 18 pages per
packet with no accounting.
Would it be reasonable to have octep_vf_oq_drop_rx() put_page() the head
page (or have the caller do so before invoking it) and put_page() each
fragment page after dma_unmap_page(), mirroring the release sequence in
octep_vf_oq_free_ring_buffers()?
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH net v3 0/2] octeon_ep, octeon_ep_vf: fix skb frags overflow in the RX path
2026-07-04 6:15 [PATCH net v3 0/2] octeon_ep, octeon_ep_vf: fix skb frags overflow in the RX path Maoyi Xie
2026-07-04 6:15 ` [PATCH net v3 1/2] octeon_ep: " Maoyi Xie
2026-07-04 6:15 ` [PATCH net v3 2/2] octeon_ep_vf: " Maoyi Xie
@ 2026-07-20 23:50 ` Jakub Kicinski
2 siblings, 0 replies; 7+ messages in thread
From: Jakub Kicinski @ 2026-07-20 23:50 UTC (permalink / raw)
To: Maoyi Xie
Cc: Veerasenareddy Burru, Sathesh Edara, Andrew Lunn,
David S . Miller, Eric Dumazet, Paolo Abeni, Maciej Fijalkowski,
netdev, linux-kernel
On Sat, 4 Jul 2026 14:15:09 +0800 Maoyi Xie wrote:
> Both octeon_ep and octeon_ep_vf build an skb for a multi-buffer RX packet
> by adding one fragment per buffer_size chunk of a device-reported length.
> Neither bounds the count against MAX_SKB_FRAGS. A long packet yields about
> 18 fragments, one past the default MAX_SKB_FRAGS of 17, so
> skb_add_rx_frag() writes past shinfo->frags[].
>
> Each driver now checks the fragment count before it builds the skb and
> drops a packet that would not fit.
Why are you working on this driver?
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-07-20 23:50 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-04 6:15 [PATCH net v3 0/2] octeon_ep, octeon_ep_vf: fix skb frags overflow in the RX path Maoyi Xie
2026-07-04 6:15 ` [PATCH net v3 1/2] octeon_ep: " Maoyi Xie
2026-07-20 23:49 ` Jakub Kicinski
2026-07-04 6:15 ` [PATCH net v3 2/2] octeon_ep_vf: " Maoyi Xie
2026-07-06 9:40 ` Maciej Fijalkowski
2026-07-20 23:49 ` Jakub Kicinski
2026-07-20 23:50 ` [PATCH net v3 0/2] octeon_ep, " Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox