Skip to content

Commit

Permalink
batman-adv: Make bat_priv->primary_if an rcu protected pointer
Browse files Browse the repository at this point in the history
The rcu protected macros rcu_dereference() and rcu_assign_pointer()
for the bat_priv->primary_if need to be used, as well as spin/rcu locking.

Otherwise we might end up using a primary_if pointer pointing to already
freed memory.

Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
  • Loading branch information
Marek Lindner authored and Sven Eckelmann committed May 1, 2011
1 parent 71e4aa9 commit 32ae9b2
Show file tree
Hide file tree
Showing 12 changed files with 280 additions and 118 deletions.
33 changes: 19 additions & 14 deletions net/batman-adv/gateway_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -439,30 +439,32 @@ int gw_client_seq_print_text(struct seq_file *seq, void *offset)
{
struct net_device *net_dev = (struct net_device *)seq->private;
struct bat_priv *bat_priv = netdev_priv(net_dev);
struct hard_iface *primary_if;
struct gw_node *gw_node;
struct hlist_node *node;
int gw_count = 0;
int gw_count = 0, ret = 0;

if (!bat_priv->primary_if) {

return seq_printf(seq, "BATMAN mesh %s disabled - please "
"specify interfaces to enable it\n",
net_dev->name);
primary_if = primary_if_get_selected(bat_priv);
if (!primary_if) {
ret = seq_printf(seq, "BATMAN mesh %s disabled - please "
"specify interfaces to enable it\n",
net_dev->name);
goto out;
}

if (bat_priv->primary_if->if_status != IF_ACTIVE) {

return seq_printf(seq, "BATMAN mesh %s disabled - "
"primary interface not active\n",
net_dev->name);
if (primary_if->if_status != IF_ACTIVE) {
ret = seq_printf(seq, "BATMAN mesh %s disabled - "
"primary interface not active\n",
net_dev->name);
goto out;
}

seq_printf(seq, " %-12s (%s/%i) %17s [%10s]: gw_class ... "
"[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n",
"Gateway", "#", TQ_MAX_VALUE, "Nexthop",
"outgoingIF", SOURCE_VERSION, REVISION_VERSION_STR,
bat_priv->primary_if->net_dev->name,
bat_priv->primary_if->net_dev->dev_addr, net_dev->name);
primary_if->net_dev->name,
primary_if->net_dev->dev_addr, net_dev->name);

rcu_read_lock();
hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) {
Expand All @@ -480,7 +482,10 @@ int gw_client_seq_print_text(struct seq_file *seq, void *offset)
if (gw_count == 0)
seq_printf(seq, "No gateways in range ...\n");

return 0;
out:
if (primary_if)
hardif_free_ref(primary_if);
return ret;
}

int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb)
Expand Down
83 changes: 57 additions & 26 deletions net/batman-adv/hard-interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,47 +110,60 @@ static struct hard_iface *hardif_get_active(struct net_device *soft_iface)
return hard_iface;
}

static void update_primary_addr(struct bat_priv *bat_priv)
static void primary_if_update_addr(struct bat_priv *bat_priv)
{
struct vis_packet *vis_packet;
struct hard_iface *primary_if;

primary_if = primary_if_get_selected(bat_priv);
if (!primary_if)
goto out;

vis_packet = (struct vis_packet *)
bat_priv->my_vis_info->skb_packet->data;
memcpy(vis_packet->vis_orig,
bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
memcpy(vis_packet->sender_orig,
bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
primary_if->net_dev->dev_addr, ETH_ALEN);

out:
if (primary_if)
hardif_free_ref(primary_if);
}

static void set_primary_if(struct bat_priv *bat_priv,
struct hard_iface *hard_iface)
static void primary_if_select(struct bat_priv *bat_priv,
struct hard_iface *new_hard_iface)
{
struct hard_iface *curr_hard_iface;
struct batman_packet *batman_packet;
struct hard_iface *old_if;

if (hard_iface && !atomic_inc_not_zero(&hard_iface->refcount))
hard_iface = NULL;
spin_lock_bh(&hardif_list_lock);

old_if = bat_priv->primary_if;
bat_priv->primary_if = hard_iface;
if (new_hard_iface && !atomic_inc_not_zero(&new_hard_iface->refcount))
new_hard_iface = NULL;

if (old_if)
hardif_free_ref(old_if);
curr_hard_iface = bat_priv->primary_if;
rcu_assign_pointer(bat_priv->primary_if, new_hard_iface);

if (!bat_priv->primary_if)
return;
if (curr_hard_iface)
hardif_free_ref(curr_hard_iface);

batman_packet = (struct batman_packet *)(hard_iface->packet_buff);
if (!new_hard_iface)
goto out;

batman_packet = (struct batman_packet *)(new_hard_iface->packet_buff);
batman_packet->flags = PRIMARIES_FIRST_HOP;
batman_packet->ttl = TTL;

update_primary_addr(bat_priv);
primary_if_update_addr(bat_priv);

/***
* hacky trick to make sure that we send the HNA information via
* our new primary interface
*/
atomic_set(&bat_priv->hna_local_changed, 1);

out:
spin_unlock_bh(&hardif_list_lock);
}

static bool hardif_is_iface_up(struct hard_iface *hard_iface)
Expand Down Expand Up @@ -236,9 +249,10 @@ void update_min_mtu(struct net_device *soft_iface)
static void hardif_activate_interface(struct hard_iface *hard_iface)
{
struct bat_priv *bat_priv;
struct hard_iface *primary_if = NULL;

if (hard_iface->if_status != IF_INACTIVE)
return;
goto out;

bat_priv = netdev_priv(hard_iface->soft_iface);

Expand All @@ -249,14 +263,18 @@ static void hardif_activate_interface(struct hard_iface *hard_iface)
* the first active interface becomes our primary interface or
* the next active interface after the old primay interface was removed
*/
if (!bat_priv->primary_if)
set_primary_if(bat_priv, hard_iface);
primary_if = primary_if_get_selected(bat_priv);
if (!primary_if)
primary_if_select(bat_priv, hard_iface);

bat_info(hard_iface->soft_iface, "Interface activated: %s\n",
hard_iface->net_dev->name);

update_min_mtu(hard_iface->soft_iface);
return;

out:
if (primary_if)
hardif_free_ref(primary_if);
}

static void hardif_deactivate_interface(struct hard_iface *hard_iface)
Expand Down Expand Up @@ -386,12 +404,13 @@ int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name)
void hardif_disable_interface(struct hard_iface *hard_iface)
{
struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
struct hard_iface *primary_if = NULL;

if (hard_iface->if_status == IF_ACTIVE)
hardif_deactivate_interface(hard_iface);

if (hard_iface->if_status != IF_INACTIVE)
return;
goto out;

bat_info(hard_iface->soft_iface, "Removing interface: %s\n",
hard_iface->net_dev->name);
Expand All @@ -400,11 +419,12 @@ void hardif_disable_interface(struct hard_iface *hard_iface)
bat_priv->num_ifaces--;
orig_hash_del_if(hard_iface, bat_priv->num_ifaces);

if (hard_iface == bat_priv->primary_if) {
primary_if = primary_if_get_selected(bat_priv);
if (hard_iface == primary_if) {
struct hard_iface *new_if;

new_if = hardif_get_active(hard_iface->soft_iface);
set_primary_if(bat_priv, new_if);
primary_if_select(bat_priv, new_if);

if (new_if)
hardif_free_ref(new_if);
Expand All @@ -425,6 +445,10 @@ void hardif_disable_interface(struct hard_iface *hard_iface)

hard_iface->soft_iface = NULL;
hardif_free_ref(hard_iface);

out:
if (primary_if)
hardif_free_ref(primary_if);
}

static struct hard_iface *hardif_add_interface(struct net_device *net_dev)
Expand Down Expand Up @@ -514,6 +538,7 @@ static int hard_if_event(struct notifier_block *this,
{
struct net_device *net_dev = (struct net_device *)ptr;
struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev);
struct hard_iface *primary_if = NULL;
struct bat_priv *bat_priv;

if (!hard_iface && event == NETDEV_REGISTER)
Expand Down Expand Up @@ -549,8 +574,12 @@ static int hard_if_event(struct notifier_block *this,
update_mac_addresses(hard_iface);

bat_priv = netdev_priv(hard_iface->soft_iface);
if (hard_iface == bat_priv->primary_if)
update_primary_addr(bat_priv);
primary_if = primary_if_get_selected(bat_priv);
if (!primary_if)
goto hardif_put;

if (hard_iface == primary_if)
primary_if_update_addr(bat_priv);
break;
default:
break;
Expand All @@ -559,6 +588,8 @@ static int hard_if_event(struct notifier_block *this,
hardif_put:
hardif_free_ref(hard_iface);
out:
if (primary_if)
hardif_free_ref(primary_if);
return NOTIFY_DONE;
}

Expand Down
18 changes: 18 additions & 0 deletions net/batman-adv/hard-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,22 @@ static inline void hardif_free_ref(struct hard_iface *hard_iface)
call_rcu(&hard_iface->rcu, hardif_free_rcu);
}

static inline struct hard_iface *primary_if_get_selected(
struct bat_priv *bat_priv)
{
struct hard_iface *hard_iface;

rcu_read_lock();
hard_iface = rcu_dereference(bat_priv->primary_if);
if (!hard_iface)
goto out;

if (!atomic_inc_not_zero(&hard_iface->refcount))
hard_iface = NULL;

out:
rcu_read_unlock();
return hard_iface;
}

#endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */
19 changes: 14 additions & 5 deletions net/batman-adv/icmp_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
{
struct socket_client *socket_client = file->private_data;
struct bat_priv *bat_priv = socket_client->bat_priv;
struct hard_iface *primary_if = NULL;
struct sk_buff *skb;
struct icmp_packet_rr *icmp_packet;

Expand All @@ -167,15 +168,21 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
return -EINVAL;
}

if (!bat_priv->primary_if)
return -EFAULT;
primary_if = primary_if_get_selected(bat_priv);

if (!primary_if) {
len = -EFAULT;
goto out;
}

if (len >= sizeof(struct icmp_packet_rr))
packet_len = sizeof(struct icmp_packet_rr);

skb = dev_alloc_skb(packet_len + sizeof(struct ethhdr));
if (!skb)
return -ENOMEM;
if (!skb) {
len = -ENOMEM;
goto out;
}

skb_reserve(skb, sizeof(struct ethhdr));
icmp_packet = (struct icmp_packet_rr *)skb_put(skb, packet_len);
Expand Down Expand Up @@ -233,7 +240,7 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
goto dst_unreach;

memcpy(icmp_packet->orig,
bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
primary_if->net_dev->dev_addr, ETH_ALEN);

if (packet_len == sizeof(struct icmp_packet_rr))
memcpy(icmp_packet->rr,
Expand All @@ -248,6 +255,8 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
free_skb:
kfree_skb(skb);
out:
if (primary_if)
hardif_free_ref(primary_if);
if (neigh_node)
neigh_node_free_ref(neigh_node);
if (orig_node)
Expand Down
34 changes: 21 additions & 13 deletions net/batman-adv/originator.c
Original file line number Diff line number Diff line change
Expand Up @@ -405,29 +405,34 @@ int orig_seq_print_text(struct seq_file *seq, void *offset)
struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_node *node, *node_tmp;
struct hlist_head *head;
struct hard_iface *primary_if;
struct orig_node *orig_node;
struct neigh_node *neigh_node, *neigh_node_tmp;
int batman_count = 0;
int last_seen_secs;
int last_seen_msecs;
int i;
int i, ret = 0;

primary_if = primary_if_get_selected(bat_priv);

if ((!bat_priv->primary_if) ||
(bat_priv->primary_if->if_status != IF_ACTIVE)) {
if (!bat_priv->primary_if)
return seq_printf(seq, "BATMAN mesh %s disabled - "
"please specify interfaces to enable it\n",
net_dev->name);
if (!primary_if) {
ret = seq_printf(seq, "BATMAN mesh %s disabled - "
"please specify interfaces to enable it\n",
net_dev->name);
goto out;
}

return seq_printf(seq, "BATMAN mesh %s "
"disabled - primary interface not active\n",
net_dev->name);
if (primary_if->if_status != IF_ACTIVE) {
ret = seq_printf(seq, "BATMAN mesh %s "
"disabled - primary interface not active\n",
net_dev->name);
goto out;
}

seq_printf(seq, "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n",
SOURCE_VERSION, REVISION_VERSION_STR,
bat_priv->primary_if->net_dev->name,
bat_priv->primary_if->net_dev->dev_addr, net_dev->name);
primary_if->net_dev->name,
primary_if->net_dev->dev_addr, net_dev->name);
seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n",
"Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop",
"outgoingIF", "Potential nexthops");
Expand Down Expand Up @@ -474,7 +479,10 @@ int orig_seq_print_text(struct seq_file *seq, void *offset)
if (batman_count == 0)
seq_printf(seq, "No batman nodes in range ...\n");

return 0;
out:
if (primary_if)
hardif_free_ref(primary_if);
return ret;
}

static int orig_node_add_if(struct orig_node *orig_node, int max_if_num)
Expand Down
Loading

0 comments on commit 32ae9b2

Please sign in to comment.