[Devel] [PATCH vz10 01/24] ext4: attach jinode when opening the balloon inode for write

Pavel Tikhomirov ptikhomirov at virtuozzo.com
Thu Jul 23 14:38:10 MSK 2026


This patch fixes the crash in question yes.

But, it only does jnode attach, but not everything that
ext4_file_open() would've done. So we can get some problems in future
with currently skipped fscrypt/fsverity open hooks, dquot_file_open,
FMODE_NOWAIT | FMODE_CAN_ODIRECT, atomic-write mode, the
emergency/shutdown gate. Or even with something new after future rebases.

AI offers to do:

-     int err, fd, ro;
+     int err, fd, ro, flags;
      struct file *filp;
      struct dentry *de;
      struct path path;
-     fmode_t mode;
@@
      path.dentry = de;
      path.mnt = mntget(mnt);
-     ro = mnt_want_write(path.mnt);
-     if (ro)
-             mode = O_RDONLY;
-     else
-             mode = O_RDWR;
-     filp = alloc_file(&path, mode, &ext4_file_operations);
-     if (!ro)
-             mnt_drop_write(path.mnt);
-     if (IS_ERR(filp)) {
-             err = PTR_ERR(filp);
-             goto err_filp;
-     }
-
-     /*
-      * alloc_file() does not invoke ->open, so ext4_file_open()'s
-      * jinode setup for write-opened inodes is skipped here. ...
-      */
-     if (!ro) {
-             err = ext4_inode_attach_jinode(balloon_ino);
-             if (err < 0) {
-                     fput(filp);
-                     put_unused_fd(fd);
-                     return err;
-             }
-     }
-
-     filp->f_flags |= O_LARGEFILE;
+     ro = mnt_want_write(path.mnt);
+     if (!ro)
+             mnt_drop_write(path.mnt);
+     flags = O_LARGEFILE | (ro ? O_RDONLY : O_RDWR);
+
+     filp = dentry_open(&path, flags, current_cred());
+     path_put(&path);
+     if (IS_ERR(filp)) {
+             err = PTR_ERR(filp);
+             goto err_de;
+     }
+
      fd_install(fd, filp);
      return fd;

-err_filp:
-     path_put(&path);
 err_de:
      put_unused_fd(fd);

Which basically means to use dentry_open() instead of alloc_file(),
getting all missing functionality for free without workarounds.

E.g.:

 +-> dentry_open
   +-> vfs_open
     +-> do_dentry_open
       +-> file_get_write_access // critical difference
       +-> f->f_op->open
         +-> ext4_file_open
           +-> ext4_inode_attach_jinode

One critical difference here is that the old alloc_file() path
only probed writability and dropped it, so an open balloon fd did
not pin the mount. With dentry_open we know prevent e.g. ro-remount
while ploop has this balloon fd open.

On 7/6/26 12:59, Konstantin Khorenko wrote:
> ext4_open_balloon() hands userspace a writable fd for the hidden
> balloon inode built with alloc_file(). Unlike the normal open path,
> alloc_file() does not call ->open, so ext4_file_open()'s
> ext4_inode_attach_jinode() for write-opened inodes never runs and
> EXT4_I(balloon_ino)->jinode stays NULL.
> 
> A subsequent fallocate() (or any write that has to zero a partial
> block in the default data=ordered mode) reaches
> __ext4_block_zero_page_range() -> ext4_jbd2_inode_add_write() ->
> jbd2_journal_inode_ranged_write() with a NULL jinode and oopses:
> 
>   BUG: kernel NULL pointer dereference, address: 0000000000000028
>   RIP: jbd2_journal_file_inode+0x5f/0x150 [jbd2]
>   Call Trace:
>    __ext4_block_zero_page_range+0x195/0x300 [ext4]
>    ext4_alloc_file_blocks.isra.0+0x2af/0x380 [ext4]
>    ext4_do_fallocate+0xab/0x200 [ext4]
>    ext4_fallocate+0x121/0x160 [ext4]
> 
> So an EXT4_IOC_OPEN_BALLOON caller (which needs only CAP_SYS_ADMIN)
> can crash the host. Attach the jinode right after alloc_file() when
> the balloon was opened for write, mirroring ext4_file_open(), and
> fail the open if the attach fails.
> 
> Fixes: 55ad5a773877 ("ext4: Provide a balloon nipple for management")
> Feature: fs/ext4: fast online shrink support
> https://virtuozzo.atlassian.net/browse/VSTOR-137234
> Signed-off-by: Konstantin Khorenko <khorenko at virtuozzo.com>
> ---
>  fs/ext4/ioctl.c | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
> 
> diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
> index 954799144751..d8bf99b1f953 100644
> --- a/fs/ext4/ioctl.c
> +++ b/fs/ext4/ioctl.c
> @@ -1269,6 +1269,22 @@ static int ext4_open_balloon(struct super_block *sb, struct vfsmount *mnt)
>  		goto err_filp;
>  	}
>  
> +	/*
> +	 * alloc_file() does not invoke ->open, so ext4_file_open()'s
> +	 * jinode setup for write-opened inodes is skipped here. Attach it
> +	 * ourselves, otherwise a later fallocate()/write() that has to
> +	 * zero a partial block in data=ordered mode dereferences a NULL
> +	 * EXT4_I(balloon_ino)->jinode and oopses.
> +	 */
> +	if (!ro) {
> +		err = ext4_inode_attach_jinode(balloon_ino);
> +		if (err < 0) {
> +			fput(filp);
> +			put_unused_fd(fd);
> +			return err;
> +		}
> +	}
> +
>  	filp->f_flags |= O_LARGEFILE;
>  	fd_install(fd, filp);
>  	return fd;

-- 
Best regards, Pavel Tikhomirov
Senior Software Developer, Virtuozzo.



More information about the Devel mailing list