diff options
author | Vasily Averin <vvs@virtuozzo.com> | 2017-11-10 02:19:26 -0500 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2017-11-27 16:45:11 -0500 |
commit | 6b18dd1c03e07262ea0866084856b2a3c5ba8d09 (patch) | |
tree | 0e9780bbbd7ae2aa0913a5ebbff8822d5fa7edd4 | |
parent | ee24eac3ebb781c12a654985e33ecaa07f4d0f95 (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.c | 16 |
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; | |||
57 | static struct svc_rqst *nlmsvc_rqst; | 57 | static struct svc_rqst *nlmsvc_rqst; |
58 | unsigned long nlmsvc_timeout; | 58 | unsigned long nlmsvc_timeout; |
59 | 59 | ||
60 | atomic_t nlm_ntf_refcnt = ATOMIC_INIT(0); | ||
61 | DECLARE_WAIT_QUEUE_HEAD(nlm_ntf_wq); | ||
62 | |||
60 | unsigned int lockd_net_id; | 63 | unsigned 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 | ||
308 | out: | 314 | out: |
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 | ||
336 | out: | 345 | out: |
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 | ||
353 | static void lockd_svc_exit_thread(void) | 363 | static 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 | ||