[PATCH v3 0/3] net/smc: bound wire-controlled CDC cursors against the local buffers

From: Bryam Vargas via B4 Relay

Date: Sun Jun 14 2026 - 04:24:02 EST


A peer's CDC producer/consumer cursors are copied from the wire and used,
without an upper bound against the local buffers, as (a) a raw index into the
RMB on the urgent path, (b) the receive length in smc_rx_recvmsg(), and (c) the
send length in smc_tx_sendmsg() on the SMC-D DMB-merge path. A malicious or
buggy peer can forge a cursor so each of these runs past the relevant buffer:
an out-of-bounds read of adjacent kernel memory (disclosed to the peer) on the
receive/urgent side, and an out-of-bounds write of attacker-influenced length
and content on the send side.

This series bounds each wire-controlled value at its point of use against the
local buffer, enforcing invariants the code already documents
("0 <= bytes_to_rcv <= rmb_desc->len", "0 <= sndbuf_space <= sndbuf_desc->len").
Conforming peers always keep these values in range, so the bounds are no-ops in
normal operation.

1/3 bounds the producer cursor count to rmb_desc->len at the SMC-R/SMC-D
conversion boundary (the urgent-path raw index). The bound is applied to
the producer cursor only -- the consumer cursor indexes the peer's RMB and
is bounded by peer_rmbe_size, so clamping it to our rmb_desc->len would
under-credit peer_rmbe_space and stall transmit to a peer with a larger
RMB.
2/3 bounds the readable count in smc_rx_recvmsg() so the wrap-around copy
cannot read past the RMB.
3/3 bounds the write space in smc_tx_sendmsg() so the wrap-around copy cannot
write past the send buffer.

This supersedes two separately-posted patches and folds them into one series
together with the producer-cursor fix, after review feedback that they share a
root cause:
- net/smc: bound peer producer cursor and bytes_to_rcv on SMC-D CDC receive
https://lore.kernel.org/netdev/20260610084803.186516-1-hexlabsecurity@xxxxxxxxx/
- net/smc: bound sndbuf_space on the SMC-D DMB-merge receive path
https://lore.kernel.org/netdev/20260610090928.192177-1-hexlabsecurity@xxxxxxxxx/

Changes since those postings (addressing the review):
- The receive/send bounds were previously applied in the CDC receive tasklet,
after the atomic_add(). As the review noted, that read-then-set is not
atomic, and a recvmsg()/sendmsg() on another CPU can observe the inflated
value in the window between the atomic_add() and the clamp: recvmsg() runs
under lock_sock(), which leaves the slock free, so it is not serialized
against the bh_lock_sock() CDC tasklet. The bound now lives at the consumer,
where the value is used to size the copy, which is race-free.
- The bounds now also reject a negative value (if (x < 0 || x > len)): across
many forged CDC messages the signed accumulator can wrap negative, which a
plain "> len" check misses and min_t(size_t, ...) then turns into a huge
length.
- The SMC-R producer-cursor bound is applied only to the producer cursor at
the call site, not in the shared smc_cdc_cursor_to_host() helper, so the
consumer cursor (bounded by peer_rmbe_size) is no longer truncated.

Verified with an in-kernel KASAN A/B matrix on x86-64 (SMC-D loopback,
CONFIG_SMC_LO; no special hardware): each sink produces a slab-out-of-bounds
read/write for a forged cursor and is clean with the patch, and both the
cross-CPU race and the negative-accumulator case are reproduced and closed.
Logs available on request.

---
Bryam Vargas (3):
net/smc: bound the wire-controlled producer cursor to the RMB
net/smc: bound the receive length to the RMB in smc_rx_recvmsg()
net/smc: bound the send length to the send buffer in smc_tx_sendmsg()

net/smc/smc_cdc.h | 27 ++++++++++++++++++++++++---
net/smc/smc_rx.c | 12 ++++++++++++
net/smc/smc_tx.c | 13 +++++++++++++
3 files changed, 49 insertions(+), 3 deletions(-)
---
base-commit: 8e65320d91cdc3b241d4b94855c88459b91abf66
change-id: 20260614-b4-disp-edd64be9-b094cf67fded

Best regards,
--
Bryam Vargas <hexlabsecurity@xxxxxxxxx>