sctp: clear the transport of some out_chunk_list chunks in sctp_assoc_rm_peer
authorXin Long <lucien.xin@gmail.com>
Mon, 29 Oct 2018 15:10:29 +0000 (23:10 +0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 30 Oct 2018 03:49:44 +0000 (20:49 -0700)
If a transport is removed by asconf but there still are some chunks with
this transport queuing on out_chunk_list, later an use-after-free issue
will be caused when accessing this transport from these chunks in
sctp_outq_flush().

This is an old bug, we fix it by clearing the transport of these chunks
in out_chunk_list when removing a transport in sctp_assoc_rm_peer().

Reported-by: syzbot+56a40ceee5fb35932f4d@syzkaller.appspotmail.com
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/sctp/associola.c

index a827a1f562bf323d03cd5e70ffce67da53401a61..6a28b96e779e68d5259138e11da17c1216e18b71 100644 (file)
@@ -499,8 +499,9 @@ void sctp_assoc_set_primary(struct sctp_association *asoc,
 void sctp_assoc_rm_peer(struct sctp_association *asoc,
                        struct sctp_transport *peer)
 {
-       struct list_head        *pos;
-       struct sctp_transport   *transport;
+       struct sctp_transport *transport;
+       struct list_head *pos;
+       struct sctp_chunk *ch;
 
        pr_debug("%s: association:%p addr:%pISpc\n",
                 __func__, asoc, &peer->ipaddr.sa);
@@ -564,7 +565,6 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
         */
        if (!list_empty(&peer->transmitted)) {
                struct sctp_transport *active = asoc->peer.active_path;
-               struct sctp_chunk *ch;
 
                /* Reset the transport of each chunk on this list */
                list_for_each_entry(ch, &peer->transmitted,
@@ -586,6 +586,10 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
                                sctp_transport_hold(active);
        }
 
+       list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list)
+               if (ch->transport == peer)
+                       ch->transport = NULL;
+
        asoc->peer.transport_count--;
 
        sctp_transport_free(peer);