Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit df33f711 authored by Andrii Nakryiko's avatar Andrii Nakryiko Committed by Sayali Lokhande
Browse files

fs: fix NULL dereference due to data race in prepend_path()



Fix data race in prepend_path() with re-reading mnt->mnt_ns twice
without holding the lock.

is_mounted() does check for NULL, but is_anon_ns(mnt->mnt_ns) might
re-read the pointer again which could be NULL already, if in between
reads one of kern_unmount()/kern_unmount_array()/umount_tree() sets
mnt->mnt_ns to NULL.

This is seen in production with the following stack trace:

  BUG: kernel NULL pointer dereference, address: 0000000000000048
  ...
  RIP: 0010:prepend_path.isra.4+0x1ce/0x2e0
  Call Trace:
    d_path+0xe6/0x150
    proc_pid_readlink+0x8f/0x100
    vfs_readlink+0xf8/0x110
    do_readlinkat+0xfd/0x120
    __x64_sys_readlinkat+0x1a/0x20
    do_syscall_64+0x42/0x110
    entry_SYSCALL_64_after_hwframe+0x44/0xa9

Fixes: f2683bd8 ("[PATCH] fix d_absolute_path() interplay with fsmount()")
Change-Id: If634d570fb7f37f4a77ae4f10970013dbc0762cb
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Reviewed-by: default avatarJosef Bacik <josef@toxicpanda.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Git-commit: 09cad07547445bf3a41683e4d3abcd154c123ef5
Git-repo: https://android.googlesource.com/kernel/common/


Signed-off-by: default avatarSayali Lokhande <sayalil@codeaurora.org>
parent aa860b5b
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -102,6 +102,8 @@ static int prepend_path(const struct path *path,

		if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
			struct mount *parent = READ_ONCE(mnt->mnt_parent);
			struct mnt_namespace *mnt_ns;

			/* Escaped? */
			if (dentry != vfsmnt->mnt_root) {
				bptr = *buffer;
@@ -116,7 +118,9 @@ static int prepend_path(const struct path *path,
				vfsmnt = &mnt->mnt;
				continue;
			}
			if (is_mounted(vfsmnt) && !is_anon_ns(mnt->mnt_ns))
			mnt_ns = READ_ONCE(mnt->mnt_ns);
			/* open-coded is_mounted() to use local mnt_ns */
			if (!IS_ERR_OR_NULL(mnt_ns) && !is_anon_ns(mnt_ns))
				error = 1;	// absolute root
			else
				error = 2;	// detached or not attached yet