race of lockd inetaddr notifiers vs nlmsvc_rqst change
[ Upstream commit 6b18dd1c03
]
lockd_inet[6]addr_event use nlmsvc_rqst without taken nlmsvc_mutex,
nlmsvc_rqst can be changed during execution of notifiers and crash the host.
Patch enables access to nlmsvc_rqst only when it was correctly initialized
and delays its cleanup until notifiers are no longer in use.
Note that nlmsvc_rqst can be temporally set to ERR_PTR, so the "if
(nlmsvc_rqst)" check in notifiers is insufficient on its own.
Signed-off-by: Vasily Averin <vvs@virtuozzo.com>
Tested-by: Scott Mayhew <smayhew@redhat.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
pull/10/head
parent
631db7f389
commit
156c80d4e1
|
@ -57,6 +57,9 @@ static struct task_struct *nlmsvc_task;
|
|||
static struct svc_rqst *nlmsvc_rqst;
|
||||
unsigned long nlmsvc_timeout;
|
||||
|
||||
atomic_t nlm_ntf_refcnt = ATOMIC_INIT(0);
|
||||
DECLARE_WAIT_QUEUE_HEAD(nlm_ntf_wq);
|
||||
|
||||
unsigned int lockd_net_id;
|
||||
|
||||
/*
|
||||
|
@ -292,7 +295,8 @@ static int lockd_inetaddr_event(struct notifier_block *this,
|
|||
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
|
||||
struct sockaddr_in sin;
|
||||
|
||||
if (event != NETDEV_DOWN)
|
||||
if ((event != NETDEV_DOWN) ||
|
||||
!atomic_inc_not_zero(&nlm_ntf_refcnt))
|
||||
goto out;
|
||||
|
||||
if (nlmsvc_rqst) {
|
||||
|
@ -303,6 +307,8 @@ static int lockd_inetaddr_event(struct notifier_block *this,
|
|||
svc_age_temp_xprts_now(nlmsvc_rqst->rq_server,
|
||||
(struct sockaddr *)&sin);
|
||||
}
|
||||
atomic_dec(&nlm_ntf_refcnt);
|
||||
wake_up(&nlm_ntf_wq);
|
||||
|
||||
out:
|
||||
return NOTIFY_DONE;
|
||||
|
@ -319,7 +325,8 @@ static int lockd_inet6addr_event(struct notifier_block *this,
|
|||
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
|
||||
struct sockaddr_in6 sin6;
|
||||
|
||||
if (event != NETDEV_DOWN)
|
||||
if ((event != NETDEV_DOWN) ||
|
||||
!atomic_inc_not_zero(&nlm_ntf_refcnt))
|
||||
goto out;
|
||||
|
||||
if (nlmsvc_rqst) {
|
||||
|
@ -331,6 +338,8 @@ static int lockd_inet6addr_event(struct notifier_block *this,
|
|||
svc_age_temp_xprts_now(nlmsvc_rqst->rq_server,
|
||||
(struct sockaddr *)&sin6);
|
||||
}
|
||||
atomic_dec(&nlm_ntf_refcnt);
|
||||
wake_up(&nlm_ntf_wq);
|
||||
|
||||
out:
|
||||
return NOTIFY_DONE;
|
||||
|
@ -347,10 +356,12 @@ static void lockd_unregister_notifiers(void)
|
|||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
|
||||
#endif
|
||||
wait_event(nlm_ntf_wq, atomic_read(&nlm_ntf_refcnt) == 0);
|
||||
}
|
||||
|
||||
static void lockd_svc_exit_thread(void)
|
||||
{
|
||||
atomic_dec(&nlm_ntf_refcnt);
|
||||
lockd_unregister_notifiers();
|
||||
svc_exit_thread(nlmsvc_rqst);
|
||||
}
|
||||
|
@ -375,6 +386,7 @@ static int lockd_start_svc(struct svc_serv *serv)
|
|||
goto out_rqst;
|
||||
}
|
||||
|
||||
atomic_inc(&nlm_ntf_refcnt);
|
||||
svc_sock_update_bufs(serv);
|
||||
serv->sv_maxconn = nlm_max_connections;
|
||||
|
||||
|
|
Loading…
Reference in New Issue