aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVasily Averin <vvs@virtuozzo.com>2017-11-10 02:19:26 -0500
committerJ. Bruce Fields <bfields@redhat.com>2017-11-27 16:45:11 -0500
commit6b18dd1c03e07262ea0866084856b2a3c5ba8d09 (patch)
tree0e9780bbbd7ae2aa0913a5ebbff8822d5fa7edd4
parentee24eac3ebb781c12a654985e33ecaa07f4d0f95 (diff)
race of lockd inetaddr notifiers vs 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 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>
-rw-r--r--fs/lockd/svc.c16
1 files changed, 14 insertions, 2 deletions
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 4acf0ebd9802..9c36d614bf89 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -57,6 +57,9 @@ static struct task_struct *nlmsvc_task;
57static struct svc_rqst *nlmsvc_rqst; 57static struct svc_rqst *nlmsvc_rqst;
58unsigned long nlmsvc_timeout; 58unsigned long nlmsvc_timeout;
59 59
60atomic_t nlm_ntf_refcnt = ATOMIC_INIT(0);
61DECLARE_WAIT_QUEUE_HEAD(nlm_ntf_wq);
62
60unsigned int lockd_net_id; 63unsigned int lockd_net_id;
61 64
62/* 65/*
@@ -293,7 +296,8 @@ static int lockd_inetaddr_event(struct notifier_block *this,
293 struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; 296 struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
294 struct sockaddr_in sin; 297 struct sockaddr_in sin;
295 298
296 if (event != NETDEV_DOWN) 299 if ((event != NETDEV_DOWN) ||
300 !atomic_inc_not_zero(&nlm_ntf_refcnt))
297 goto out; 301 goto out;
298 302
299 if (nlmsvc_rqst) { 303 if (nlmsvc_rqst) {
@@ -304,6 +308,8 @@ static int lockd_inetaddr_event(struct notifier_block *this,
304 svc_age_temp_xprts_now(nlmsvc_rqst->rq_server, 308 svc_age_temp_xprts_now(nlmsvc_rqst->rq_server,
305 (struct sockaddr *)&sin); 309 (struct sockaddr *)&sin);
306 } 310 }
311 atomic_dec(&nlm_ntf_refcnt);
312 wake_up(&nlm_ntf_wq);
307 313
308out: 314out:
309 return NOTIFY_DONE; 315 return NOTIFY_DONE;
@@ -320,7 +326,8 @@ static int lockd_inet6addr_event(struct notifier_block *this,
320 struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; 326 struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
321 struct sockaddr_in6 sin6; 327 struct sockaddr_in6 sin6;
322 328
323 if (event != NETDEV_DOWN) 329 if ((event != NETDEV_DOWN) ||
330 !atomic_inc_not_zero(&nlm_ntf_refcnt))
324 goto out; 331 goto out;
325 332
326 if (nlmsvc_rqst) { 333 if (nlmsvc_rqst) {
@@ -332,6 +339,8 @@ static int lockd_inet6addr_event(struct notifier_block *this,
332 svc_age_temp_xprts_now(nlmsvc_rqst->rq_server, 339 svc_age_temp_xprts_now(nlmsvc_rqst->rq_server,
333 (struct sockaddr *)&sin6); 340 (struct sockaddr *)&sin6);
334 } 341 }
342 atomic_dec(&nlm_ntf_refcnt);
343 wake_up(&nlm_ntf_wq);
335 344
336out: 345out:
337 return NOTIFY_DONE; 346 return NOTIFY_DONE;
@@ -348,10 +357,12 @@ static void lockd_unregister_notifiers(void)
348#if IS_ENABLED(CONFIG_IPV6) 357#if IS_ENABLED(CONFIG_IPV6)
349 unregister_inet6addr_notifier(&lockd_inet6addr_notifier); 358 unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
350#endif 359#endif
360 wait_event(nlm_ntf_wq, atomic_read(&nlm_ntf_refcnt) == 0);
351} 361}
352 362
353static void lockd_svc_exit_thread(void) 363static void lockd_svc_exit_thread(void)
354{ 364{
365 atomic_dec(&nlm_ntf_refcnt);
355 lockd_unregister_notifiers(); 366 lockd_unregister_notifiers();
356 svc_exit_thread(nlmsvc_rqst); 367 svc_exit_thread(nlmsvc_rqst);
357} 368}
@@ -376,6 +387,7 @@ static int lockd_start_svc(struct svc_serv *serv)
376 goto out_rqst; 387 goto out_rqst;
377 } 388 }
378 389
390 atomic_inc(&nlm_ntf_refcnt);
379 svc_sock_update_bufs(serv); 391 svc_sock_update_bufs(serv);
380 serv->sv_maxconn = nlm_max_connections; 392 serv->sv_maxconn = nlm_max_connections;
381 393