Project

General

Profile

Actions

Bug #475

open

tt: Fix current event queuing guarentees for batadv_tt_local_remove_now()

Added by Sven Eckelmann 5 days ago. Updated 5 days ago.

Status:
New
Priority:
Normal
Target version:
-
Start date:
10/02/2026
Due date:
% Done:

0%

Estimated time:

Description

From
https://sashiko.dev/#/patchset/20260930094558.3723766-1-sw@simonwunderlich.de?part=
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260831135117.574836-1-sw%40simonwunderlich.de?part=8

> > diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
> > index 354d9416c1b75..c904d67791f8f 100644
> > --- a/net/batman-adv/translation-table.c
> > +++ b/net/batman-adv/translation-table.c
> [ ... ]
> > @@ -1496,8 +1496,6 @@ batadv_tt_local_remove_now(struct batadv_priv *bat_priv,
> >      struct batadv_tt_common_entry *common = &tt_local_entry->common;
> >      struct hlist_node *tt_removed_node;
> >  
> > -    batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL);
> > -
> >      /* remove exactly this object when still present in hash */
> >      tt_removed_node = batadv_hash_remove(bat_priv->tt.local_hash,
> >                           batadv_compare_tt_entry,
> > @@ -1505,6 +1503,8 @@ batadv_tt_local_remove_now(struct batadv_priv *bat_priv,
> >      if (!tt_removed_node)
> >          return;
> >  
> > +    batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL);
> > +
> 

Does moving this event queueing after the hash removal create a lockless
window where a concurrent ADD can be incorrectly cancelled by the delayed DEL
event?

If Thread A executes batadv_tt_local_remove_now() and calls
batadv_hash_remove(), the old entry is removed and the hash bucket lock is
dropped.

During the lockless window before Thread A queues its DEL event, if Thread B
executes batadv_tt_local_add() for the same MAC/VLAN, it would create a new
entry, insert it into the hash, and queue an ADD event in changes_list.

When Thread A resumes and calls batadv_tt_local_event(...,
BATADV_TT_CLIENT_DEL), could this delayed DEL event match the MAC/VLAN and
cancel out Thread B's newly queued ADD event?

This might leave Thread B's new entry in the local hash (with its CRC updated
locally), but delete its ADD event from the outgoing OGM diff, leading to
state desynchronization across the mesh network.

Could this regression be triggered by normal mesh network behavior, such as a
client rapidly reconnecting or flapping?

Note: This regression appears to survive the follow-up patches in the series.
Commit 'batman-adv: tt: queue local DEL event under bucket lock' only protects
batadv_tt_local_mark_removed(), leaving batadv_tt_local_remove_now()
unmodified and vulnerable to this race.


Related issues 2 (2 open — 0 closed)

Related to batman-adv - Bug #480: tt: prevent batadv_tt_local_remove_now concurrency problems with batadv_tt_local_commit_changes_nolock()In Progressbatman-adv developers10/02/2026

Actions
Related to batman-adv - Bug #482: tt: don't queue local events for unhashed entriesIn Progressbatman-adv developers10/02/2026

Actions
Actions #1

Updated by Sven Eckelmann 5 days ago

And the netdev AI complaint https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260930094558.3723766-2-sw%40simonwunderlich.de

> batadv_hash_remove() takes and releases the bucket list_lock before it
> returns. batadv_tt_local_event() only takes tt.changes_list_lock after
> that. Nothing seems to serialize this gap against batadv_tt_local_add()
> called from batadv_interface_tx() for the same MAC and VID.
> 
> The remove side can be reached from a remote TT announcement:
> 
> batadv_tt_global_add()
>   batadv_tt_global_purge_local()
>     batadv_tt_local_remove()
>       batadv_tt_local_remove_now()
> 
> Consider entry E, which is still NEW and has a queued add event:
> 
> CPU0 (remove path)
> batadv_hash_remove(E)
>   /* E unlinked, its add event still queued */
> 
> CPU1 (batadv_tt_local_add())
> batadv_tt_local_hash_find()      /* misses E */
> batadv_tt_local_create()         /* new entry E' */
> batadv_hash_add(E')
> batadv_tt_local_event(E', BATADV_NO_FLAGS)
>   /* same addr/vid as E's pending add, so only flags are updated */
> 
> CPU0
> batadv_tt_local_event(E, BATADV_TT_CLIENT_DEL)
>   /* matches the remaining add by addr/vid only */
>   if (del_op_requested != del_op_entry) {
>       list_del(&entry->list);
>       kmem_cache_free(batadv_tt_change_cache, entry);
>       changes--;
> 
> E' is then in tt.local_hash with BATADV_TT_CLIENT_NEW set and no add
> event queued. The commit message says this should be prevented.
> 
> Later packets from the client do not queue it again, because
> batadv_tt_local_add_existing() returns false for a NEW entry that is not
> PENDING.
> 
> If local_changes drops to 0, batadv_tt_local_commit_changes_nolock()
> returns early and E' stays unannounced. A later unrelated commit would
> move E' into the CRC through batadv_tt_local_transition_new() without an
> add diff. Neighbours would then see a CRC mismatch and fall back to full
> table requests.
> 
> Before this patch the delete event was queued while E was still in the
> hash. An add that ran after the unlink would then get a fresh add event
> that nothing cancels.
> 
> The later patches in the series don't seem to change this part of
> batadv_tt_local_remove_now().
> 
> Could the delete event instead be queued inside the same bucket list_lock
> critical section as the unlink, and only when this exact object was
> found? batadv_tt_local_set_pending() in "batman-adv: tt: queue local DEL
> event under bucket lock" already takes list_lock and then
> changes_list_lock in that order.

The only option I have in mind is also the one mentioned in the last section. But this will need more changes because the locking is done inside batadv_hash_remove and there is no code to allow handling the lock selection outside of this (or a similar) function.

Actions #2

Updated by Sven Eckelmann 5 days ago

  • Subject changed from Fix current event queuing guarentees for batadv_tt_local_remove_now() to tt: Fix current event queuing guarentees for batadv_tt_local_remove_now()
Actions #3

Updated by Sven Eckelmann 5 days ago

  • Related to Bug #480: tt: prevent batadv_tt_local_remove_now concurrency problems with batadv_tt_local_commit_changes_nolock() added
Actions #4

Updated by Sven Eckelmann 5 days ago

Actions #5

Updated by Sven Eckelmann 5 days ago

  • Related to Bug #482: tt: don't queue local events for unhashed entries added
Actions

Also available in: Atom PDF