mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
batman-adv: bla: fix report_work leak on backbone_gw purge
batadv_bla_purge_backbone_gw() removes stale backbone gateway entries,
but fails to properly handle their associated report_work:
- If report_work is running, the purge must wait for it to finish before
freeing the backbone_gw, otherwise the worker may access freed memory
(e.g. bat_priv).
- If report_work is pending, the purge must cancel it and release the
reference held for that pending work item.
The previous implementation called hlist_for_each_entry_safe() inside a
spin_lock_bh() section, but cancel_work_sync() may sleep and therefore
cannot be called from within a spinlock-protected region.
Restructure the loop to handle one entry per spinlock critical section:
acquire the lock, find the next entry to purge, remove it from the hash
list, then release the lock before calling cancel_work_sync() and
dropping the hash_entry reference. Repeat until no more entries require
purging.
Cc: stable@kernel.org
Fixes: 23721387c4 ("batman-adv: add basic bridge loop avoidance code")
Reviewed-by: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
This commit is contained in:
parent
aa3153bd13
commit
0459430add
|
|
@ -1224,6 +1224,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
|
|||
struct hlist_head *head;
|
||||
struct batadv_hashtable *hash;
|
||||
spinlock_t *list_lock; /* protects write access to the hash lists */
|
||||
bool purged;
|
||||
int i;
|
||||
|
||||
hash = bat_priv->bla.backbone_hash;
|
||||
|
|
@ -1234,30 +1235,45 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now)
|
|||
head = &hash->table[i];
|
||||
list_lock = &hash->list_locks[i];
|
||||
|
||||
spin_lock_bh(list_lock);
|
||||
hlist_for_each_entry_safe(backbone_gw, node_tmp,
|
||||
head, hash_entry) {
|
||||
if (now)
|
||||
goto purge_now;
|
||||
if (!batadv_has_timed_out(backbone_gw->lasttime,
|
||||
BATADV_BLA_BACKBONE_TIMEOUT))
|
||||
continue;
|
||||
do {
|
||||
purged = false;
|
||||
|
||||
batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv,
|
||||
"%s(): backbone gw %pM timed out\n",
|
||||
__func__, backbone_gw->orig);
|
||||
spin_lock_bh(list_lock);
|
||||
hlist_for_each_entry_safe(backbone_gw, node_tmp,
|
||||
head, hash_entry) {
|
||||
if (now)
|
||||
goto purge_now;
|
||||
if (!batadv_has_timed_out(backbone_gw->lasttime,
|
||||
BATADV_BLA_BACKBONE_TIMEOUT))
|
||||
continue;
|
||||
|
||||
batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv,
|
||||
"%s(): backbone gw %pM timed out\n",
|
||||
__func__, backbone_gw->orig);
|
||||
|
||||
purge_now:
|
||||
/* don't wait for the pending request anymore */
|
||||
if (atomic_read(&backbone_gw->request_sent))
|
||||
atomic_dec(&bat_priv->bla.num_requests);
|
||||
purged = true;
|
||||
|
||||
batadv_bla_del_backbone_claims(backbone_gw);
|
||||
/* don't wait for the pending request anymore */
|
||||
if (atomic_read(&backbone_gw->request_sent))
|
||||
atomic_dec(&bat_priv->bla.num_requests);
|
||||
|
||||
hlist_del_rcu(&backbone_gw->hash_entry);
|
||||
batadv_backbone_gw_put(backbone_gw);
|
||||
}
|
||||
spin_unlock_bh(list_lock);
|
||||
batadv_bla_del_backbone_claims(backbone_gw);
|
||||
|
||||
hlist_del_rcu(&backbone_gw->hash_entry);
|
||||
break;
|
||||
}
|
||||
spin_unlock_bh(list_lock);
|
||||
|
||||
if (purged) {
|
||||
/* reference for pending report_work */
|
||||
if (cancel_work_sync(&backbone_gw->report_work))
|
||||
batadv_backbone_gw_put(backbone_gw);
|
||||
|
||||
/* reference for hash_entry */
|
||||
batadv_backbone_gw_put(backbone_gw);
|
||||
}
|
||||
} while (purged);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user