[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