* [PATCH net-next-2.6] net: fix dev_seq_next()
@ 2011-01-27 4:08 Eric Dumazet
2011-01-27 19:33 ` Paul E. McKenney
2011-01-27 22:22 ` David Miller
0 siblings, 2 replies; 3+ messages in thread
From: Eric Dumazet @ 2011-01-27 4:08 UTC (permalink / raw)
To: David Miller, Paul E. McKenney; +Cc: netdev
Paul, the following comment in include/linux/rculist.h is misleading :
"Why is there no list_empty_rcu()? Because list_empty() serves this
purpose..."
This is probably why I made the error ;)
list_empty() has a meaning only if state cannot change right after its
use.
In an rcu_read_lock() section, state _can_ change, so there is no way
list_empty() can be used at all.
Thanks
[PATCH net-next-2.6] 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>
---
Given this was discovered by code analysis rather than a bug report, I
prepared a patch for net-next-2.6. Once fully tested, this could be
backported to 2.6.33
include/linux/netdevice.h | 9 ++++++++-
net/core/dev.c | 11 +++++++----
2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 8858422..c7d7074 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1447,7 +1447,7 @@ static inline struct net_device *next_net_device_rcu(struct net_device *dev)
struct net *net;
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);
}
@@ -1457,6 +1457,13 @@ static inline struct net_device *first_net_device(struct net *net)
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 unsigned long netdev_boot_base(const char *prefix, int unit);
extern struct net_device *dev_getbyhwaddr_rcu(struct net *net, unsigned short type,
diff --git a/net/core/dev.c b/net/core/dev.c
index 1b4c07f..ddd5df2 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -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)
{
- struct net_device *dev = (v == SEQ_START_TOKEN) ?
- first_net_device(seq_file_net(seq)) :
- next_net_device((struct net_device *)v);
+ struct net_device *dev = v;
+
+ if (v == SEQ_START_TOKEN)
+ dev = first_net_device_rcu(seq_file_net(seq));
+ else
+ dev = next_net_device_rcu(dev);
++*pos;
- return rcu_dereference(dev);
+ return dev;
}
void dev_seq_stop(struct seq_file *seq, void *v)
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH net-next-2.6] net: fix dev_seq_next()
2011-01-27 4:08 [PATCH net-next-2.6] net: fix dev_seq_next() Eric Dumazet
@ 2011-01-27 19:33 ` Paul E. McKenney
2011-01-27 22:22 ` David Miller
1 sibling, 0 replies; 3+ messages in thread
From: Paul E. McKenney @ 2011-01-27 19:33 UTC (permalink / raw)
To: Eric Dumazet; +Cc: David Miller, netdev
On Thu, Jan 27, 2011 at 05:08:02AM +0100, Eric Dumazet wrote:
> Paul, the following comment in include/linux/rculist.h is misleading :
>
> "Why is there no list_empty_rcu()? Because list_empty() serves this
> purpose..."
>
> This is probably why I made the error ;)
>
> list_empty() has a meaning only if state cannot change right after its
> use.
>
> In an rcu_read_lock() section, state _can_ change, so there is no way
> list_empty() can be used at all.
My apologies for my messup!!!
So, there are two things that I need to fix:
1. There needs to be a list_empty_rcu() which contains an
rcu_access_pointer() in order to keep sparse happy.
2. The comment at the beginning of include/linux/rculist.h
needs to warn that the return value from this new
list_empty_rcu() API can become instantly obsolete,
so the caller must either hold the update-side lock
or be prepared to deal with obsolete values.
Or am I missing something?
Thanx, Paul
> Thanks
>
> [PATCH net-next-2.6] 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>
> ---
> Given this was discovered by code analysis rather than a bug report, I
> prepared a patch for net-next-2.6. Once fully tested, this could be
> backported to 2.6.33
>
> include/linux/netdevice.h | 9 ++++++++-
> net/core/dev.c | 11 +++++++----
> 2 files changed, 15 insertions(+), 5 deletions(-)
>
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 8858422..c7d7074 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -1447,7 +1447,7 @@ static inline struct net_device *next_net_device_rcu(struct net_device *dev)
> struct net *net;
>
> 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);
> }
>
> @@ -1457,6 +1457,13 @@ static inline struct net_device *first_net_device(struct net *net)
> 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 unsigned long netdev_boot_base(const char *prefix, int unit);
> extern struct net_device *dev_getbyhwaddr_rcu(struct net *net, unsigned short type,
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 1b4c07f..ddd5df2 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -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)
> {
> - struct net_device *dev = (v == SEQ_START_TOKEN) ?
> - first_net_device(seq_file_net(seq)) :
> - next_net_device((struct net_device *)v);
> + struct net_device *dev = v;
> +
> + if (v == SEQ_START_TOKEN)
> + dev = first_net_device_rcu(seq_file_net(seq));
> + else
> + dev = next_net_device_rcu(dev);
>
> ++*pos;
> - return rcu_dereference(dev);
> + return dev;
> }
>
> void dev_seq_stop(struct seq_file *seq, void *v)
>
>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH net-next-2.6] net: fix dev_seq_next()
2011-01-27 4:08 [PATCH net-next-2.6] net: fix dev_seq_next() Eric Dumazet
2011-01-27 19:33 ` Paul E. McKenney
@ 2011-01-27 22:22 ` David Miller
1 sibling, 0 replies; 3+ messages in thread
From: David Miller @ 2011-01-27 22:22 UTC (permalink / raw)
To: eric.dumazet; +Cc: paulmck, netdev
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Thu, 27 Jan 2011 05:08:02 +0100
> [PATCH net-next-2.6] 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>
Applied.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2011-01-27 22:22 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-01-27 4:08 [PATCH net-next-2.6] net: fix dev_seq_next() Eric Dumazet
2011-01-27 19:33 ` Paul E. McKenney
2011-01-27 22:22 ` David Miller
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.