From: liuyong <pkfxxxing@gmail.com>
To: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>,
Xin Long <lucien.xin@gmail.com>
Cc: linux-sctp@vger.kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: [PATCH 1/1] sctp: revalidate the association socket in timer callbacks
Date: Tue, 10 Nov 2026 20:02:09 +0800 [thread overview]
Message-ID: <20261110120212.1110283-2-pkfxxxing@gmail.com> (raw)
In-Reply-To: <20261110120212.1110283-1-pkfxxxing@gmail.com>
SCTP timer callbacks cache asoc->base.sk before acquiring its BH lock.
An accept or peeloff can move the association to a new socket in between.
The callback can then run the state machine under the old socket lock,
while an abort on the new socket tears down the association's queues.
The timer's transport and association references do not keep those
queues alive. With delays inserted to widen the race, KASAN reported a
slab-use-after-free in sctp_chunk_abandoned() from the T3 retransmit path.
Recheck the association's socket after acquiring its BH lock. Treat a
mismatch like a busy socket and use the existing reference-counted timer
rescheduling path. The next callback reads the current socket again,
preventing state-machine execution under the old socket's lock. Retain
the user-ownership check to handle a migration already in progress.
Apply the check to the T3, association, heartbeat, protocol-unreachable,
reconfiguration and probe timers. Their BH context and the endpoint's
RCU-deferred sock_put() protect the cached socket during the callback.
Pair the socket pointer reads with WRITE_ONCE() in sctp_assoc_migrate(),
taking the new socket reference before updating the pointer.
The issue was discovered and fixed by Qihoo 360's Tulongfeng multi-agent
system.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: liuyong <pkfxxxing@gmail.com>
---
net/sctp/associola.c | 4 ++--
net/sctp/sm_sideeffect.c | 41 ++++++++++++++++++++--------------------
2 files changed, 22 insertions(+), 23 deletions(-)
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 4521be3bd8..c5d520c8d7 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1088,8 +1088,8 @@ void sctp_assoc_migrate(struct sctp_association *assoc, struct sock *newsk)
sctp_endpoint_hold(assoc->ep);
/* Get a reference to the new sock. */
- assoc->base.sk = newsk;
- sock_hold(assoc->base.sk);
+ sock_hold(newsk);
+ WRITE_ONCE(assoc->base.sk, newsk);
/* Add the association to the new endpoint's list of associations. */
sctp_endpoint_add_asoc(newsp->ep, assoc);
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 35f540fb15..bd6b23369f 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -233,15 +233,14 @@ void sctp_generate_t3_rtx_event(struct timer_list *t)
struct sctp_transport *transport =
timer_container_of(transport, t, T3_rtx_timer);
struct sctp_association *asoc = transport->asoc;
- struct sock *sk = asoc->base.sk;
+ struct sock *sk = READ_ONCE(asoc->base.sk);
struct net *net = sock_net(sk);
int error;
- /* Check whether a task is in the sock. */
-
+ /* Defer if the socket is busy or the association has migrated. */
bh_lock_sock(sk);
- if (sock_owned_by_user(sk)) {
- pr_debug("%s: sock is busy\n", __func__);
+ if (sock_owned_by_user(sk) || sk != READ_ONCE(asoc->base.sk)) {
+ pr_debug("%s: sock is busy or moved\n", __func__);
/* Try again later. */
sctp_transport_hold(transport);
@@ -271,13 +270,13 @@ void sctp_generate_t3_rtx_event(struct timer_list *t)
static void sctp_generate_timeout_event(struct sctp_association *asoc,
enum sctp_event_timeout timeout_type)
{
- struct sock *sk = asoc->base.sk;
+ struct sock *sk = READ_ONCE(asoc->base.sk);
struct net *net = sock_net(sk);
int error = 0;
bh_lock_sock(sk);
- if (sock_owned_by_user(sk)) {
- pr_debug("%s: sock is busy: timer %d\n", __func__,
+ if (sock_owned_by_user(sk) || sk != READ_ONCE(asoc->base.sk)) {
+ pr_debug("%s: sock is busy or moved: timer %d\n", __func__,
timeout_type);
/* Try again later. */
@@ -362,7 +361,7 @@ static void sctp_generate_autoclose_event(struct timer_list *t)
sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_AUTOCLOSE);
}
-/* Generate a heart beat event. If the sock is busy, reschedule. Make
+/* Generate a heart beat event. If the sock is busy or moved, reschedule. Make
* sure that the transport is still valid.
*/
void sctp_generate_heartbeat_event(struct timer_list *t)
@@ -370,14 +369,14 @@ void sctp_generate_heartbeat_event(struct timer_list *t)
struct sctp_transport *transport = timer_container_of(transport, t,
hb_timer);
struct sctp_association *asoc = transport->asoc;
- struct sock *sk = asoc->base.sk;
+ struct sock *sk = READ_ONCE(asoc->base.sk);
struct net *net = sock_net(sk);
u32 elapsed, timeout;
int error = 0;
bh_lock_sock(sk);
- if (sock_owned_by_user(sk)) {
- pr_debug("%s: sock is busy\n", __func__);
+ if (sock_owned_by_user(sk) || sk != READ_ONCE(asoc->base.sk)) {
+ pr_debug("%s: sock is busy or moved\n", __func__);
/* Try again later. */
sctp_transport_hold(transport);
@@ -418,12 +417,12 @@ void sctp_generate_proto_unreach_event(struct timer_list *t)
struct sctp_transport *transport =
timer_container_of(transport, t, proto_unreach_timer);
struct sctp_association *asoc = transport->asoc;
- struct sock *sk = asoc->base.sk;
+ struct sock *sk = READ_ONCE(asoc->base.sk);
struct net *net = sock_net(sk);
bh_lock_sock(sk);
- if (sock_owned_by_user(sk)) {
- pr_debug("%s: sock is busy\n", __func__);
+ if (sock_owned_by_user(sk) || sk != READ_ONCE(asoc->base.sk)) {
+ pr_debug("%s: sock is busy or moved\n", __func__);
/* Try again later. */
sctp_transport_hold(transport);
@@ -454,13 +453,13 @@ void sctp_generate_reconf_event(struct timer_list *t)
struct sctp_transport *transport =
timer_container_of(transport, t, reconf_timer);
struct sctp_association *asoc = transport->asoc;
- struct sock *sk = asoc->base.sk;
+ struct sock *sk = READ_ONCE(asoc->base.sk);
struct net *net = sock_net(sk);
int error = 0;
bh_lock_sock(sk);
- if (sock_owned_by_user(sk)) {
- pr_debug("%s: sock is busy\n", __func__);
+ if (sock_owned_by_user(sk) || sk != READ_ONCE(asoc->base.sk)) {
+ pr_debug("%s: sock is busy or moved\n", __func__);
/* Try again later. */
sctp_transport_hold(transport);
@@ -492,13 +491,13 @@ void sctp_generate_probe_event(struct timer_list *t)
struct sctp_transport *transport = timer_container_of(transport, t,
probe_timer);
struct sctp_association *asoc = transport->asoc;
- struct sock *sk = asoc->base.sk;
+ struct sock *sk = READ_ONCE(asoc->base.sk);
struct net *net = sock_net(sk);
int error = 0;
bh_lock_sock(sk);
- if (sock_owned_by_user(sk)) {
- pr_debug("%s: sock is busy\n", __func__);
+ if (sock_owned_by_user(sk) || sk != READ_ONCE(asoc->base.sk)) {
+ pr_debug("%s: sock is busy or moved\n", __func__);
/* Try again later. */
sctp_transport_hold(transport);
--
2.43.0
prev parent reply other threads:[~2026-11-10 12:03 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 12:02 [PATCH 0/1] sctp: fix timer locking across association migration liuyong
2026-11-10 12:02 ` liuyong [this message]
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=20261110120212.1110283-2-pkfxxxing@gmail.com \
--to=pkfxxxing@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sctp@vger.kernel.org \
--cc=lucien.xin@gmail.com \
--cc=marcelo.leitner@gmail.com \
--cc=netdev@vger.kernel.org \
/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®