]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
erofs: ensure valid f_path for page cache sharing
authorGao Xiang <xiang@kernel.org>
Mon, 27 Jul 2026 04:27:39 +0000 (12:27 +0800)
committerGao Xiang <xiang@kernel.org>
Mon, 27 Jul 2026 04:31:02 +0000 (12:31 +0800)
Previously, backing files for page cache sharing were set up with
f_path left as NULL (only f_inode was valid).  It worked, but a recent
mincore fix relies on f_path.mnt and crashes (found by "erofs/028" on
7.2-rc4):

 BUG: kernel NULL pointer dereference, address: 0000000000000018
 #PF: supervisor read access in kernel mode
 #PF: error_code(0x0000) - not-present page
 PGD 0 P4D 0
 Oops: Oops: 0000 [#1] SMP PTI
 CPU: 3 UID: 0 PID: 675528 Comm: fincore Not tainted 7.2.0-rc4-00002-g[]-dirty #1 PREEMPT(lazy)
 Hardware name: Red Hat KVM, BIOS 1.16.0-4.al8 04/01/2014
 RIP: 0010:__do_sys_mincore+0xc0/0x2c0
 ...

Specify valid paths using valid disconnected dentries together with
erofs_ishare_mnt instead of leaving f_path empty, so they are more
like real backing files in a pseudo filesystem and standard
backing_file_open() can be used directly.

Fixes: e187bc02f8fa ("mm: do file ownership checks with the proper mount idmap")
Acked-by: Hongbo Li <hongbohbli@tencent.com>
Signed-off-by: Gao Xiang <xiang@kernel.org>
fs/erofs/Kconfig
fs/erofs/internal.h
fs/erofs/ishare.c

index 4789b1077d8cef32faa82ac38cc4cf45e737fc80..1feb28cfe557d62ad581fba466b18e0266dd415a 100644 (file)
@@ -189,6 +189,7 @@ config EROFS_FS_PCPU_KTHREAD_HIPRI
 config EROFS_FS_PAGE_CACHE_SHARE
        bool "EROFS page cache share support (experimental)"
        depends on EROFS_FS && EROFS_FS_XATTR
+       select FS_STACK
        help
          This enables page cache sharing among inodes with identical
          content fingerprints on the same machine.
index 580f8d9f14e7f9ee03d9bae675acdfc8c8d4695b..57bd21859c65d92256d8936c9fe936e19ab769e5 100644 (file)
@@ -288,8 +288,8 @@ struct erofs_inode {
                        struct erofs_inode_fingerprint fingerprint;
                        spinlock_t ishare_lock;
                };
-               /* for each real inode */
-               struct inode *sharedinode;
+               /* for each real filesystem inode */
+               struct dentry *sharedentry;
        };
 #endif
        /* the corresponding vfs inode */
index a1a20a5ba548f57f41df2fbc1e4b43200fd2d431..fa7d4112dec59084b25e892ad59d6fb4df41dc33 100644 (file)
@@ -2,14 +2,13 @@
 /*
  * Copyright (C) 2024, Alibaba Cloud
  */
+#include <linux/backing-file.h>
 #include <linux/xxhash.h>
 #include <linux/mount.h>
 #include <linux/security.h>
 #include "internal.h"
 #include "xattr.h"
 
-#include "../internal.h"
-
 static struct vfsmount *erofs_ishare_mnt;
 
 static int erofs_ishare_iget5_eq(struct inode *inode, void *data)
@@ -33,10 +32,12 @@ static int erofs_ishare_iget5_set(struct inode *inode, void *data)
 
 bool erofs_ishare_fill_inode(struct inode *inode)
 {
+       static const struct file_operations empty_fops = {};
        struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb);
        const struct address_space_operations *aops;
        struct erofs_inode *vi = EROFS_I(inode);
        struct erofs_inode_fingerprint fp;
+       struct dentry *sd;
        struct inode *si;
 
        aops = erofs_get_aops(inode);
@@ -49,7 +50,9 @@ bool erofs_ishare_fill_inode(struct inode *inode)
                          xxh32(fp.opaque, fp.size, 0),
                          erofs_ishare_iget5_eq, erofs_ishare_iget5_set, &fp);
        if (si && (inode_state_read_once(si) & I_NEW)) {
+               si->i_fop = &empty_fops;
                si->i_mapping->a_ops = aops;
+               si->i_mode = 0444 | S_IFREG;
                si->i_size = inode->i_size;
                unlock_new_inode(si);
        } else {
@@ -65,7 +68,10 @@ bool erofs_ishare_fill_inode(struct inode *inode)
                        return false;
                }
        }
-       vi->sharedinode = si;
+       sd = d_obtain_alias(si); /* disconnected denties for sharedinodes */
+       if (IS_ERR(sd))
+               return false;
+       vi->sharedentry = sd;
        INIT_LIST_HEAD(&vi->ishare_list);
        spin_lock(&EROFS_I(si)->ishare_lock);
        list_add(&vi->ishare_list, &EROFS_I(si)->ishare_list);
@@ -75,48 +81,40 @@ bool erofs_ishare_fill_inode(struct inode *inode)
 
 void erofs_ishare_free_inode(struct inode *inode)
 {
-       struct erofs_inode *vi = EROFS_I(inode);
-       struct inode *sharedinode = vi->sharedinode;
+       struct erofs_inode *vi = EROFS_I(inode), *svi;
 
-       if (!sharedinode)
+       if (!vi->sharedentry)
                return;
-       spin_lock(&EROFS_I(sharedinode)->ishare_lock);
+       svi = EROFS_I(d_inode(vi->sharedentry));
+       spin_lock(&svi->ishare_lock);
        list_del(&vi->ishare_list);
-       spin_unlock(&EROFS_I(sharedinode)->ishare_lock);
-       iput(sharedinode);
-       vi->sharedinode = NULL;
+       spin_unlock(&svi->ishare_lock);
+       dput(vi->sharedentry);
+       vi->sharedentry = NULL;
 }
 
 static int erofs_ishare_file_open(struct inode *inode, struct file *file)
 {
-       struct inode *sharedinode = EROFS_I(inode)->sharedinode;
-       struct file *realfile;
+       struct path sharedpath = {
+               .mnt = erofs_ishare_mnt,
+               .dentry = EROFS_I(inode)->sharedentry,
+       };
+       struct file *rf;
 
        if (file->f_flags & O_DIRECT)
                return -EINVAL;
-       realfile = alloc_empty_backing_file(O_RDONLY|O_NOATIME, current_cred(),
-                                           file);
-       if (IS_ERR(realfile))
-               return PTR_ERR(realfile);
-       ihold(sharedinode);
-       realfile->f_op = &erofs_file_fops;
-       realfile->f_inode = sharedinode;
-       realfile->f_mapping = sharedinode->i_mapping;
-       path_get(&file->f_path);
-       backing_file_set_user_path(realfile, &file->f_path);
-
-       file_ra_state_init(&realfile->f_ra, file->f_mapping);
-       realfile->private_data = EROFS_I(inode);
-       file->private_data = realfile;
+
+       rf = backing_file_open(file, file->f_flags | O_NOATIME,
+                              &sharedpath, current_cred());
+       if (IS_ERR(rf))
+               return PTR_ERR(rf);
+       file->private_data = rf;
        return 0;
 }
 
 static int erofs_ishare_file_release(struct inode *inode, struct file *file)
 {
-       struct file *realfile = file->private_data;
-
-       iput(realfile->f_inode);
-       fput(realfile);
+       fput(file->private_data);
        file->private_data = NULL;
        return 0;
 }