From nobody Fri Jul 24 21:53:41 2026 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 59B8E3451A7 for ; Fri, 24 Jul 2026 06:16:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784873786; cv=none; b=dVhSN7Mx/2K+y9wrEKVh0uxzmqC75RskK195+QoCXqUoxU/aVHObKYqNMNOmUUMRwvlj7NnpmfrXeJ+DZxxMpGbeN+Iz1bVQtYew2qu3BhIOk6sLQHMMtmcKyhbZ+7UBbUjSHrhXDJfCnFz3GgmPGBAYqScvrXXYhHiEhYcfRog= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784873786; c=relaxed/simple; bh=VIUW7PW/uZqfKyp+8j8eU4HQWxJISEwkAnQpF6bu2dk=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=gwVp/7Y+vZ89PijpACDgpG45fRcIcGu+uBac6SDkFHQvqQ0hiHtwqudlr9VcUbzIUpwg8syVIvRnjoRzd8J5kn1L+2/L8GXaP4sw+006/WtO+AsJBk0EpORB3eLzc1gJi/et90RvKPUUL+4jXwLqVZRU9fteG/mGS/P3dJie2ec= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=isslab.korea.ac.kr; spf=none smtp.mailfrom=isslab.korea.ac.kr; dkim=pass (2048-bit key) header.d=isslab-korea-ac-kr.20251104.gappssmtp.com header.i=@isslab-korea-ac-kr.20251104.gappssmtp.com header.b=eU4Pazni; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=isslab.korea.ac.kr Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=isslab.korea.ac.kr Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=isslab-korea-ac-kr.20251104.gappssmtp.com header.i=@isslab-korea-ac-kr.20251104.gappssmtp.com header.b="eU4Pazni" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2cce6a0c9c3so1036825ad.1 for ; Thu, 23 Jul 2026 23:16:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=isslab-korea-ac-kr.20251104.gappssmtp.com; s=20251104; t=1784873782; x=1785478582; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=FP8PQYAIkyulgqMYRvtLfZS51Oh76yCgMwm2aH1IOHc=; b=eU4PaznilsqGrp7BQe5iniRtWg2rszY6iI0BHBbcujcP3XeRGjZicKgY/qrB+N+FrK 0XWd0qj/mhWSDz++pVSnJ+O27zLoF/SmpwVkjJU2WovMqeJME4M9QtRdyfRbRU/mpDwQ GCAoxmMtc3elui0Ha3/p24x139BgXs4G9ib088JQCM75PBD4p81CGah+Syksg4fQ32uJ 4I5QBGDgsw/RXnRwo8pP5ISTZXDCYwy05OEk59SyR8HSgBZyyzn9gsq0rv3qVTKJf2zp EHXNMXbusYb+8UDIMxjr+h5YJh+XEkzrmSeEJ9n7F0S7TzXySS8nE8d5b41slJD08Vlj 3mXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784873782; x=1785478582; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=FP8PQYAIkyulgqMYRvtLfZS51Oh76yCgMwm2aH1IOHc=; b=J7XkAkiJpMxq9ElDVKnHwT4d1uJkr08aLpz+0i1kMHsm6ffAzpBQH5lCE/0v5xKFIH SveSVcUQk/fkWnu96VTHL83DgUjWe8Kd/0govWAHSeXmdT+liyQiEeen0wISvCU6S946 IJaOHBUBh9+1m11hgK9uMMiB8FIBccvqFIG5aQSg0LLGZEGm8FCoh0p87iHEOeSfxqvm gXapDNNb8+qbiZ0nH8tu8bNtZcqggCtVM9Cz6GmV759AFGgWdaNOrW0sGVbGKZN7DP5I N1cYdrMQFtxFzwAW+i7A1TSa5XIoQNVs1aYqRybD5y4gnKsdb1Dg7Fd4hndE/ipegVYZ ta0w== X-Forwarded-Encrypted: i=1; AHgh+Rp2O6AufzzuNuofqq8aroo8Wi4RDYbv+7rZcltunqsDDdd9iu+CN1B8e0kdEowuOPorrSZYPMmCEAwbxXE=@vger.kernel.org X-Gm-Message-State: AOJu0Yw4CbhBmn9derxP7Q6aI7UVViVcVSH30959xQiSbxl7mS6MUoQ8 UpHc+Eb/GBYqN9SPq5kuomlUKU9NGFIjbPVq7Fj7JwkqRkrzXF0gtD4eemxqd8ZdbqY= X-Gm-Gg: AR+sD12Cs5k0v9K6W4ZYw+BRXzWDn89s3K9asG7B1Kz2aNpX8gGG1mhSNVOJ5OPw550 W6TO8s8TznCsIwFSqw403VYfHVQIa2SoeGL5z9cHUlGBfWo8yjjyVUYTFQexw5cF/13vf5GH/aF frmTlJ0flNhJxbxHLiac6jEfoz5tKn0ZBi13dPInRrtuMSFo9I1C5E9WeqhMZMHiGXj3E/RA4WY HCQSCAQTGUjfJcIwrVfVLc/Xv+Z55bPs51fSz/MzwttKfb/A3WUwwZ3n8QCOx/0zXZz2p40skC/ 8Tv9kjmU3y7eQcwwskOA5rxJRYD1/wMb0SfVBEevOQGmCmlSpf3W+r7AOIK1OMPJIV633l8n/S6 +IuCoIDQRkCUKf+soSvFVW5Wb69FZrnHmbVd2FJWIlx4QgwcRF2ctcYqW48M0cHLShemRsevFVE MSgU5EUJSd7A== X-Received: by 2002:a17:903:18b:b0:2c9:e86e:aa0a with SMTP id d9443c01a7336-2cfa6825611mr69043765ad.0.1784873782350; Thu, 23 Jul 2026 23:16:22 -0700 (PDT) Received: from yhlee-960QFG.. ([125.131.91.97]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2cf8efa4697sm45600445ad.11.2026.07.23.23.16.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Jul 2026 23:16:21 -0700 (PDT) From: Yehyeong Lee To: netdev@vger.kernel.org Cc: Yehyeong Lee , "D. Wythe" , Dust Li , Sidraya Jayagond , Wenjia Zhang , Mahanta Jambigi , Tony Lu , Wen Gu , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Guangguan Wang , linux-rdma@vger.kernel.org, linux-s390@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH net v2] net/smc: fix out-of-bounds read of rkey array in SMC-Rv2 LLC processing Date: Fri, 24 Jul 2026 15:16:06 +0900 Message-ID: <20260724061606.433735-1-yhlee@isslab.korea.ac.kr> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" SMC-Rv2 uses two receive-buffer layouts, selected by smc_link_shared_v2_rxbuf() (i.e. lnk->wr_rx_sge_cnt > 1): - shared (device max_recv_sge >=3D 2): each receive work request posts a small primary SGE plus a spillover SGE that DMAs the message body into the per-link-group lgr->wr_rx_buf_v2 (SMC_WR_BUF_V2_SIZE =3D 8 KiB) at offset SMC_WR_TX_SIZE. - non-shared (device max_recv_sge =3D=3D 1): each receive work request pos= ts a single SGE of wr_rx_buflen =3D=3D SMC_WR_BUF_V2_SIZE, so the whole message lands in the per-WR receive buffer. smc_llc_rx_handler() hands the received buffer to smc_llc_enqueue(), which copies only sizeof(union smc_llc_msg) bytes into the fixed-size qentry->msg and queues the request; the LLC request is then processed later from the llc_event_work work item, after smc_wr_rx_process_cqes() has already reposted (and thus recycled) the per-WR receive buffer. For the shared layout the rkey array survives in wr_rx_buf_v2, and smc_llc_rmt_delete_rkey() and smc_llc_save_add_link_rkeys() read it from there. For the non-shared layout nothing preserves the message body: both functions instead read the rkey array from the truncated qentry copy ((struct smc_llc_msg_delete_rkey_v2 *)llc, and (u8 *)add_llc + SMC_WR_TX_SIZE), running past the end of the ~72-byte qentry allocation. The loop bound num_rkeys comes straight off the wire and is only capped at SMC_LLC_RKEYS_PER_MSG_V2 (255), so a peer that sends a DELETE_RKEY or ADD_LINK message with an oversized rkey count triggers an out-of-bounds read of up to ~1 KiB (delete_rkey) or ~4 KiB (add_link) past a kmalloc-96 object. BUG: KASAN: slab-out-of-bounds in smc_llc_rmt_delete_rkey+0x6a4/0x780 Read of size 4 at addr ffff8880059ee748 by task kworker/0:0H/11 Workqueue: events_highpri smc_llc_event_work Call Trace: dump_stack_lvl+0x53/0x70 print_report+0xd0/0x630 kasan_report+0xce/0x100 smc_llc_rmt_delete_rkey+0x6a4/0x780 smc_llc_event_handler+0xa13/0xef0 smc_llc_event_work+0x189/0x260 process_one_work+0x633/0x1030 worker_thread+0x45b/0xd10 kthread+0x2c6/0x3b0 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 Allocated by task 43: __kmalloc_cache_noprof+0x158/0x370 smc_llc_enqueue+0x72/0x560 smc_wr_rx_tasklet_fn+0x474/0xa80 ... The buggy address is located 0 bytes to the right of allocated 72-byte region [ffff8880059ee700, ffff8880059ee748) Preserve the message body for the non-shared layout as well: in smc_llc_rx_handler() copy the bytes beyond SMC_WR_TX_SIZE into wr_rx_buf_v2 + SMC_WR_TX_SIZE before the receive work request is reposted, exactly mirroring the spillover SGE of the shared layout. The header (which for a message that fits in SMC_WR_TX_SIZE contains the whole rkey array) is not copied here; smc_llc_rmt_delete_rkey() now restores it from the per-qentry copy for both layouts, so a different v2 LLC message that reaches the shared wr_rx_buf_v2 between reception and processing cannot clobber it. The rkey array is then read from wr_rx_buf_v2, bounded by its SMC_WR_BUF_V2_SIZE size. The body copy is guarded by lgr->smc_version =3D=3D SMC_V2, since wr_rx_buf_v2 is only allocated for v2 link groups; keying it off the link-group version rather than only the wire llc_version prevents a NULL dereference should a v2-versioned LLC message arrive on a v1 link group. It also removes a latent use-after-free on the SMC-R server add-link path, where the non-shared code dereferenced add_llc after its qentry had been freed by smc_llc_flow_qentry_del(). The rkey count is still only capped at SMC_LLC_RKEYS_PER_MSG_V2; the reads now stay within the 8 KiB buffer regardless, and validating num_rkeys against the received message length is left as a separate hardening. Triggering the bug requires a RoCE device that advertises max_recv_sge =3D=3D 1 (a configuration the Fixes: change added support for) and a peer emitting an oversized rkey count. Reproduced under KASAN with soft-RoCE (rdma_rxe) after forcing wr_rx_sge_cnt =3D 1. Fixes: 27ef6a9981fe ("net/smc: support SMC-R V2 for rdma devices with max_r= ecv_sge equals to 1") Signed-off-by: Yehyeong Lee --- Resent as a standalone thread per pv-bot: threaded. Previous posting: 20260724045310.430202-1-yhlee@isslab.korea.ac.kr net/smc/smc_llc.c | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c index 954b2ff1815c..84d8d9024efa 100644 --- a/net/smc/smc_llc.c +++ b/net/smc/smc_llc.c @@ -1099,9 +1099,8 @@ int smc_llc_cli_add_link(struct smc_link *link, struc= t smc_llc_qentry *qentry) if (rc) goto out_clear_lnk; if (lgr->smc_version =3D=3D SMC_V2) { - u8 *llc_msg =3D smc_link_shared_v2_rxbuf(link) ? - (u8 *)lgr->wr_rx_buf_v2 : (u8 *)llc; - smc_llc_save_add_link_rkeys(link, lnk_new, llc_msg); + smc_llc_save_add_link_rkeys(link, lnk_new, + (u8 *)lgr->wr_rx_buf_v2); } else { rc =3D smc_llc_cli_rkey_exchange(link, lnk_new); if (rc) { @@ -1501,9 +1500,8 @@ int smc_llc_srv_add_link(struct smc_link *link, if (rc) goto out_err; if (lgr->smc_version =3D=3D SMC_V2) { - u8 *llc_msg =3D smc_link_shared_v2_rxbuf(link) ? - (u8 *)lgr->wr_rx_buf_v2 : (u8 *)add_llc; - smc_llc_save_add_link_rkeys(link, link_new, llc_msg); + smc_llc_save_add_link_rkeys(link, link_new, + (u8 *)lgr->wr_rx_buf_v2); } else { rc =3D smc_llc_srv_rkey_exchange(link, link_new); if (rc) @@ -1812,12 +1810,15 @@ static void smc_llc_rmt_delete_rkey(struct smc_link= _group *lgr) if (lgr->smc_version =3D=3D SMC_V2) { struct smc_llc_msg_delete_rkey_v2 *llcv2; =20 - if (smc_link_shared_v2_rxbuf(link)) { - memcpy(lgr->wr_rx_buf_v2, llc, sizeof(*llc)); - llcv2 =3D (struct smc_llc_msg_delete_rkey_v2 *)lgr->wr_rx_buf_v2; - } else { - llcv2 =3D (struct smc_llc_msg_delete_rkey_v2 *)llc; - } + /* Restore the header - and, for a message that fits in + * SMC_WR_TX_SIZE, the whole rkey array - from the per-qentry copy + * for both rxbuf layouts, so a v2 message that reached + * wr_rx_buf_v2 in the meantime cannot clobber it. The body of a + * larger message is already in wr_rx_buf_v2 (shared: spillover + * DMA; non-shared: smc_llc_rx_handler()). + */ + memcpy(lgr->wr_rx_buf_v2, llc, sizeof(*llc)); + llcv2 =3D (struct smc_llc_msg_delete_rkey_v2 *)lgr->wr_rx_buf_v2; llcv2->num_inval_rkeys =3D 0; =20 max =3D min_t(u8, llcv2->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2); @@ -2104,6 +2105,26 @@ static void smc_llc_rx_handler(struct ib_wc *wc, voi= d *buf) } else { if (llc->raw.hdr.length_v2 < sizeof(*llc)) return; /* invalid message */ + /* For the non-shared v2 rxbuf layout the message body beyond + * SMC_WR_TX_SIZE lives only in the per-WR receive buffer, which + * is reposted as soon as this handler returns. The LLC event + * handlers run later from a work item and read the body from + * wr_rx_buf_v2, so copy it there now - only the body, mirroring + * the shared-rxbuf layout where the spillover SGE DMAs the body + * into wr_rx_buf_v2 + SMC_WR_TX_SIZE. The header is left to the + * handlers, which restore it from the per-qentry copy, so a later + * message reaching wr_rx_buf_v2 cannot clobber it. wr_rx_buf_v2 + * only exists for SMC_V2 link groups, so the link-group version + * is checked (not just the wire llc_version) to avoid a NULL + * dereference on a v1 link group. + */ + if (link->lgr->smc_version =3D=3D SMC_V2 && + !smc_link_shared_v2_rxbuf(link) && + wc->byte_len > SMC_WR_TX_SIZE) + memcpy((u8 *)link->lgr->wr_rx_buf_v2 + SMC_WR_TX_SIZE, + (u8 *)llc + SMC_WR_TX_SIZE, + min_t(u32, wc->byte_len, SMC_WR_BUF_V2_SIZE) - + SMC_WR_TX_SIZE); } =20 smc_llc_enqueue(link, llc); --=20 2.43.0