]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
ax25: Fix NULL pointer dereferences in ax25 timers
authorDuoming Zhou <duoming@zju.edu.cn>
Fri, 15 Apr 2022 16:14:21 +0000 (19:14 +0300)
committerStefan Bader <stefan.bader@canonical.com>
Fri, 20 May 2022 12:42:41 +0000 (14:42 +0200)
BugLink: https://bugs.launchpad.net/bugs/1969857
commit fc6d01ff9ef03b66d4a3a23b46fc3c3d8cf92009 upstream.

The previous commit 7ec02f5ac8a5 ("ax25: fix NPD bug in ax25_disconnect")
move ax25_disconnect into lock_sock() in order to prevent NPD bugs. But
there are race conditions that may lead to null pointer dereferences in
ax25_heartbeat_expiry(), ax25_t1timer_expiry(), ax25_t2timer_expiry(),
ax25_t3timer_expiry() and ax25_idletimer_expiry(), when we use
ax25_kill_by_device() to detach the ax25 device.

One of the race conditions that cause null pointer dereferences can be
shown as below:

      (Thread 1)                    |      (Thread 2)
ax25_connect()                      |
 ax25_std_establish_data_link()     |
  ax25_start_t1timer()              |
   mod_timer(&ax25->t1timer,..)     |
                                    | ax25_kill_by_device()
   (wait a time)                    |  ...
                                    |  s->ax25_dev = NULL; //(1)
   ax25_t1timer_expiry()            |
    ax25->ax25_dev->values[..] //(2)|  ...
     ...                            |

We set null to ax25_cb->ax25_dev in position (1) and dereference
the null pointer in position (2).

The corresponding fail log is shown below:
===============================================================
BUG: kernel NULL pointer dereference, address: 0000000000000050
CPU: 1 PID: 0 Comm: swapper/1 Not tainted 5.17.0-rc6-00794-g45690b7d0
RIP: 0010:ax25_t1timer_expiry+0x12/0x40
...
Call Trace:
 call_timer_fn+0x21/0x120
 __run_timers.part.0+0x1ca/0x250
 run_timer_softirq+0x2c/0x60
 __do_softirq+0xef/0x2f3
 irq_exit_rcu+0xb6/0x100
 sysvec_apic_timer_interrupt+0xa2/0xd0
...

This patch moves ax25_disconnect() before s->ax25_dev = NULL
and uses del_timer_sync() to delete timers in ax25_disconnect().
If ax25_disconnect() is called by ax25_kill_by_device() or
ax25->ax25_dev is NULL, the reason in ax25_disconnect() will be
equal to ENETUNREACH, it will wait all timers to stop before we
set null to s->ax25_dev in ax25_kill_by_device().

Fixes: 7ec02f5ac8a5 ("ax25: fix NPD bug in ax25_disconnect")
Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
Signed-off-by: David S. Miller <davem@davemloft.net>
[OP: backport to 5.15: adjust context]
Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Stefan Bader <stefan.bader@canonical.com>
net/ax25/af_ax25.c
net/ax25/ax25_subr.c

index df01f790a34c245b54dc4acf325ab25889082734..3116d8d1b5cfe30434c47405f02b20934f5fe888 100644 (file)
@@ -89,20 +89,20 @@ again:
                        sk = s->sk;
                        if (!sk) {
                                spin_unlock_bh(&ax25_list_lock);
-                               s->ax25_dev = NULL;
                                ax25_disconnect(s, ENETUNREACH);
+                               s->ax25_dev = NULL;
                                spin_lock_bh(&ax25_list_lock);
                                goto again;
                        }
                        sock_hold(sk);
                        spin_unlock_bh(&ax25_list_lock);
                        lock_sock(sk);
+                       ax25_disconnect(s, ENETUNREACH);
                        s->ax25_dev = NULL;
                        if (sk->sk_socket) {
                                dev_put(ax25_dev->dev);
                                ax25_dev_put(ax25_dev);
                        }
-                       ax25_disconnect(s, ENETUNREACH);
                        release_sock(sk);
                        spin_lock_bh(&ax25_list_lock);
                        sock_put(sk);
index 15ab812c4fe4b29cfdec023ddeadd7f45bd77e1a..3a476e4f6cd0b3f82836104c6119d99a7e795d6d 100644 (file)
@@ -261,12 +261,20 @@ void ax25_disconnect(ax25_cb *ax25, int reason)
 {
        ax25_clear_queues(ax25);
 
-       if (!ax25->sk || !sock_flag(ax25->sk, SOCK_DESTROY))
-               ax25_stop_heartbeat(ax25);
-       ax25_stop_t1timer(ax25);
-       ax25_stop_t2timer(ax25);
-       ax25_stop_t3timer(ax25);
-       ax25_stop_idletimer(ax25);
+       if (reason == ENETUNREACH) {
+               del_timer_sync(&ax25->timer);
+               del_timer_sync(&ax25->t1timer);
+               del_timer_sync(&ax25->t2timer);
+               del_timer_sync(&ax25->t3timer);
+               del_timer_sync(&ax25->idletimer);
+       } else {
+               if (!ax25->sk || !sock_flag(ax25->sk, SOCK_DESTROY))
+                       ax25_stop_heartbeat(ax25);
+               ax25_stop_t1timer(ax25);
+               ax25_stop_t2timer(ax25);
+               ax25_stop_t3timer(ax25);
+               ax25_stop_idletimer(ax25);
+       }
 
        ax25->state = AX25_STATE_0;