mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
batman-adv: tt: queue local DEL event under bucket lock
batadv_tt_local_remove() sets BATADV_TT_CLIENT_PENDING on an already
announced local entry and only afterwards queues the DEL change event. It
holds neither the hash bucket list_lock nor bat_priv->tt.commit_lock.
It can therefore be potentially interrupted in the middle:
CPU0 CPU1
batadv_tt_local_remove()
flags |= ..._PENDING;
batadv_tt_local_commit_changes()
..._purge_pending_clients()
hlist_del_rcu(&...->hash_entry);
batadv_tt_local_update_crc()
atomic_inc(&bat_priv->tt.vn);
batadv_tt_local_event()
/* DEL queued only now */
The client then disappears from the local table and from the CRC of the new
TTVN after batadv_tt_local_commit_changes() without a DEL change being
announced for it. Neighbours receiving the new CRC without previously
seeing the DEL will try to recover via a full table request.
Move the event into batadv_tt_local_mark_removed() and hold the bucket
list_lock of the entry around both the flag change and the DEL event to
avoid this scenario.
Fixes: 976b159b3c ("batman-adv: tt: use protected flag modifications")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831135117.574836-1-sw%40simonwunderlich.de?part=12
Signed-off-by: Sven Eckelmann <sven@narfation.org>
This commit is contained in:
parent
597c0f2f69
commit
f95b9f28a9
|
|
@ -1427,13 +1427,20 @@ int batadv_tt_local_dump(struct sk_buff *msg, struct netlink_callback *cb)
|
|||
* @message: debug message describing the reason for the change
|
||||
*
|
||||
* Schedule the TT change announcement for the entry. The caller must already
|
||||
* have added BATADV_TT_CLIENT_PENDING to the @tt_local_entry
|
||||
* have added BATADV_TT_CLIENT_PENDING to the @tt_local_entry and must hold the
|
||||
* hash bucket list_lock of @tt_local_entry since setting the flag.
|
||||
*/
|
||||
static void
|
||||
batadv_tt_local_set_pending_event(struct batadv_priv *bat_priv,
|
||||
struct batadv_tt_local_entry *tt_local_entry,
|
||||
u16 flags, const char *message)
|
||||
{
|
||||
struct batadv_hashtable *hash = bat_priv->tt.local_hash;
|
||||
u32 i;
|
||||
|
||||
i = batadv_choose_tt(&tt_local_entry->common, hash->size);
|
||||
lockdep_assert_held(&hash->list_locks[i]);
|
||||
|
||||
batadv_tt_local_event(bat_priv, tt_local_entry, flags);
|
||||
|
||||
batadv_dbg(BATADV_DBG_TT, bat_priv,
|
||||
|
|
@ -1443,20 +1450,37 @@ batadv_tt_local_set_pending_event(struct batadv_priv *bat_priv,
|
|||
}
|
||||
|
||||
/**
|
||||
* batadv_tt_local_mark_removed() - mark a local entry as removed
|
||||
* batadv_tt_local_mark_removed() - mark a local entry as removed and queue DEL
|
||||
* @bat_priv: the bat priv with all the mesh interface information
|
||||
* @tt_local_entry: local TT entry to mark
|
||||
* @message: message to append to the log on deletion
|
||||
* @roaming: true if the deletion is due to a roaming event
|
||||
* @curr_flags: pointer to store the flags of the entry before it was marked
|
||||
*
|
||||
* An already announced entry is marked as BATADV_TT_CLIENT_PENDING and the
|
||||
* (roamed) DEL change is queued. Both happen under the hash bucket list_lock
|
||||
* of the entry to prevent concurrent batadv_tt_local_purge_pending_clients()
|
||||
* from removing the entry.
|
||||
*
|
||||
* Return: true if the entry has to be kept in the local table until the next
|
||||
* ttvn increment, false if it can be purged immediately.
|
||||
*/
|
||||
static bool
|
||||
batadv_tt_local_mark_removed(struct batadv_tt_local_entry *tt_local_entry,
|
||||
bool roaming, u16 *curr_flags)
|
||||
batadv_tt_local_mark_removed(struct batadv_priv *bat_priv,
|
||||
struct batadv_tt_local_entry *tt_local_entry,
|
||||
const char *message, bool roaming, u16 *curr_flags)
|
||||
{
|
||||
spinlock_t *list_lock; /* protects write access to the hash lists */
|
||||
struct batadv_tt_common_entry *common = &tt_local_entry->common;
|
||||
struct batadv_hashtable *hash = bat_priv->tt.local_hash;
|
||||
bool pending = false;
|
||||
u16 flags;
|
||||
u32 i;
|
||||
|
||||
i = batadv_choose_tt(common, hash->size);
|
||||
list_lock = &hash->list_locks[i];
|
||||
|
||||
spin_lock_bh(list_lock);
|
||||
|
||||
scoped_guard(spinlock_bh, &common->flags_lock) {
|
||||
*curr_flags = common->flags;
|
||||
|
|
@ -1474,6 +1498,17 @@ batadv_tt_local_mark_removed(struct batadv_tt_local_entry *tt_local_entry,
|
|||
}
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
flags = BATADV_TT_CLIENT_DEL;
|
||||
if (roaming)
|
||||
flags |= BATADV_TT_CLIENT_ROAM;
|
||||
|
||||
batadv_tt_local_set_pending_event(bat_priv, tt_local_entry,
|
||||
flags, message);
|
||||
}
|
||||
|
||||
spin_unlock_bh(list_lock);
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
|
|
@ -1532,28 +1567,17 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
|
|||
{
|
||||
struct batadv_tt_local_entry *tt_local_entry;
|
||||
u16 curr_flags;
|
||||
u16 flags;
|
||||
|
||||
tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
|
||||
if (!tt_local_entry)
|
||||
return BATADV_NO_FLAGS;
|
||||
|
||||
if (batadv_tt_local_mark_removed(tt_local_entry, roaming, &curr_flags)) {
|
||||
/* queue (roamed) del event which was prepared by
|
||||
* batadv_tt_local_mark_removed()
|
||||
*/
|
||||
flags = BATADV_TT_CLIENT_DEL;
|
||||
if (roaming)
|
||||
flags |= BATADV_TT_CLIENT_ROAM;
|
||||
|
||||
batadv_tt_local_set_pending_event(bat_priv, tt_local_entry,
|
||||
flags, message);
|
||||
} else {
|
||||
/* if this client has been added right now, it is possible to
|
||||
* immediately purge it
|
||||
*/
|
||||
/* if this client has been added right now, it is possible to
|
||||
* immediately purge it
|
||||
*/
|
||||
if (!batadv_tt_local_mark_removed(bat_priv, tt_local_entry, message,
|
||||
roaming, &curr_flags))
|
||||
batadv_tt_local_remove_now(bat_priv, tt_local_entry);
|
||||
}
|
||||
|
||||
batadv_tt_local_entry_put(tt_local_entry);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user