net: arcnet: com20020 fix error handling

There are two issues when handling error case in com20020pci_probe()

1. priv might be not initialized yet when calling com20020pci_remove()
from com20020pci_probe(), since the priv is set at the very last but it
can jump to error handling in the middle and priv remains NULL.
2. memory leak - the net device is allocated in alloc_arcdev but not
properly released if error happens in the middle of the big for loop

[    1.529110] BUG: kernel NULL pointer dereference, address: 0000000000000008
[    1.531447] RIP: 0010:com20020pci_remove+0x15/0x60 [com20020_pci]
[    1.536805] Call Trace:
[    1.536939]  com20020pci_probe+0x3f2/0x48c [com20020_pci]
[    1.537226]  local_pci_probe+0x48/0x80
[    1.539918]  com20020pci_init+0x3f/0x1000 [com20020_pci]

Signed-off-by: Tong Zhang <ztong0001@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Tong Zhang 2021-03-14 14:08:36 -04:00 коммит произвёл David S. Miller
Родитель ad236ccde1
Коммит 6577b9a551
1 изменённых файлов: 19 добавлений и 15 удалений

Просмотреть файл

@ -127,6 +127,8 @@ static int com20020pci_probe(struct pci_dev *pdev,
int i, ioaddr, ret;
struct resource *r;
ret = 0;
if (pci_enable_device(pdev))
return -EIO;
@ -139,6 +141,8 @@ static int com20020pci_probe(struct pci_dev *pdev,
priv->ci = ci;
mm = &ci->misc_map;
pci_set_drvdata(pdev, priv);
INIT_LIST_HEAD(&priv->list_dev);
if (mm->size) {
@ -161,7 +165,7 @@ static int com20020pci_probe(struct pci_dev *pdev,
dev = alloc_arcdev(device);
if (!dev) {
ret = -ENOMEM;
goto out_port;
break;
}
dev->dev_port = i;
@ -178,7 +182,7 @@ static int com20020pci_probe(struct pci_dev *pdev,
pr_err("IO region %xh-%xh already allocated\n",
ioaddr, ioaddr + cm->size - 1);
ret = -EBUSY;
goto out_port;
goto err_free_arcdev;
}
/* Dummy access after Reset
@ -216,18 +220,18 @@ static int com20020pci_probe(struct pci_dev *pdev,
if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) {
pr_err("IO address %Xh is empty!\n", ioaddr);
ret = -EIO;
goto out_port;
goto err_free_arcdev;
}
if (com20020_check(dev)) {
ret = -EIO;
goto out_port;
goto err_free_arcdev;
}
card = devm_kzalloc(&pdev->dev, sizeof(struct com20020_dev),
GFP_KERNEL);
if (!card) {
ret = -ENOMEM;
goto out_port;
goto err_free_arcdev;
}
card->index = i;
@ -253,29 +257,29 @@ static int com20020pci_probe(struct pci_dev *pdev,
ret = devm_led_classdev_register(&pdev->dev, &card->tx_led);
if (ret)
goto out_port;
goto err_free_arcdev;
ret = devm_led_classdev_register(&pdev->dev, &card->recon_led);
if (ret)
goto out_port;
goto err_free_arcdev;
dev_set_drvdata(&dev->dev, card);
ret = com20020_found(dev, IRQF_SHARED);
if (ret)
goto out_port;
goto err_free_arcdev;
devm_arcnet_led_init(dev, dev->dev_id, i);
list_add(&card->list, &priv->list_dev);
continue;
err_free_arcdev:
free_arcdev(dev);
break;
}
pci_set_drvdata(pdev, priv);
return 0;
out_port:
com20020pci_remove(pdev);
if (ret)
com20020pci_remove(pdev);
return ret;
}