devtmpfs: don't use vfs_getattr_nosec to query i_mode
authorChristoph Hellwig <hch@lst.de>
Wed, 23 Apr 2025 04:59:41 +0000 (06:59 +0200)
committerChristian Brauner <brauner@kernel.org>
Fri, 25 Apr 2025 10:11:45 +0000 (12:11 +0200)
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: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Reported-by: Xiao Ni <xni@redhat.com>
Fixes: 777d0961ff95 ("fs: move the bdex_statx call to vfs_getattr_nosec")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/20250423045941.1667425-1-hch@lst.de
Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Tested-by: Xiao Ni <xni@redhat.com>
Tested-by: Ayush Jain <Ayush.jain3@amd.com>
Tested-by: Heiko Carstens <hca@linux.ibm.com>
Reviewed-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
drivers/base/devtmpfs.c

index 6dd1a8860f1c923c7fa5a2915c740479ebb4839a..31bfb3194b4c29a1d6a002449045bf4e4141911d 100644 (file)
@@ -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));