Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sfc: fix crash when reading stats while NIC is resetting

efx_net_stats() (.ndo_get_stats64) can be called during an ethtool
selftest, during which time nic_data->mc_stats is NULL as the NIC has
been fini'd. In this case do not attempt to fetch the latest stats
from the hardware, else we will crash on a NULL dereference:
BUG: kernel NULL pointer dereference, address: 0000000000000038
RIP efx_nic_update_stats
abridged calltrace:
efx_ef10_update_stats_pf
efx_net_stats
dev_get_stats
dev_seq_printf_stats
Skipping the read is safe, we will simply give out stale stats.
To ensure that the free in efx_ef10_fini_nic() does not race against
efx_ef10_update_stats_pf(), which could cause a TOCTTOU bug, take the
efx->stats_lock in fini_nic (it is already held across update_stats).

Fixes: d3142c193dca ("sfc: refactor EF10 stats handling")
Reviewed-by: Pieter Jansen van Vuuren <pieter.jansen-van-vuuren@amd.com>
Signed-off-by: Edward Cree <ecree.xilinx@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Edward Cree and committed by
David S. Miller
d1b35543 f1bc9fc4

+10 -3
+10 -3
drivers/net/ethernet/sfc/ef10.c
··· 1297 1297 { 1298 1298 struct efx_ef10_nic_data *nic_data = efx->nic_data; 1299 1299 1300 + spin_lock_bh(&efx->stats_lock); 1300 1301 kfree(nic_data->mc_stats); 1301 1302 nic_data->mc_stats = NULL; 1303 + spin_unlock_bh(&efx->stats_lock); 1302 1304 } 1303 1305 1304 1306 static int efx_ef10_init_nic(struct efx_nic *efx) ··· 1854 1852 1855 1853 efx_ef10_get_stat_mask(efx, mask); 1856 1854 1857 - efx_nic_copy_stats(efx, nic_data->mc_stats); 1858 - efx_nic_update_stats(efx_ef10_stat_desc, EF10_STAT_COUNT, 1859 - mask, stats, nic_data->mc_stats, false); 1855 + /* If NIC was fini'd (probably resetting), then we can't read 1856 + * updated stats right now. 1857 + */ 1858 + if (nic_data->mc_stats) { 1859 + efx_nic_copy_stats(efx, nic_data->mc_stats); 1860 + efx_nic_update_stats(efx_ef10_stat_desc, EF10_STAT_COUNT, 1861 + mask, stats, nic_data->mc_stats, false); 1862 + } 1860 1863 1861 1864 /* Update derived statistics */ 1862 1865 efx_nic_fix_nodesc_drop_stat(efx,