Unverified Commit e079d7c4 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Christian Brauner
Browse files

devtmpfs: don't use vfs_getattr_nosec to query i_mode



The recent move of the bdev_statx call to the low-level vfs_getattr_nosec
helper caused it being used by devtmpfs, which leads to deadlocks in
md teardown due to the block device lookup and put interfering with the
unusual lifetime rules in md.

But as handle_remove only works on inodes created and owned by devtmpfs
itself there is no need to use vfs_getattr_nosec vs simply reading the
mode from the inode directly.  Switch to that to avoid the bdev lookup
or any other unintentional side effect.

Reported-by: default avatarShin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Reported-by: default avatarXiao Ni <xni@redhat.com>
Fixes: 777d0961 ("fs: move the bdex_statx call to vfs_getattr_nosec")
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/20250423045941.1667425-1-hch@lst.de


Tested-by: default avatarShin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Tested-by: default avatarXiao Ni <xni@redhat.com>
Tested-by: default avatarAyush Jain <Ayush.jain3@amd.com>
Tested-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Reviewed-by: default avatarChristian Brauner <brauner@kernel.org>
Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 0d039eac
Loading
Loading
Loading
Loading
+9 −13
Original line number Diff line number Diff line
@@ -296,7 +296,7 @@ static int delete_path(const char *nodepath)
	return err;
}

static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat)
static int dev_mynode(struct device *dev, struct inode *inode)
{
	/* did we create it */
	if (inode->i_private != &thread)
@@ -304,13 +304,13 @@ static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *sta

	/* does the dev_t match */
	if (is_blockdev(dev)) {
		if (!S_ISBLK(stat->mode))
		if (!S_ISBLK(inode->i_mode))
			return 0;
	} else {
		if (!S_ISCHR(stat->mode))
		if (!S_ISCHR(inode->i_mode))
			return 0;
	}
	if (stat->rdev != dev->devt)
	if (inode->i_rdev != dev->devt)
		return 0;

	/* ours */
@@ -321,20 +321,16 @@ static int handle_remove(const char *nodename, struct device *dev)
{
	struct path parent;
	struct dentry *dentry;
	struct kstat stat;
	struct path p;
	struct inode *inode;
	int deleted = 0;
	int err;
	int err = 0;

	dentry = kern_path_locked(nodename, &parent);
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);

	p.mnt = parent.mnt;
	p.dentry = dentry;
	err = vfs_getattr(&p, &stat, STATX_TYPE | STATX_MODE,
			  AT_STATX_SYNC_AS_STAT);
	if (!err && dev_mynode(dev, d_inode(dentry), &stat)) {
	inode = d_inode(dentry);
	if (dev_mynode(dev, inode)) {
		struct iattr newattrs;
		/*
		 * before unlinking this node, reset permissions
@@ -342,7 +338,7 @@ static int handle_remove(const char *nodename, struct device *dev)
		 */
		newattrs.ia_uid = GLOBAL_ROOT_UID;
		newattrs.ia_gid = GLOBAL_ROOT_GID;
		newattrs.ia_mode = stat.mode & ~0777;
		newattrs.ia_mode = inode->i_mode & ~0777;
		newattrs.ia_valid =
			ATTR_UID|ATTR_GID|ATTR_MODE;
		inode_lock(d_inode(dentry));