All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ksmbd: fix racy issue from using ->d_parent and ->d_name
@ 2022-02-08  1:09 Namjae Jeon
  2022-02-08 18:34   ` kernel test robot
  0 siblings, 1 reply; 3+ messages in thread
From: Namjae Jeon @ 2022-02-08  1:09 UTC (permalink / raw)
  To: linux-cifs, linux-fsdevel; +Cc: smfrench, hyc.lee, Namjae Jeon, Al Viro

Al pointed out that ksmbd has racy issue from using ->d_parent and ->d_name
in ksmbd_vfs_unlink and smb2_vfs_rename(). and he suggested changing from
the way it start with dget_parent(), which can cause retry loop and
unexpected errors, to find the parent of child, lock it and then look for
a child in locked directory.

This patch introduce a new helper(vfs_path_parent_lookup()) to avoid
out of share access and export vfs functions like the following ones to use
vfs_path_parent_lookup() and filename_parentat().
 - __lookup_hash().
 - getname_kernel() and putname().
 - filename_parentat()

Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
---
 fs/internal.h                |   3 +
 fs/ksmbd/mgmt/user_session.c |   2 +-
 fs/ksmbd/smb2pdu.c           | 124 ++--------
 fs/ksmbd/vfs.c               | 465 +++++++++++++++--------------------
 fs/ksmbd/vfs.h               |  18 +-
 fs/ksmbd/vfs_cache.c         |   5 +-
 fs/namei.c                   |  43 +++-
 include/linux/namei.h        |   4 +
 8 files changed, 272 insertions(+), 392 deletions(-)

diff --git a/fs/internal.h b/fs/internal.h
index 8590c973c2f4..633a950cce1f 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -58,6 +58,9 @@ extern int finish_clean_context(struct fs_context *fc);
  */
 extern int filename_lookup(int dfd, struct filename *name, unsigned flags,
 			   struct path *path, struct path *root);
+int vfs_path_parent_lookup(struct dentry *dentry, struct vfsmount *mnt,
+			   struct filename *filename, unsigned int flags,
+			   struct path *parent, struct qstr *last, int *type);
 extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
 			   const char *, unsigned int, struct path *);
 int do_rmdir(int dfd, struct filename *name);
diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c
index 8d8ffd8c6f19..f4279f450053 100644
--- a/fs/ksmbd/mgmt/user_session.c
+++ b/fs/ksmbd/mgmt/user_session.c
@@ -161,8 +161,8 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)
 	if (sess->user)
 		ksmbd_free_user(sess->user);
 
-	ksmbd_tree_conn_session_logoff(sess);
 	ksmbd_destroy_file_table(&sess->file_table);
+	ksmbd_tree_conn_session_logoff(sess);
 	ksmbd_session_rpc_clear_list(sess);
 	free_channel_list(sess);
 	kfree(sess->Preauth_HashValue);
diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c
index 67e8e28e3fc3..2477d1da4f0c 100644
--- a/fs/ksmbd/smb2pdu.c
+++ b/fs/ksmbd/smb2pdu.c
@@ -2803,11 +2803,9 @@ int smb2_open(struct ksmbd_work *work)
 		if (!file_present) {
 			daccess = cpu_to_le32(GENERIC_ALL_FLAGS);
 		} else {
-			rc = ksmbd_vfs_query_maximal_access(user_ns,
-							    path.dentry,
-							    &daccess);
-			if (rc)
-				goto err_out;
+			ksmbd_vfs_query_maximal_access(share, user_ns,
+						       path.dentry, name,
+						       &daccess);
 			already_permitted = true;
 		}
 		maximal_access = daccess;
@@ -2869,8 +2867,7 @@ int smb2_open(struct ksmbd_work *work)
 
 			if ((daccess & FILE_DELETE_LE) ||
 			    (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) {
-				rc = ksmbd_vfs_may_delete(user_ns,
-							  path.dentry);
+				rc = ksmbd_vfs_may_delete(share, name);
 				if (rc)
 					goto err_out;
 			}
@@ -3187,8 +3184,8 @@ int smb2_open(struct ksmbd_work *work)
 		struct create_context *mxac_ccontext;
 
 		if (maximal_access == 0)
-			ksmbd_vfs_query_maximal_access(user_ns,
-						       path.dentry,
+			ksmbd_vfs_query_maximal_access(share, user_ns,
+						       path.dentry, name,
 						       &maximal_access);
 		mxac_ccontext = (struct create_context *)(rsp->Buffer +
 				le32_to_cpu(rsp->CreateContextsLength));
@@ -5369,44 +5366,19 @@ int smb2_echo(struct ksmbd_work *work)
 
 static int smb2_rename(struct ksmbd_work *work,
 		       struct ksmbd_file *fp,
-		       struct user_namespace *user_ns,
 		       struct smb2_file_rename_info *file_info,
 		       struct nls_table *local_nls)
 {
 	struct ksmbd_share_config *share = fp->tcon->share_conf;
-	char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL;
-	char *pathname = NULL;
-	struct path path;
-	bool file_present = true;
-	int rc;
+	char *new_name = NULL;
+	int rc, flags = 0;
 
 	ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n");
-	pathname = kmalloc(PATH_MAX, GFP_KERNEL);
-	if (!pathname)
-		return -ENOMEM;
-
-	abs_oldname = d_path(&fp->filp->f_path, pathname, PATH_MAX);
-	if (IS_ERR(abs_oldname)) {
-		rc = -EINVAL;
-		goto out;
-	}
-	old_name = strrchr(abs_oldname, '/');
-	if (old_name && old_name[1] != '\0') {
-		old_name++;
-	} else {
-		ksmbd_debug(SMB, "can't get last component in path %s\n",
-			    abs_oldname);
-		rc = -ENOENT;
-		goto out;
-	}
-
 	new_name = smb2_get_name(file_info->FileName,
 				 le32_to_cpu(file_info->FileNameLength),
 				 local_nls);
-	if (IS_ERR(new_name)) {
-		rc = PTR_ERR(new_name);
-		goto out;
-	}
+	if (IS_ERR(new_name))
+		return PTR_ERR(new_name);
 
 	if (strchr(new_name, ':')) {
 		int s_type;
@@ -5432,7 +5404,7 @@ static int smb2_rename(struct ksmbd_work *work,
 		if (rc)
 			goto out;
 
-		rc = ksmbd_vfs_setxattr(user_ns,
+		rc = ksmbd_vfs_setxattr(file_mnt_user_ns(fp->filp),
 					fp->filp->f_path.dentry,
 					xattr_stream_name,
 					NULL, 0, 0);
@@ -5447,47 +5419,23 @@ static int smb2_rename(struct ksmbd_work *work,
 	}
 
 	ksmbd_debug(SMB, "new name %s\n", new_name);
-	rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1);
-	if (rc) {
-		if (rc != -ENOENT)
-			goto out;
-		file_present = false;
-	} else {
-		path_put(&path);
-	}
-
 	if (ksmbd_share_veto_filename(share, new_name)) {
 		rc = -ENOENT;
 		ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name);
 		goto out;
 	}
 
-	if (file_info->ReplaceIfExists) {
-		if (file_present) {
-			rc = ksmbd_vfs_remove_file(work, new_name);
-			if (rc) {
-				if (rc != -ENOTEMPTY)
-					rc = -EINVAL;
-				ksmbd_debug(SMB, "cannot delete %s, rc %d\n",
-					    new_name, rc);
-				goto out;
-			}
-		}
-	} else {
-		if (file_present &&
-		    strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) {
-			rc = -EEXIST;
-			ksmbd_debug(SMB,
-				    "cannot rename already existing file\n");
-			goto out;
-		}
-	}
+	if (!file_info->ReplaceIfExists)
+		flags = RENAME_NOREPLACE;
 
-	rc = ksmbd_vfs_fp_rename(work, fp, new_name);
+	rc = ksmbd_vfs_rename(work, &fp->filp->f_path, new_name, flags);
 out:
-	kfree(pathname);
-	if (!IS_ERR(new_name))
+	if (rc) {
 		kfree(new_name);
+	} else {
+		kfree(fp->filename);
+		fp->filename = new_name;
+	}
 	return rc;
 }
 
@@ -5538,7 +5486,8 @@ static int smb2_create_link(struct ksmbd_work *work,
 
 	if (file_info->ReplaceIfExists) {
 		if (file_present) {
-			rc = ksmbd_vfs_remove_file(work, link_name);
+			rc = ksmbd_vfs_unlink(work->tcon->share_conf,
+					      link_name);
 			if (rc) {
 				rc = -EINVAL;
 				ksmbd_debug(SMB, "cannot delete %s\n",
@@ -5738,12 +5687,6 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
 			   struct smb2_file_rename_info *rename_info,
 			   unsigned int buf_len)
 {
-	struct user_namespace *user_ns;
-	struct ksmbd_file *parent_fp;
-	struct dentry *parent;
-	struct dentry *dentry = fp->filp->f_path.dentry;
-	int ret;
-
 	if (!(fp->daccess & FILE_DELETE_LE)) {
 		pr_err("no right to delete : 0x%x\n", fp->daccess);
 		return -EACCES;
@@ -5753,30 +5696,7 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp,
 			le32_to_cpu(rename_info->FileNameLength))
 		return -EINVAL;
 
-	user_ns = file_mnt_user_ns(fp->filp);
-	if (ksmbd_stream_fd(fp))
-		goto next;
-
-	parent = dget_parent(dentry);
-	ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry);
-	if (ret) {
-		dput(parent);
-		return ret;
-	}
-
-	parent_fp = ksmbd_lookup_fd_inode(d_inode(parent));
-	inode_unlock(d_inode(parent));
-	dput(parent);
-
-	if (parent_fp) {
-		if (parent_fp->daccess & FILE_DELETE_LE) {
-			pr_err("parent dir is opened with delete access\n");
-			return -ESHARE;
-		}
-	}
-next:
-	return smb2_rename(work, fp, user_ns, rename_info,
-			   work->sess->conn->local_nls);
+	return smb2_rename(work, fp, rename_info, work->sess->conn->local_nls);
 }
 
 static int set_file_disposition_info(struct ksmbd_file *fp,
diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c
index 19d36393974c..9321b05891b0 100644
--- a/fs/ksmbd/vfs.c
+++ b/fs/ksmbd/vfs.c
@@ -37,19 +37,6 @@
 #include "mgmt/user_session.h"
 #include "mgmt/user_config.h"
 
-static char *extract_last_component(char *path)
-{
-	char *p = strrchr(path, '/');
-
-	if (p && p[1] != '\0') {
-		*p = '\0';
-		p++;
-	} else {
-		p = NULL;
-	}
-	return p;
-}
-
 static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work,
 				    struct inode *parent_inode,
 				    struct inode *inode)
@@ -61,69 +48,57 @@ static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work,
 	i_uid_write(inode, i_uid_read(parent_inode));
 }
 
-/**
- * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable
- *
- * the parent dentry got by dget_parent or @parent could be
- * unstable, we try to lock a parent inode and lookup the
- * child dentry again.
- *
- * the reference count of @parent isn't incremented.
- */
-int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent,
-			  struct dentry *child)
+int ksmbd_vfs_may_delete(struct ksmbd_share_config *share, char *filename)
 {
 	struct dentry *dentry;
-	int ret = 0;
+	struct path path;
+	struct qstr last;
+	struct filename *filename_struct;
+	struct inode *dir;
+	int type, err = 0;
+
+	filename_struct = getname_kernel(filename);
+	if (IS_ERR(filename_struct))
+		return PTR_ERR(filename_struct);
+
+	err = vfs_path_parent_lookup(share->vfs_path.dentry,
+				     share->vfs_path.mnt, filename_struct,
+				     LOOKUP_NO_SYMLINKS | LOOKUP_BENEATH,
+				     &path, &last, &type);
+	if (err)
+		goto putname;
 
-	inode_lock_nested(d_inode(parent), I_MUTEX_PARENT);
-	dentry = lookup_one(user_ns, child->d_name.name, parent,
-			    child->d_name.len);
+	dir = d_inode(path.dentry);
+
+	inode_lock_nested(dir, I_MUTEX_PARENT);
+	dentry = __lookup_hash(&last, path.dentry, 0);
 	if (IS_ERR(dentry)) {
-		ret = PTR_ERR(dentry);
-		goto out_err;
+		err = PTR_ERR(dentry);
+		goto unlock_inode;
 	}
 
-	if (dentry != child) {
-		ret = -ESTALE;
+	if (d_is_negative(dentry)) {
+		err = -ENOENT;
 		dput(dentry);
-		goto out_err;
+		goto unlock_inode;
 	}
 
-	dput(dentry);
-	return 0;
-out_err:
-	inode_unlock(d_inode(parent));
-	return ret;
-}
-
-int ksmbd_vfs_may_delete(struct user_namespace *user_ns,
-			 struct dentry *dentry)
-{
-	struct dentry *parent;
-	int ret;
-
-	parent = dget_parent(dentry);
-	ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry);
-	if (ret) {
-		dput(parent);
-		return ret;
-	}
-
-	ret = inode_permission(user_ns, d_inode(parent),
+	err = inode_permission(mnt_user_ns(path.mnt), dir,
 			       MAY_EXEC | MAY_WRITE);
-
-	inode_unlock(d_inode(parent));
-	dput(parent);
-	return ret;
+	dput(dentry);
+unlock_inode:
+	inode_unlock(dir);
+	path_put(&path);
+putname:
+	putname(filename_struct);
+	return err;
 }
 
-int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
-				   struct dentry *dentry, __le32 *daccess)
+void ksmbd_vfs_query_maximal_access(struct ksmbd_share_config *share,
+				    struct user_namespace *user_ns,
+				    struct dentry *dentry, char *filename,
+				    __le32 *daccess)
 {
-	struct dentry *parent;
-	int ret = 0;
-
 	*daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL);
 
 	if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE))
@@ -138,19 +113,8 @@ int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
 	if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC))
 		*daccess |= FILE_EXECUTE_LE;
 
-	parent = dget_parent(dentry);
-	ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry);
-	if (ret) {
-		dput(parent);
-		return ret;
-	}
-
-	if (!inode_permission(user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE))
+	if (!ksmbd_vfs_may_delete(share, filename))
 		*daccess |= FILE_DELETE_LE;
-
-	inode_unlock(d_inode(parent));
-	dput(parent);
-	return ret;
 }
 
 /**
@@ -580,64 +544,6 @@ int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id)
 	return err;
 }
 
-/**
- * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink
- * @name:	directory or file name that is relative to share
- *
- * Return:	0 on success, otherwise error
- */
-int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name)
-{
-	struct user_namespace *user_ns;
-	struct path path;
-	struct dentry *parent;
-	int err;
-
-	if (ksmbd_override_fsids(work))
-		return -ENOMEM;
-
-	err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false);
-	if (err) {
-		ksmbd_debug(VFS, "can't get %s, err %d\n", name, err);
-		ksmbd_revert_fsids(work);
-		return err;
-	}
-
-	user_ns = mnt_user_ns(path.mnt);
-	parent = dget_parent(path.dentry);
-	err = ksmbd_vfs_lock_parent(user_ns, parent, path.dentry);
-	if (err) {
-		dput(parent);
-		path_put(&path);
-		ksmbd_revert_fsids(work);
-		return err;
-	}
-
-	if (!d_inode(path.dentry)->i_nlink) {
-		err = -ENOENT;
-		goto out_err;
-	}
-
-	if (S_ISDIR(d_inode(path.dentry)->i_mode)) {
-		err = vfs_rmdir(user_ns, d_inode(parent), path.dentry);
-		if (err && err != -ENOTEMPTY)
-			ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name,
-				    err);
-	} else {
-		err = vfs_unlink(user_ns, d_inode(parent), path.dentry, NULL);
-		if (err)
-			ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name,
-				    err);
-	}
-
-out_err:
-	inode_unlock(d_inode(parent));
-	dput(parent);
-	path_put(&path);
-	ksmbd_revert_fsids(work);
-	return err;
-}
-
 /**
  * ksmbd_vfs_link() - vfs helper for creating smb hardlink
  * @oldname:	source file name
@@ -692,149 +598,138 @@ int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname,
 	return err;
 }
 
-static int ksmbd_validate_entry_in_use(struct dentry *src_dent)
+int ksmbd_vfs_rename(struct ksmbd_work *work, struct path *path, char *newname,
+		     int flags)
 {
-	struct dentry *dst_dent;
-
-	spin_lock(&src_dent->d_lock);
-	list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) {
-		struct ksmbd_file *child_fp;
+	struct dentry *old_dentry, *new_dentry, *trap;
+	struct path old_path, new_path;
+	struct qstr old_last, new_last;
+	struct renamedata rd;
+	struct filename *from, *to;
+	struct ksmbd_share_config *share_conf = work->tcon->share_conf;
+	struct ksmbd_file *parent_fp;
+	int old_type, new_type;
+	int err, lookup_flags = LOOKUP_NO_SYMLINKS;
+	char *pathname, *abs_oldname;
 
-		if (d_really_is_negative(dst_dent))
-			continue;
+	if (ksmbd_override_fsids(work))
+		return -ENOMEM;
 
-		child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent));
-		if (child_fp) {
-			spin_unlock(&src_dent->d_lock);
-			ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n");
-			return -EACCES;
-		}
+	pathname = kmalloc(PATH_MAX, GFP_KERNEL);
+	if (!pathname) {
+		ksmbd_revert_fsids(work);
+		return -ENOMEM;
 	}
-	spin_unlock(&src_dent->d_lock);
 
-	return 0;
-}
+	abs_oldname = d_path(path, pathname, PATH_MAX);
+	if (IS_ERR(abs_oldname)) {
+		err = -EINVAL;
+		goto free_pathname;
+	}
 
-static int __ksmbd_vfs_rename(struct ksmbd_work *work,
-			      struct user_namespace *src_user_ns,
-			      struct dentry *src_dent_parent,
-			      struct dentry *src_dent,
-			      struct user_namespace *dst_user_ns,
-			      struct dentry *dst_dent_parent,
-			      struct dentry *trap_dent,
-			      char *dst_name)
-{
-	struct dentry *dst_dent;
-	int err;
+	from = getname_kernel(abs_oldname);
+	if (IS_ERR(from)) {
+		err = PTR_ERR(from);
+		goto free_pathname;
+	}
 
-	if (!work->tcon->posix_extensions) {
-		err = ksmbd_validate_entry_in_use(src_dent);
-		if (err)
-			return err;
+	to = getname_kernel(newname);
+	if (IS_ERR(to)) {
+		err = PTR_ERR(to);
+		goto putname_from;
 	}
 
-	if (d_really_is_negative(src_dent_parent))
-		return -ENOENT;
-	if (d_really_is_negative(dst_dent_parent))
-		return -ENOENT;
-	if (d_really_is_negative(src_dent))
-		return -ENOENT;
-	if (src_dent == trap_dent)
-		return -EINVAL;
+	err = filename_parentat(AT_FDCWD, from, lookup_flags, &old_path,
+				&old_last, &old_type);
+	if (err)
+		goto putnames;
 
-	if (ksmbd_override_fsids(work))
-		return -ENOMEM;
+	err = vfs_path_parent_lookup(share_conf->vfs_path.dentry,
+				     share_conf->vfs_path.mnt, to,
+				     lookup_flags | LOOKUP_BENEATH,
+				     &new_path, &new_last, &new_type);
+	if (err)
+		goto out1;
 
-	dst_dent = lookup_one(dst_user_ns, dst_name, dst_dent_parent,
-			      strlen(dst_name));
-	err = PTR_ERR(dst_dent);
-	if (IS_ERR(dst_dent)) {
-		pr_err("lookup failed %s [%d]\n", dst_name, err);
-		goto out;
+	if (d_is_symlink(new_path.dentry)) {
+		err = -EACCES;
+		goto out4;
 	}
 
-	err = -ENOTEMPTY;
-	if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) {
-		struct renamedata rd = {
-			.old_mnt_userns	= src_user_ns,
-			.old_dir	= d_inode(src_dent_parent),
-			.old_dentry	= src_dent,
-			.new_mnt_userns	= dst_user_ns,
-			.new_dir	= d_inode(dst_dent_parent),
-			.new_dentry	= dst_dent,
-		};
-		err = vfs_rename(&rd);
+	trap = lock_rename(old_path.dentry, new_path.dentry);
+	old_dentry = __lookup_hash(&old_last, old_path.dentry, 0);
+	if (IS_ERR(old_dentry)) {
+		err = PTR_ERR(old_dentry);
+		goto out2;
+	}
+	if (d_is_negative(old_dentry)) {
+		err = -ENOENT;
+		goto out3;
 	}
-	if (err)
-		pr_err("vfs_rename failed err %d\n", err);
-	if (dst_dent)
-		dput(dst_dent);
-out:
-	ksmbd_revert_fsids(work);
-	return err;
-}
 
-int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
-			char *newname)
-{
-	struct user_namespace *user_ns;
-	struct path dst_path;
-	struct dentry *src_dent_parent, *dst_dent_parent;
-	struct dentry *src_dent, *trap_dent, *src_child;
-	char *dst_name;
-	int err;
+	new_dentry = __lookup_hash(&new_last, new_path.dentry,
+				   LOOKUP_RENAME_TARGET);
+	if (IS_ERR(new_dentry)) {
+		err = PTR_ERR(new_dentry);
+		goto out3;
+	}
 
-	dst_name = extract_last_component(newname);
-	if (!dst_name) {
-		dst_name = newname;
-		newname = "";
+	if (d_is_symlink(new_dentry)) {
+		err = -EACCES;
+		goto out4;
 	}
 
-	src_dent_parent = dget_parent(fp->filp->f_path.dentry);
-	src_dent = fp->filp->f_path.dentry;
+	if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) {
+		err = -EEXIST;
+		goto out4;
+	}
 
-	err = ksmbd_vfs_kern_path(work, newname,
-				  LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY,
-				  &dst_path, false);
-	if (err) {
-		ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err);
-		goto out;
+	if (old_dentry == trap) {
+		err = -EINVAL;
+		goto out4;
 	}
-	dst_dent_parent = dst_path.dentry;
-
-	trap_dent = lock_rename(src_dent_parent, dst_dent_parent);
-	dget(src_dent);
-	dget(dst_dent_parent);
-	user_ns = file_mnt_user_ns(fp->filp);
-	src_child = lookup_one(user_ns, src_dent->d_name.name, src_dent_parent,
-			       src_dent->d_name.len);
-	if (IS_ERR(src_child)) {
-		err = PTR_ERR(src_child);
-		goto out_lock;
-	}
-
-	if (src_child != src_dent) {
-		err = -ESTALE;
-		dput(src_child);
-		goto out_lock;
-	}
-	dput(src_child);
-
-	err = __ksmbd_vfs_rename(work,
-				 user_ns,
-				 src_dent_parent,
-				 src_dent,
-				 mnt_user_ns(dst_path.mnt),
-				 dst_dent_parent,
-				 trap_dent,
-				 dst_name);
-out_lock:
-	dput(src_dent);
-	dput(dst_dent_parent);
-	unlock_rename(src_dent_parent, dst_dent_parent);
-	path_put(&dst_path);
-out:
-	dput(src_dent_parent);
+
+	if (new_dentry == trap) {
+		err = -ENOTEMPTY;
+		goto out4;
+	}
+
+	parent_fp = ksmbd_lookup_fd_inode(old_path.dentry->d_inode);
+	if (parent_fp) {
+		if (parent_fp->daccess & FILE_DELETE_LE) {
+			pr_err("parent dir is opened with delete access\n");
+			err = -ESHARE;
+			goto out4;
+		}
+	}
+
+	rd.old_mnt_userns	= mnt_user_ns(old_path.mnt),
+	rd.old_dir		= old_path.dentry->d_inode,
+	rd.old_dentry		= old_dentry,
+	rd.new_mnt_userns	= mnt_user_ns(new_path.mnt),
+	rd.new_dir		= new_path.dentry->d_inode,
+	rd.new_dentry		= new_dentry,
+	rd.flags		= flags,
+	err = vfs_rename(&rd);
+	if (err)
+		ksmbd_debug(VFS, "vfs_rename failed err %d\n", err);
+out4:
+	dput(new_dentry);
+out3:
+	dput(old_dentry);
+out2:
+	unlock_rename(new_path.dentry, old_path.dentry);
+	path_put(&new_path);
+out1:
+	path_put(&old_path);
+
+putnames:
+	putname(to);
+putname_from:
+	putname(from);
+free_pathname:
+	kfree(pathname);
+	ksmbd_revert_fsids(work);
 	return err;
 }
 
@@ -1084,26 +979,57 @@ int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns,
 	return vfs_removexattr(user_ns, dentry, attr_name);
 }
 
-int ksmbd_vfs_unlink(struct user_namespace *user_ns,
-		     struct dentry *dir, struct dentry *dentry)
+int ksmbd_vfs_unlink(struct ksmbd_share_config *share, char *filename)
 {
-	int err = 0;
-
-	err = ksmbd_vfs_lock_parent(user_ns, dir, dentry);
+	struct dentry *dentry;
+	struct path path;
+	struct qstr last;
+	struct filename *filename_struct;
+	struct inode *inode = NULL, *dir;
+	int type, err = 0;
+
+	filename_struct = getname_kernel(filename);
+	if (IS_ERR(filename_struct))
+		return PTR_ERR(filename_struct);
+
+	err = vfs_path_parent_lookup(share->vfs_path.dentry,
+				     share->vfs_path.mnt, filename_struct,
+				     LOOKUP_NO_SYMLINKS | LOOKUP_BENEATH,
+				     &path, &last, &type);
 	if (err)
-		return err;
-	dget(dentry);
+		goto putname;
 
-	if (S_ISDIR(d_inode(dentry)->i_mode))
-		err = vfs_rmdir(user_ns, d_inode(dir), dentry);
-	else
-		err = vfs_unlink(user_ns, d_inode(dir), dentry, NULL);
+	dir = d_inode(path.dentry);
+
+	inode_lock_nested(dir, I_MUTEX_PARENT);
+	dentry = __lookup_hash(&last, path.dentry, 0);
+	if (IS_ERR(dentry)) {
+		err = PTR_ERR(dentry);
+		goto unlock_inode;
+	}
 
+	if (d_is_negative(dentry)) {
+		err = -ENOENT;
+		dput(dentry);
+		goto unlock_inode;
+	}
+
+	inode = dentry->d_inode;
+	ihold(inode);
+	if (S_ISDIR(inode->i_mode))
+		err = vfs_rmdir(mnt_user_ns(path.mnt), dir, dentry);
+	else
+		err = vfs_unlink(mnt_user_ns(path.mnt), dir, dentry, NULL);
+	iput(inode);
 	dput(dentry);
-	inode_unlock(d_inode(dir));
+unlock_inode:
+	inode_unlock(dir);
 	if (err)
-		ksmbd_debug(VFS, "failed to delete, err %d\n", err);
+		pr_err("failed to delete, err %d\n", err);
 
+	path_put(&path);
+putname:
+	putname(filename_struct);
 	return err;
 }
 
@@ -1258,6 +1184,7 @@ int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name,
 				goto out;
 			else if (is_last) {
 				*path = parent;
+				memcpy(name, filepath, path_len);
 				goto out;
 			}
 
diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h
index 8c37aaf936ab..8e800e5f2e7b 100644
--- a/fs/ksmbd/vfs.h
+++ b/fs/ksmbd/vfs.h
@@ -69,11 +69,11 @@ struct ksmbd_kstat {
 	__le32			file_attributes;
 };
 
-int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent,
-			  struct dentry *child);
-int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry);
-int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
-				   struct dentry *dentry, __le32 *daccess);
+int ksmbd_vfs_may_delete(struct ksmbd_share_config *share, char *filename);
+void ksmbd_vfs_query_maximal_access(struct ksmbd_share_config *share,
+				    struct user_namespace *user_ns,
+				    struct dentry *dentry, char *filename,
+				    __le32 *daccess);
 int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode);
 int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode);
 int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp,
@@ -82,12 +82,11 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
 		    char *buf, size_t count, loff_t *pos, bool sync,
 		    ssize_t *written);
 int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id);
-int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name);
 int ksmbd_vfs_link(struct ksmbd_work *work,
 		   const char *oldname, const char *newname);
 int ksmbd_vfs_getattr(struct path *path, struct kstat *stat);
-int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
-			char *newname);
+int ksmbd_vfs_rename(struct ksmbd_work *work, struct path *path, char *newname,
+		     int flags);
 int ksmbd_vfs_truncate(struct ksmbd_work *work,
 		       struct ksmbd_file *fp, loff_t size);
 struct srv_copychunk;
@@ -129,8 +128,7 @@ struct file_allocated_range_buffer;
 int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
 			 struct file_allocated_range_buffer *ranges,
 			 unsigned int in_count, unsigned int *out_count);
-int ksmbd_vfs_unlink(struct user_namespace *user_ns,
-		     struct dentry *dir, struct dentry *dentry);
+int ksmbd_vfs_unlink(struct ksmbd_share_config *share, char *filename);
 void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat);
 int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
 				struct user_namespace *user_ns,
diff --git a/fs/ksmbd/vfs_cache.c b/fs/ksmbd/vfs_cache.c
index 29c1db66bd0f..70b7c96002b9 100644
--- a/fs/ksmbd/vfs_cache.c
+++ b/fs/ksmbd/vfs_cache.c
@@ -243,7 +243,6 @@ void ksmbd_release_inode_hash(void)
 
 static void __ksmbd_inode_close(struct ksmbd_file *fp)
 {
-	struct dentry *dir, *dentry;
 	struct ksmbd_inode *ci = fp->f_ci;
 	int err;
 	struct file *filp;
@@ -262,11 +261,9 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
 	if (atomic_dec_and_test(&ci->m_count)) {
 		write_lock(&ci->m_lock);
 		if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) {
-			dentry = filp->f_path.dentry;
-			dir = dentry->d_parent;
 			ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING);
 			write_unlock(&ci->m_lock);
-			ksmbd_vfs_unlink(file_mnt_user_ns(filp), dir, dentry);
+			ksmbd_vfs_unlink(fp->tcon->share_conf, fp->filename);
 			write_lock(&ci->m_lock);
 		}
 		write_unlock(&ci->m_lock);
diff --git a/fs/namei.c b/fs/namei.c
index b867a92c078e..3a035112a18b 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -252,6 +252,7 @@ getname_kernel(const char * filename)
 
 	return result;
 }
+EXPORT_SYMBOL(getname_kernel);
 
 void putname(struct filename *name)
 {
@@ -269,6 +270,7 @@ void putname(struct filename *name)
 	} else
 		__putname(name);
 }
+EXPORT_SYMBOL(putname);
 
 /**
  * check_acl - perform ACL permission checking
@@ -1587,8 +1589,8 @@ static struct dentry *lookup_dcache(const struct qstr *name,
  * when directory is guaranteed to have no in-lookup children
  * at all.
  */
-static struct dentry *__lookup_hash(const struct qstr *name,
-		struct dentry *base, unsigned int flags)
+struct dentry *__lookup_hash(const struct qstr *name, struct dentry *base,
+			     unsigned int flags)
 {
 	struct dentry *dentry = lookup_dcache(name, base, flags);
 	struct dentry *old;
@@ -1612,6 +1614,7 @@ static struct dentry *__lookup_hash(const struct qstr *name,
 	}
 	return dentry;
 }
+EXPORT_SYMBOL(__lookup_hash);
 
 static struct dentry *lookup_fast(struct nameidata *nd,
 				  struct inode **inode,
@@ -2556,16 +2559,16 @@ static int path_parentat(struct nameidata *nd, unsigned flags,
 }
 
 /* Note: this does not consume "name" */
-static int filename_parentat(int dfd, struct filename *name,
-			     unsigned int flags, struct path *parent,
-			     struct qstr *last, int *type)
+static int __filename_parentat(int dfd, struct filename *name,
+			       unsigned int flags, struct path *parent,
+			       struct qstr *last, int *type, struct path *root)
 {
 	int retval;
 	struct nameidata nd;
 
 	if (IS_ERR(name))
 		return PTR_ERR(name);
-	set_nameidata(&nd, dfd, name, NULL);
+	set_nameidata(&nd, dfd, name, root);
 	retval = path_parentat(&nd, flags | LOOKUP_RCU, parent);
 	if (unlikely(retval == -ECHILD))
 		retval = path_parentat(&nd, flags, parent);
@@ -2580,6 +2583,13 @@ static int filename_parentat(int dfd, struct filename *name,
 	return retval;
 }
 
+int filename_parentat(int dfd, struct filename *name, unsigned int flags,
+		      struct path *parent, struct qstr *last, int *type)
+{
+	return __filename_parentat(dfd, name, flags, parent, last, type, NULL);
+}
+EXPORT_SYMBOL(filename_parentat);
+
 /* does lookup, returns the object with parent locked */
 static struct dentry *__kern_path_locked(struct filename *name, struct path *path)
 {
@@ -2623,6 +2633,27 @@ int kern_path(const char *name, unsigned int flags, struct path *path)
 }
 EXPORT_SYMBOL(kern_path);
 
+/**
+ * vfs_path_parent_lookup - lookup a parent path relative to a dentry-vfsmount pair
+ * @dentry:  pointer to dentry of the base directory
+ * @mnt: pointer to vfs mount of the base directory
+ * @filename: filename structure
+ * @flags: lookup flags
+ * @parent: pointer to struct path to fill
+ * @last: last component
+ * @type: type of the last component
+ */
+int vfs_path_parent_lookup(struct dentry *dentry, struct vfsmount *mnt,
+			   struct filename *filename, unsigned int flags,
+			   struct path *parent, struct qstr *last, int *type)
+{
+	struct path root = {.mnt = mnt, .dentry = dentry};
+
+	return  __filename_parentat(AT_FDCWD, filename, flags, parent, last,
+				    type, &root);
+}
+EXPORT_SYMBOL(vfs_path_parent_lookup);
+
 /**
  * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair
  * @dentry:  pointer to dentry of the base directory
diff --git a/include/linux/namei.h b/include/linux/namei.h
index e89329bb3134..3d9a46df2dbd 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -57,6 +57,10 @@ static inline int user_path_at(int dfd, const char __user *name, unsigned flags,
 	return user_path_at_empty(dfd, name, flags, path, NULL);
 }
 
+struct dentry *__lookup_hash(const struct qstr *name, struct dentry *base,
+			     unsigned int flags);
+int filename_parentat(int dfd, struct filename *name, unsigned int flags,
+		      struct path *parent, struct qstr *last, int *type);
 extern int kern_path(const char *, unsigned, struct path *);
 
 extern struct dentry *kern_path_create(int, const char *, struct path *, unsigned int);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] ksmbd: fix racy issue from using ->d_parent and ->d_name
  2022-02-08  1:09 [PATCH] ksmbd: fix racy issue from using ->d_parent and ->d_name Namjae Jeon
@ 2022-02-08 18:34   ` kernel test robot
  0 siblings, 0 replies; 3+ messages in thread
From: kernel test robot @ 2022-02-08 18:34 UTC (permalink / raw)
  To: Namjae Jeon; +Cc: llvm, kbuild-all

Hi Namjae,

I love your patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v5.17-rc3 next-20220208]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Namjae-Jeon/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name/20220208-092438
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 555f3d7be91a873114c9656069f1a9fa476ec41a
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20220209/202202090207.MiyIsofZ-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project e8bff9ae54a55b4dbfeb6ba55f723abbd81bf494)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/f4cb65c1c670f5332092a7eb75d569bbd4e46a5f
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Namjae-Jeon/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name/20220208-092438
        git checkout f4cb65c1c670f5332092a7eb75d569bbd4e46a5f
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash fs/ksmbd/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> fs/ksmbd/vfs.c:654:6: warning: variable 'old_dentry' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
           if (d_is_symlink(new_path.dentry)) {
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/ksmbd/vfs.c:719:7: note: uninitialized use occurs here
           dput(old_dentry);
                ^~~~~~~~~~
   fs/ksmbd/vfs.c:654:2: note: remove the 'if' if its condition is always false
           if (d_is_symlink(new_path.dentry)) {
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/ksmbd/vfs.c:604:27: note: initialize the variable 'old_dentry' to silence this warning
           struct dentry *old_dentry, *new_dentry, *trap;
                                    ^
                                     = NULL
>> fs/ksmbd/vfs.c:654:6: warning: variable 'new_dentry' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
           if (d_is_symlink(new_path.dentry)) {
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/ksmbd/vfs.c:717:7: note: uninitialized use occurs here
           dput(new_dentry);
                ^~~~~~~~~~
   fs/ksmbd/vfs.c:654:2: note: remove the 'if' if its condition is always false
           if (d_is_symlink(new_path.dentry)) {
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/ksmbd/vfs.c:604:40: note: initialize the variable 'new_dentry' to silence this warning
           struct dentry *old_dentry, *new_dentry, *trap;
                                                 ^
                                                  = NULL
   2 warnings generated.


vim +654 fs/ksmbd/vfs.c

   600	
   601	int ksmbd_vfs_rename(struct ksmbd_work *work, struct path *path, char *newname,
   602			     int flags)
   603	{
   604		struct dentry *old_dentry, *new_dentry, *trap;
   605		struct path old_path, new_path;
   606		struct qstr old_last, new_last;
   607		struct renamedata rd;
   608		struct filename *from, *to;
   609		struct ksmbd_share_config *share_conf = work->tcon->share_conf;
   610		struct ksmbd_file *parent_fp;
   611		int old_type, new_type;
   612		int err, lookup_flags = LOOKUP_NO_SYMLINKS;
   613		char *pathname, *abs_oldname;
   614	
   615		if (ksmbd_override_fsids(work))
   616			return -ENOMEM;
   617	
   618		pathname = kmalloc(PATH_MAX, GFP_KERNEL);
   619		if (!pathname) {
   620			ksmbd_revert_fsids(work);
   621			return -ENOMEM;
   622		}
   623	
   624		abs_oldname = d_path(path, pathname, PATH_MAX);
   625		if (IS_ERR(abs_oldname)) {
   626			err = -EINVAL;
   627			goto free_pathname;
   628		}
   629	
   630		from = getname_kernel(abs_oldname);
   631		if (IS_ERR(from)) {
   632			err = PTR_ERR(from);
   633			goto free_pathname;
   634		}
   635	
   636		to = getname_kernel(newname);
   637		if (IS_ERR(to)) {
   638			err = PTR_ERR(to);
   639			goto putname_from;
   640		}
   641	
   642		err = filename_parentat(AT_FDCWD, from, lookup_flags, &old_path,
   643					&old_last, &old_type);
   644		if (err)
   645			goto putnames;
   646	
   647		err = vfs_path_parent_lookup(share_conf->vfs_path.dentry,
   648					     share_conf->vfs_path.mnt, to,
   649					     lookup_flags | LOOKUP_BENEATH,
   650					     &new_path, &new_last, &new_type);
   651		if (err)
   652			goto out1;
   653	
 > 654		if (d_is_symlink(new_path.dentry)) {
   655			err = -EACCES;
   656			goto out4;
   657		}
   658	
   659		trap = lock_rename(old_path.dentry, new_path.dentry);
   660		old_dentry = __lookup_hash(&old_last, old_path.dentry, 0);
   661		if (IS_ERR(old_dentry)) {
   662			err = PTR_ERR(old_dentry);
   663			goto out2;
   664		}
   665		if (d_is_negative(old_dentry)) {
   666			err = -ENOENT;
   667			goto out3;
   668		}
   669	
   670		new_dentry = __lookup_hash(&new_last, new_path.dentry,
   671					   LOOKUP_RENAME_TARGET);
   672		if (IS_ERR(new_dentry)) {
   673			err = PTR_ERR(new_dentry);
   674			goto out3;
   675		}
   676	
   677		if (d_is_symlink(new_dentry)) {
   678			err = -EACCES;
   679			goto out4;
   680		}
   681	
   682		if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) {
   683			err = -EEXIST;
   684			goto out4;
   685		}
   686	
   687		if (old_dentry == trap) {
   688			err = -EINVAL;
   689			goto out4;
   690		}
   691	
   692		if (new_dentry == trap) {
   693			err = -ENOTEMPTY;
   694			goto out4;
   695		}
   696	
   697		parent_fp = ksmbd_lookup_fd_inode(old_path.dentry->d_inode);
   698		if (parent_fp) {
   699			if (parent_fp->daccess & FILE_DELETE_LE) {
   700				pr_err("parent dir is opened with delete access\n");
   701				err = -ESHARE;
   702				goto out4;
   703			}
   704		}
   705	
   706		rd.old_mnt_userns	= mnt_user_ns(old_path.mnt),
   707		rd.old_dir		= old_path.dentry->d_inode,
   708		rd.old_dentry		= old_dentry,
   709		rd.new_mnt_userns	= mnt_user_ns(new_path.mnt),
   710		rd.new_dir		= new_path.dentry->d_inode,
   711		rd.new_dentry		= new_dentry,
   712		rd.flags		= flags,
   713		err = vfs_rename(&rd);
   714		if (err)
   715			ksmbd_debug(VFS, "vfs_rename failed err %d\n", err);
   716	out4:
   717		dput(new_dentry);
   718	out3:
   719		dput(old_dentry);
   720	out2:
   721		unlock_rename(new_path.dentry, old_path.dentry);
   722		path_put(&new_path);
   723	out1:
   724		path_put(&old_path);
   725	
   726	putnames:
   727		putname(to);
   728	putname_from:
   729		putname(from);
   730	free_pathname:
   731		kfree(pathname);
   732		ksmbd_revert_fsids(work);
   733		return err;
   734	}
   735	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] ksmbd: fix racy issue from using ->d_parent and ->d_name
@ 2022-02-08 18:34   ` kernel test robot
  0 siblings, 0 replies; 3+ messages in thread
From: kernel test robot @ 2022-02-08 18:34 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 7451 bytes --]

Hi Namjae,

I love your patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v5.17-rc3 next-20220208]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Namjae-Jeon/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name/20220208-092438
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 555f3d7be91a873114c9656069f1a9fa476ec41a
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20220209/202202090207.MiyIsofZ-lkp(a)intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project e8bff9ae54a55b4dbfeb6ba55f723abbd81bf494)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/f4cb65c1c670f5332092a7eb75d569bbd4e46a5f
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Namjae-Jeon/ksmbd-fix-racy-issue-from-using-d_parent-and-d_name/20220208-092438
        git checkout f4cb65c1c670f5332092a7eb75d569bbd4e46a5f
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash fs/ksmbd/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> fs/ksmbd/vfs.c:654:6: warning: variable 'old_dentry' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
           if (d_is_symlink(new_path.dentry)) {
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/ksmbd/vfs.c:719:7: note: uninitialized use occurs here
           dput(old_dentry);
                ^~~~~~~~~~
   fs/ksmbd/vfs.c:654:2: note: remove the 'if' if its condition is always false
           if (d_is_symlink(new_path.dentry)) {
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/ksmbd/vfs.c:604:27: note: initialize the variable 'old_dentry' to silence this warning
           struct dentry *old_dentry, *new_dentry, *trap;
                                    ^
                                     = NULL
>> fs/ksmbd/vfs.c:654:6: warning: variable 'new_dentry' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
           if (d_is_symlink(new_path.dentry)) {
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/ksmbd/vfs.c:717:7: note: uninitialized use occurs here
           dput(new_dentry);
                ^~~~~~~~~~
   fs/ksmbd/vfs.c:654:2: note: remove the 'if' if its condition is always false
           if (d_is_symlink(new_path.dentry)) {
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/ksmbd/vfs.c:604:40: note: initialize the variable 'new_dentry' to silence this warning
           struct dentry *old_dentry, *new_dentry, *trap;
                                                 ^
                                                  = NULL
   2 warnings generated.


vim +654 fs/ksmbd/vfs.c

   600	
   601	int ksmbd_vfs_rename(struct ksmbd_work *work, struct path *path, char *newname,
   602			     int flags)
   603	{
   604		struct dentry *old_dentry, *new_dentry, *trap;
   605		struct path old_path, new_path;
   606		struct qstr old_last, new_last;
   607		struct renamedata rd;
   608		struct filename *from, *to;
   609		struct ksmbd_share_config *share_conf = work->tcon->share_conf;
   610		struct ksmbd_file *parent_fp;
   611		int old_type, new_type;
   612		int err, lookup_flags = LOOKUP_NO_SYMLINKS;
   613		char *pathname, *abs_oldname;
   614	
   615		if (ksmbd_override_fsids(work))
   616			return -ENOMEM;
   617	
   618		pathname = kmalloc(PATH_MAX, GFP_KERNEL);
   619		if (!pathname) {
   620			ksmbd_revert_fsids(work);
   621			return -ENOMEM;
   622		}
   623	
   624		abs_oldname = d_path(path, pathname, PATH_MAX);
   625		if (IS_ERR(abs_oldname)) {
   626			err = -EINVAL;
   627			goto free_pathname;
   628		}
   629	
   630		from = getname_kernel(abs_oldname);
   631		if (IS_ERR(from)) {
   632			err = PTR_ERR(from);
   633			goto free_pathname;
   634		}
   635	
   636		to = getname_kernel(newname);
   637		if (IS_ERR(to)) {
   638			err = PTR_ERR(to);
   639			goto putname_from;
   640		}
   641	
   642		err = filename_parentat(AT_FDCWD, from, lookup_flags, &old_path,
   643					&old_last, &old_type);
   644		if (err)
   645			goto putnames;
   646	
   647		err = vfs_path_parent_lookup(share_conf->vfs_path.dentry,
   648					     share_conf->vfs_path.mnt, to,
   649					     lookup_flags | LOOKUP_BENEATH,
   650					     &new_path, &new_last, &new_type);
   651		if (err)
   652			goto out1;
   653	
 > 654		if (d_is_symlink(new_path.dentry)) {
   655			err = -EACCES;
   656			goto out4;
   657		}
   658	
   659		trap = lock_rename(old_path.dentry, new_path.dentry);
   660		old_dentry = __lookup_hash(&old_last, old_path.dentry, 0);
   661		if (IS_ERR(old_dentry)) {
   662			err = PTR_ERR(old_dentry);
   663			goto out2;
   664		}
   665		if (d_is_negative(old_dentry)) {
   666			err = -ENOENT;
   667			goto out3;
   668		}
   669	
   670		new_dentry = __lookup_hash(&new_last, new_path.dentry,
   671					   LOOKUP_RENAME_TARGET);
   672		if (IS_ERR(new_dentry)) {
   673			err = PTR_ERR(new_dentry);
   674			goto out3;
   675		}
   676	
   677		if (d_is_symlink(new_dentry)) {
   678			err = -EACCES;
   679			goto out4;
   680		}
   681	
   682		if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) {
   683			err = -EEXIST;
   684			goto out4;
   685		}
   686	
   687		if (old_dentry == trap) {
   688			err = -EINVAL;
   689			goto out4;
   690		}
   691	
   692		if (new_dentry == trap) {
   693			err = -ENOTEMPTY;
   694			goto out4;
   695		}
   696	
   697		parent_fp = ksmbd_lookup_fd_inode(old_path.dentry->d_inode);
   698		if (parent_fp) {
   699			if (parent_fp->daccess & FILE_DELETE_LE) {
   700				pr_err("parent dir is opened with delete access\n");
   701				err = -ESHARE;
   702				goto out4;
   703			}
   704		}
   705	
   706		rd.old_mnt_userns	= mnt_user_ns(old_path.mnt),
   707		rd.old_dir		= old_path.dentry->d_inode,
   708		rd.old_dentry		= old_dentry,
   709		rd.new_mnt_userns	= mnt_user_ns(new_path.mnt),
   710		rd.new_dir		= new_path.dentry->d_inode,
   711		rd.new_dentry		= new_dentry,
   712		rd.flags		= flags,
   713		err = vfs_rename(&rd);
   714		if (err)
   715			ksmbd_debug(VFS, "vfs_rename failed err %d\n", err);
   716	out4:
   717		dput(new_dentry);
   718	out3:
   719		dput(old_dentry);
   720	out2:
   721		unlock_rename(new_path.dentry, old_path.dentry);
   722		path_put(&new_path);
   723	out1:
   724		path_put(&old_path);
   725	
   726	putnames:
   727		putname(to);
   728	putname_from:
   729		putname(from);
   730	free_pathname:
   731		kfree(pathname);
   732		ksmbd_revert_fsids(work);
   733		return err;
   734	}
   735	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2022-02-08 18:35 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-08  1:09 [PATCH] ksmbd: fix racy issue from using ->d_parent and ->d_name Namjae Jeon
2022-02-08 18:34 ` kernel test robot
2022-02-08 18:34   ` kernel test robot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.