[PATCH net v5 2/3] net/smc: bound the peer rkey counts in SMC-Rv2 LLC messages
Yehyeong Lee <[email protected]> Sat, 1 Aug 2026 18:42:07 +0900
| Newsgroups | org.kernel.vger.linux-hardening,org.kernel.vger.linux-kernel,org.kernel.vger.linux-rdma,org.kernel.vger.linux-s390,org.kernel.vger.netdev,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
On a link whose device has max_recv_sge == 1 there is no shared v2 receive
buffer, and smc_llc_save_add_link_rkeys() takes the v2 extension from 44
bytes past the start of the queue entry's inline message:
ext = (struct smc_llc_msg_add_link_v2_ext *)(llc_msg + SMC_WR_TX_SIZE);
The entry is a 72-byte allocation and the extension starts at offset 68, so
ext->num_rkeys at offset 94 is already past it. This happens on every
SMC-Rv2 link addition, whatever the peer sends:
BUG: KASAN: slab-out-of-bounds in smc_llc_save_add_link_rkeys+0x333/0x350
Read of size 2 at addr ffff8880056406de by task smctest/106
Call Trace:
smc_llc_save_add_link_rkeys+0x333/0x350
smc_llc_cli_add_link+0xca7/0x1e80
__smc_connect+0x3f5c/0x4980
smc_connect+0x42c/0x580
__sys_connect+0xfc/0x130
Allocated by task 44:
smc_llc_enqueue+0x72/0x560
smc_wr_rx_tasklet_fn+0x474/0xa80
The buggy address is located 22 bytes to the right of
allocated 72-byte region [ffff888005640680, ffff8880056406c8)
Whatever that read finds then bounds the ext->rt[] loop, so a peer that
declares 255 rkeys reads much further. smc_llc_rmt_delete_rkey() has the
same shape for llcv2->rkey[].
Bound both loops by the buffer they read from, and skip the extension
altogether when there is no shared v2 receive buffer. The extension
does arrive on the link, but smc_llc_enqueue() copies only
sizeof(union smc_llc_msg) into the queue entry, so what that code read
past the 44 inline bytes was heap and not peer data.
Fixes: 27ef6a9981fe ("net/smc: support SMC-R V2 for rdma devices with max_recv_sge equals to 1")
Cc: [email protected]
Signed-off-by: Yehyeong Lee <[email protected]>
---
v4 -> v5: corrected the reason given for skipping the extension. It does
arrive on the link; what is not there is the copy in the queue entry. No
functional change.
Measured over rxe with KASAN and max_recv_sge forced to 1, five test cells
(plain 1-rkey delete, delete declaring 255, plain ADD_LINK v2, ADD_LINK
declaring 255, and an SMC-Rv1 link group). Without this patch four of the
five report; with it none do. With kasan_multi_shot the unpatched kernel
reports 491 times in a single ADD_LINK run, the patched one not at all.
net/smc/smc_llc.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c
index 90746cd1e29a..02561f39d6d8 100644
--- a/net/smc/smc_llc.c
+++ b/net/smc/smc_llc.c
@@ -1000,13 +1000,21 @@ static void smc_llc_save_add_link_rkeys(struct smc_link *link,
struct smc_link *link_new,
u8 *llc_msg)
{
+ const u32 rt_off = offsetof(struct smc_llc_msg_add_link_v2_ext, rt);
struct smc_llc_msg_add_link_v2_ext *ext;
struct smc_link_group *lgr = link->lgr;
int max, i;
+ /* Without a shared v2 receive buffer the extension is not copied
+ * into the queue entry, so not even ext->num_rkeys is there.
+ */
+ if (!smc_link_shared_v2_rxbuf(link))
+ return;
ext = (struct smc_llc_msg_add_link_v2_ext *)(llc_msg +
SMC_WR_TX_SIZE);
max = min_t(u8, ext->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2);
+ max = min_t(u32, max, (SMC_WR_BUF_V2_SIZE - SMC_WR_TX_SIZE - rt_off) /
+ sizeof(ext->rt[0]));
down_write(&lgr->rmbs_lock);
for (i = 0; i < max; i++) {
smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
@@ -1813,17 +1821,25 @@ static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr)
link = qentry->link;
if (lgr->smc_version == SMC_V2) {
+ const u32 rkey_off =
+ offsetof(struct smc_llc_msg_delete_rkey_v2, rkey);
struct smc_llc_msg_delete_rkey_v2 *llcv2;
+ u32 buf_len;
if (smc_link_shared_v2_rxbuf(link)) {
memcpy(lgr->wr_rx_buf_v2, llc, sizeof(*llc));
llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)lgr->wr_rx_buf_v2;
+ buf_len = SMC_WR_BUF_V2_SIZE;
} else {
llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)llc;
+ buf_len = sizeof(qentry->msg);
}
llcv2->num_inval_rkeys = 0;
max = min_t(u8, llcv2->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2);
+ /* bound by the buffer llcv2 points at */
+ max = min_t(u32, max, (buf_len - rkey_off) /
+ sizeof(llcv2->rkey[0]));
for (i = 0; i < max; i++) {
if (smc_rtoken_delete(link, llcv2->rkey[i]))
llcv2->num_inval_rkeys++;
--
2.43.0