Skip to content

Commit 1eca45f

Browse files
vaverinJ. Bruce Fields
authored andcommitted
NFSD: fix corruption in notifier registration
By design notifier can be registered once only, however nfsd registers the same inetaddr notifiers per net-namespace. When this happen it corrupts list of notifiers, as result some notifiers can be not called on proper event, traverse on list can be cycled forever, and second unregister can access already freed memory. Cc: [email protected] fixes: 3668499 ("nfsd: Register callbacks on the inetaddr_chain and inet6addr_chain") Signed-off-by: Vasily Averin <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Cc: [email protected] Signed-off-by: J. Bruce Fields <[email protected]>
1 parent 25d5529 commit 1eca45f

File tree

1 file changed

+14
-4
lines changed

1 file changed

+14
-4
lines changed

fs/nfsd/nfssvc.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -366,14 +366,21 @@ static struct notifier_block nfsd_inet6addr_notifier = {
366366
};
367367
#endif
368368

369+
/* Only used under nfsd_mutex, so this atomic may be overkill: */
370+
static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
371+
369372
static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
370373
{
371374
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
372375

373-
unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
376+
/* check if the notifier still has clients */
377+
if (atomic_dec_return(&nfsd_notifier_refcount) == 0) {
378+
unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
374379
#if IS_ENABLED(CONFIG_IPV6)
375-
unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
380+
unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
376381
#endif
382+
}
383+
377384
/*
378385
* write_ports can create the server without actually starting
379386
* any threads--if we get shut down before any threads are
@@ -488,10 +495,13 @@ int nfsd_create_serv(struct net *net)
488495
}
489496

490497
set_max_drc();
491-
register_inetaddr_notifier(&nfsd_inetaddr_notifier);
498+
/* check if the notifier is already set */
499+
if (atomic_inc_return(&nfsd_notifier_refcount) == 1) {
500+
register_inetaddr_notifier(&nfsd_inetaddr_notifier);
492501
#if IS_ENABLED(CONFIG_IPV6)
493-
register_inet6addr_notifier(&nfsd_inet6addr_notifier);
502+
register_inet6addr_notifier(&nfsd_inet6addr_notifier);
494503
#endif
504+
}
495505
do_gettimeofday(&nn->nfssvc_boot); /* record boot time */
496506
return 0;
497507
}

0 commit comments

Comments
 (0)