[RHEL7,COMMIT] ms/netfilter: ctnetlink: netns exit must wait for callbacks

Submitted by Konstantin Khorenko on May 28, 2020, 8:38 a.m.

Details

Message ID 202005280838.04S8co9B017079@finist-ce7.sw.ru
State New
Series "ms/netfilter: ctnetlink: netns exit must wait for callbacks"
Headers show

Commit Message

Konstantin Khorenko May 28, 2020, 8:38 a.m.
The commit is pushed to "branch-rh7-3.10.0-1127.8.2.vz7.151.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-1127.8.2.vz7.151.6
------>
commit 84701b8b4a67e1e97d232b4cc4cfb626c7bd881c
Author: Florian Westphal <fw@strlen.de>
Date:   Fri Nov 15 12:39:23 2019 +0100

    ms/netfilter: ctnetlink: netns exit must wait for callbacks
    
    Curtis Taylor and Jon Maxwell reported and debugged a crash on 3.10
    based kernel.
    
    Crash occurs in ctnetlink_conntrack_events because net->nfnl socket is
    NULL.  The nfnl socket was set to NULL by netns destruction running on
    another cpu.
    
    The exiting network namespace calls the relevant destructors in the
    following order:
    
    1. ctnetlink_net_exit_batch
    
    This nulls out the event callback pointer in struct netns.
    
    2. nfnetlink_net_exit_batch
    
    This nulls net->nfnl socket and frees it.
    
    3. nf_conntrack_cleanup_net_list
    
    This removes all remaining conntrack entries.
    
    This is order is correct. The only explanation for the crash so ar is:
    
    cpu1: conntrack is dying, eviction occurs:
     -> nf_ct_delete()
       -> nf_conntrack_event_report \
         -> nf_conntrack_eventmask_report
           -> notify->fcn() (== ctnetlink_conntrack_events).
    
    cpu1: a. fetches rcu protected pointer to obtain ctnetlink event callback.
          b. gets interrupted.
     cpu2: runs netns exit handlers:
         a runs ctnetlink destructor, event cb pointer set to NULL.
         b runs nfnetlink destructor, nfnl socket is closed and set to NULL.
    cpu1: c. resumes and trips over NULL net->nfnl.
    
    Problem appears to be that ctnetlink_net_exit_batch only prevents future
    callers of nf_conntrack_eventmask_report() from obtaining the callback.
    It doesn't wait of other cpus that might have already obtained the
    callbacks address.
    
    I don't see anything in upstream kernels that would prevent similar
    crash: We need to wait for all cpus to have exited the event callback.
    
    Fixes: 9592a5c01e79dbc59eb56fa ("netfilter: ctnetlink: netns support")
    Signed-off-by: Florian Westphal <fw@strlen.de>
    Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
    
    https://jira.sw.ru/browse/PSBM-104387
    
    (cherry picked from commit 18a110b022a5c02e7dc9f6109d0bd93e58ac6ebb)
    Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
---
 net/netfilter/nf_conntrack_netlink.c | 3 +++
 1 file changed, 3 insertions(+)

Patch hide | download patch | download mbox

diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 302997f1a0458..30fd8b13362b2 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3316,6 +3316,9 @@  static void __net_exit ctnetlink_net_exit_batch(struct list_head *net_exit_list)
 
 	list_for_each_entry(net, net_exit_list, exit_list)
 		ctnetlink_net_exit(net);
+
+	/* wait for other cpus until they are done with ctnl_notifiers */
+	synchronize_rcu();
 }
 
 static struct pernet_operations ctnetlink_net_ops = {