• P
    list: Fix double fetch of pointer in hlist_entry_safe() · f65846a1
    Paul E. McKenney 提交于
    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: NCAI Qian <caiqian@redhat.com>
    Reported-by: NEric Dumazet <eric.dumazet@gmail.com>
    Signed-off-by: NPaul E. McKenney <paulmck@linux.vnet.ibm.com>
    Tested-by: NLi Zefan <lizefan@huawei.com>
    f65846a1
list.h 20.7 KB