]> git.baikalelectronics.ru Git - kernel.git/commit
list: Fix double fetch of pointer in hlist_entry_safe()
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Sat, 9 Mar 2013 15:38:41 +0000 (07:38 -0800)
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Thu, 14 Mar 2013 20:18:30 +0000 (13:18 -0700)
commitdd824ee2f4311cfd4d36486273fc6aaeabd8fd83
tree87bd07485309f57d4002a5cd634636e9a2ce3025
parent1d627f8ebb8f17386e39f8027c369ddadbf55bc3
list: Fix double fetch of pointer in hlist_entry_safe()

The current version of hlist_entry_safe() fetches the pointer twice,
once to test for NULL and the other to compute the offset back to the
enclosing structure.  This is OK for normal lock-based use because in
that case, the pointer cannot change.  However, when the pointer is
protected by RCU (as in "rcu_dereference(p)"), then the pointer can
change at any time.  This use case can result in the following sequence
of events:

1. CPU 0 invokes hlist_entry_safe(), fetches the RCU-protected
pointer as sees that it is non-NULL.

2. CPU 1 invokes hlist_del_rcu(), deleting the entry that CPU 0
just fetched a pointer to.  Because this is the last entry
in the list, the pointer fetched by CPU 0 is now NULL.

3. CPU 0 refetches the pointer, obtains NULL, and then gets a
NULL-pointer crash.

This commit therefore applies gcc's "({ })" statement expression to
create a temporary variable so that the specified pointer is fetched
only once, avoiding the above sequence of events.  Please note that
it is the caller's responsibility to use rcu_dereference() as needed.
This allows RCU-protected uses to work correctly without imposing
any additional overhead on the non-RCU case.

Many thanks to Eric Dumazet for spotting root cause!

Reported-by: CAI Qian <caiqian@redhat.com>
Reported-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Tested-by: Li Zefan <lizefan@huawei.com>
include/linux/list.h