diff options
author | Marek Lindner <lindner_marek@yahoo.de> | 2011-02-10 14:33:51 +0000 |
---|---|---|
committer | Marek Lindner <lindner_marek@yahoo.de> | 2011-03-05 12:50:07 +0100 |
commit | ed75ccbe26f4a672a41556120390e67c80a2c441 (patch) | |
tree | 3f4f844d2311e662b4c42e7a275188d1346ac77d /net/batman-adv/bat_sysfs.c | |
parent | 7d2b554826195372764910da2f0dcb0d9b869108 (diff) | |
download | lwn-ed75ccbe26f4a672a41556120390e67c80a2c441.tar.gz lwn-ed75ccbe26f4a672a41556120390e67c80a2c441.zip |
batman-adv: Correct rcu refcounting for batman_if
It might be possible that 2 threads access the same data in the same
rcu grace period. The first thread calls call_rcu() to decrement the
refcount and free the data while the second thread increases the
refcount to use the data. To avoid this race condition all refcount
operations have to be atomic.
Reported-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Diffstat (limited to 'net/batman-adv/bat_sysfs.c')
-rw-r--r-- | net/batman-adv/bat_sysfs.c | 20 |
1 files changed, 9 insertions, 11 deletions
diff --git a/net/batman-adv/bat_sysfs.c b/net/batman-adv/bat_sysfs.c index f7b93a0805fe..93ae20aaad0a 100644 --- a/net/batman-adv/bat_sysfs.c +++ b/net/batman-adv/bat_sysfs.c @@ -450,7 +450,7 @@ static ssize_t show_mesh_iface(struct kobject *kobj, struct attribute *attr, length = sprintf(buff, "%s\n", batman_if->if_status == IF_NOT_IN_USE ? "none" : batman_if->soft_iface->name); - kref_put(&batman_if->refcount, hardif_free_ref); + hardif_free_ref(batman_if); return length; } @@ -461,7 +461,7 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, struct net_device *net_dev = kobj_to_netdev(kobj); struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); int status_tmp = -1; - int ret; + int ret = count; if (!batman_if) return count; @@ -472,7 +472,7 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, if (strlen(buff) >= IFNAMSIZ) { pr_err("Invalid parameter for 'mesh_iface' setting received: " "interface name too long '%s'\n", buff); - kref_put(&batman_if->refcount, hardif_free_ref); + hardif_free_ref(batman_if); return -EINVAL; } @@ -482,17 +482,14 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, status_tmp = IF_I_WANT_YOU; if ((batman_if->if_status == status_tmp) || ((batman_if->soft_iface) && - (strncmp(batman_if->soft_iface->name, buff, IFNAMSIZ) == 0))) { - kref_put(&batman_if->refcount, hardif_free_ref); - return count; - } + (strncmp(batman_if->soft_iface->name, buff, IFNAMSIZ) == 0))) + goto out; if (status_tmp == IF_NOT_IN_USE) { rtnl_lock(); hardif_disable_interface(batman_if); rtnl_unlock(); - kref_put(&batman_if->refcount, hardif_free_ref); - return count; + goto out; } /* if the interface already is in use */ @@ -503,8 +500,9 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, } ret = hardif_enable_interface(batman_if, buff); - kref_put(&batman_if->refcount, hardif_free_ref); +out: + hardif_free_ref(batman_if); return ret; } @@ -537,7 +535,7 @@ static ssize_t show_iface_status(struct kobject *kobj, struct attribute *attr, break; } - kref_put(&batman_if->refcount, hardif_free_ref); + hardif_free_ref(batman_if); return length; } |