net: arcnet: com20020 fix error handling
[ Upstream commit 6577b9a551aedb86bca6d4438c28386361845108 ] 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> Stable-dep-of: 6b17a597fc2f ("arcnet: restoring support for multiple Sohard Arcnet cards") Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
bd99f8aef0
commit
bb40edc88d
1 changed files with 19 additions and 15 deletions
|
@ -127,6 +127,8 @@ static int com20020pci_probe(struct pci_dev *pdev,
|
||||||
int i, ioaddr, ret;
|
int i, ioaddr, ret;
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
if (pci_enable_device(pdev))
|
if (pci_enable_device(pdev))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
|
@ -142,6 +144,8 @@ static int com20020pci_probe(struct pci_dev *pdev,
|
||||||
priv->ci = ci;
|
priv->ci = ci;
|
||||||
mm = &ci->misc_map;
|
mm = &ci->misc_map;
|
||||||
|
|
||||||
|
pci_set_drvdata(pdev, priv);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&priv->list_dev);
|
INIT_LIST_HEAD(&priv->list_dev);
|
||||||
|
|
||||||
if (mm->size) {
|
if (mm->size) {
|
||||||
|
@ -164,7 +168,7 @@ static int com20020pci_probe(struct pci_dev *pdev,
|
||||||
dev = alloc_arcdev(device);
|
dev = alloc_arcdev(device);
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out_port;
|
break;
|
||||||
}
|
}
|
||||||
dev->dev_port = i;
|
dev->dev_port = i;
|
||||||
|
|
||||||
|
@ -181,7 +185,7 @@ static int com20020pci_probe(struct pci_dev *pdev,
|
||||||
pr_err("IO region %xh-%xh already allocated\n",
|
pr_err("IO region %xh-%xh already allocated\n",
|
||||||
ioaddr, ioaddr + cm->size - 1);
|
ioaddr, ioaddr + cm->size - 1);
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto out_port;
|
goto err_free_arcdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dummy access after Reset
|
/* Dummy access after Reset
|
||||||
|
@ -219,18 +223,18 @@ static int com20020pci_probe(struct pci_dev *pdev,
|
||||||
if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) {
|
if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) {
|
||||||
pr_err("IO address %Xh is empty!\n", ioaddr);
|
pr_err("IO address %Xh is empty!\n", ioaddr);
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto out_port;
|
goto err_free_arcdev;
|
||||||
}
|
}
|
||||||
if (com20020_check(dev)) {
|
if (com20020_check(dev)) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto out_port;
|
goto err_free_arcdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
card = devm_kzalloc(&pdev->dev, sizeof(struct com20020_dev),
|
card = devm_kzalloc(&pdev->dev, sizeof(struct com20020_dev),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!card) {
|
if (!card) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out_port;
|
goto err_free_arcdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
card->index = i;
|
card->index = i;
|
||||||
|
@ -256,29 +260,29 @@ static int com20020pci_probe(struct pci_dev *pdev,
|
||||||
|
|
||||||
ret = devm_led_classdev_register(&pdev->dev, &card->tx_led);
|
ret = devm_led_classdev_register(&pdev->dev, &card->tx_led);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_port;
|
goto err_free_arcdev;
|
||||||
|
|
||||||
ret = devm_led_classdev_register(&pdev->dev, &card->recon_led);
|
ret = devm_led_classdev_register(&pdev->dev, &card->recon_led);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_port;
|
goto err_free_arcdev;
|
||||||
|
|
||||||
dev_set_drvdata(&dev->dev, card);
|
dev_set_drvdata(&dev->dev, card);
|
||||||
|
|
||||||
ret = com20020_found(dev, IRQF_SHARED);
|
ret = com20020_found(dev, IRQF_SHARED);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_port;
|
goto err_free_arcdev;
|
||||||
|
|
||||||
devm_arcnet_led_init(dev, dev->dev_id, i);
|
devm_arcnet_led_init(dev, dev->dev_id, i);
|
||||||
|
|
||||||
list_add(&card->list, &priv->list_dev);
|
list_add(&card->list, &priv->list_dev);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
err_free_arcdev:
|
||||||
|
free_arcdev(dev);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
if (ret)
|
||||||
pci_set_drvdata(pdev, priv);
|
com20020pci_remove(pdev);
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out_port:
|
|
||||||
com20020pci_remove(pdev);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue