[maint,v2] batman-adv: Avoid race in TT TVLV allocator helper

Message ID 20180509190740.11465-1-sven@narfation.org (mailing list archive)
State Accepted, archived
Delegated to: Simon Wunderlich
Headers
Series [maint,v2] batman-adv: Avoid race in TT TVLV allocator helper |

Commit Message

Sven Eckelmann May 9, 2018, 7:07 p.m. UTC
  The functions batadv_tt_prepare_tvlv_local_data and
batadv_tt_prepare_tvlv_global_data are responsible for preparing a buffer
which can be used to store the TVLV container for TT and add the VLAN
information to it.

This will be done in three phases:

1. count the number of VLANs and their entries
2. allocate the buffer using the counters from the previous step and limits
   from the caller (parameter tt_len)
3. insert the VLAN information to the buffer

The step 1 and 3 operate on a list which contains the VLANs. The access to
these lists must be protected with an appropriate lock or otherwise they
might operate on on different entries. This could for example happen when
another context is adding VLAN entries to this list.

This could lead to a buffer overflow in these functions when enough entries
were added between step 1 and 3 to the VLAN lists that the buffer room for
the entries (*tt_change) is smaller then the now required extra buffer for
new VLAN entries.

Fixes: 21a57f6e7a3b ("batman-adv: make the TT CRC logic VLAN specific")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
v2:
 - adjusted last paragraph because tt_change (third parameter "return")
   doesn't depend on step 3

This change is completely untested. The issue for this problem can be found
in https://www.open-mesh.org/issues/354

 net/batman-adv/translation-table.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
  

Comments

Antonio Quartulli May 10, 2018, 1:27 p.m. UTC | #1
On 10/05/18 03:07, Sven Eckelmann wrote:
> The functions batadv_tt_prepare_tvlv_local_data and
> batadv_tt_prepare_tvlv_global_data are responsible for preparing a buffer
> which can be used to store the TVLV container for TT and add the VLAN
> information to it.
> 
> This will be done in three phases:
> 
> 1. count the number of VLANs and their entries
> 2. allocate the buffer using the counters from the previous step and limits
>    from the caller (parameter tt_len)
> 3. insert the VLAN information to the buffer
> 
> The step 1 and 3 operate on a list which contains the VLANs. The access to
> these lists must be protected with an appropriate lock or otherwise they
> might operate on on different entries. This could for example happen when
> another context is adding VLAN entries to this list.
> 
> This could lead to a buffer overflow in these functions when enough entries
> were added between step 1 and 3 to the VLAN lists that the buffer room for
> the entries (*tt_change) is smaller then the now required extra buffer for
> new VLAN entries.
> 
> Fixes: 21a57f6e7a3b ("batman-adv: make the TT CRC logic VLAN specific")
> Signed-off-by: Sven Eckelmann <sven@narfation.org>

Acked-by: Antonio Quartulli <a@unstable.cc>

Good catch. Unfortunately this issue was caused by my misunderstanding
of the RCU mechanism when used with lists.


Cheers,
  
Sven Eckelmann May 10, 2018, 2:12 p.m. UTC | #2
On Donnerstag, 10. Mai 2018 15:27:34 CEST Antonio Quartulli wrote:
[...]
> > Fixes: 21a57f6e7a3b ("batman-adv: make the TT CRC logic VLAN specific")
> > Signed-off-by: Sven Eckelmann <sven@narfation.org>
> 
> Acked-by: Antonio Quartulli <a@unstable.cc>

Thanks, applied in 286be89a3349 [1].

Kind regards,
	Sven

[1] https://git.open-mesh.org/batman-adv.git/commit/286be89a33497ba9000aa5c2960f1f4114953522
  

Patch

diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 0225616d..7fa3a0a0 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -862,7 +862,7 @@  batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
 	struct batadv_orig_node_vlan *vlan;
 	u8 *tt_change_ptr;
 
-	rcu_read_lock();
+	spin_lock_bh(&orig_node->vlan_list_lock);
 	hlist_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) {
 		num_vlan++;
 		num_entries += atomic_read(&vlan->tt.num_entries);
@@ -900,7 +900,7 @@  batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node,
 	*tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
 
 out:
-	rcu_read_unlock();
+	spin_unlock_bh(&orig_node->vlan_list_lock);
 	return tvlv_len;
 }
 
@@ -936,7 +936,7 @@  batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
 	u8 *tt_change_ptr;
 	int change_offset;
 
-	rcu_read_lock();
+	spin_lock_bh(&bat_priv->softif_vlan_list_lock);
 	hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
 		num_vlan++;
 		num_entries += atomic_read(&vlan->tt.num_entries);
@@ -974,7 +974,7 @@  batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv,
 	*tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr;
 
 out:
-	rcu_read_unlock();
+	spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
 	return tvlv_len;
 }