From: mingming cao <mmc@linux.ibm.com>
To: netdev-bot+sashiko@kernel.org
Cc: netdev@vger.kernel.org, maddy@linux.ibm.com, mpe@ellerman.id.au,
npiggin@gmail.com, chleroy@kernel.org, ritesh.list@gmail.com,
sshegde@linux.ibm.com, nnac123@linux.ibm.com,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@kernel.org,
kuba@kernel.org, pabeni@redhat.com,
linuxppc-dev@lists.ozlabs.org, linux-kernel@vger.kernel.org,
horms@kernel.org, davemarq@linux.ibm.com, bjking1@linux.ibm.com,
stephen@networkplumber.org
Subject: Re: [PATCH net-next v3 4/8] ibmveth: step past bad RX correlators instead of spinning or oopsing
Date: Sun, 11 Oct 2026 22:03:55 -0700 [thread overview]
Message-ID: <93cc87b4-14ce-43d9-a29e-d9a6795bb0d5@linux.ibm.com> (raw)
In-Reply-To: <179166081271.434549.2458181310224040226@kernel.org>
On 10/10/26 12:33 PM, netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential
> issue(s) to consider.
>
> Critical: 0 · High: 3 (1) · Medium: 0 · Low: 0
>
> If you believe none of the issues are legitimate, please reply to each
> issue of Medium severity or higher and explain why the AI is wrong.
> If any of them are legitimate and you plan to respin, please let patchwork
> know by including "pw-bot: cr" as a separate line at the end of your reply
> (one such reply per series is enough).
>
> - [High] This patch's short-frame check is incomplete.
> - [High] Returning budget - 1 does not make ibmveth_poll()'s schedule-
> and-continue path safe when threaded NAPI and busy polling are both…
> - [High] net/ibmveth: TOCTOU double-fetch of RX correlator in
> harvest_buffer
>
> Pre-existing issues:
> - [High] This problem wasn't introduced by this patch, but
> ibmveth_rx_csum_helper() calls ip_fast_csum(iph, iph->ihl) whenever…
>
> --
Thank you for the review. I agree with all four High findings on this
patch.
I will address them in v4 on the same net-next base (d8674294aefe).
Patches 1-3 and 5-8 are unchanged from v3. Patch 4 fixes the poll
tail (no restart_poll after napi_complete_done; mask then queue the
NAPI), passes the correlator poll already read into harvest, and
keeps the buffer-bound and ETH_HLEN drops without claiming they cover
the RX offload helpers.
The helper header checks (short frames, ihl,
doff, fragments, and the ip_fast_csum() case) are split into a new
patch 9 at the end of the series, with their own Fixes: tags and KUnit
cases.
I will post v4 on netdev shortly.
v4 was tested with KUnit on qemu pseries and on a POWER10 LPAR (the v3
suite plus large-send iperf3 over IPv4/IPv6 at MTU 1500 and 9000).
Thanks,
Mingming
pw-bot: cr
> ibmveth: step past bad RX correlators instead of spinning or oopsing
>
> This patch makes ibmveth_poll() advance the RX ring past slots with a bad
> correlator, including correlators that name an inactive pool, instead of
> breaking out and restarting on the same slot. It counts those slots as
> drops and schedules a reset. It also drops frames that do not fit the pool
> buffer or are shorter than an Ethernet header, and returns budget - 1 once
> napi_schedule() has queued the NAPI.
>
>> diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
>> index cee0e9783b2ab..55c0b5d6e0a9f 100644
>> --- a/drivers/net/ethernet/ibm/ibmveth.c
>> +++ b/drivers/net/ethernet/ibm/ibmveth.c
> [ ... ]
>
>> @@ -1481,19 +1525,46 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
> [ ... ]
>
>> } else {
>> struct sk_buff *skb, *new_skb;
>> int length = ibmveth_rxq_frame_length(adapter);
>> int offset = ibmveth_rxq_frame_offset(adapter);
>> int csum_good = ibmveth_rxq_csum_good(adapter);
>> int lrg_pkt = ibmveth_rxq_large_packet(adapter);
>> + u64 correlator = ibmveth_rxq_correlator(adapter);
>> + unsigned int pool, room, off, len;
>> __sum16 iph_check = 0;
> [Severity: High]
> The commit message says the bound uses the pool "from the correlator
> that was validated, read once, not from a second read of the ring".
> Doesn't ibmveth_rxq_harvest_buffer() still do that second read?
>
> ibmveth_poll() now reads the correlator once with
> ibmveth_rxq_correlator(). It uses that value to look up skb in
> ibmveth_rxq_get_buffer() and to compute room. Every harvest after that
> ignores it, though: this call, the copybreak call and the one on the
> bad-frame path. ibmveth_rxq_harvest_buffer() loads
> queue_addr[rx_queue.index].correlator again with a plain load and
> passes that to ibmveth_remove_buffer_from_pool(). Nothing ties the
> second value to the one poll validated.
>
> Suppose the entry changes between the two loads, so skb is looked up
> from slot A and harvested from slot B. On this reuse=false path:
>
> - rx_buff_pool[].skbuff[B] is cleared and dma_addr[B] is unmapped.
> B goes on free_map, and the skb that was in slot B is leaked.
>
> - skb A goes to napi_gro_receive() while it is still in skbuff[A],
> still DMA mapped and not on free_map. The driver still treats it
> as a posted buffer.
>
> - On close or reset, ibmveth_free_buffer_pool() finds skb A in
> skbuff[A], unmaps it and calls dev_kfree_skb_any() on it after the
> stack has already freed it.
>
> The copybreak and bad-frame paths would recycle slot B instead of A
> in the same way.
>
> ibmveth_rxq_correlator_valid() does not catch this, because both
> correlators can be in range and name live skbs.
>
> PHYP should not rewrite an entry once the toggle is set. But this
> patch treats the correlator as untrusted, and the changelog says it
> is read once. Should ibmveth_rxq_harvest_buffer() take the correlator
> from its caller, so poll uses one value for lookup, bound check and
> harvest? The new KUnit harvest test could read the ring entry itself
> before calling it.
>
> [ ... ]
>
>> + pool = correlator >> 32;
>> + room = min_t(unsigned int, skb_tailroom(skb),
>> + adapter->rx_buff_pool[pool].buff_size);
>> + off = offset;
>> + len = length;
>> + if (unlikely(len < ETH_HLEN || off >= room ||
>> + len > room - off)) {
>> + if (net_ratelimit())
>> + netdev_err(netdev,
>> + "bad RX frame offset %u length %u (buffer %u), dropping\n",
>> + off, len, room);
>> + ibmveth_rxq_harvest_buffer(adapter, true);
>> + netdev->stats.rx_dropped++;
>> + frames_processed++;
>> + continue;
>> + }
> [Severity: High]
>
> The commit message says this check fixes the case where "the checksum
> helpers would write past a short frame". Is an ETH_HLEN minimum enough
> for that?
>
> Once this check passes, ibmveth_poll() and its helpers assume a full IP
> header and TCP header and never look at skb->len:
>
> ibmveth_poll()
> iph_check = iph->check;
> ibmveth_rx_mss_helper() /* lrg_pkt or iph_check == 0xffff */
> skb_shinfo(skb)->gso_size = ntohs(tcph->check);
> tcph->check = 0;
> hdr_len = offset + tcph->doff * 4;
> skb_shinfo(skb)->gso_segs =
> DIV_ROUND_UP(skb->len - hdr_len, ...);
> ibmveth_rx_csum_helper() /* csum_good */
> iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
> struct tcphdr *tcph = (struct tcphdr *)(skb->data + iphlen);
> if (tcph->check == 0x0000) {
> tcph->check = ~csum_tcpudp_magic(...);
>
> A frame of about 14 to 92 bytes passes the new check, but these helpers
> still read and write past the end of the frame. In
> ibmveth_rx_mss_helper(), skb->len - hdr_len can also wrap.
>
> On the copybreak path, those accesses land in the uninitialized tailroom
> of new_skb.
>
> On the non-copybreak path, they land in the pool buffer past the frame.
> That path runs when the GFP_ATOMIC copybreak allocation fails, or when
> rx_copybreak is set at or below the frame length.
>
> The new bound also accepts a short frame at the very end of the buffer.
> For pool 0 with room = 512, off = room - 34 and len = 34 leave
> skb->tail == skb->end after skb_put(). eth_type_trans() then leaves 20
> bytes of IPv4 header, so tcph sits at skb->end.
>
> Wouldn't the tcph->check writes then land in struct skb_shared_info
> (hwtstamps, gso_type, tskey or dataref, depending on ihl)?
> skb_partial_csum_set() rejects this only after the write has already
> happened.
>
> Should there be per-protocol skb->len checks, or pskb_may_pull(), before
> the IP and TCP headers are parsed, rather than a fixed minimum?
>
> [Severity: High]
>
> This isn't a bug introduced by this patch, but since this patch hardens
> the RX path: should ibmveth_rx_csum_helper() check iph->ihl before it
> calls ip_fast_csum()?
>
> if (iph->check == 0xffff) {
> iph->check = 0;
> iph->check = ip_fast_csum((unsigned char *)iph,
> iph->ihl);
> }
>
> On ppc64, ip_fast_csum_nofold() in arch/powerpc/include/asm/checksum.h
> documents that ihl "is always >= 5" and does:
>
> for (i = 0; i < ihl - 1; i++, ptr++)
> s += *ptr;
>
> ihl is unsigned, so with ihl == 0, ihl - 1 wraps to 0xFFFFFFFF and the
> loop reads about 16 GiB starting at the RX buffer, in NAPI softirq
> context.
>
> The ihl, check and ethertype fields and the IBMVETH_RXQ_CSUM_GOOD flag
> all come from the sending partition. Could a single IPv4 frame with
> ihl = 0 and check = 0xffff from a peer on the same virtual LAN oops or
> stall this partition?
>
> [ ... ]
>
>> @@ -1581,10 +1661,16 @@ static int ibmveth_poll(struct napi_struct *napi, int budget)
>> if (ibmveth_rxq_pending_buffer(adapter) && napi_schedule(napi)) {
>> lpar_rc = h_vio_signal(adapter->vdev->unit_address,
>> VIO_IRQ_DISABLE);
>> + rescheduled = 1;
>> goto restart_poll;
>> }
>>
>> out:
>> + /* napi_schedule() already queued us. Returning budget would
>> + * make busy_poll_stop() queue the napi a second time.
>> + */
>> + if (rescheduled && budget && frames_processed >= budget)
>> + return budget - 1;
>> return frames_processed;
>> }
> [Severity: High]
>
> Does returning budget - 1 cover the case where threaded NAPI and busy
> polling are both enabled?
>
> busy_poll_stop() clears NAPI_STATE_IN_BUSY_POLL before it calls the
> driver poll. Its own comment says "Unless rc == budget we no longer own
> the NAPI instance". From there:
>
> busy_poll_stop()
> ibmveth_poll()
> napi_complete_done() /* succeeds */
> h_vio_signal(..., VIO_IRQ_ENABLE)
> napi_schedule()
> ____napi_schedule()
> set_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
> wake_up_process(thread);
> goto restart_poll; /* keeps processing the ring */
>
> The woken kthread sees NAPI_STATE_SCHED_THREADED in napi_thread_wait()
> and calls __napi_poll()->ibmveth_poll() on another CPU. Meanwhile the
> original call is still running from restart_poll.
>
> Can the two pollers then race, with no lock, on rx_queue.index and
> toggle, rx_buff_pool[].skbuff[], free_map and producer_index? For
> example, one harvest with reuse=false could clear and unmap an skb while
> the other poller passes the same skb to napi_gro_receive().
>
> The budget - 1 return only runs after this overlap. Continuing to poll
> after napi_schedule() predates this patch, but this patch now reasons
> about NAPI ownership after napi_schedule(). Would it be better to stop
> processing once napi_schedule() succeeds, instead of jumping back to
> restart_poll?
>
next prev parent reply other threads:[~2026-10-12 5:04 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-09 18:32 [PATCH net-next v3 0/8] ibmveth: fix hangs, use-after-frees and netpoll races Mingming Cao
2026-10-09 18:32 ` [PATCH net-next v3 1/8] ibmveth: fix netpoll races with RX replenish Mingming Cao
2026-10-09 18:32 ` [PATCH net-next v3 2/8] ibmveth: do not close twice after a failed reopen Mingming Cao
2026-10-09 18:32 ` [PATCH net-next v3 3/8] ibmveth: disable the reset work before unregister in remove Mingming Cao
2026-10-09 18:32 ` [PATCH net-next v3 4/8] ibmveth: step past bad RX correlators instead of spinning or oopsing Mingming Cao
2026-10-10 19:33 ` netdev-bot+sashiko
2026-10-12 5:03 ` mingming cao [this message]
2026-10-09 18:32 ` [PATCH net-next v3 5/8] ibmveth: release the pool kobjects when probe fails Mingming Cao
2026-10-09 18:32 ` [PATCH net-next v3 6/8] ibmveth: return the error when set_channels cannot add TX queues Mingming Cao
2026-10-09 18:32 ` [PATCH net-next v3 7/8] ibmveth: wait for in-flight transmits in ibmveth_close() Mingming Cao
2026-10-09 18:32 ` [PATCH net-next v3 8/8] ibmveth: wait for the RX poll to return before freeing the RX queue Mingming Cao
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=93cc87b4-14ce-43d9-a29e-d9a6795bb0d5@linux.ibm.com \
--to=mmc@linux.ibm.com \
--cc=andrew+netdev@lunn.ch \
--cc=bjking1@linux.ibm.com \
--cc=chleroy@kernel.org \
--cc=davem@davemloft.net \
--cc=davemarq@linux.ibm.com \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=maddy@linux.ibm.com \
--cc=mpe@ellerman.id.au \
--cc=netdev-bot+sashiko@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=nnac123@linux.ibm.com \
--cc=npiggin@gmail.com \
--cc=pabeni@redhat.com \
--cc=ritesh.list@gmail.com \
--cc=sshegde@linux.ibm.com \
--cc=stephen@networkplumber.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.