can: peak_pci: peak_pci_remove(): fix UAF
authorZheyu Ma <zheyuma97@gmail.com>
Thu, 14 Oct 2021 06:28:33 +0000 (06:28 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 27 Oct 2021 07:34:00 +0000 (09:34 +0200)
commit 949fe9b35570361bc6ee2652f89a0561b26eec98 upstream.

When remove the module peek_pci, referencing 'chan' again after
releasing 'dev' will cause UAF.

Fix this by releasing 'dev' later.

The following log reveals it:

[   35.961814 ] BUG: KASAN: use-after-free in peak_pci_remove+0x16f/0x270 [peak_pci]
[   35.963414 ] Read of size 8 at addr ffff888136998ee8 by task modprobe/5537
[   35.965513 ] Call Trace:
[   35.965718 ]  dump_stack_lvl+0xa8/0xd1
[   35.966028 ]  print_address_description+0x87/0x3b0
[   35.966420 ]  kasan_report+0x172/0x1c0
[   35.966725 ]  ? peak_pci_remove+0x16f/0x270 [peak_pci]
[   35.967137 ]  ? trace_irq_enable_rcuidle+0x10/0x170
[   35.967529 ]  ? peak_pci_remove+0x16f/0x270 [peak_pci]
[   35.967945 ]  __asan_report_load8_noabort+0x14/0x20
[   35.968346 ]  peak_pci_remove+0x16f/0x270 [peak_pci]
[   35.968752 ]  pci_device_remove+0xa9/0x250

Fixes: e6d9c80b7ca1 ("can: peak_pci: add support of some new PEAK-System PCI cards")
Link: https://lore.kernel.org/all/1634192913-15639-1-git-send-email-zheyuma97@gmail.com
Cc: stable@vger.kernel.org
Signed-off-by: Zheyu Ma <zheyuma97@gmail.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/can/sja1000/peak_pci.c

index 131026fbc2d77cbc3ccb5903daa10f8920f8ae17..e12fc5d88382da0c32a45046322b8b970de77806 100644 (file)
@@ -736,16 +736,15 @@ static void peak_pci_remove(struct pci_dev *pdev)
                struct net_device *prev_dev = chan->prev_dev;
 
                dev_info(&pdev->dev, "removing device %s\n", dev->name);
+               /* do that only for first channel */
+               if (!prev_dev && chan->pciec_card)
+                       peak_pciec_remove(chan->pciec_card);
                unregister_sja1000dev(dev);
                free_sja1000dev(dev);
                dev = prev_dev;
 
-               if (!dev) {
-                       /* do that only for first channel */
-                       if (chan->pciec_card)
-                               peak_pciec_remove(chan->pciec_card);
+               if (!dev)
                        break;
-               }
                priv = netdev_priv(dev);
                chan = priv->priv;
        }