mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
net/smc: Hold a socket reference for transmit work
SMC transmit work is queued without holding a socket reference. After
an active close has cancelled tx_work, a received CDC message can queue
it again while the socket is in SMC_PEERCLOSEWAIT1. Passive close can
then reach SMC_CLOSED, call smc_conn_free() and drop the last socket
reference before smc_tx_work() acquires the socket lock. The worker
then accesses the freed socket.
KASAN reported:
BUG: KASAN: slab-use-after-free in lock_sock_nested+0x97/0x180
Write of size 8 by task kworker/0:1/11
Workqueue: smc_tx_wq-00000000 smc_tx_work
Call Trace:
lock_sock_nested+0x97/0x180
smc_tx_work+0x5d/0x170
process_one_work+0x5ce/0xeb0
worker_thread+0x45b/0xd10
Allocated by task 24:
sk_prot_alloc+0x56/0x210
sk_alloc+0x2b/0x6f0
smc_tcp_listen_work+0x16d/0xfc0
Freed by task 182:
slab_free_after_rcu_debug+0xa6/0x1e0
rcu_core+0x509/0x1850
Hold a socket reference for each successful enqueue and release it when
the worker finishes or a pending invocation is cancelled. Cover all three
queue sites and all cancellation sites, including the socket options.
Check conn->freed in smc_tx_pending() under the socket lock: retaining the
socket does not retain connection resources released by smc_conn_free().
This also covers pending transmit processing from smc_release_cb().
Use queue_delayed_work() for the busy-slot retry as well. All tx_work
delays are zero, so an already queued invocation needs no timer update.
Unlike mod_delayed_work(), its return value distinguishes a successful
enqueue from work disabled temporarily by cancel_delayed_work_sync(),
allowing the extra reference to be returned when no work was queued.
Cc: stable+noautosel@kernel.org # LLM report + LLM fix, not seen in real life
Fixes: e6727f3900 ("smc: send data (through RDMA)")
Link: https://lists.openwall.net/netdev/2026/09/09/105
Signed-off-by: Chengfeng Ye <nicoyip.dev@gmail.com>
Link: https://patch.msgid.link/20260927075120.3695060-1-nicoyip.dev@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
a0227fa24e
commit
1bc449382b
|
|
@ -3147,7 +3147,8 @@ int smc_setsockopt(struct socket *sock, int level, int optname,
|
|||
if (val) {
|
||||
SMC_STAT_INC(smc, ndly_cnt);
|
||||
smc_tx_pending(&smc->conn);
|
||||
cancel_delayed_work(&smc->conn.tx_work);
|
||||
if (cancel_delayed_work(&smc->conn.tx_work))
|
||||
sock_put(sk);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -3158,7 +3159,8 @@ int smc_setsockopt(struct socket *sock, int level, int optname,
|
|||
if (!val) {
|
||||
SMC_STAT_INC(smc, cork_cnt);
|
||||
smc_tx_pending(&smc->conn);
|
||||
cancel_delayed_work(&smc->conn.tx_work);
|
||||
if (cancel_delayed_work(&smc->conn.tx_work))
|
||||
sock_put(sk);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -118,7 +118,8 @@ static void smc_close_cancel_work(struct smc_sock *smc)
|
|||
release_sock(sk);
|
||||
if (cancel_work_sync(&smc->conn.close_work))
|
||||
sock_put(sk);
|
||||
cancel_delayed_work_sync(&smc->conn.tx_work);
|
||||
if (cancel_delayed_work_sync(&smc->conn.tx_work))
|
||||
sock_put(sk);
|
||||
lock_sock(sk);
|
||||
}
|
||||
|
||||
|
|
@ -230,7 +231,8 @@ int smc_close_active(struct smc_sock *smc)
|
|||
case SMC_ACTIVE:
|
||||
smc_close_stream_wait(smc, timeout);
|
||||
release_sock(sk);
|
||||
cancel_delayed_work_sync(&conn->tx_work);
|
||||
if (cancel_delayed_work_sync(&conn->tx_work))
|
||||
sock_put(sk);
|
||||
lock_sock(sk);
|
||||
if (sk->sk_state == SMC_ACTIVE) {
|
||||
/* send close request */
|
||||
|
|
@ -264,7 +266,8 @@ int smc_close_active(struct smc_sock *smc)
|
|||
if (!smc_cdc_rxed_any_close(conn))
|
||||
smc_close_stream_wait(smc, timeout);
|
||||
release_sock(sk);
|
||||
cancel_delayed_work_sync(&conn->tx_work);
|
||||
if (cancel_delayed_work_sync(&conn->tx_work))
|
||||
sock_put(sk);
|
||||
lock_sock(sk);
|
||||
if (sk->sk_state != SMC_APPCLOSEWAIT1 &&
|
||||
sk->sk_state != SMC_APPCLOSEWAIT2)
|
||||
|
|
@ -372,7 +375,8 @@ static void smc_close_passive_work(struct work_struct *work)
|
|||
/* peer has not received all data */
|
||||
smc_close_passive_abort_received(smc);
|
||||
release_sock(sk);
|
||||
cancel_delayed_work_sync(&conn->tx_work);
|
||||
if (cancel_delayed_work_sync(&conn->tx_work))
|
||||
sock_put(sk);
|
||||
lock_sock(sk);
|
||||
goto wakeup;
|
||||
}
|
||||
|
|
@ -462,7 +466,8 @@ int smc_close_shutdown_write(struct smc_sock *smc)
|
|||
case SMC_ACTIVE:
|
||||
smc_close_stream_wait(smc, timeout);
|
||||
release_sock(sk);
|
||||
cancel_delayed_work_sync(&conn->tx_work);
|
||||
if (cancel_delayed_work_sync(&conn->tx_work))
|
||||
sock_put(sk);
|
||||
lock_sock(sk);
|
||||
if (sk->sk_state != SMC_ACTIVE)
|
||||
goto again;
|
||||
|
|
@ -475,7 +480,8 @@ int smc_close_shutdown_write(struct smc_sock *smc)
|
|||
if (!smc_cdc_rxed_any_close(conn))
|
||||
smc_close_stream_wait(smc, timeout);
|
||||
release_sock(sk);
|
||||
cancel_delayed_work_sync(&conn->tx_work);
|
||||
if (cancel_delayed_work_sync(&conn->tx_work))
|
||||
sock_put(sk);
|
||||
lock_sock(sk);
|
||||
if (sk->sk_state != SMC_APPCLOSEWAIT1)
|
||||
goto again;
|
||||
|
|
|
|||
|
|
@ -1070,7 +1070,9 @@ static int smc_switch_cursor(struct smc_sock *smc, struct smc_cdc_tx_pend *pend,
|
|||
smc->sk.sk_state != SMC_CLOSED) {
|
||||
rc = smcr_cdc_msg_send_validation(conn, pend, wr_buf);
|
||||
if (!rc) {
|
||||
queue_delayed_work(conn->lgr->tx_wq, &conn->tx_work, 0);
|
||||
sock_hold(&smc->sk);
|
||||
if (!queue_delayed_work(conn->lgr->tx_wq, &conn->tx_work, 0))
|
||||
sock_put(&smc->sk);
|
||||
smc->sk.sk_data_ready(&smc->sk);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -570,8 +570,10 @@ static int smcr_tx_sndbuf_nonempty(struct smc_connection *conn)
|
|||
if (conn->killed)
|
||||
return -EPIPE;
|
||||
rc = 0;
|
||||
mod_delayed_work(conn->lgr->tx_wq, &conn->tx_work,
|
||||
SMC_TX_WORK_DELAY);
|
||||
sock_hold(&smc->sk);
|
||||
if (!queue_delayed_work(conn->lgr->tx_wq, &conn->tx_work,
|
||||
SMC_TX_WORK_DELAY))
|
||||
sock_put(&smc->sk);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -667,7 +669,7 @@ void smc_tx_pending(struct smc_connection *conn)
|
|||
struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
|
||||
int rc;
|
||||
|
||||
if (smc->sk.sk_err)
|
||||
if (smc->sk.sk_err || conn->freed)
|
||||
return;
|
||||
|
||||
rc = smc_tx_sndbuf_nonempty(conn);
|
||||
|
|
@ -690,6 +692,7 @@ void smc_tx_work(struct work_struct *work)
|
|||
lock_sock(&smc->sk);
|
||||
smc_tx_pending(conn);
|
||||
release_sock(&smc->sk);
|
||||
sock_put(&smc->sk);
|
||||
}
|
||||
|
||||
void smc_tx_consumer_update(struct smc_connection *conn, bool force)
|
||||
|
|
@ -718,8 +721,13 @@ void smc_tx_consumer_update(struct smc_connection *conn, bool force)
|
|||
return;
|
||||
if ((smc_cdc_get_slot_and_msg_send(conn) < 0) &&
|
||||
!conn->killed) {
|
||||
queue_delayed_work(conn->lgr->tx_wq, &conn->tx_work,
|
||||
SMC_TX_WORK_DELAY);
|
||||
struct smc_sock *smc =
|
||||
container_of(conn, struct smc_sock, conn);
|
||||
|
||||
sock_hold(&smc->sk);
|
||||
if (!queue_delayed_work(conn->lgr->tx_wq, &conn->tx_work,
|
||||
SMC_TX_WORK_DELAY))
|
||||
sock_put(&smc->sk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user