Commit 5841c1bb authored by Sven Eckelmann's avatar Sven Eckelmann Committed by Greg Kroah-Hartman

batman-adv: Avoid free/alloc race when handling OGM2 buffer

commit a8d23cbb upstream.

A B.A.T.M.A.N. V virtual interface has an OGM2 packet buffer which is
initialized using data from the netdevice notifier and other rtnetlink
related hooks. It is sent regularly via various slave interfaces of the
batadv virtual interface and in this process also modified (realloced) to
integrate additional state information via TVLV containers.

It must be avoided that the worker item is executed without a common lock
with the netdevice notifier/rtnetlink helpers. Otherwise it can either
happen that half modified data is sent out or the functions modifying the
OGM2 buffer try to access already freed memory regions.

Fixes: 0da00359 ("batman-adv: OGMv2 - add basic infrastructure")
Signed-off-by: default avatarSven Eckelmann <sven@narfation.org>
Signed-off-by: default avatarSimon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 5f6dd57c
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/mutex.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/rculist.h> #include <linux/rculist.h>
...@@ -127,22 +129,19 @@ static void batadv_v_ogm_send_to_if(struct sk_buff *skb, ...@@ -127,22 +129,19 @@ static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
} }
/** /**
* batadv_v_ogm_send - periodic worker broadcasting the own OGM * batadv_v_ogm_send_softif() - periodic worker broadcasting the own OGM
* @work: work queue item * @bat_priv: the bat priv with all the soft interface information
*/ */
static void batadv_v_ogm_send(struct work_struct *work) static void batadv_v_ogm_send_softif(struct batadv_priv *bat_priv)
{ {
struct batadv_hard_iface *hard_iface; struct batadv_hard_iface *hard_iface;
struct batadv_priv_bat_v *bat_v;
struct batadv_priv *bat_priv;
struct batadv_ogm2_packet *ogm_packet; struct batadv_ogm2_packet *ogm_packet;
struct sk_buff *skb, *skb_tmp; struct sk_buff *skb, *skb_tmp;
unsigned char *ogm_buff, *pkt_buff; unsigned char *ogm_buff, *pkt_buff;
int ogm_buff_len; int ogm_buff_len;
u16 tvlv_len = 0; u16 tvlv_len = 0;
bat_v = container_of(work, struct batadv_priv_bat_v, ogm_wq.work); lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex);
bat_priv = container_of(bat_v, struct batadv_priv, bat_v);
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
goto out; goto out;
...@@ -209,6 +208,23 @@ static void batadv_v_ogm_send(struct work_struct *work) ...@@ -209,6 +208,23 @@ static void batadv_v_ogm_send(struct work_struct *work)
return; return;
} }
/**
* batadv_v_ogm_send() - periodic worker broadcasting the own OGM
* @work: work queue item
*/
static void batadv_v_ogm_send(struct work_struct *work)
{
struct batadv_priv_bat_v *bat_v;
struct batadv_priv *bat_priv;
bat_v = container_of(work, struct batadv_priv_bat_v, ogm_wq.work);
bat_priv = container_of(bat_v, struct batadv_priv, bat_v);
mutex_lock(&bat_priv->bat_v.ogm_buff_mutex);
batadv_v_ogm_send_softif(bat_priv);
mutex_unlock(&bat_priv->bat_v.ogm_buff_mutex);
}
/** /**
* batadv_v_ogm_iface_enable - prepare an interface for B.A.T.M.A.N. V * batadv_v_ogm_iface_enable - prepare an interface for B.A.T.M.A.N. V
* @hard_iface: the interface to prepare * @hard_iface: the interface to prepare
...@@ -235,11 +251,15 @@ void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *primary_iface) ...@@ -235,11 +251,15 @@ void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *primary_iface)
struct batadv_priv *bat_priv = netdev_priv(primary_iface->soft_iface); struct batadv_priv *bat_priv = netdev_priv(primary_iface->soft_iface);
struct batadv_ogm2_packet *ogm_packet; struct batadv_ogm2_packet *ogm_packet;
mutex_lock(&bat_priv->bat_v.ogm_buff_mutex);
if (!bat_priv->bat_v.ogm_buff) if (!bat_priv->bat_v.ogm_buff)
return; goto unlock;
ogm_packet = (struct batadv_ogm2_packet *)bat_priv->bat_v.ogm_buff; ogm_packet = (struct batadv_ogm2_packet *)bat_priv->bat_v.ogm_buff;
ether_addr_copy(ogm_packet->orig, primary_iface->net_dev->dev_addr); ether_addr_copy(ogm_packet->orig, primary_iface->net_dev->dev_addr);
unlock:
mutex_unlock(&bat_priv->bat_v.ogm_buff_mutex);
} }
/** /**
...@@ -827,6 +847,8 @@ int batadv_v_ogm_init(struct batadv_priv *bat_priv) ...@@ -827,6 +847,8 @@ int batadv_v_ogm_init(struct batadv_priv *bat_priv)
atomic_set(&bat_priv->bat_v.ogm_seqno, random_seqno); atomic_set(&bat_priv->bat_v.ogm_seqno, random_seqno);
INIT_DELAYED_WORK(&bat_priv->bat_v.ogm_wq, batadv_v_ogm_send); INIT_DELAYED_WORK(&bat_priv->bat_v.ogm_wq, batadv_v_ogm_send);
mutex_init(&bat_priv->bat_v.ogm_buff_mutex);
return 0; return 0;
} }
...@@ -838,7 +860,11 @@ void batadv_v_ogm_free(struct batadv_priv *bat_priv) ...@@ -838,7 +860,11 @@ void batadv_v_ogm_free(struct batadv_priv *bat_priv)
{ {
cancel_delayed_work_sync(&bat_priv->bat_v.ogm_wq); cancel_delayed_work_sync(&bat_priv->bat_v.ogm_wq);
mutex_lock(&bat_priv->bat_v.ogm_buff_mutex);
kfree(bat_priv->bat_v.ogm_buff); kfree(bat_priv->bat_v.ogm_buff);
bat_priv->bat_v.ogm_buff = NULL; bat_priv->bat_v.ogm_buff = NULL;
bat_priv->bat_v.ogm_buff_len = 0; bat_priv->bat_v.ogm_buff_len = 0;
mutex_unlock(&bat_priv->bat_v.ogm_buff_mutex);
} }
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/mutex.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/sched.h> /* for linux/wait.h */ #include <linux/sched.h> /* for linux/wait.h */
...@@ -966,12 +967,14 @@ struct batadv_softif_vlan { ...@@ -966,12 +967,14 @@ struct batadv_softif_vlan {
* @ogm_buff: buffer holding the OGM packet * @ogm_buff: buffer holding the OGM packet
* @ogm_buff_len: length of the OGM packet buffer * @ogm_buff_len: length of the OGM packet buffer
* @ogm_seqno: OGM sequence number - used to identify each OGM * @ogm_seqno: OGM sequence number - used to identify each OGM
* @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len
* @ogm_wq: workqueue used to schedule OGM transmissions * @ogm_wq: workqueue used to schedule OGM transmissions
*/ */
struct batadv_priv_bat_v { struct batadv_priv_bat_v {
unsigned char *ogm_buff; unsigned char *ogm_buff;
int ogm_buff_len; int ogm_buff_len;
atomic_t ogm_seqno; atomic_t ogm_seqno;
struct mutex ogm_buff_mutex;
struct delayed_work ogm_wq; struct delayed_work ogm_wq;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment