[PATCH net v2] sctp: fix ASCONF address change on single-homed hosts

From: Andrey Misiurov

Date: Thu Oct 29 2026 - 15:36:07 EST


Commit 9b2854f86f0b ("sctp: don't free the ASCONF's own transport in
DEL-IP processing") refuses a DEL-IP of the transport the ASCONF was
located by, so that a later wildcard DEL-IP cannot use it after free.

This also breaks the single-homed address change (RFC 5061 5.3.2). The
host sends [Address Parameter L] [ADD-IP S] [DEL-IP L], the association
is located by L, and DEL-IP L is now refused. The sender never gets an
all-success ASCONF-ACK, keeps src_out_of_asoc_ok set and stops sending
data, so the association stalls.

Fix the wildcard DEL-IP instead: look up the transport of the packet
source rather than reusing asconf->transport, and clear
asconf->transport when a plain DEL-IP removes it. The wildcard delete
can now free the old asconf->transport itself, so point it at the
surviving peer; otherwise a bundled chunk would reuse the freed
transport.

Found by manual code inspection while reviewing a backport of commit
9b2854f86f0b; reproduced on a two-namespace single-homed renumber.

Fixes: 9b2854f86f0b ("sctp: don't free the ASCONF's own transport in DEL-IP processing")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Andrey Misiurov <andrey.misyrov@xxxxxxxxx>
---
v2: the reworked wildcard DEL-IP branch could itself free
asconf->transport via sctp_assoc_del_nonprimary_peers() and leave it
dangling for a bundled DATA chunk (sctp_assoc_bh_rcv() ->
peer.last_data_from) and the following SACK; retarget
asconf->transport to the surviving peer. The pre-existing wildcard
SET_PRIMARY / sctp_source() point is left out of scope.
Reported by the netdev Sashiko AI review.
v1: https://lore.kernel.org/all/20261022163639.2566945-1-andrey.misyrov@xxxxxxxxx/

net/sctp/sm_make_chunk.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index ddc31a5..9719464 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3151,9 +3151,12 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
* packet MUST be deleted.
*/
if (af->is_any(&addr)) {
- sctp_assoc_set_primary(asoc, asconf->transport);
- sctp_assoc_del_nonprimary_peers(asoc,
- asconf->transport);
+ peer = sctp_assoc_lookup_paddr(asoc, &asconf->source);
+ if (!peer)
+ return SCTP_ERROR_REQ_REFUSED;
+ sctp_assoc_set_primary(asoc, peer);
+ sctp_assoc_del_nonprimary_peers(asoc, peer);
+ asconf->transport = peer;
return SCTP_ERROR_NO_ERROR;
}

@@ -3166,13 +3169,9 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
if (!peer)
return SCTP_ERROR_DNS_FAILED;

- /* Don't free asconf->transport; a later wildcard DEL-IP
- * parameter reuses it.
- */
- if (peer == asconf->transport)
- return SCTP_ERROR_REQ_REFUSED;
-
sctp_assoc_rm_peer(asoc, peer);
+ if (asconf->transport == peer)
+ asconf->transport = NULL;
break;
case SCTP_PARAM_SET_PRIMARY:
/* ADDIP Section 4.2.4