Commit 3fc982f0 authored by Sven Eckelmann's avatar Sven Eckelmann Committed by Kelsey Skunberg

batman-adv: Fix reference counting of vlan object for tt_local_entry

BugLink: https://bugs.launchpad.net/bugs/1868629

commit a33d970d upstream.

The batadv_tt_local_entry was specific to a batadv_softif_vlan and held an
implicit reference to it. But this reference was never stored in form of a
pointer in the tt_local_entry itself. Instead batadv_tt_local_remove,
batadv_tt_local_table_free and batadv_tt_local_purge_pending_clients depend
on a consistent state of bat_priv->softif_vlan_list and that
batadv_softif_vlan_get always returns the batadv_softif_vlan object which
it has a reference for. But batadv_softif_vlan_get cannot guarantee that
because it is working only with rcu_read_lock on this list. It can
therefore happen that an vid is in this list twice or that
batadv_softif_vlan_get cannot find the batadv_softif_vlan for an vid due to
some other list operations taking place at the same time.

Instead add a batadv_softif_vlan pointer directly in batadv_tt_local_entry
which will be used for the reference counter decremented on release of
batadv_tt_local_entry.

Fixes: 35df3b29 ("batman-adv: fix TT VLAN inconsistency on VLAN re-add")
Signed-off-by: default avatarSven Eckelmann <sven@narfation.org>
Acked-by: default avatarAntonio Quartulli <a@unstable.cc>
Signed-off-by: default avatarMarek Lindner <mareklindner@neomailbox.ch>
Signed-off-by: default avatarAntonio Quartulli <a@unstable.cc>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarKhalid Elmously <khalid.elmously@canonical.com>
Signed-off-by: default avatarKelsey Skunberg <kelsey.skunberg@canonical.com>
parent eb38dc88
...@@ -197,8 +197,11 @@ batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr, ...@@ -197,8 +197,11 @@ batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
static void static void
batadv_tt_local_entry_free_ref(struct batadv_tt_local_entry *tt_local_entry) batadv_tt_local_entry_free_ref(struct batadv_tt_local_entry *tt_local_entry)
{ {
if (atomic_dec_and_test(&tt_local_entry->common.refcount)) if (atomic_dec_and_test(&tt_local_entry->common.refcount)) {
batadv_softif_vlan_free_ref(tt_local_entry->vlan);
kfree_rcu(tt_local_entry, common.rcu); kfree_rcu(tt_local_entry, common.rcu);
}
} }
/** /**
...@@ -638,7 +641,6 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr, ...@@ -638,7 +641,6 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
if (unlikely(hash_added != 0)) { if (unlikely(hash_added != 0)) {
/* remove the reference for the hash */ /* remove the reference for the hash */
batadv_tt_local_entry_free_ref(tt_local); batadv_tt_local_entry_free_ref(tt_local);
batadv_softif_vlan_free_ref(vlan);
goto out; goto out;
} }
...@@ -942,7 +944,6 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) ...@@ -942,7 +944,6 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
struct batadv_tt_common_entry *tt_common_entry; struct batadv_tt_common_entry *tt_common_entry;
struct batadv_tt_local_entry *tt_local; struct batadv_tt_local_entry *tt_local;
struct batadv_hard_iface *primary_if; struct batadv_hard_iface *primary_if;
struct batadv_softif_vlan *vlan;
struct hlist_head *head; struct hlist_head *head;
unsigned short vid; unsigned short vid;
u32 i; u32 i;
...@@ -979,13 +980,6 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) ...@@ -979,13 +980,6 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
no_purge = tt_common_entry->flags & np_flag; no_purge = tt_common_entry->flags & np_flag;
vlan = batadv_softif_vlan_get(bat_priv, vid);
if (!vlan) {
seq_printf(seq, "Cannot retrieve VLAN %d\n",
BATADV_PRINT_VID(vid));
continue;
}
seq_printf(seq, seq_printf(seq,
" * %pM %4i [%c%c%c%c%c%c] %3u.%03u (%#.8x)\n", " * %pM %4i [%c%c%c%c%c%c] %3u.%03u (%#.8x)\n",
tt_common_entry->addr, tt_common_entry->addr,
...@@ -1003,9 +997,7 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) ...@@ -1003,9 +997,7 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
BATADV_TT_CLIENT_ISOLA) ? 'I' : '.'), BATADV_TT_CLIENT_ISOLA) ? 'I' : '.'),
no_purge ? 0 : last_seen_secs, no_purge ? 0 : last_seen_secs,
no_purge ? 0 : last_seen_msecs, no_purge ? 0 : last_seen_msecs,
vlan->tt.crc); tt_local->vlan->tt.crc);
batadv_softif_vlan_free_ref(vlan);
} }
rcu_read_unlock(); rcu_read_unlock();
} }
...@@ -1050,7 +1042,6 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr, ...@@ -1050,7 +1042,6 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
{ {
struct batadv_tt_local_entry *tt_local_entry; struct batadv_tt_local_entry *tt_local_entry;
u16 flags, curr_flags = BATADV_NO_FLAGS; u16 flags, curr_flags = BATADV_NO_FLAGS;
struct batadv_softif_vlan *vlan;
void *tt_entry_exists; void *tt_entry_exists;
tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid); tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid);
...@@ -1090,14 +1081,6 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr, ...@@ -1090,14 +1081,6 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
/* extra call to free the local tt entry */ /* extra call to free the local tt entry */
batadv_tt_local_entry_free_ref(tt_local_entry); batadv_tt_local_entry_free_ref(tt_local_entry);
/* decrease the reference held for this vlan */
vlan = batadv_softif_vlan_get(bat_priv, vid);
if (!vlan)
goto out;
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_free_ref(vlan);
out: out:
if (tt_local_entry) if (tt_local_entry)
batadv_tt_local_entry_free_ref(tt_local_entry); batadv_tt_local_entry_free_ref(tt_local_entry);
...@@ -1170,7 +1153,6 @@ static void batadv_tt_local_table_free(struct batadv_priv *bat_priv) ...@@ -1170,7 +1153,6 @@ static void batadv_tt_local_table_free(struct batadv_priv *bat_priv)
spinlock_t *list_lock; /* protects write access to the hash lists */ spinlock_t *list_lock; /* protects write access to the hash lists */
struct batadv_tt_common_entry *tt_common_entry; struct batadv_tt_common_entry *tt_common_entry;
struct batadv_tt_local_entry *tt_local; struct batadv_tt_local_entry *tt_local;
struct batadv_softif_vlan *vlan;
struct hlist_node *node_tmp; struct hlist_node *node_tmp;
struct hlist_head *head; struct hlist_head *head;
u32 i; u32 i;
...@@ -1192,14 +1174,6 @@ static void batadv_tt_local_table_free(struct batadv_priv *bat_priv) ...@@ -1192,14 +1174,6 @@ static void batadv_tt_local_table_free(struct batadv_priv *bat_priv)
struct batadv_tt_local_entry, struct batadv_tt_local_entry,
common); common);
/* decrease the reference held for this vlan */
vlan = batadv_softif_vlan_get(bat_priv,
tt_common_entry->vid);
if (vlan) {
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_free_ref(vlan);
}
batadv_tt_local_entry_free_ref(tt_local); batadv_tt_local_entry_free_ref(tt_local);
} }
spin_unlock_bh(list_lock); spin_unlock_bh(list_lock);
...@@ -3229,7 +3203,6 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) ...@@ -3229,7 +3203,6 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
struct batadv_hashtable *hash = bat_priv->tt.local_hash; struct batadv_hashtable *hash = bat_priv->tt.local_hash;
struct batadv_tt_common_entry *tt_common; struct batadv_tt_common_entry *tt_common;
struct batadv_tt_local_entry *tt_local; struct batadv_tt_local_entry *tt_local;
struct batadv_softif_vlan *vlan;
struct hlist_node *node_tmp; struct hlist_node *node_tmp;
struct hlist_head *head; struct hlist_head *head;
spinlock_t *list_lock; /* protects write access to the hash lists */ spinlock_t *list_lock; /* protects write access to the hash lists */
...@@ -3259,13 +3232,6 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) ...@@ -3259,13 +3232,6 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv)
struct batadv_tt_local_entry, struct batadv_tt_local_entry,
common); common);
/* decrease the reference held for this vlan */
vlan = batadv_softif_vlan_get(bat_priv, tt_common->vid);
if (vlan) {
batadv_softif_vlan_free_ref(vlan);
batadv_softif_vlan_free_ref(vlan);
}
batadv_tt_local_entry_free_ref(tt_local); batadv_tt_local_entry_free_ref(tt_local);
} }
spin_unlock_bh(list_lock); spin_unlock_bh(list_lock);
......
...@@ -947,10 +947,12 @@ struct batadv_tt_common_entry { ...@@ -947,10 +947,12 @@ struct batadv_tt_common_entry {
* struct batadv_tt_local_entry - translation table local entry data * struct batadv_tt_local_entry - translation table local entry data
* @common: general translation table data * @common: general translation table data
* @last_seen: timestamp used for purging stale tt local entries * @last_seen: timestamp used for purging stale tt local entries
* @vlan: soft-interface vlan of the entry
*/ */
struct batadv_tt_local_entry { struct batadv_tt_local_entry {
struct batadv_tt_common_entry common; struct batadv_tt_common_entry common;
unsigned long last_seen; unsigned long last_seen;
struct batadv_softif_vlan *vlan;
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment