ext4_compat_ioctl() only forwards an explicit allow-list of commands to ext4_ioctl() and returns -ENOIOCTLCMD for everything else. Several commands were never added to that list: EXT4_IOC_MIGRATE EXT4_IOC_ALLOC_DA_BLKS EXT4_IOC_SWAP_BOOT EXT4_IOC_GET_TUNE_SB_PARAM EXT4_IOC_SET_TUNE_SB_PARAM As a result, 32-bit userspace running on a 64-bit kernel (for example a 32-bit tune2fs or e4defrag) gets -ENOTTY for these ioctls, even though they work for native 64-bit callers. None of these commands needs any translation: MIGRATE, ALLOC_DA_BLKS and SWAP_BOOT are _IO() commands that ignore their argument, and struct ext4_tune_sb_params consists only of fixed-size __u8/__u16/ __u32/__u64 members whose layout is identical for 32-bit and 64-bit ABIs (its __u64 members are naturally aligned at offsets 16, 24 and 32, and the structure size is 232 bytes on both). The ioctl numbers therefore encode the same size, and the commands can simply be passed through to ext4_ioctl() with compat_ptr() applied to the argument. Add the missing commands to the pass-through list and extend the ioctl dispatch KUnit test to check that the compat handler forwards them (when CONFIG_COMPAT is enabled). Fixes: c14c6fd5c56a ("ext4: Add EXT4_IOC_MIGRATE ioctl") Fixes: ccd2506bd431 ("ext4: add EXT4_IOC_ALLOC_DA_BLKS ioctl") Fixes: 393d1d1d7693 ("ext4: implementation of a new ioctl called EXT4_IOC_SWAP_BOOT") Fixes: 04a91570ac67 ("ext4: implemet new ioctls to set and get superblock parameters") Signed-off-by: Theodore Ts'o --- fs/ext4/ioctl-test.c | 19 +++++++++++++++++++ fs/ext4/ioctl.c | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/fs/ext4/ioctl-test.c b/fs/ext4/ioctl-test.c index 831b7e032fad..8afeccc755f6 100644 --- a/fs/ext4/ioctl-test.c +++ b/fs/ext4/ioctl-test.c @@ -1423,6 +1423,25 @@ static void test_force_shutdown_and_ioctl_dispatch(struct kunit *test) -EBADF); KUNIT_EXPECT_EQ(test, ext4_ioctl(&filp, EXT4_IOC_SWAP_BOOT, 0), -EBADF); +#ifdef CONFIG_COMPAT + /* + * Architecture-neutral commands must be passed through by + * the compat handler instead of failing with -ENOIOCTLCMD. + */ + KUNIT_EXPECT_EQ(test, + ext4_compat_ioctl(&filp, EXT4_IOC_SWAP_BOOT, 0), + -EBADF); + KUNIT_EXPECT_EQ(test, + ext4_compat_ioctl(&filp, + EXT4_IOC_GET_TUNE_SB_PARAM, + 0), + -EFAULT); + KUNIT_EXPECT_EQ(test, + ext4_compat_ioctl(&filp, + EXT4_IOC_SET_TUNE_SB_PARAM, + 0), + -EFAULT); +#endif filp.f_mode = FMODE_READ | FMODE_WRITE; /* EXT4_IOC_CLEAR_ES_CACHE empties inode extent status tree */ diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 9e2bde83a033..d6f47ff00e61 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -2064,6 +2064,11 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case FS_IOC_SETFSLABEL: case EXT4_IOC_GETFSUUID: case EXT4_IOC_SETFSUUID: + case EXT4_IOC_GET_TUNE_SB_PARAM: + case EXT4_IOC_SET_TUNE_SB_PARAM: + case EXT4_IOC_MIGRATE: + case EXT4_IOC_ALLOC_DA_BLKS: + case EXT4_IOC_SWAP_BOOT: break; default: return -ENOIOCTLCMD; -- 2.56.0.542.g3ef89b7020-goog