sctp: avoid livelock while updating retransmit path

sctp_assoc_update_retran_path() can loop forever when every remaining
transport, including retran_path, is SCTP_UNCONFIRMED: the state check
runs before the wraparound test, so the loop cannot observe that it has
completed a full pass.

Fix this by considering a transport only when it is not UNCONFIRMED,
then checking whether the walk has returned to retran_path. This makes
the full-pass termination independent of the transport state while
preserving the existing fallback selection semantics.

Also restore the NULL guard around the retran_path assignment. In the
all-UNCONFIRMED case there is no eligible replacement transport, and
installing NULL would leave later retransmit-path users and the debug
print with a NULL path.

Fixes: 4c47af4d5e ("net: sctp: rework multihoming retransmission path selection to rfc4960")
Signed-off-by: Yiqi Sun <sunyiqixm@gmail.com>
Acked-by: Xin Long <lucien.xin@gmail.com>
Link: https://patch.msgid.link/20260915095017.942213-1-sunyiqixm@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Yiqi Sun 2026-09-15 17:50:17 +08:00 committed by Jakub Kicinski
parent 6c01564da3
commit d2c31b8374

View File

@ -1289,18 +1289,19 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
/* Manually skip the head element. */
if (&trans->transports == &asoc->peer.transport_addr_list)
continue;
if (trans->state == SCTP_UNCONFIRMED)
continue;
trans_next = sctp_trans_elect_best(trans, trans_next);
/* Active is good enough for immediate return. */
if (trans_next->state == SCTP_ACTIVE)
break;
if (trans->state != SCTP_UNCONFIRMED) {
trans_next = sctp_trans_elect_best(trans, trans_next);
/* Active is good enough for immediate return. */
if (trans_next->state == SCTP_ACTIVE)
break;
}
/* We've reached the end, time to update path. */
if (trans == asoc->peer.retran_path)
break;
}
asoc->peer.retran_path = trans_next;
if (trans_next)
asoc->peer.retran_path = trans_next;
pr_debug("%s: association:%p updated new path to addr:%pISpc\n",
__func__, asoc, &asoc->peer.retran_path->ipaddr.sa);