fs/ocfs2: fix race in ocfs2_dentry_attach_lock() [Linux 4.14.128]

fs/ocfs2: fix race in ocfs2_dentry_attach_lock() [Linux 4.14.128]

This Linux kernel change "fs/ocfs2: fix race in ocfs2_dentry_attach_lock()" is included in the Linux 4.14.128 release. This change is authored by Wengang Wang <wen.gang.wang [at] oracle.com> on Thu Jun 13 15:56:01 2019 -0700. The commit for this change in Linux stable tree is f1c0864 (patch) which is from upstream commit be99ca2. 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 be99ca2.

fs/ocfs2: fix race in ocfs2_dentry_attach_lock()

commit be99ca2716972a712cde46092c54dee5e6192bf8 upstream.

ocfs2_dentry_attach_lock() can be executed in parallel threads against the
same dentry.  Make that race safe.  The race is like this:

            thread A                               thread B

(A1) enter ocfs2_dentry_attach_lock,
seeing dentry->d_fsdata is NULL,
and no alias found by
ocfs2_find_local_alias, so kmalloc
a new ocfs2_dentry_lock structure
to local variable "dl", dl1

               .....

                                    (B1) enter ocfs2_dentry_attach_lock,
                                    seeing dentry->d_fsdata is NULL,
                                    and no alias found by
                                    ocfs2_find_local_alias so kmalloc
                                    a new ocfs2_dentry_lock structure
                                    to local variable "dl", dl2.

                                                   ......

(A2) set dentry->d_fsdata with dl1,
call ocfs2_dentry_lock() and increase
dl1->dl_lockres.l_ro_holders to 1 on
success.
              ......

                                    (B2) set dentry->d_fsdata with dl2
                                    call ocfs2_dentry_lock() and increase
                    dl2->dl_lockres.l_ro_holders to 1 on
                    success.

                                                  ......

(A3) call ocfs2_dentry_unlock()
and decrease
dl2->dl_lockres.l_ro_holders to 0
on success.
             ....

                                    (B3) call ocfs2_dentry_unlock(),
                                    decreasing
                    dl2->dl_lockres.l_ro_holders, but
                    see it's zero now, panic

Link: http://lkml.kernel.org/r/20190529174636.22364-1-wen.gang.wang@oracle.com
Signed-off-by: Wengang Wang <wen.gang.wang@oracle.com>
Reported-by: Daniel Sobe <daniel.sobe@nxp.com>
Tested-by: Daniel Sobe <daniel.sobe@nxp.com>
Reviewed-by: Changwei Ge <gechangwei@live.cn>
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Mark Fasheh <mark@fasheh.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Gang He <ghe@suse.com>
Cc: Jun Piao <piaojun@huawei.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

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

 fs/ocfs2/dcache.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c
index 2903730..e8ace3b 100644
--- a/fs/ocfs2/dcache.c
+++ b/fs/ocfs2/dcache.c
@@ -310,6 +310,18 @@ int ocfs2_dentry_attach_lock(struct dentry *dentry,

 out_attach:
    spin_lock(&dentry_attach_lock);
+   if (unlikely(dentry->d_fsdata && !alias)) {
+       /* d_fsdata is set by a racing thread which is doing
+        * the same thing as this thread is doing. Leave the racing
+        * thread going ahead and we return here.
+        */
+       spin_unlock(&dentry_attach_lock);
+       iput(dl->dl_inode);
+       ocfs2_lock_res_free(&dl->dl_lockres);
+       kfree(dl);
+       return 0;
+   }
+
    dentry->d_fsdata = dl;
    dl->dl_count++;
    spin_unlock(&dentry_attach_lock);

Leave a Reply

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