All of lore.kernel.org
 help / color / mirror / Atom feed
* [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.