batman-adv: Merge bugfixes from 2019.1
[feed/routing.git] / batman-adv / patches / 0063-batman-adv-Reduce-tt_global-hash-refcnt-only-for-rem.patch
diff --git a/batman-adv/patches/0063-batman-adv-Reduce-tt_global-hash-refcnt-only-for-rem.patch b/batman-adv/patches/0063-batman-adv-Reduce-tt_global-hash-refcnt-only-for-rem.patch
new file mode 100644 (file)
index 0000000..aed387b
--- /dev/null
@@ -0,0 +1,66 @@
+From: Sven Eckelmann <sven@narfation.org>
+Date: Sat, 23 Feb 2019 15:09:06 +0100
+Subject: batman-adv: Reduce tt_global hash refcnt only for removed entry
+
+The batadv_hash_remove is a function which searches the hashtable for an
+entry using a needle, a hashtable bucket selection function and a compare
+function. It will lock the bucket list and delete an entry when the compare
+function matches it with the needle. It returns the pointer to the
+hlist_node which matches or NULL when no entry matches the needle.
+
+The batadv_tt_global_free is not itself protected in anyway to avoid that
+any other function is modifying the hashtable between the search for the
+entry and the call to batadv_hash_remove. It can therefore happen that the
+entry either doesn't exist anymore or an entry was deleted which is not the
+same object as the needle. In such an situation, the reference counter (for
+the reference stored in the hashtable) must not be reduced for the needle.
+Instead the reference counter of the actually removed entry has to be
+reduced.
+
+Otherwise the reference counter will underflow and the object might be
+freed before all its references were dropped. The kref helpers reported
+this problem as:
+
+  refcount_t: underflow; use-after-free.
+
+Fixes: 7bad46397eff ("batman-adv: protect the local and the global trans-tables with rcu")
+Reported-by: Martin Weinelt <martin@linuxlounge.net>
+Signed-off-by: Sven Eckelmann <sven@narfation.org>
+Acked-by: Antonio Quartulli <a@unstable.cc>
+
+Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/bd6df24da0063fe50828c287d05bdc1876f4f6cc
+
+diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
+index d30e86dc5ad64e5de9487224b802d6fbdcf5f440..a1b83416be842810f4ca49212c3afb91c598a64b 100644
+--- a/net/batman-adv/translation-table.c
++++ b/net/batman-adv/translation-table.c
+@@ -614,14 +614,26 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv,
+                                 struct batadv_tt_global_entry *tt_global,
+                                 const char *message)
+ {
++      struct batadv_tt_global_entry *tt_removed_entry;
++      struct hlist_node *tt_removed_node;
++
+       batadv_dbg(BATADV_DBG_TT, bat_priv,
+                  "Deleting global tt entry %pM (vid: %d): %s\n",
+                  tt_global->common.addr,
+                  BATADV_PRINT_VID(tt_global->common.vid), message);
+-      batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt,
+-                         batadv_choose_tt, &tt_global->common);
+-      batadv_tt_global_entry_put(tt_global);
++      tt_removed_node = batadv_hash_remove(bat_priv->tt.global_hash,
++                                           batadv_compare_tt,
++                                           batadv_choose_tt,
++                                           &tt_global->common);
++      if (!tt_removed_node)
++              return;
++
++      /* drop reference of remove hash entry */
++      tt_removed_entry = hlist_entry(tt_removed_node,
++                                     struct batadv_tt_global_entry,
++                                     common.hash_entry);
++      batadv_tt_global_entry_put(tt_removed_entry);
+ }
+ /**