[B.A.T.M.A.N.] [v2, maint] batman-adv: Fix use-after-free/double-free of tt_req_node

Martin Weinelt martin at darmstadt.freifunk.net
Tue May 31 09:00:02 CEST 2016


Tested-by: Martin Weinelt <martin at darmstadt.freifunk.net>

On 30.05.2016 08:11, Sven Eckelmann wrote:
> The tt_req_node is added and removed from a list inside a spinlock. But the
> locking is sometimes removed even when the object is still referenced and
> will be used later via this reference. For example batadv_send_tt_request
> can create a new tt_req_node (including add to a list) and later
> re-acquires the lock to remove it from the list and to free it. But at this
> time another context could have already removed this tt_req_node from the
> list and freed it.
> 
> CPU#0
> 
>     batadv_batman_skb_recv from net_device 0
>     -> batadv_iv_ogm_receive
>       -> batadv_iv_ogm_process
>         -> batadv_iv_ogm_process_per_outif
>           -> batadv_tvlv_ogm_receive
>             -> batadv_tvlv_ogm_receive
>               -> batadv_tvlv_containers_process
>                 -> batadv_tvlv_call_handler
>                   -> batadv_tt_tvlv_ogm_handler_v1
>                     -> batadv_tt_update_orig
>                       -> batadv_send_tt_request
>                         -> batadv_tt_req_node_new
>                            spin_lock(...)
>                            allocates new tt_req_node and adds it to list
>                            spin_unlock(...)
>                            return tt_req_node
> 
> CPU#1
> 
>     batadv_batman_skb_recv from net_device 1
>     -> batadv_recv_unicast_tvlv
>       -> batadv_tvlv_containers_process
>         -> batadv_tvlv_call_handler
>           -> batadv_tt_tvlv_unicast_handler_v1
>             -> batadv_handle_tt_response
>                spin_lock(...)
>                tt_req_node gets removed from list and is freed
>                spin_unlock(...)
> 
> CPU#0
> 
>                       <- returned to batadv_send_tt_request
>                          spin_lock(...)
>                          tt_req_node gets removed from list and is freed
>                          MEMORY CORRUPTION/SEGFAULT/...
>                          spin_unlock(...)
> 
> This can only be solved via reference counting to allow multiple contexts
> to handle the list manipulation while making sure that only the last
> context holding a reference will free the object.
> 
> Fixes: cea194d90b11 ("batman-adv: improved client announcement mechanism")
> Signed-off-by: Sven Eckelmann <sven at narfation.org>
> ---
> v2:
>  - fixed list->object in commit message
>  - add example what could have gone wrong in commit message
> ---
>  net/batman-adv/translation-table.c | 27 +++++++++++++++++++++++----
>  net/batman-adv/types.h             |  2 ++
>  2 files changed, 25 insertions(+), 4 deletions(-)
> 
> diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
> index 48adb91..46f90c5 100644
> --- a/net/batman-adv/translation-table.c
> +++ b/net/batman-adv/translation-table.c
> @@ -2272,6 +2272,19 @@ static u32 batadv_tt_local_crc(struct batadv_priv *bat_priv,
>  	return crc;
>  }
>  
> +/**
> + * batadv_tt_req_node_release - free tt_req node entry
> + * @ref: kref pointer of the tt req_node entry
> + */
> +static void batadv_tt_req_node_release(struct kref *ref)
> +{
> +	struct batadv_tt_req_node *node;
> +
> +	node = container_of(ref, struct batadv_tt_req_node, refcount);
> +
> +	kfree(node);
> +}
> +
>  static void batadv_tt_req_list_free(struct batadv_priv *bat_priv)
>  {
>  	struct batadv_tt_req_node *node;
> @@ -2281,7 +2294,7 @@ static void batadv_tt_req_list_free(struct batadv_priv *bat_priv)
>  
>  	hlist_for_each_entry_safe(node, safe, &bat_priv->tt.req_list, list) {
>  		hlist_del_init(&node->list);
> -		kfree(node);
> +		kref_put(&node->refcount, batadv_tt_req_node_release);
>  	}
>  
>  	spin_unlock_bh(&bat_priv->tt.req_list_lock);
> @@ -2318,7 +2331,7 @@ static void batadv_tt_req_purge(struct batadv_priv *bat_priv)
>  		if (batadv_has_timed_out(node->issued_at,
>  					 BATADV_TT_REQUEST_TIMEOUT)) {
>  			hlist_del_init(&node->list);
> -			kfree(node);
> +			kref_put(&node->refcount, batadv_tt_req_node_release);
>  		}
>  	}
>  	spin_unlock_bh(&bat_priv->tt.req_list_lock);
> @@ -2350,9 +2363,11 @@ batadv_tt_req_node_new(struct batadv_priv *bat_priv,
>  	if (!tt_req_node)
>  		goto unlock;
>  
> +	kref_init(&tt_req_node->refcount);
>  	ether_addr_copy(tt_req_node->addr, orig_node->orig);
>  	tt_req_node->issued_at = jiffies;
>  
> +	kref_get(&tt_req_node->refcount);
>  	hlist_add_head(&tt_req_node->list, &bat_priv->tt.req_list);
>  unlock:
>  	spin_unlock_bh(&bat_priv->tt.req_list_lock);
> @@ -2619,9 +2634,13 @@ out:
>  		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);
> +		kref_put(&tt_req_node->refcount, batadv_tt_req_node_release);
>  		spin_unlock_bh(&bat_priv->tt.req_list_lock);
> -		kfree(tt_req_node);
>  	}
> +
> +	if (tt_req_node)
> +		kref_put(&tt_req_node->refcount, batadv_tt_req_node_release);
> +
>  	kfree(tvlv_tt_data);
>  	return ret;
>  }
> @@ -3057,7 +3076,7 @@ static void batadv_handle_tt_response(struct batadv_priv *bat_priv,
>  		if (!batadv_compare_eth(node->addr, resp_src))
>  			continue;
>  		hlist_del_init(&node->list);
> -		kfree(node);
> +		kref_put(&node->refcount, batadv_tt_req_node_release);
>  	}
>  
>  	spin_unlock_bh(&bat_priv->tt.req_list_lock);
> diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
> index 1e47fbe..d75beef 100644
> --- a/net/batman-adv/types.h
> +++ b/net/batman-adv/types.h
> @@ -1129,11 +1129,13 @@ struct batadv_tt_change_node {
>   * struct batadv_tt_req_node - data to keep track of the tt requests in flight
>   * @addr: mac address address of the originator this request was sent to
>   * @issued_at: timestamp used for purging stale tt requests
> + * @refcount: number of contexts the object is used by
>   * @list: list node for batadv_priv_tt::req_list
>   */
>  struct batadv_tt_req_node {
>  	u8 addr[ETH_ALEN];
>  	unsigned long issued_at;
> +	struct kref refcount;
>  	struct hlist_node list;
>  };
>  
> 



More information about the B.A.T.M.A.N mailing list