[RHEL7,COMMIT] lockd: race of lockd inetaddr notifiers with nlmsvc_rqst change

Submitted by Konstantin Khorenko on Oct. 31, 2017, 10:16 a.m.

Details

Message ID 201710311016.v9VAGJFJ017800@finist_ce7.work
State New
Series "race of lockd inetaddr notifiers with nlmsvc_rqst change"
Headers show

Commit Message

Konstantin Khorenko Oct. 31, 2017, 10:16 a.m.
The commit is pushed to "branch-rh7-3.10.0-693.1.1.vz7.37.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-693.1.1.vz7.37.20
------>
commit ff9269036e800b15c3d7feb81b4fefce461527f6
Author: Vasily Averin <vvs@virtuozzo.com>
Date:   Tue Oct 31 13:16:19 2017 +0300

    lockd: race of lockd inetaddr notifiers with nlmsvc_rqst change
    
    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 exit.
    
    https://jira.sw.ru/browse/PSBM-76103
    Signed-off-by: Vasily Averin <vvs@virtuozzo.com>
---
 fs/lockd/svc.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

Patch hide | download patch | download mbox

diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index ba276bf..5920bae 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -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);
+
 int lockd_net_id;
 
 /*
@@ -289,7 +292,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) {
@@ -300,6 +304,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;
@@ -316,7 +322,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) {
@@ -326,6 +333,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;
@@ -342,10 +351,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);
 }
@@ -369,6 +380,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;