CVE-2025-39948CWE-401

CVE-2025-39948

Critical · published October 4, 2025

CVSS v3.1
9.8
EPSS
0%
Percentile
21.6
In the wild
Unconfirmed
What it is

In the Linux kernel, the following vulnerability has been resolved:

ice: fix Rx page leak on multi-buffer frames

The ice_put_rx_mbuf() function handles calling ice_put_rx_buf() for each

buffer in the current frame. This function was introduced as part of

handling multi-buffer XDP support in the ice driver.

It works by iterating over the buffers from first_desc up to 1 plus the

total number of fragments in the frame, cached from before the XDP program

was executed.

If the hardware posts a descriptor with a size of 0, the logic used in

ice_put_rx_mbuf() breaks. Such descriptors get skipped and don't get added

as fragments in ice_add_xdp_frag. Since the buffer isn't counted as a

fragment, we do not iterate over it in ice_put_rx_mbuf(), and thus we don't

call ice_put_rx_buf().

Because we don't call ice_put_rx_buf(), we don't attempt to re-use the

page or free it. This leaves a stale page in the ring, as we don't

increment next_to_alloc.

The ice_reuse_rx_page() assumes that the next_to_alloc has been incremented

properly, and that it always points to a buffer with a NULL page. Since

this function doesn't check, it will happily recycle a page over the top

of the next_to_alloc buffer, losing track of the old page.

Note that this leak only occurs for multi-buffer frames. The

ice_put_rx_mbuf() function always handles at least one buffer, so a

single-buffer frame will always get handled correctly. It is not clear

precisely why the hardware hands us descriptors with a size of 0 sometimes,

but it happens somewhat regularly with "jumbo frames" used by 9K MTU.

To fix ice_put_rx_mbuf(), we need to make sure to call ice_put_rx_buf() on

all buffers between first_desc and next_to_clean. Borrow the logic of a

similar function in i40e used for this same purpose. Use the same logic

also in ice_get_pgcnts().

Instead of iterating over just the number of fragments, use a loop which

iterates until the current index reaches to the next_to_clean element just

past the current frame. Unlike i40e, the ice_put_rx_mbuf() function does

call ice_put_rx_buf() on the last buffer of the frame indicating the end of

packet.

For non-linear (multi-buffer) frames, we need to take care when adjusting

the pagecnt_bias. An XDP program might release fragments from the tail of

the frame, in which case that fragment page is already released. Only

update the pagecnt_bias for the first descriptor and fragments still

remaining post-XDP program. Take care to only access the shared info for

fragmented buffers, as this avoids a significant cache miss.

The xdp_xmit value only needs to be updated if an XDP program is run, and

only once per packet. Drop the xdp_xmit pointer argument from

ice_put_rx_mbuf(). Instead, set xdp_xmit in the ice_clean_rx_irq() function

directly. This avoids needing to pass the argument and avoids an extra

bit-wise OR for each buffer in the frame.

Move the increment of the ntc local variable to ensure its updated *before*

all calls to ice_get_pgcnts() or ice_put_rx_mbuf(), as the loop logic

requires the index of the element just after the current frame.

Now that we use an index pointer in the ring to identify the packet, we no

longer need to track or cache the number of fragments in the rx_ring.

The record
Technical detail
CVSS v3.1
9.8 · CRITICAL
Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVSS v4.0
Not supplied
EPSS
0.00301 · 21.6th percentile
Weakness
CWE-401 · Missing Release of Memory after Effective Lifetime
Published
2025-10-04T12:15Z
Affected products (17)
ProductVersionsFixed in
linux/linux_kernel≥ 6.6.78, < 6.76.7
linux/linux_kernel≥ 6.12.14, < 6.12.496.12.49
linux/linux_kernel≥ 6.13.3, < 6.146.14
linux/linux_kernel≥ 6.14.1, < 6.16.96.16.9
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
linux/linux_kernelall versions—
References (4)
EPSS history
Timeline
  • 04 OCT 07:31Z
    ice: fix Rx page leak on multi-buffer frames
    cvelistv5