Commit 6577b9a5 authored by Tong Zhang's avatar Tong Zhang Committed by David S. Miller

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: default avatarTong Zhang <ztong0001@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ad236ccd
...@@ -127,6 +127,8 @@ static int com20020pci_probe(struct pci_dev *pdev, ...@@ -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;
...@@ -139,6 +141,8 @@ static int com20020pci_probe(struct pci_dev *pdev, ...@@ -139,6 +141,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) {
...@@ -161,7 +165,7 @@ static int com20020pci_probe(struct pci_dev *pdev, ...@@ -161,7 +165,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;
...@@ -178,7 +182,7 @@ static int com20020pci_probe(struct pci_dev *pdev, ...@@ -178,7 +182,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
...@@ -216,18 +220,18 @@ static int com20020pci_probe(struct pci_dev *pdev, ...@@ -216,18 +220,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;
...@@ -253,29 +257,29 @@ static int com20020pci_probe(struct pci_dev *pdev, ...@@ -253,29 +257,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;
pci_set_drvdata(pdev, priv); err_free_arcdev:
free_arcdev(dev);
return 0; break;
}
out_port: if (ret)
com20020pci_remove(pdev); com20020pci_remove(pdev);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment