netvsc: fix use after free on module removal
The NAPI data structure is embedded in the netvsc_device structure and is freed when device is closed. There is still a reference (in NAPI list) to this which causes a crash in netif_napi_del when device is removed. Fix by managing NAPI instances correctly. Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
dfb05553a5
commit
76bb5db5c7
|
@ -584,8 +584,9 @@ void netvsc_device_remove(struct hv_device *device)
|
||||||
/* Now, we can close the channel safely */
|
/* Now, we can close the channel safely */
|
||||||
vmbus_close(device->channel);
|
vmbus_close(device->channel);
|
||||||
|
|
||||||
|
/* And dissassociate NAPI context from device */
|
||||||
for (i = 0; i < net_device->num_chn; i++)
|
for (i = 0; i < net_device->num_chn; i++)
|
||||||
napi_disable(&net_device->chan_table[i].napi);
|
netif_napi_del(&net_device->chan_table[i].napi);
|
||||||
|
|
||||||
/* Release all resources */
|
/* Release all resources */
|
||||||
free_netvsc_device_rcu(net_device);
|
free_netvsc_device_rcu(net_device);
|
||||||
|
@ -1320,8 +1321,6 @@ int netvsc_device_add(struct hv_device *device,
|
||||||
struct netvsc_channel *nvchan = &net_device->chan_table[i];
|
struct netvsc_channel *nvchan = &net_device->chan_table[i];
|
||||||
|
|
||||||
nvchan->channel = device->channel;
|
nvchan->channel = device->channel;
|
||||||
netif_napi_add(ndev, &nvchan->napi,
|
|
||||||
netvsc_poll, NAPI_POLL_WEIGHT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open the channel */
|
/* Open the channel */
|
||||||
|
@ -1339,6 +1338,8 @@ int netvsc_device_add(struct hv_device *device,
|
||||||
netdev_dbg(ndev, "hv_netvsc channel opened successfully\n");
|
netdev_dbg(ndev, "hv_netvsc channel opened successfully\n");
|
||||||
|
|
||||||
/* Enable NAPI handler for init callbacks */
|
/* Enable NAPI handler for init callbacks */
|
||||||
|
netif_napi_add(ndev, &net_device->chan_table[0].napi,
|
||||||
|
netvsc_poll, NAPI_POLL_WEIGHT);
|
||||||
napi_enable(&net_device->chan_table[0].napi);
|
napi_enable(&net_device->chan_table[0].napi);
|
||||||
|
|
||||||
/* Writing nvdev pointer unlocks netvsc_send(), make sure chn_table is
|
/* Writing nvdev pointer unlocks netvsc_send(), make sure chn_table is
|
||||||
|
@ -1357,7 +1358,7 @@ int netvsc_device_add(struct hv_device *device,
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
close:
|
close:
|
||||||
napi_disable(&net_device->chan_table[0].napi);
|
netif_napi_del(&net_device->chan_table[0].napi);
|
||||||
|
|
||||||
/* Now, we can close the channel safely */
|
/* Now, we can close the channel safely */
|
||||||
vmbus_close(device->channel);
|
vmbus_close(device->channel);
|
||||||
|
|
|
@ -1009,13 +1009,16 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc)
|
||||||
|
|
||||||
/* Set the channel before opening.*/
|
/* Set the channel before opening.*/
|
||||||
nvchan->channel = new_sc;
|
nvchan->channel = new_sc;
|
||||||
|
netif_napi_add(ndev, &nvchan->napi,
|
||||||
|
netvsc_poll, NAPI_POLL_WEIGHT);
|
||||||
|
|
||||||
ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE,
|
ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE,
|
||||||
nvscdev->ring_size * PAGE_SIZE, NULL, 0,
|
nvscdev->ring_size * PAGE_SIZE, NULL, 0,
|
||||||
netvsc_channel_cb, nvchan);
|
netvsc_channel_cb, nvchan);
|
||||||
|
if (ret == 0)
|
||||||
|
napi_enable(&nvchan->napi);
|
||||||
napi_enable(&nvchan->napi);
|
else
|
||||||
|
netdev_err(ndev, "sub channel open failed (%d)\n", ret);
|
||||||
|
|
||||||
if (refcount_dec_and_test(&nvscdev->sc_offered))
|
if (refcount_dec_and_test(&nvscdev->sc_offered))
|
||||||
complete(&nvscdev->channel_init_wait);
|
complete(&nvscdev->channel_init_wait);
|
||||||
|
|
Loading…
Reference in New Issue