NFS: fix mount/umount race in nlmclnt. [Linux 3.16.72]

This Linux kernel change "NFS: fix mount/umount race in nlmclnt" is included in the Linux 3.16.72 release. This change is authored by NeilBrown <neilb [at] suse.com> on Tue Mar 19 11:33:24 2019 +1100. The commit for this change in Linux stable tree is 761e3db (patch) which is from upstream commit 4a9be28. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 4a9be28.

NFS: fix mount/umount race in nlmclnt.

commit 4a9be28c45bf02fa0436808bb6c0baeba30e120e upstream.

If the last NFSv3 unmount from a given host races with a mount from the
same host, we can destroy an nlm_host that is still in use.

Specifically nlmclnt_lookup_host() can increment h_count on
an nlm_host that nlmclnt_release_host() has just successfully called
refcount_dec_and_test() on.
Once nlmclnt_lookup_host() drops the mutex, nlm_destroy_host_lock()
will be called to destroy the nlmclnt which is now in use again.

The cause of the problem is that the dec_and_test happens outside the
locked region.  This is easily fixed by using
refcount_dec_and_mutex_lock().

Fixes: 8ea6ecc8b075 ("lockd: Create client-side nlm_host cache")
Signed-off-by: NeilBrown <[email protected]>
Signed-off-by: Trond Myklebust <[email protected]>
[bwh: Backported to 3.16: use atomic instead of refcount API]
Signed-off-by: Ben Hutchings <[email protected]>

There are 3 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 fs/lockd/host.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index b31117c..6f12147 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -288,12 +288,11 @@ void nlmclnt_release_host(struct nlm_host *host)

    WARN_ON_ONCE(host->h_server);

-   if (atomic_dec_and_test(&host->h_count)) {
+   if (atomic_dec_and_mutex_lock(&host->h_count, &nlm_host_mutex)) {
        WARN_ON_ONCE(!list_empty(&host->h_lockowners));
        WARN_ON_ONCE(!list_empty(&host->h_granted));
        WARN_ON_ONCE(!list_empty(&host->h_reclaim));

-       mutex_lock(&nlm_host_mutex);
        nlm_destroy_host_locked(host);
        mutex_unlock(&nlm_host_mutex);
    }

Leave a Reply

Your email address will not be published. Required fields are marked *