From: Sven Eckelmann Date: Thu, 3 Oct 2019 17:02:01 +0200 Subject: batman-adv: Avoid free/alloc race when handling OGM2 buffer A B.A.T.M.A.N. V virtual interface has an OGM2 packet buffer which is initialized using data from the RTNL lock protected 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: 632835348e65 ("batman-adv: OGMv2 - add basic infrastructure") Signed-off-by: Sven Eckelmann Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/14ee24576213ff02272b7f8d975c7c61d5448aa2 diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 5f4fe1889053d4ea7624e4500dcefe2601371024..f5feaa8c4fd228228fea519771e2c9e123b10345 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -127,14 +128,12 @@ static void batadv_v_ogm_send_to_if(struct sk_buff *skb, } /** - * batadv_v_ogm_send - periodic worker broadcasting the own OGM - * @work: work queue item + * batadv_v_ogm_send_softif() - periodic worker broadcasting the own OGM + * @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_priv_bat_v *bat_v; - struct batadv_priv *bat_priv; struct batadv_ogm2_packet *ogm_packet; struct sk_buff *skb, *skb_tmp; unsigned char *ogm_buff, *pkt_buff; @@ -142,8 +141,7 @@ static void batadv_v_ogm_send(struct work_struct *work) u16 tvlv_len = 0; int ret; - bat_v = container_of(work, struct batadv_priv_bat_v, ogm_wq.work); - bat_priv = container_of(bat_v, struct batadv_priv, bat_v); + ASSERT_RTNL(); if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) goto out; @@ -235,6 +233,22 @@ out: 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; + + rtnl_lock(); + bat_v = container_of(work, struct batadv_priv_bat_v, ogm_wq.work); + bat_priv = container_of(bat_v, struct batadv_priv, bat_v); + batadv_v_ogm_send_softif(bat_priv); + rtnl_unlock(); +} + /** * batadv_v_ogm_iface_enable - prepare an interface for B.A.T.M.A.N. V * @hard_iface: the interface to prepare @@ -261,6 +275,8 @@ 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_ogm2_packet *ogm_packet; + ASSERT_RTNL(); + if (!bat_priv->bat_v.ogm_buff) return; @@ -870,6 +886,8 @@ int batadv_v_ogm_init(struct batadv_priv *bat_priv) unsigned char *ogm_buff; u32 random_seqno; + ASSERT_RTNL(); + bat_priv->bat_v.ogm_buff_len = BATADV_OGM2_HLEN; ogm_buff = kzalloc(bat_priv->bat_v.ogm_buff_len, GFP_ATOMIC); if (!ogm_buff) diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 5c5762f0f89c8b79b52288104d975dc3753bbf82..11ced015ab639f0d82f12ae533a92f356734cafa 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -990,8 +990,8 @@ struct batadv_softif_vlan { /** * struct batadv_priv_bat_v - B.A.T.M.A.N. V per soft-interface private data - * @ogm_buff: buffer holding the OGM packet - * @ogm_buff_len: length of the OGM packet buffer + * @ogm_buff: buffer holding the OGM packet. rtnl protected + * @ogm_buff_len: length of the OGM packet buffer. rtnl protected * @ogm_seqno: OGM sequence number - used to identify each OGM * @ogm_wq: workqueue used to schedule OGM transmissions */