tls: device: protect async RX resync state during device-down

tls_device_rx_resync_new_rec() can run in process context with the
socket lock held, but without an RCU read lock: from the strparser work
item (tls_strp_work()) and from the receive paths (tls_rx_rec_wait(),
tls_rx_rec_done()). The resync_async pointer belongs to the driver RX
context, which tls_device_down() tears down from the netdev notifier
without taking the socket lock; only the softirq entry to the parser is
implicitly inside an RCU section.

If the parser passes the TLS_RX_DEV_DEGRADED check before device-down
sets it, tls_device_down() can complete synchronize_net() and delete the
driver context before the parser accesses resync_async->req or its
record log. In the asynchronous stage the parser also writes the record
log, so this is a write to freed memory, not only a stale read. The RCU
section in tls_device_resync_rx() starts after those accesses and
therefore does not prevent the use-after-free.

Found by code inspection; not reproduced. Hitting the window requires
the parser to be preempted between the DEGRADED check and the
resync_async access across the device-down grace period and the
driver's teardown.

Fixes: ed9b7646b0 ("net/tls: Add asynchronous resync")
Signed-off-by: Rishikesh Jethwani <rjethwani@purestorage.com>
Link: https://patch.msgid.link/20261013213517.2219663-1-rjethwani@purestorage.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Rishikesh Jethwani 2026-10-13 15:35:17 -06:00 committed by Jakub Kicinski
parent 56f8d91973
commit f89a228125

View File

@ -783,8 +783,15 @@ void tls_device_rx_resync_new_rec(struct sock *sk, u32 rcd_len, u32 seq)
if (tls_ctx->rx_conf != TLS_HW)
return;
/* The parser also runs in process context, without an RCU read lock.
* Protect the driver's resync_async state before checking the flags:
* device-down sets DEGRADED and waits for synchronize_net() before
* freeing it, without taking the socket lock.
*/
rcu_read_lock();
if (unlikely(test_bit(TLS_RX_DEV_DEGRADED, &tls_ctx->flags)))
return;
goto out;
prot = &tls_ctx->prot_info;
rx_ctx = tls_offload_ctx_rx(tls_ctx);
@ -799,11 +806,11 @@ void tls_device_rx_resync_new_rec(struct sock *sk, u32 rcd_len, u32 seq)
if (likely(!is_req_pending) || req_seq != seq ||
!atomic64_try_cmpxchg(&rx_ctx->resync_req, &resync_req, 0))
return;
goto out;
break;
case TLS_OFFLOAD_SYNC_TYPE_CORE_NEXT_HINT:
if (likely(!rx_ctx->resync_nh_do_now))
return;
goto out;
/* head of next rec is already in, note that the sock_inq will
* include the currently parsed message when called from parser
@ -812,7 +819,7 @@ void tls_device_rx_resync_new_rec(struct sock *sk, u32 rcd_len, u32 seq)
if (sock_data > rcd_len) {
trace_tls_device_rx_resync_nh_delay(sk, sock_data,
rcd_len);
return;
goto out;
}
rx_ctx->resync_nh_do_now = 0;
@ -823,16 +830,18 @@ void tls_device_rx_resync_new_rec(struct sock *sk, u32 rcd_len, u32 seq)
resync_req = atomic64_read(&rx_ctx->resync_async->req);
is_req_pending = resync_req;
if (likely(!is_req_pending))
return;
goto out;
if (!tls_device_rx_resync_async(rx_ctx->resync_async,
resync_req, &seq, &rcd_delta))
return;
goto out;
tls_bigint_subtract(rcd_sn, rcd_delta);
break;
}
tls_device_resync_rx(tls_ctx, sk, seq, rcd_sn);
out:
rcu_read_unlock();
}
static void tls_device_core_ctrl_rx_resync(struct tls_context *tls_ctx,