From 14e98d86ac7a0e7725fb3a38fa8d2c643d03db50 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Tue, 5 Jul 2016 12:03:44 +0200 Subject: [PATCH] batman-adv: Add reference counting + nullptr fixes * Avoid nullptr dereference in bla after vlan_insert_tag * Avoid nullptr dereference in dat after vlan_insert_tag * Avoid tt_req_node list put for unhashed entry * Fix orig_node_vlan leak on orig_node_release * Fix non-atomic bla_claim::backbone_gw access * Fix reference leak in batadv_find_router * Free last_bonding_candidate on release of orig_node Signed-off-by: Sven Eckelmann --- ...-nullptr-dereference-in-bla-after-vl.patch | 35 +++ ...-nullptr-dereference-in-dat-after-vl.patch | 49 +++ ...-tt_req_node-list-put-for-unhashed-e.patch | 41 +++ ...rig_node_vlan-leak-on-orig_node_rele.patch | 43 +++ ...on-atomic-bla_claim-backbone_gw-acce.patch | 291 ++++++++++++++++++ ...reference-leak-in-batadv_find_router.patch | 120 ++++++++ ...last_bonding_candidate-on-release-of.patch | 45 +++ 7 files changed, 624 insertions(+) create mode 100644 batman-adv/patches/0015-batman-adv-Avoid-nullptr-dereference-in-bla-after-vl.patch create mode 100644 batman-adv/patches/0016-batman-adv-Avoid-nullptr-dereference-in-dat-after-vl.patch create mode 100644 batman-adv/patches/0017-batman-adv-Avoid-tt_req_node-list-put-for-unhashed-e.patch create mode 100644 batman-adv/patches/0018-batman-adv-Fix-orig_node_vlan-leak-on-orig_node_rele.patch create mode 100644 batman-adv/patches/0019-batman-adv-Fix-non-atomic-bla_claim-backbone_gw-acce.patch create mode 100644 batman-adv/patches/0020-batman-adv-Fix-reference-leak-in-batadv_find_router.patch create mode 100644 batman-adv/patches/0021-batman-adv-Free-last_bonding_candidate-on-release-of.patch diff --git a/batman-adv/patches/0015-batman-adv-Avoid-nullptr-dereference-in-bla-after-vl.patch b/batman-adv/patches/0015-batman-adv-Avoid-nullptr-dereference-in-bla-after-vl.patch new file mode 100644 index 0000000..907c1a1 --- /dev/null +++ b/batman-adv/patches/0015-batman-adv-Avoid-nullptr-dereference-in-bla-after-vl.patch @@ -0,0 +1,35 @@ +From: Sven Eckelmann +Date: Sat, 2 Jul 2016 09:52:13 +0200 +Subject: [PATCH] batman-adv: Avoid nullptr dereference in bla after vlan_insert_tag + +vlan_insert_tag can return NULL on errors. The bridge loop avoidance code +therefore has to check the return value of vlan_insert_tag for NULL before +it can safely operate on this pointer. + +Fixes: a9ce0dc43e2c ("batman-adv: add basic bridge loop avoidance code") +Signed-off-by: Sven Eckelmann +Signed-off-by: Marek Lindner + +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/e4cffba4d3353ea15287abbfbdd65208aa62c156 +--- + net/batman-adv/bridge_loop_avoidance.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index 0a6c8b8..fe8b62f 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -409,9 +409,12 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, u8 *mac, + break; + } + +- if (vid & BATADV_VLAN_HAS_TAG) ++ if (vid & BATADV_VLAN_HAS_TAG) { + skb = vlan_insert_tag(skb, htons(ETH_P_8021Q), + vid & VLAN_VID_MASK); ++ if (!skb) ++ goto out; ++ } + + skb_reset_mac_header(skb); + skb->protocol = eth_type_trans(skb, soft_iface); diff --git a/batman-adv/patches/0016-batman-adv-Avoid-nullptr-dereference-in-dat-after-vl.patch b/batman-adv/patches/0016-batman-adv-Avoid-nullptr-dereference-in-dat-after-vl.patch new file mode 100644 index 0000000..d896811 --- /dev/null +++ b/batman-adv/patches/0016-batman-adv-Avoid-nullptr-dereference-in-dat-after-vl.patch @@ -0,0 +1,49 @@ +From: Sven Eckelmann +Date: Sat, 2 Jul 2016 09:52:14 +0200 +Subject: [PATCH] batman-adv: Avoid nullptr dereference in dat after vlan_insert_tag + +vlan_insert_tag can return NULL on errors. The distributed arp table code +therefore has to check the return value of vlan_insert_tag for NULL before +it can safely operate on this pointer. + +Fixes: 53c6c262a581 ("batman-adv: tag locally generated ARP reply if needed") +Signed-off-by: Sven Eckelmann +Signed-off-by: Marek Lindner + +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/898382d11fa1f737cd4f7033db1088c601fd11ed +--- + net/batman-adv/distributed-arp-table.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c +index 3e6b262..5a89be0 100644 +--- a/net/batman-adv/distributed-arp-table.c ++++ b/net/batman-adv/distributed-arp-table.c +@@ -1009,9 +1009,12 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, + if (!skb_new) + goto out; + +- if (vid & BATADV_VLAN_HAS_TAG) ++ if (vid & BATADV_VLAN_HAS_TAG) { + skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q), + vid & VLAN_VID_MASK); ++ if (!skb_new) ++ goto out; ++ } + + skb_reset_mac_header(skb_new); + skb_new->protocol = eth_type_trans(skb_new, +@@ -1089,9 +1092,12 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, + */ + skb_reset_mac_header(skb_new); + +- if (vid & BATADV_VLAN_HAS_TAG) ++ if (vid & BATADV_VLAN_HAS_TAG) { + skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q), + vid & VLAN_VID_MASK); ++ if (!skb_new) ++ goto out; ++ } + + /* To preserve backwards compatibility, the node has choose the outgoing + * format based on the incoming request packet type. The assumption is diff --git a/batman-adv/patches/0017-batman-adv-Avoid-tt_req_node-list-put-for-unhashed-e.patch b/batman-adv/patches/0017-batman-adv-Avoid-tt_req_node-list-put-for-unhashed-e.patch new file mode 100644 index 0000000..09f90d3 --- /dev/null +++ b/batman-adv/patches/0017-batman-adv-Avoid-tt_req_node-list-put-for-unhashed-e.patch @@ -0,0 +1,41 @@ +From: Sven Eckelmann +Date: Fri, 24 Jun 2016 21:43:32 +0200 +Subject: [PATCH] batman-adv: Avoid tt_req_node list put for unhashed entry + +It can happen that a tt_req_node list entry was already removed from +tt.req_list when batadv_send_tt_request reaches the end of the function. +The reference counter was already reduced by 1 for the list entry and thus +the reference counter is not allowed to be reduced again. Otherwise, the +entry is freed too early and the next batadv_tt_req_node_put in this +function will operate on freed memory. + +Fixes: cea194d90b11 ("batman-adv: improved client announcement mechanism") +Signed-off-by: Sven Eckelmann +Signed-off-by: Marek Lindner + +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/03ecc9f957b837c755f09251c5f684996521e487 +--- + net/batman-adv/translation-table.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 23fb7ea..f7d44c6 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -2639,11 +2639,13 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv, + out: + if (primary_if) + batadv_hardif_put(primary_if); ++ + if (ret && tt_req_node) { + spin_lock_bh(&bat_priv->tt.req_list_lock); +- /* hlist_del_init() verifies tt_req_node still is in the list */ +- hlist_del_init(&tt_req_node->list); +- batadv_tt_req_node_put(tt_req_node); ++ if (!hlist_unhashed(&tt_req_node->list)) { ++ hlist_del_init(&tt_req_node->list); ++ batadv_tt_req_node_put(tt_req_node); ++ } + spin_unlock_bh(&bat_priv->tt.req_list_lock); + } + diff --git a/batman-adv/patches/0018-batman-adv-Fix-orig_node_vlan-leak-on-orig_node_rele.patch b/batman-adv/patches/0018-batman-adv-Fix-orig_node_vlan-leak-on-orig_node_rele.patch new file mode 100644 index 0000000..13c74e4 --- /dev/null +++ b/batman-adv/patches/0018-batman-adv-Fix-orig_node_vlan-leak-on-orig_node_rele.patch @@ -0,0 +1,43 @@ +From: Sven Eckelmann +Date: Thu, 30 Jun 2016 20:10:46 +0200 +Subject: [PATCH] batman-adv: Fix orig_node_vlan leak on orig_node_release + +batadv_orig_node_new uses batadv_orig_node_vlan_new to allocate a new +batadv_orig_node_vlan and add it to batadv_orig_node::vlan_list. References +to this list have also to be cleaned when the batadv_orig_node is removed. + +Fixes: 21a57f6e7a3b ("batman-adv: make the TT CRC logic VLAN specific") +Signed-off-by: Sven Eckelmann +Signed-off-by: Marek Lindner + +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/719afd254e812c7ff8688ce79bebb7324ec438d6 +--- + net/batman-adv/originator.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c +index 28241a4..3a3948a 100644 +--- a/net/batman-adv/originator.c ++++ b/net/batman-adv/originator.c +@@ -781,6 +781,7 @@ static void batadv_orig_node_release(struct kref *ref) + struct batadv_neigh_node *neigh_node; + struct batadv_orig_node *orig_node; + struct batadv_orig_ifinfo *orig_ifinfo; ++ struct batadv_orig_node_vlan *vlan; + + orig_node = container_of(ref, struct batadv_orig_node, refcount); + +@@ -800,6 +801,13 @@ static void batadv_orig_node_release(struct kref *ref) + } + spin_unlock_bh(&orig_node->neigh_list_lock); + ++ spin_lock_bh(&orig_node->vlan_list_lock); ++ hlist_for_each_entry_safe(vlan, node_tmp, &orig_node->vlan_list, list) { ++ hlist_del_rcu(&vlan->list); ++ batadv_orig_node_vlan_put(vlan); ++ } ++ spin_unlock_bh(&orig_node->vlan_list_lock); ++ + /* Free nc_nodes */ + batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL); + diff --git a/batman-adv/patches/0019-batman-adv-Fix-non-atomic-bla_claim-backbone_gw-acce.patch b/batman-adv/patches/0019-batman-adv-Fix-non-atomic-bla_claim-backbone_gw-acce.patch new file mode 100644 index 0000000..9631ebe --- /dev/null +++ b/batman-adv/patches/0019-batman-adv-Fix-non-atomic-bla_claim-backbone_gw-acce.patch @@ -0,0 +1,291 @@ +From: Sven Eckelmann +Date: Fri, 1 Jul 2016 15:49:43 +0200 +Subject: [PATCH] batman-adv: Fix non-atomic bla_claim::backbone_gw access + +The pointer batadv_bla_claim::backbone_gw can be changed at any time. +Therefore, access to it must be protected to ensure that two function +accessing the same backbone_gw are actually accessing the same. This is +especially important when the crc_lock is used or when the backbone_gw of a +claim is exchanged. + +Not doing so leads to invalid memory access and/or reference leaks. + +Fixes: a9ce0dc43e2c ("batman-adv: add basic bridge loop avoidance code") +Fixes: b307e72d119f ("batman-adv: lock crc access in bridge loop avoidance") +Signed-off-by: Sven Eckelmann +Acked-by: Simon Wunderlich +Signed-off-by: Marek Lindner + +Origin: backport, https://git.open-mesh.org/batman-adv.git/commit/e401297e3a393896e9b07bef8d6e2df203b60d43 +--- + net/batman-adv/bridge_loop_avoidance.c | 111 ++++++++++++++++++++++++++------- + net/batman-adv/types.h | 2 + + 2 files changed, 90 insertions(+), 23 deletions(-) + +diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c +index fe8b62f..30fd72e 100644 +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -176,10 +176,21 @@ static void batadv_backbone_gw_put(struct batadv_bla_backbone_gw *backbone_gw) + static void batadv_claim_release(struct kref *ref) + { + struct batadv_bla_claim *claim; ++ struct batadv_bla_backbone_gw *old_backbone_gw; + + claim = container_of(ref, struct batadv_bla_claim, refcount); + +- batadv_backbone_gw_put(claim->backbone_gw); ++ spin_lock_bh(&claim->backbone_lock); ++ old_backbone_gw = claim->backbone_gw; ++ claim->backbone_gw = NULL; ++ spin_unlock_bh(&claim->backbone_lock); ++ ++ spin_lock_bh(&old_backbone_gw->crc_lock); ++ old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN); ++ spin_unlock_bh(&old_backbone_gw->crc_lock); ++ ++ batadv_backbone_gw_put(old_backbone_gw); ++ + kfree_rcu(claim, rcu); + } + +@@ -637,8 +648,10 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, + const u8 *mac, const unsigned short vid, + struct batadv_bla_backbone_gw *backbone_gw) + { ++ struct batadv_bla_backbone_gw *old_backbone_gw; + struct batadv_bla_claim *claim; + struct batadv_bla_claim search_claim; ++ bool remove_crc = false; + int hash_added; + + ether_addr_copy(search_claim.addr, mac); +@@ -652,8 +665,10 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, + return; + + ether_addr_copy(claim->addr, mac); ++ spin_lock_init(&claim->backbone_lock); + claim->vid = vid; + claim->lasttime = jiffies; ++ kref_get(&backbone_gw->refcount); + claim->backbone_gw = backbone_gw; + + kref_init(&claim->refcount); +@@ -681,15 +696,26 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, + "bla_add_claim(): changing ownership for %pM, vid %d\n", + mac, BATADV_PRINT_VID(vid)); + +- spin_lock_bh(&claim->backbone_gw->crc_lock); +- claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN); +- spin_unlock_bh(&claim->backbone_gw->crc_lock); +- batadv_backbone_gw_put(claim->backbone_gw); ++ remove_crc = true; + } +- /* set (new) backbone gw */ ++ ++ /* replace backbone_gw atomically and adjust reference counters */ ++ spin_lock_bh(&claim->backbone_lock); ++ old_backbone_gw = claim->backbone_gw; + kref_get(&backbone_gw->refcount); + claim->backbone_gw = backbone_gw; ++ spin_unlock_bh(&claim->backbone_lock); + ++ if (remove_crc) { ++ /* remove claim address from old backbone_gw */ ++ spin_lock_bh(&old_backbone_gw->crc_lock); ++ old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN); ++ spin_unlock_bh(&old_backbone_gw->crc_lock); ++ } ++ ++ batadv_backbone_gw_put(old_backbone_gw); ++ ++ /* add claim address to new backbone_gw */ + spin_lock_bh(&backbone_gw->crc_lock); + backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN); + spin_unlock_bh(&backbone_gw->crc_lock); +@@ -700,6 +726,26 @@ claim_free_ref: + } + + /** ++ * batadv_bla_claim_get_backbone_gw - Get valid reference for backbone_gw of ++ * claim ++ * @claim: claim whose backbone_gw should be returned ++ * ++ * Return: valid reference to claim::backbone_gw ++ */ ++static struct batadv_bla_backbone_gw * ++batadv_bla_claim_get_backbone_gw(struct batadv_bla_claim *claim) ++{ ++ struct batadv_bla_backbone_gw *backbone_gw; ++ ++ spin_lock_bh(&claim->backbone_lock); ++ backbone_gw = claim->backbone_gw; ++ kref_get(&backbone_gw->refcount); ++ spin_unlock_bh(&claim->backbone_lock); ++ ++ return backbone_gw; ++} ++ ++/** + * batadv_bla_del_claim - delete a claim from the claim hash + * @bat_priv: the bat priv with all the soft interface information + * @mac: mac address of the claim to be removed +@@ -723,10 +769,6 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv, + batadv_choose_claim, claim); + batadv_claim_put(claim); /* reference from the hash is gone */ + +- spin_lock_bh(&claim->backbone_gw->crc_lock); +- claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN); +- spin_unlock_bh(&claim->backbone_gw->crc_lock); +- + /* don't need the reference from hash_find() anymore */ + batadv_claim_put(claim); + } +@@ -1175,6 +1217,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, + struct batadv_hard_iface *primary_if, + int now) + { ++ struct batadv_bla_backbone_gw *backbone_gw; + struct batadv_bla_claim *claim; + struct hlist_head *head; + struct batadv_hashtable *hash; +@@ -1189,14 +1232,17 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, + + rcu_read_lock(); + hlist_for_each_entry_rcu(claim, head, hash_entry) { ++ backbone_gw = batadv_bla_claim_get_backbone_gw(claim); + if (now) + goto purge_now; +- if (!batadv_compare_eth(claim->backbone_gw->orig, ++ ++ if (!batadv_compare_eth(backbone_gw->orig, + primary_if->net_dev->dev_addr)) +- continue; ++ goto skip; ++ + if (!batadv_has_timed_out(claim->lasttime, + BATADV_BLA_CLAIM_TIMEOUT)) +- continue; ++ goto skip; + + batadv_dbg(BATADV_DBG_BLA, bat_priv, + "bla_purge_claims(): %pM, vid %d, time out\n", +@@ -1204,8 +1250,10 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, + + purge_now: + batadv_handle_unclaim(bat_priv, primary_if, +- claim->backbone_gw->orig, ++ backbone_gw->orig, + claim->addr, claim->vid); ++skip: ++ batadv_backbone_gw_put(backbone_gw); + } + rcu_read_unlock(); + } +@@ -1623,9 +1671,11 @@ void batadv_bla_free(struct batadv_priv *bat_priv) + int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, + unsigned short vid, bool is_bcast) + { ++ struct batadv_bla_backbone_gw *backbone_gw; + struct ethhdr *ethhdr; + struct batadv_bla_claim search_claim, *claim = NULL; + struct batadv_hard_iface *primary_if; ++ bool own_claim; + int ret; + + ethhdr = eth_hdr(skb); +@@ -1657,8 +1707,12 @@ int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, + } + + /* if it is our own claim ... */ +- if (batadv_compare_eth(claim->backbone_gw->orig, +- primary_if->net_dev->dev_addr)) { ++ backbone_gw = batadv_bla_claim_get_backbone_gw(claim); ++ own_claim = batadv_compare_eth(backbone_gw->orig, ++ primary_if->net_dev->dev_addr); ++ batadv_backbone_gw_put(backbone_gw); ++ ++ if (own_claim) { + /* ... allow it in any case */ + claim->lasttime = jiffies; + goto allow; +@@ -1722,7 +1776,9 @@ int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, + { + struct ethhdr *ethhdr; + struct batadv_bla_claim search_claim, *claim = NULL; ++ struct batadv_bla_backbone_gw *backbone_gw; + struct batadv_hard_iface *primary_if; ++ bool client_roamed; + int ret = 0; + + primary_if = batadv_primary_if_get_selected(bat_priv); +@@ -1752,8 +1808,12 @@ int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, + goto allow; + + /* check if we are responsible. */ +- if (batadv_compare_eth(claim->backbone_gw->orig, +- primary_if->net_dev->dev_addr)) { ++ backbone_gw = batadv_bla_claim_get_backbone_gw(claim); ++ client_roamed = batadv_compare_eth(backbone_gw->orig, ++ primary_if->net_dev->dev_addr); ++ batadv_backbone_gw_put(backbone_gw); ++ ++ if (client_roamed) { + /* if yes, the client has roamed and we have + * to unclaim it. + */ +@@ -1801,6 +1861,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset) + struct net_device *net_dev = (struct net_device *)seq->private; + struct batadv_priv *bat_priv = netdev_priv(net_dev); + struct batadv_hashtable *hash = bat_priv->bla.claim_hash; ++ struct batadv_bla_backbone_gw *backbone_gw; + struct batadv_bla_claim *claim; + struct batadv_hard_iface *primary_if; + struct hlist_head *head; +@@ -1825,17 +1886,21 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset) + + rcu_read_lock(); + hlist_for_each_entry_rcu(claim, head, hash_entry) { +- is_own = batadv_compare_eth(claim->backbone_gw->orig, ++ backbone_gw = batadv_bla_claim_get_backbone_gw(claim); ++ ++ is_own = batadv_compare_eth(backbone_gw->orig, + primary_addr); + +- spin_lock_bh(&claim->backbone_gw->crc_lock); +- backbone_crc = claim->backbone_gw->crc; +- spin_unlock_bh(&claim->backbone_gw->crc_lock); ++ spin_lock_bh(&backbone_gw->crc_lock); ++ backbone_crc = backbone_gw->crc; ++ spin_unlock_bh(&backbone_gw->crc_lock); + seq_printf(seq, " * %pM on %5d by %pM [%c] (%#.4x)\n", + claim->addr, BATADV_PRINT_VID(claim->vid), +- claim->backbone_gw->orig, ++ backbone_gw->orig, + (is_own ? 'x' : ' '), + backbone_crc); ++ ++ batadv_backbone_gw_put(backbone_gw); + } + rcu_read_unlock(); + } +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index d75beef..41a85b5 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -1034,6 +1034,7 @@ struct batadv_bla_backbone_gw { + * @addr: mac address of claimed non-mesh client + * @vid: vlan id this client was detected on + * @backbone_gw: pointer to backbone gw claiming this client ++ * @backbone_lock: lock protecting backbone_gw pointer + * @lasttime: last time we heard of claim (locals only) + * @hash_entry: hlist node for batadv_priv_bla::claim_hash + * @refcount: number of contexts the object is used +@@ -1043,6 +1044,7 @@ struct batadv_bla_claim { + u8 addr[ETH_ALEN]; + unsigned short vid; + struct batadv_bla_backbone_gw *backbone_gw; ++ spinlock_t backbone_lock; /* protects backbone_gw */ + unsigned long lasttime; + struct hlist_node hash_entry; + struct rcu_head rcu; diff --git a/batman-adv/patches/0020-batman-adv-Fix-reference-leak-in-batadv_find_router.patch b/batman-adv/patches/0020-batman-adv-Fix-reference-leak-in-batadv_find_router.patch new file mode 100644 index 0000000..38956cf --- /dev/null +++ b/batman-adv/patches/0020-batman-adv-Fix-reference-leak-in-batadv_find_router.patch @@ -0,0 +1,120 @@ +From: Sven Eckelmann +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 +Acked-by: Simon Wunderlich +Signed-off-by: Marek Lindner + +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 27e07dd..694dc74 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 41a85b5..c143649 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; diff --git a/batman-adv/patches/0021-batman-adv-Free-last_bonding_candidate-on-release-of.patch b/batman-adv/patches/0021-batman-adv-Free-last_bonding_candidate-on-release-of.patch new file mode 100644 index 0000000..b35a801 --- /dev/null +++ b/batman-adv/patches/0021-batman-adv-Free-last_bonding_candidate-on-release-of.patch @@ -0,0 +1,45 @@ +From: Sven Eckelmann +Date: Thu, 30 Jun 2016 21:41:13 +0200 +Subject: [PATCH] batman-adv: Free last_bonding_candidate on release of orig_node + +The orig_ifinfo reference counter for last_bonding_candidate in +batadv_orig_node has to be reduced when an originator node is released. +Otherwise the orig_ifinfo is leaked and the reference counter the netdevice +is not reduced correctly. + +Fixes: 797edd9e87ac ("batman-adv: add bonding again") +Signed-off-by: Sven Eckelmann +Signed-off-by: Marek Lindner + +Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/20df5c53865a90095099f0af80536b8abfea303b +--- + net/batman-adv/originator.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c +index 3a3948a..7594afd 100644 +--- a/net/batman-adv/originator.c ++++ b/net/batman-adv/originator.c +@@ -782,6 +782,7 @@ static void batadv_orig_node_release(struct kref *ref) + struct batadv_orig_node *orig_node; + struct batadv_orig_ifinfo *orig_ifinfo; + struct batadv_orig_node_vlan *vlan; ++ struct batadv_orig_ifinfo *last_candidate; + + orig_node = container_of(ref, struct batadv_orig_node, refcount); + +@@ -799,8 +800,14 @@ static void batadv_orig_node_release(struct kref *ref) + hlist_del_rcu(&orig_ifinfo->list); + batadv_orig_ifinfo_put(orig_ifinfo); + } ++ ++ last_candidate = orig_node->last_bonding_candidate; ++ orig_node->last_bonding_candidate = NULL; + spin_unlock_bh(&orig_node->neigh_list_lock); + ++ if (last_candidate) ++ batadv_orig_ifinfo_put(last_candidate); ++ + spin_lock_bh(&orig_node->vlan_list_lock); + hlist_for_each_entry_safe(vlan, node_tmp, &orig_node->vlan_list, list) { + hlist_del_rcu(&vlan->list); -- 2.30.2