mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-15 12:47:47 +00:00
net: fix dev_seq_next()
Commit c6d14c84566d (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: Eric Dumazet <eric.dumazet@gmail.com> CC: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
065825402c
commit
ccf434380d
@ -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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user