Commit ccf43438 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

net: fix dev_seq_next()

Commit c6d14c84 (net: Introduce for_each_netdev_rcu() iterator)
added a race in dev_seq_next().

The rcu_dereference() call should be done _before_ testing the end of
list, or we might return a wrong net_device if a concurrent thread
changes net_device list under us.

Note : discovered thanks to a sparse warning :

net/core/dev.c:3919:9: error: incompatible types in comparison expression
(different address spaces)
Signed-off-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
CC: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 06582540
...@@ -1447,7 +1447,7 @@ static inline struct net_device *next_net_device_rcu(struct net_device *dev) ...@@ -1447,7 +1447,7 @@ static inline struct net_device *next_net_device_rcu(struct net_device *dev)
struct net *net; struct net *net;
net = dev_net(dev); net = dev_net(dev);
lh = rcu_dereference(dev->dev_list.next); lh = rcu_dereference(list_next_rcu(&dev->dev_list));
return lh == &net->dev_base_head ? NULL : net_device_entry(lh); return lh == &net->dev_base_head ? NULL : net_device_entry(lh);
} }
...@@ -1457,6 +1457,13 @@ static inline struct net_device *first_net_device(struct net *net) ...@@ -1457,6 +1457,13 @@ static inline struct net_device *first_net_device(struct net *net)
net_device_entry(net->dev_base_head.next); net_device_entry(net->dev_base_head.next);
} }
static inline struct net_device *first_net_device_rcu(struct net *net)
{
struct list_head *lh = rcu_dereference(list_next_rcu(&net->dev_base_head));
return lh == &net->dev_base_head ? NULL : net_device_entry(lh);
}
extern int netdev_boot_setup_check(struct net_device *dev); extern int netdev_boot_setup_check(struct net_device *dev);
extern unsigned long netdev_boot_base(const char *prefix, int unit); extern unsigned long netdev_boot_base(const char *prefix, int unit);
extern struct net_device *dev_getbyhwaddr_rcu(struct net *net, unsigned short type, extern struct net_device *dev_getbyhwaddr_rcu(struct net *net, unsigned short type,
......
...@@ -4051,12 +4051,15 @@ void *dev_seq_start(struct seq_file *seq, loff_t *pos) ...@@ -4051,12 +4051,15 @@ void *dev_seq_start(struct seq_file *seq, loff_t *pos)
void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{ {
struct net_device *dev = (v == SEQ_START_TOKEN) ? struct net_device *dev = v;
first_net_device(seq_file_net(seq)) :
next_net_device((struct net_device *)v); if (v == SEQ_START_TOKEN)
dev = first_net_device_rcu(seq_file_net(seq));
else
dev = next_net_device_rcu(dev);
++*pos; ++*pos;
return rcu_dereference(dev); return dev;
} }
void dev_seq_stop(struct seq_file *seq, void *v) void dev_seq_stop(struct seq_file *seq, void *v)
......
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