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: stable@vger.kernel.org
fixes: 36684996
("nfsd: Register callbacks on the inetaddr_chain and inet6addr_chain")
Signed-off-by: Vasily Averin <vvs@virtuozzo.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
This commit is contained in:
Родитель
25d55296dd
Коммит
1eca45f8a8
|
@ -366,14 +366,21 @@ static struct notifier_block nfsd_inet6addr_notifier = {
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Only used under nfsd_mutex, so this atomic may be overkill: */
|
||||||
|
static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
|
||||||
|
|
||||||
static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
|
static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
|
||||||
{
|
{
|
||||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||||
|
|
||||||
unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
|
/* check if the notifier still has clients */
|
||||||
|
if (atomic_dec_return(&nfsd_notifier_refcount) == 0) {
|
||||||
|
unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
|
unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* write_ports can create the server without actually starting
|
* write_ports can create the server without actually starting
|
||||||
* any threads--if we get shut down before any threads are
|
* any threads--if we get shut down before any threads are
|
||||||
|
@ -488,10 +495,13 @@ int nfsd_create_serv(struct net *net)
|
||||||
}
|
}
|
||||||
|
|
||||||
set_max_drc();
|
set_max_drc();
|
||||||
register_inetaddr_notifier(&nfsd_inetaddr_notifier);
|
/* check if the notifier is already set */
|
||||||
|
if (atomic_inc_return(&nfsd_notifier_refcount) == 1) {
|
||||||
|
register_inetaddr_notifier(&nfsd_inetaddr_notifier);
|
||||||
#if IS_ENABLED(CONFIG_IPV6)
|
#if IS_ENABLED(CONFIG_IPV6)
|
||||||
register_inet6addr_notifier(&nfsd_inet6addr_notifier);
|
register_inet6addr_notifier(&nfsd_inet6addr_notifier);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
do_gettimeofday(&nn->nfssvc_boot); /* record boot time */
|
do_gettimeofday(&nn->nfssvc_boot); /* record boot time */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче