RDMA/siw: Fix endpoint/socket association handling

Disassociating a socket from an endpoint via siw_socket_disassoc() may
release the last reference on that endpoint and free it. Therefore, don't
clear the endpoints socket pointer after calling that function, but
within.

This fixes a:

  BUG: KASAN: slab-use-after-free in siw_cm_work_handler (drivers/infiniband/sw/siw/siw_cm.c:1053 drivers/infiniband/sw/siw/siw_cm.c:1075)

which occurred after processing a malformed MPA request during connection
establishment, causing the new endpoint to be closed.

Fixes: 6c52fdc244 ("rdma/siw: connection management")
Link: https://patch.msgid.link/r/20260604160808.30948-1-bernard.metzler@linux.dev
Reported-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com>
Signed-off-by: Bernard Metzler <bernard.metzler@linux.dev>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
This commit is contained in:
Bernard Metzler 2026-06-04 18:08:08 +02:00 committed by Jason Gunthorpe
parent 1521d560b7
commit ea4f6f6c53

View File

@ -138,6 +138,7 @@ static void siw_socket_disassoc(struct socket *s)
cep = sk_to_cep(sk);
if (cep) {
siw_sk_restore_upcalls(sk, cep);
cep->sock = NULL;
siw_cep_put(cep);
} else {
pr_warn("siw: cannot restore sk callbacks: no ep\n");
@ -418,10 +419,11 @@ static void siw_free_cm_id(struct siw_cep *cep)
static void siw_destroy_cep_sock(struct siw_cep *cep)
{
if (cep->sock) {
siw_socket_disassoc(cep->sock);
sock_release(cep->sock);
cep->sock = NULL;
struct socket *s = cep->sock;
if (s) {
siw_socket_disassoc(s);
sock_release(s);
}
}
@ -1050,7 +1052,6 @@ static void siw_accept_newconn(struct siw_cep *cep)
if (new_s) {
siw_socket_disassoc(new_s);
sock_release(new_s);
new_cep->sock = NULL;
}
siw_dbg_cep(cep, "error %d\n", rv);
}
@ -1202,6 +1203,8 @@ static void siw_cm_work_handler(struct work_struct *w)
WARN(1, "Undefined CM work type: %d\n", work->type);
}
if (release_cep) {
struct socket *s = cep->sock;
siw_dbg_cep(cep,
"release: timer=%s, QP[%u]\n",
cep->mpa_timer ? "y" : "n",
@ -1227,10 +1230,9 @@ static void siw_cm_work_handler(struct work_struct *w)
cep->qp = NULL;
siw_qp_put(qp);
}
if (cep->sock) {
siw_socket_disassoc(cep->sock);
sock_release(cep->sock);
cep->sock = NULL;
if (s) {
siw_socket_disassoc(s);
sock_release(s);
}
if (cep->cm_id) {
siw_free_cm_id(cep);
@ -1561,7 +1563,6 @@ int siw_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params)
if (cep) {
siw_socket_disassoc(s);
sock_release(s);
cep->sock = NULL;
cep->qp = NULL;
@ -1937,7 +1938,6 @@ int siw_create_listen(struct iw_cm_id *id, int backlog)
siw_cep_set_inuse(cep);
siw_free_cm_id(cep);
cep->sock = NULL;
siw_socket_disassoc(s);
cep->state = SIW_EPSTATE_CLOSED;
@ -1959,6 +1959,7 @@ static void siw_drop_listeners(struct iw_cm_id *id)
*/
list_for_each_safe(p, tmp, (struct list_head *)id->provider_data) {
struct siw_cep *cep = list_entry(p, struct siw_cep, listenq);
struct socket *s = cep->sock;
list_del(p);
@ -1967,10 +1968,9 @@ static void siw_drop_listeners(struct iw_cm_id *id)
siw_cep_set_inuse(cep);
siw_free_cm_id(cep);
if (cep->sock) {
siw_socket_disassoc(cep->sock);
sock_release(cep->sock);
cep->sock = NULL;
if (s) {
siw_socket_disassoc(s);
sock_release(s);
}
cep->state = SIW_EPSTATE_CLOSED;
siw_cep_set_free_and_put(cep);