summaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/sfc/ef10.c
diff options
context:
space:
mode:
authorEdward Cree2015-05-20 12:10:03 +0200
committerDavid S. Miller2015-05-22 00:43:53 +0200
commit0d322413d6cff0bd2ccafc03ab9314dc55417e9d (patch)
treed988fe63fd837ce5127c25f6a517aae0e33d9494 /drivers/net/ethernet/sfc/ef10.c
parentsfc: Store the efx_nic struct of the current VF in the VF data struct (diff)
downloadkernel-qcow2-linux-0d322413d6cff0bd2ccafc03ab9314dc55417e9d.tar.gz
kernel-qcow2-linux-0d322413d6cff0bd2ccafc03ab9314dc55417e9d.tar.xz
kernel-qcow2-linux-0d322413d6cff0bd2ccafc03ab9314dc55417e9d.zip
sfc: protect filter table against use-after-free
If MCDI timeouts are encountered during efx_ef10_filter_table_remove(), an FLR will be queued, but efx->filter_state will still be kfree()d. The queued FLR will then call efx_ef10_filter_table_restore(), which will try to use efx->filter_state. This previously caused a panic. This patch adds an rwsem to protect the existence of efx->filter_state, separately from the spinlock protecting its contents. Users which can race against efx_ef10_filter_table_remove() should down_read this rwsem. Signed-off-by: Shradha Shah <sshah@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/sfc/ef10.c')
-rw-r--r--drivers/net/ethernet/sfc/ef10.c19
1 files changed, 19 insertions, 0 deletions
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index 78d3236a3d67..9e2e8e14313b 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -3314,6 +3314,9 @@ fail:
return rc;
}
+/* Caller must hold efx->filter_sem for read if race against
+ * efx_ef10_filter_table_remove() is possible
+ */
static void efx_ef10_filter_table_restore(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
@@ -3323,9 +3326,14 @@ static void efx_ef10_filter_table_restore(struct efx_nic *efx)
bool failed = false;
int rc;
+ WARN_ON(!rwsem_is_locked(&efx->filter_sem));
+
if (!nic_data->must_restore_filters)
return;
+ if (!table)
+ return;
+
spin_lock_bh(&efx->filter_lock);
for (filter_idx = 0; filter_idx < HUNT_FILTER_TBL_ROWS; filter_idx++) {
@@ -3361,6 +3369,7 @@ static void efx_ef10_filter_table_restore(struct efx_nic *efx)
nic_data->must_restore_filters = false;
}
+/* Caller must hold efx->filter_sem for write */
static void efx_ef10_filter_table_remove(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
@@ -3369,6 +3378,10 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx)
unsigned int filter_idx;
int rc;
+ efx->filter_state = NULL;
+ if (!table)
+ return;
+
for (filter_idx = 0; filter_idx < HUNT_FILTER_TBL_ROWS; filter_idx++) {
spec = efx_ef10_filter_entry_spec(table, filter_idx);
if (!spec)
@@ -3394,6 +3407,9 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx)
kfree(table);
}
+/* Caller must hold efx->filter_sem for read if race against
+ * efx_ef10_filter_table_remove() is possible
+ */
static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
@@ -3408,6 +3424,9 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx)
if (!efx_dev_registered(efx))
return;
+ if (!table)
+ return;
+
/* Mark old filters that may need to be removed */
spin_lock_bh(&efx->filter_lock);
n = table->dev_uc_count < 0 ? 1 : table->dev_uc_count;