Re: [PATCH net v2] net/smc: validate peer CDC cursor against RMBE size before accepting it
From: Dust Li <dust.li@linux.alibaba.com>
Date: 2026-07-28 11:37:12
Also in:
linux-rdma, linux-s390, lkml, stable
On 2026-07-28 12:04:32, Paolo Abeni wrote:
On 7/22/26 12:17 PM, Ibrahim Hashimov wrote:quoted
smc_cdc_cursor_to_host() converts the wire-format producer/consumer cursor of an incoming CDC message into a host smc_host_cursor. It rejects a cursor that goes backwards, but never checks that the cursor stays inside the buffer it indexes. Per smc_host_cursor ("an offset in an RMBE") and the invariant smc_curs_add() enforces for every local advance (0 <= count < size), a valid cursor count must be < size and a single advance can be at most one bufferful; the wire cursor is peer-controlled and was never checked against either. smcr_cdc_msg_to_host() accepts the producer and consumer cursors, and the unbounded value feeds smc_curs_diff() in smc_cdc_msg_recv_action(), which computes the advance without clamping against size. A peer can inflate it two ways: an out-of-range prod.count (e.g. 0x7fffffff), or -- since on a wrap increment smc_curs_diff() returns (size - old.count) + new.count -- a cursor with a bumped wrap and new.count above old.count. Either drives bytes_to_rcv far past rmb_desc->len, the very invariant the comment above the atomic_add() claims but does not enforce. smc_rx_recvmsg() then trusts bytes_to_rcv as the amount of valid RMB data. Its first copy chunk is safely bounded by rmb_desc->len - cons.count, but the second chunk copies (copylen - chunk_len) bytes from offset 0, and copylen came from the inflated readable -- an out-of-bounds read of the RMB's backing (v)malloc allocation, copied straight to the receiving process via _copy_to_iter(). This is a remote kernel-memory disclosure driven by a malicious SMC-R peer, needing no local privilege on the victim. The same unbounded count also reaches smc_cdc_handle_urg_data_arrival() (base + urg_curs.count - 1) -- a second, narrower OOB read. Reject the bad cursor at ingress, mirroring the cursor-sanity idiom two lines above: drop any incoming cursor whose count is outside the buffer (>= size), and any whose advance from the last accepted cursor exceeds one bufferful (smc_curs_diff() > size), which catches the wrap case. smc_cdc_cursor_to_host() gains a size parameter; smcr_cdc_msg_to_host() passes conn->rmb_desc->len for the producer cursor and conn->peer_rmbe_size for the consumer cursor, bounding it at its single origin instead of patching every downstream consumer. SMC-D (smcd_cdc_msg_to_host()) does not use this helper and is a separate, out-of-scope gap.@SMC crew: please review. The above looks so correlated and quite simple to address that possibly it makes sense to address it in the same series.
Hi Paolo, This patch was discussed in the v1 thread. Bryan had already sent a the v5 addressing the issue, so I think this patch can be dropped. https://lore.kernel.org/netdev/20260723-b4-disp-0d07164f-v5-0-6a9e235dbc4e@proton.me (local) Best regards, Dust