Skip to content

Commit

Permalink
sctp: fix race on sctp_id2asoc
Browse files Browse the repository at this point in the history
syzbot reported an use-after-free involving sctp_id2asoc.  Dmitry Vyukov
helped to root cause it and it is because of reading the asoc after it
was freed:

        CPU 1                       CPU 2
(working on socket 1)            (working on socket 2)
	                         sctp_association_destroy
sctp_id2asoc
   spin lock
     grab the asoc from idr
   spin unlock
                                   spin lock
				     remove asoc from idr
				   spin unlock
				   free(asoc)
   if asoc->base.sk != sk ... [*]

This can only be hit if trying to fetch asocs from different sockets. As
we have a single IDR for all asocs, in all SCTP sockets, their id is
unique on the system. An application can try to send stuff on an id
that matches on another socket, and the if in [*] will protect from such
usage. But it didn't consider that as that asoc may belong to another
socket, it may be freed in parallel (read: under another socket lock).

We fix it by moving the checks in [*] into the protected region. This
fixes it because the asoc cannot be freed while the lock is held.

Reported-by: [email protected]
Acked-by: Dmitry Vyukov <[email protected]>
Signed-off-by: Marcelo Ricardo Leitner <[email protected]>
Acked-by: Neil Horman <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
marceloleitner authored and davem330 committed Oct 18, 2018
1 parent 9675931 commit b336dec
Showing 1 changed file with 2 additions and 3 deletions.
5 changes: 2 additions & 3 deletions net/sctp/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,10 @@ struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)

spin_lock_bh(&sctp_assocs_id_lock);
asoc = (struct sctp_association *)idr_find(&sctp_assocs_id, (int)id);
if (asoc && (asoc->base.sk != sk || asoc->base.dead))
asoc = NULL;
spin_unlock_bh(&sctp_assocs_id_lock);

if (!asoc || (asoc->base.sk != sk) || asoc->base.dead)
return NULL;

return asoc;
}

Expand Down

0 comments on commit b336dec

Please sign in to comment.