121 lines
4.3 KiB
Diff
121 lines
4.3 KiB
Diff
From: Sven Eckelmann <sven@narfation.org>
|
|
Date: Thu, 30 Jun 2016 20:11:34 +0200
|
|
Subject: [PATCH] batman-adv: Fix reference leak in batadv_find_router
|
|
|
|
The replacement of last_bonding_candidate in batadv_orig_node has to be an
|
|
atomic operation. Otherwise it is possible that the reference counter of a
|
|
batadv_orig_ifinfo is reduced which was no longer the
|
|
last_bonding_candidate when the new candidate is added. This can either
|
|
lead to an invalid memory access or to reference leaks which make it
|
|
impossible to an interface which was added to batman-adv.
|
|
|
|
Fixes: 797edd9e87ac ("batman-adv: add bonding again")
|
|
Signed-off-by: Sven Eckelmann <sven@narfation.org>
|
|
Acked-by: Simon Wunderlich <sw@simonwunderlich.de>
|
|
Signed-off-by: Marek Lindner <mareklindner@neomailbox.ch>
|
|
|
|
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/6ecc711374afd93ee0c2216b38ae52d3ce680c3f
|
|
---
|
|
net/batman-adv/routing.c | 52 ++++++++++++++++++++++++++++++++++++------------
|
|
net/batman-adv/types.h | 4 +++-
|
|
2 files changed, 42 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
|
|
index 6c2901a..bfac086 100644
|
|
--- a/net/batman-adv/routing.c
|
|
+++ b/net/batman-adv/routing.c
|
|
@@ -456,6 +456,29 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
|
|
}
|
|
|
|
/**
|
|
+ * batadv_last_bonding_replace - Replace last_bonding_candidate of orig_node
|
|
+ * @orig_node: originator node whose bonding candidates should be replaced
|
|
+ * @new_candidate: new bonding candidate or NULL
|
|
+ */
|
|
+static void
|
|
+batadv_last_bonding_replace(struct batadv_orig_node *orig_node,
|
|
+ struct batadv_orig_ifinfo *new_candidate)
|
|
+{
|
|
+ struct batadv_orig_ifinfo *old_candidate;
|
|
+
|
|
+ spin_lock_bh(&orig_node->neigh_list_lock);
|
|
+ old_candidate = orig_node->last_bonding_candidate;
|
|
+
|
|
+ if (new_candidate)
|
|
+ kref_get(&new_candidate->refcount);
|
|
+ orig_node->last_bonding_candidate = new_candidate;
|
|
+ spin_unlock_bh(&orig_node->neigh_list_lock);
|
|
+
|
|
+ if (old_candidate)
|
|
+ batadv_orig_ifinfo_put(old_candidate);
|
|
+}
|
|
+
|
|
+/**
|
|
* batadv_find_router - find a suitable router for this originator
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
* @orig_node: the destination node
|
|
@@ -562,10 +585,6 @@ next:
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
- /* last_bonding_candidate is reset below, remove the old reference. */
|
|
- if (orig_node->last_bonding_candidate)
|
|
- batadv_orig_ifinfo_put(orig_node->last_bonding_candidate);
|
|
-
|
|
/* After finding candidates, handle the three cases:
|
|
* 1) there is a next candidate, use that
|
|
* 2) there is no next candidate, use the first of the list
|
|
@@ -574,21 +593,28 @@ next:
|
|
if (next_candidate) {
|
|
batadv_neigh_node_put(router);
|
|
|
|
- /* remove references to first candidate, we don't need it. */
|
|
- if (first_candidate) {
|
|
- batadv_neigh_node_put(first_candidate_router);
|
|
- batadv_orig_ifinfo_put(first_candidate);
|
|
- }
|
|
+ kref_get(&next_candidate_router->refcount);
|
|
router = next_candidate_router;
|
|
- orig_node->last_bonding_candidate = next_candidate;
|
|
+ batadv_last_bonding_replace(orig_node, next_candidate);
|
|
} else if (first_candidate) {
|
|
batadv_neigh_node_put(router);
|
|
|
|
- /* refcounting has already been done in the loop above. */
|
|
+ kref_get(&first_candidate_router->refcount);
|
|
router = first_candidate_router;
|
|
- orig_node->last_bonding_candidate = first_candidate;
|
|
+ batadv_last_bonding_replace(orig_node, first_candidate);
|
|
} else {
|
|
- orig_node->last_bonding_candidate = NULL;
|
|
+ batadv_last_bonding_replace(orig_node, NULL);
|
|
+ }
|
|
+
|
|
+ /* cleanup of candidates */
|
|
+ if (first_candidate) {
|
|
+ batadv_neigh_node_put(first_candidate_router);
|
|
+ batadv_orig_ifinfo_put(first_candidate);
|
|
+ }
|
|
+
|
|
+ if (next_candidate) {
|
|
+ batadv_neigh_node_put(next_candidate_router);
|
|
+ batadv_orig_ifinfo_put(next_candidate);
|
|
}
|
|
|
|
return router;
|
|
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
|
|
index 0051222..74d865a 100644
|
|
--- a/net/batman-adv/types.h
|
|
+++ b/net/batman-adv/types.h
|
|
@@ -330,7 +330,9 @@ struct batadv_orig_node {
|
|
DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
|
|
u32 last_bcast_seqno;
|
|
struct hlist_head neigh_list;
|
|
- /* neigh_list_lock protects: neigh_list and router */
|
|
+ /* neigh_list_lock protects: neigh_list, ifinfo_list,
|
|
+ * last_bonding_candidate and router
|
|
+ */
|
|
spinlock_t neigh_list_lock;
|
|
struct hlist_node hash_entry;
|
|
struct batadv_priv *bat_priv;
|