mirror of
https://github.com/torvalds/linux.git
synced 2026-10-04 02:09:03 +02:00
sctp: hold asoc or transport before mod_timer() in timer handlers
Take the association or transport reference before rearming a timer in the
timer handlers.
The existing code calls mod_timer() before taking the reference needed by
the rearmed timer without holding the sock lock. This creates a race with
timer cleanup: if the timer is deleted after mod_timer() returns but before
the reference is taken, the cleanup path can drop the timer's reference and
destroy the transport or association. The timer handler then takes a
reference on the already freed object and eventually drops it, causing a
refcount underflow.
Hold the object before mod_timer() and drop the reference if mod_timer()
reports that the timer was already pending in timer handlers. Apply the
same ordering to the proto-unreachable path, which can rearm a transport
timer outside the timer handlers without holding the sock lock.
Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Reported-by: Tangxin Xie <xietangxin@h-partners.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Link: https://patch.msgid.link/c31b5e3ee2b7274e804f5eba2f21e2412e7eef7a.1790013825.git.lucien.xin@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
6836807c5f
commit
cae23ae3f7
|
|
@ -436,9 +436,10 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
|
|||
if (timer_pending(&t->proto_unreach_timer))
|
||||
return;
|
||||
else {
|
||||
if (!mod_timer(&t->proto_unreach_timer,
|
||||
jiffies + (HZ/20)))
|
||||
sctp_transport_hold(t);
|
||||
sctp_transport_hold(t);
|
||||
if (mod_timer(&t->proto_unreach_timer,
|
||||
jiffies + (HZ / 20)))
|
||||
sctp_transport_put(t);
|
||||
}
|
||||
} else {
|
||||
struct net *net = sock_net(sk);
|
||||
|
|
|
|||
|
|
@ -244,8 +244,9 @@ void sctp_generate_t3_rtx_event(struct timer_list *t)
|
|||
pr_debug("%s: sock is busy\n", __func__);
|
||||
|
||||
/* Try again later. */
|
||||
if (!mod_timer(&transport->T3_rtx_timer, jiffies + (HZ/20)))
|
||||
sctp_transport_hold(transport);
|
||||
sctp_transport_hold(transport);
|
||||
if (mod_timer(&transport->T3_rtx_timer, jiffies + (HZ / 20)))
|
||||
sctp_transport_put(transport);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
|
|
@ -280,8 +281,9 @@ static void sctp_generate_timeout_event(struct sctp_association *asoc,
|
|||
timeout_type);
|
||||
|
||||
/* Try again later. */
|
||||
if (!mod_timer(&asoc->timers[timeout_type], jiffies + (HZ/20)))
|
||||
sctp_association_hold(asoc);
|
||||
sctp_association_hold(asoc);
|
||||
if (mod_timer(&asoc->timers[timeout_type], jiffies + (HZ / 20)))
|
||||
sctp_association_put(asoc);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
|
|
@ -378,8 +380,9 @@ void sctp_generate_heartbeat_event(struct timer_list *t)
|
|||
pr_debug("%s: sock is busy\n", __func__);
|
||||
|
||||
/* Try again later. */
|
||||
if (!mod_timer(&transport->hb_timer, jiffies + (HZ/20)))
|
||||
sctp_transport_hold(transport);
|
||||
sctp_transport_hold(transport);
|
||||
if (mod_timer(&transport->hb_timer, jiffies + (HZ / 20)))
|
||||
sctp_transport_put(transport);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
|
|
@ -388,8 +391,9 @@ void sctp_generate_heartbeat_event(struct timer_list *t)
|
|||
timeout = sctp_transport_timeout(transport);
|
||||
if (elapsed < timeout) {
|
||||
elapsed = timeout - elapsed;
|
||||
if (!mod_timer(&transport->hb_timer, jiffies + elapsed))
|
||||
sctp_transport_hold(transport);
|
||||
sctp_transport_hold(transport);
|
||||
if (mod_timer(&transport->hb_timer, jiffies + elapsed))
|
||||
sctp_transport_put(transport);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
|
|
@ -422,9 +426,10 @@ void sctp_generate_proto_unreach_event(struct timer_list *t)
|
|||
pr_debug("%s: sock is busy\n", __func__);
|
||||
|
||||
/* Try again later. */
|
||||
if (!mod_timer(&transport->proto_unreach_timer,
|
||||
jiffies + (HZ/20)))
|
||||
sctp_transport_hold(transport);
|
||||
sctp_transport_hold(transport);
|
||||
if (mod_timer(&transport->proto_unreach_timer,
|
||||
jiffies + (HZ / 20)))
|
||||
sctp_transport_put(transport);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
|
|
@ -458,8 +463,9 @@ void sctp_generate_reconf_event(struct timer_list *t)
|
|||
pr_debug("%s: sock is busy\n", __func__);
|
||||
|
||||
/* Try again later. */
|
||||
if (!mod_timer(&transport->reconf_timer, jiffies + (HZ / 20)))
|
||||
sctp_transport_hold(transport);
|
||||
sctp_transport_hold(transport);
|
||||
if (mod_timer(&transport->reconf_timer, jiffies + (HZ / 20)))
|
||||
sctp_transport_put(transport);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
|
|
@ -495,8 +501,9 @@ void sctp_generate_probe_event(struct timer_list *t)
|
|||
pr_debug("%s: sock is busy\n", __func__);
|
||||
|
||||
/* Try again later. */
|
||||
if (!mod_timer(&transport->probe_timer, jiffies + (HZ / 20)))
|
||||
sctp_transport_hold(transport);
|
||||
sctp_transport_hold(transport);
|
||||
if (mod_timer(&transport->probe_timer, jiffies + (HZ / 20)))
|
||||
sctp_transport_put(transport);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user