From 04cf48427e075bf083744dae35c4ffc323694c1d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 2 Feb 2020 20:49:03 +0000 Subject: [PATCH] 4.19-stable patches added patches: vfs-fix-do_last-regression.patch --- queue-4.19/series | 1 + queue-4.19/vfs-fix-do_last-regression.patch | 63 +++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 queue-4.19/series create mode 100644 queue-4.19/vfs-fix-do_last-regression.patch diff --git a/queue-4.19/series b/queue-4.19/series new file mode 100644 index 00000000000..ec6c1699354 --- /dev/null +++ b/queue-4.19/series @@ -0,0 +1 @@ +vfs-fix-do_last-regression.patch diff --git a/queue-4.19/vfs-fix-do_last-regression.patch b/queue-4.19/vfs-fix-do_last-regression.patch new file mode 100644 index 00000000000..4d42be6ccda --- /dev/null +++ b/queue-4.19/vfs-fix-do_last-regression.patch @@ -0,0 +1,63 @@ +From 6404674acd596de41fd3ad5f267b4525494a891a Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Sat, 1 Feb 2020 16:26:45 +0000 +Subject: vfs: fix do_last() regression + +From: Al Viro + +commit 6404674acd596de41fd3ad5f267b4525494a891a upstream. + +Brown paperbag time: fetching ->i_uid/->i_mode really should've been +done from nd->inode. I even suggested that, but the reason for that has +slipped through the cracks and I went for dir->d_inode instead - made +for more "obvious" patch. + +Analysis: + + - at the entry into do_last() and all the way to step_into(): dir (aka + nd->path.dentry) is known not to have been freed; so's nd->inode and + it's equal to dir->d_inode unless we are already doomed to -ECHILD. + inode of the file to get opened is not known. + + - after step_into(): inode of the file to get opened is known; dir + might be pointing to freed memory/be negative/etc. + + - at the call of may_create_in_sticky(): guaranteed to be out of RCU + mode; inode of the file to get opened is known and pinned; dir might + be garbage. + +The last was the reason for the original patch. Except that at the +do_last() entry we can be in RCU mode and it is possible that +nd->path.dentry->d_inode has already changed under us. + +In that case we are going to fail with -ECHILD, but we need to be +careful; nd->inode is pointing to valid struct inode and it's the same +as nd->path.dentry->d_inode in "won't fail with -ECHILD" case, so we +should use that. + +Reported-by: "Rantala, Tommi T. (Nokia - FI/Espoo)" +Reported-by: syzbot+190005201ced78a74ad6@syzkaller.appspotmail.com +Wearing-brown-paperbag: Al Viro +Cc: stable@kernel.org +Fixes: d0cb50185ae9 ("do_last(): fetch directory ->i_mode and ->i_uid before it's too late") +Signed-off-by: Al Viro +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman + +--- + fs/namei.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -3259,8 +3259,8 @@ static int do_last(struct nameidata *nd, + struct file *file, const struct open_flags *op) + { + struct dentry *dir = nd->path.dentry; +- kuid_t dir_uid = dir->d_inode->i_uid; +- umode_t dir_mode = dir->d_inode->i_mode; ++ kuid_t dir_uid = nd->inode->i_uid; ++ umode_t dir_mode = nd->inode->i_mode; + int open_flag = op->open_flag; + bool will_truncate = (open_flag & O_TRUNC) != 0; + bool got_write = false; -- 2.47.3