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 <[email protected]>
---
 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;
-- 
2.47.1

_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to