]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
net/x25: Fix null-ptr-deref caused by x25_disconnect
authorDuoming Zhou <duoming@zju.edu.cn>
Sat, 26 Mar 2022 10:43:46 +0000 (18:43 +0800)
committerStefan Bader <stefan.bader@canonical.com>
Fri, 20 May 2022 13:19:15 +0000 (15:19 +0200)
BugLink: https://bugs.launchpad.net/bugs/1971497
[ Upstream commit 7781607938c8371d4c2b243527430241c62e39c2 ]

When the link layer is terminating, x25->neighbour will be set to NULL
in x25_disconnect(). As a result, it could cause null-ptr-deref bugs in
x25_sendmsg(),x25_recvmsg() and x25_connect(). One of the bugs is
shown below.

    (Thread 1)                 |  (Thread 2)
x25_link_terminated()          | x25_recvmsg()
 x25_kill_by_neigh()           |  ...
  x25_disconnect()             |  lock_sock(sk)
   ...                         |  ...
   x25->neighbour = NULL //(1) |
   ...                         |  x25->neighbour->extended //(2)

The code sets NULL to x25->neighbour in position (1) and dereferences
x25->neighbour in position (2), which could cause null-ptr-deref bug.

This patch adds lock_sock() in x25_kill_by_neigh() in order to synchronize
with x25_sendmsg(), x25_recvmsg() and x25_connect(). What`s more, the
sock held by lock_sock() is not NULL, because it is extracted from x25_list
and uses x25_list_lock to synchronize.

Fixes: 4becb7ee5b3d ("net/x25: Fix x25_neigh refcnt leak when x25 disconnect")
Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
Reviewed-by: Lin Ma <linma@zju.edu.cn>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Stefan Bader <stefan.bader@canonical.com>
net/x25/af_x25.c

index d8d603aa488754fe795329d6d58ebef48ddaf7de..c94aa587e0c92fadb7da256e32a0fab8a80034a1 100644 (file)
@@ -1767,10 +1767,15 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
 
        write_lock_bh(&x25_list_lock);
 
-       sk_for_each(s, &x25_list)
-               if (x25_sk(s)->neighbour == nb)
+       sk_for_each(s, &x25_list) {
+               if (x25_sk(s)->neighbour == nb) {
+                       write_unlock_bh(&x25_list_lock);
+                       lock_sock(s);
                        x25_disconnect(s, ENETUNREACH, 0, 0);
-
+                       release_sock(s);
+                       write_lock_bh(&x25_list_lock);
+               }
+       }
        write_unlock_bh(&x25_list_lock);
 
        /* Remove any related forwards */