mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Sidraya Jayagond <sidraya@linux.ibm.com>
To: Yehyeong Lee <yhlee@isslab.korea.ac.kr>,
	alibuda@linux.alibaba.com, dust.li@linux.alibaba.com,
	wenjia@linux.ibm.com, kuba@kernel.org, davem@davemloft.net,
	edumazet@google.com, pabeni@redhat.com
Cc: leitao@debian.org, horms@kernel.org, mjambigi@linux.ibm.com,
	tonylu@linux.alibaba.com, guwen@linux.alibaba.com,
	guangguan.wang@linux.alibaba.com, kees@kernel.org,
	gustavoars@kernel.org, netdev@vger.kernel.org,
	linux-rdma@vger.kernel.org, linux-s390@vger.kernel.org,
	linux-hardening@vger.kernel.org, linux-kernel@vger.kernel.org,
	stable@vger.kernel.org
Subject: Re: [PATCH net v6 3/3] net/smc: carry oversized SMC-Rv2 LLC messages in the queue entry
Date: Tue, 18 Aug 2026 21:07:55 +0530	[thread overview]
Message-ID: <d3b6d485-bef3-4a04-8fde-d78c80de05c6@linux.ibm.com> (raw)
In-Reply-To: <20260811231902.47089-4-yhlee@isslab.korea.ac.kr>



On 12/08/26 4:49 am, Yehyeong Lee wrote:
> smc_llc_rmt_delete_rkey() and smc_llc_save_add_link_rkeys() read the part
> of a v2 message that does not fit into the 44-byte union smc_llc_msg, and
> both bound themselves by the size of the buffer it landed in, not by what
> arrived.  On a link with a shared v2 receive buffer a 44-byte
> DELETE_RKEY_V2 declaring 255 rkeys reaches rkey[9..254] in whatever an
> earlier message left in lgr->wr_rx_buf_v2, and passes each of them to
> smc_rtoken_delete().  One of those 255 matched a registered rtoken and
> deleted it.  An ADD_LINK on such a link installs up to 255 rtokens from
> the same bytes.
> 
> Copy the tail into the queue entry, so its length is the length of the
> message that arrived, and declare the rkeys that fit inline as a member of
> the union instead of reaching them through a cast.  The same
> DELETE_RKEY_V2 now processes the 9 rkeys it carries.  The copy is limited
> to the longest tail the two functions can read, so the peer does not pick
> the size of the entry.
> 
> The bound the previous patch placed on links without a shared v2 receive
> buffer is no longer needed.
> 
> Fixes: 27ef6a9981fe ("net/smc: support SMC-R V2 for rdma devices with max_recv_sge equals to 1")
> Cc: stable@vger.kernel.org
> Suggested-by: D. Wythe <alibuda@linux.alibaba.com>
> Signed-off-by: Yehyeong Lee <yhlee@isslab.korea.ac.kr>
> ---
> Changes since v5: added the Fixes: and Cc: stable tags; asserted that the two
> DELETE_RKEY_V2 layouts agree on offsetof(rkey); limited the copied tail to
> what the two readers can use; corrected the comment in smc_wr_init_sge().
> 
> Measured over rxe with KASAN: a DELETE_RKEY_V2 carrying 12 rkeys over a link
> with a shared v2 receive buffer round-trips all 12 values, the last three
> coming from the copied tail; 8, 9 and 10 rkeys and a 44-byte message declaring
> 10 give 8, 9, 10 and 9 processed rkeys respectively.  kmemleak reports nothing
> over the link-addition path, and does report the queue entry when the free
> added by patch 1 is removed again.
> 
> Five runs per cell with and without the new limit: a 44-byte DELETE_RKEY_V2
> declaring 255 rkeys reports 9 processed on a link with and without a shared
> v2 receive buffer, an ADD_LINK v2 extension installs the 6 rtokens the peer
> sent, and no KASAN report appears.  The only message the limit changes in
> that lab is a REQ_ADD_LINK, which copied 16 bytes that have no reader and
> now copies none.  On the unpatched kernel the same DELETE_RKEY_V2 reports
> 254 and 255, and the ADD_LINK installs 255 rtokens per call.
> 

Nit: There are a few extra spaces after sentence ending punctuation in
the commit message. Please remove them.

>  net/smc/smc_llc.c | 125 ++++++++++++++++++++++++++++++++--------------
>  net/smc/smc_wr.c  |   6 +--
>  2 files changed, 91 insertions(+), 40 deletions(-)
> 
> diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c
> index 748d65186f68..393aa0af18d1 100644
> --- a/net/smc/smc_llc.c
> +++ b/net/smc/smc_llc.c
> @@ -157,6 +157,7 @@ struct smc_llc_msg_confirm_rkey {	/* type 0x06 */
>  };
>  
>  #define SMC_LLC_DEL_RKEY_MAX	8
> +#define SMC_LLC_DEL_RKEY_V2_INLINE	9
>  #define SMC_LLC_FLAG_RKEY_RETRY	0x10
>  #define SMC_LLC_FLAG_RKEY_NEG	0x20
>  
> @@ -177,6 +178,15 @@ struct smc_llc_msg_delete_rkey_v2 {	/* type 0x29 */
>  	__be32 rkey[];
>  };
>  
> +/* the leading rkeys of a DELETE_RKEY_V2 fit into union smc_llc_msg */
> +struct smc_llc_msg_delete_rkey_v2_inline {	/* type 0x29 */
> +	struct smc_llc_hdr hd;
> +	u8 num_rkeys;
> +	u8 num_inval_rkeys;
> +	u8 reserved[2];
> +	__be32 rkey[SMC_LLC_DEL_RKEY_V2_INLINE];
> +};
> +
>  union smc_llc_msg {
>  	struct smc_llc_msg_confirm_link confirm_link;
>  	struct smc_llc_msg_add_link add_link;
> @@ -186,6 +196,7 @@ union smc_llc_msg {
>  
>  	struct smc_llc_msg_confirm_rkey confirm_rkey;
>  	struct smc_llc_msg_delete_rkey delete_rkey;
> +	struct smc_llc_msg_delete_rkey_v2_inline delete_rkey_v2;
>  
>  	struct smc_llc_msg_test_link test_link;
>  	struct {
> @@ -194,15 +205,25 @@ union smc_llc_msg {
>  	} raw;
>  };
>  
> +static_assert(SMC_LLC_DEL_RKEY_V2_INLINE ==
> +	      (sizeof(union smc_llc_msg) -
> +	       offsetof(struct smc_llc_msg_delete_rkey_v2, rkey)) /
> +	      sizeof(__be32));
> +static_assert(offsetof(struct smc_llc_msg_delete_rkey_v2_inline, rkey) ==
> +	      offsetof(struct smc_llc_msg_delete_rkey_v2, rkey));
> +
>  #define SMC_LLC_FLAG_RESP		0x80
>  
>  struct smc_llc_qentry {
>  	struct list_head list;
>  	struct smc_link *link;
> +	u16 body_len;
>  	union smc_llc_msg msg;
> +	u8 body[] __counted_by(body_len);
>  };
>  
> -static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc);
> +static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc,
> +			    u32 byte_len);
>  
>  struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow)
>  {
> @@ -998,22 +1019,19 @@ static int smc_llc_cli_conf_link(struct smc_link *link,
>  
>  static void smc_llc_save_add_link_rkeys(struct smc_link *link,
>  					struct smc_link *link_new,
> -					u8 *llc_msg)
> +					struct smc_llc_qentry *qentry)
>  {
>  	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))
> +	/* the rkey count itself is only there if enough bytes arrived */
> +	if (qentry->body_len < rt_off)
>  		return;
> -	ext = (struct smc_llc_msg_add_link_v2_ext *)(llc_msg +
> -						     SMC_WR_TX_SIZE);
> +	ext = (struct smc_llc_msg_add_link_v2_ext *)qentry->body;
>  	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) /
> +	max = min_t(u32, max, (qentry->body_len - rt_off) /
>  			      sizeof(ext->rt[0]));
>  	down_write(&lgr->rmbs_lock);
>  	for (i = 0; i < max; i++) {
> @@ -1107,9 +1125,7 @@ int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry)
>  	if (rc)
>  		goto out_clear_lnk;
>  	if (lgr->smc_version == SMC_V2) {
> -		u8 *llc_msg = 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, qentry);
>  	} else {
>  		rc = smc_llc_cli_rkey_exchange(link, lnk_new);
>  		if (rc) {
> @@ -1510,9 +1526,7 @@ int smc_llc_srv_add_link(struct smc_link *link,
>  	if (rc)
>  		goto out_err;
>  	if (lgr->smc_version == SMC_V2) {
> -		u8 *llc_msg = 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, qentry);
>  	} else {
>  		rc = smc_llc_srv_rkey_exchange(link, link_new);
>  		if (rc)
> @@ -1561,7 +1575,8 @@ void smc_llc_add_link_local(struct smc_link *link)
>  	add_llc.hd.common.llc_type = SMC_LLC_ADD_LINK;
>  	smc_llc_init_msg_hdr(&add_llc.hd, link->lgr, sizeof(add_llc));
>  	/* no dev and port needed */
> -	smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc);
> +	smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc,
> +			sizeof(union smc_llc_msg));
>  }
>  
>  /* worker to process an add link message */
> @@ -1597,7 +1612,8 @@ void smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id)
>  	del_llc.link_num = del_link_id;
>  	del_llc.reason = htonl(SMC_LLC_DEL_LOST_PATH);
>  	del_llc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
> -	smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc);
> +	smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc,
> +			sizeof(union smc_llc_msg));
>  }
>  
>  static void smc_llc_process_cli_delete_link(struct smc_link_group *lgr)
> @@ -1819,27 +1835,28 @@ 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);
> -		}
> +		struct smc_llc_msg_delete_rkey_v2_inline *llcv2;
> +
> +		/* The leading SMC_LLC_DEL_RKEY_V2_INLINE rkeys are declared in
> +		 * the message itself, any further ones were received into
> +		 * qentry->body.
> +		 */
> +		llcv2 = &qentry->msg.delete_rkey_v2;
>  		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]));
> +		max = min_t(u32, max, SMC_LLC_DEL_RKEY_V2_INLINE +
> +				      qentry->body_len / sizeof(__be32));
>  		for (i = 0; i < max; i++) {
> -			if (smc_rtoken_delete(link, llcv2->rkey[i]))
> +			__be32 rkey;
> +
> +			if (i < SMC_LLC_DEL_RKEY_V2_INLINE)
> +				rkey = llcv2->rkey[i];
> +			else
> +				memcpy(&rkey, qentry->body +
> +					      (i - SMC_LLC_DEL_RKEY_V2_INLINE) *
> +					      sizeof(rkey), sizeof(rkey));
> +			if (smc_rtoken_delete(link, rkey))
>  				llcv2->num_inval_rkeys++;
>  		}
>  		memset(&llc->rkey[0], 0, sizeof(llc->rkey));
> @@ -2080,18 +2097,52 @@ static void smc_llc_rx_response(struct smc_link *link,
>  	wake_up(&link->lgr->llc_msg_waiter);
>  }
>  
> -static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc)
> +/* the longest tail either reader of qentry->body can use */
> +static u32 smc_llc_max_body_len(union smc_llc_msg *llc)
> +{
> +	switch (llc->raw.hdr.common.llc_type) {
> +	case SMC_LLC_ADD_LINK:
> +		return offsetof(struct smc_llc_msg_add_link_v2_ext, rt) +
> +		       SMC_LLC_RKEYS_PER_MSG_V2 *
> +		       sizeof(struct smc_llc_msg_add_link_cont_rt);
> +	case SMC_LLC_DELETE_RKEY:
> +		return (SMC_LLC_RKEYS_PER_MSG_V2 -
> +			SMC_LLC_DEL_RKEY_V2_INLINE) * sizeof(__be32);
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc,
> +			    u32 byte_len)
>  {
>  	struct smc_link_group *lgr = link->lgr;
>  	struct smc_llc_qentry *qentry;
>  	unsigned long flags;
> +	u16 body_len = 0;
> +
> +	/* V2 messages can be longer than the inline union smc_llc_msg. Carry
> +	 * the remainder in the qentry itself, so that its lifetime and its
> +	 * length match the message the peer actually sent.
> +	 */
> +	if (lgr->smc_version == SMC_V2 && byte_len > SMC_WR_TX_SIZE)
> +		body_len = min_t(u32, byte_len, SMC_WR_BUF_V2_SIZE) -
> +			   SMC_WR_TX_SIZE;
> +	body_len = min_t(u32, body_len, smc_llc_max_body_len(llc));
>  
> -	qentry = kmalloc_obj(*qentry, GFP_ATOMIC);
> +	qentry = kmalloc_flex(*qentry, body, body_len, GFP_ATOMIC);
>  	if (!qentry)
>  		return;
> +	qentry->body_len = body_len;
>  	qentry->link = link;
>  	INIT_LIST_HEAD(&qentry->list);
>  	memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg));
> +	if (body_len) {
> +		u8 *src = smc_link_shared_v2_rxbuf(link) ?
> +			(u8 *)lgr->wr_rx_buf_v2 : (u8 *)llc;
> +
> +		memcpy(qentry->body, src + SMC_WR_TX_SIZE, body_len);
> +	}
>  
>  	/* process responses immediately */
>  	if ((llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) &&
> @@ -2123,7 +2174,7 @@ static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
>  			return; /* invalid message */
>  	}
>  
> -	smc_llc_enqueue(link, llc);
> +	smc_llc_enqueue(link, llc, wc->byte_len);
>  }
>  
>  /***************************** worker, utils *********************************/
> diff --git a/net/smc/smc_wr.c b/net/smc/smc_wr.c
> index 59c92b46945c..97ba46893b17 100644
> --- a/net/smc/smc_wr.c
> +++ b/net/smc/smc_wr.c
> @@ -602,9 +602,9 @@ static void smc_wr_init_sge(struct smc_link *lnk)
>  
>  	/* With SMC-Rv2 there can be messages larger than SMC_WR_TX_SIZE.
>  	 * Each ib_recv_wr gets 2 sges, the second one is a spillover buffer
> -	 * and the same buffer for all sges. When a larger message arrived then
> -	 * the content of the first small sge is copied to the beginning of
> -	 * the larger spillover buffer, allowing easy data mapping.
> +	 * and the same buffer for all sges. The spillover sge starts at
> +	 * SMC_WR_TX_SIZE, so the leading bytes of that buffer are never
> +	 * written.
>  	 */
>  	for (i = 0; i < lnk->wr_rx_cnt; i++) {
>  		int x = i * lnk->wr_rx_sge_cnt;
Patch looks good to me.

Reviewed-by: Sidraya Jayagond <sidraya@linux.ibm.com>

  reply	other threads:[~2026-08-18 15:38 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-11 23:18 [PATCH net v6 0/3] net/smc: fix out-of-bounds and use-after-free in SMC-Rv2 LLC processing Yehyeong Lee
2026-08-11 23:19 ` [PATCH net v6 1/3] net/smc: fix use-after-free of the LLC qentry in smc_llc_srv_add_link() Yehyeong Lee
2026-08-18 15:29   ` Sidraya Jayagond
2026-08-11 23:19 ` [PATCH net v6 2/3] net/smc: bound the peer rkey counts in SMC-Rv2 LLC messages Yehyeong Lee
2026-08-18 15:32   ` Sidraya Jayagond
2026-08-11 23:19 ` [PATCH net v6 3/3] net/smc: carry oversized SMC-Rv2 LLC messages in the queue entry Yehyeong Lee
2026-08-18 15:37   ` Sidraya Jayagond [this message]
2026-08-13  6:15 ` [PATCH net v6 0/3] net/smc: fix out-of-bounds and use-after-free in SMC-Rv2 LLC processing Yehyeong Lee
2026-08-17 18:29 ` Jakub Kicinski

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=d3b6d485-bef3-4a04-8fde-d78c80de05c6@linux.ibm.com \
    --to=sidraya@linux.ibm.com \
    --cc=alibuda@linux.alibaba.com \
    --cc=davem@davemloft.net \
    --cc=dust.li@linux.alibaba.com \
    --cc=edumazet@google.com \
    --cc=guangguan.wang@linux.alibaba.com \
    --cc=gustavoars@kernel.org \
    --cc=guwen@linux.alibaba.com \
    --cc=horms@kernel.org \
    --cc=kees@kernel.org \
    --cc=kuba@kernel.org \
    --cc=leitao@debian.org \
    --cc=linux-hardening@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rdma@vger.kernel.org \
    --cc=linux-s390@vger.kernel.org \
    --cc=mjambigi@linux.ibm.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=stable@vger.kernel.org \
    --cc=tonylu@linux.alibaba.com \
    --cc=wenjia@linux.ibm.com \
    --cc=yhlee@isslab.korea.ac.kr \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®