diff options
| author | Christian Brauner <brauner@kernel.org> | 2026-03-23 14:41:19 +0300 |
|---|---|---|
| committer | Christian Brauner <brauner@kernel.org> | 2026-03-23 14:41:58 +0300 |
| commit | e247fd37e597b3536d26cfa5fcc558832586f57c (patch) | |
| tree | 425259dd28393a196c0520d6f8750ba61c074182 | |
| parent | 3d6bb84f6bb3f4c05fc47fd02ce75ce3032a4ce1 (diff) | |
| parent | e8767a3134ca69a307b37f7f58ca088dbee6eb82 (diff) | |
| download | linux-e247fd37e597b3536d26cfa5fcc558832586f57c.tar.xz | |
Merge patch series "fix architecture-specific compat_ftruncate64 implementations"
Christoph Hellwig <hch@lst.de> says:
This series fixes a really old bug found by code inspection, where the
architecture-specific 32-bit compat ftruncate64 implementations enforce
the non-LFS file size limit unless opened with O_LARGEFILE.
* patches from https://patch.msgid.link/20260323070205.2939118-1-hch@lst.de:
fs: remove do_sys_truncate
fs: pass on FTRUNCATE_* flags to do_truncate
fs: fix archiecture-specific compat_ftruncate64
Link: https://patch.msgid.link/20260323070205.2939118-1-hch@lst.de
Signed-off-by: Christian Brauner <brauner@kernel.org>
| -rw-r--r-- | arch/arm64/kernel/sys32.c | 2 | ||||
| -rw-r--r-- | arch/mips/kernel/linux32.c | 2 | ||||
| -rw-r--r-- | arch/parisc/kernel/sys_parisc.c | 4 | ||||
| -rw-r--r-- | arch/powerpc/kernel/sys_ppc32.c | 2 | ||||
| -rw-r--r-- | arch/sparc/kernel/sys_sparc32.c | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/sys_ia32.c | 3 | ||||
| -rw-r--r-- | fs/internal.h | 3 | ||||
| -rw-r--r-- | fs/open.c | 40 | ||||
| -rw-r--r-- | include/linux/syscalls.h | 16 | ||||
| -rw-r--r-- | io_uring/truncate.c | 2 |
10 files changed, 32 insertions, 44 deletions
diff --git a/arch/arm64/kernel/sys32.c b/arch/arm64/kernel/sys32.c index 96bcfb907443..12a948f3a504 100644 --- a/arch/arm64/kernel/sys32.c +++ b/arch/arm64/kernel/sys32.c @@ -89,7 +89,7 @@ COMPAT_SYSCALL_DEFINE4(aarch32_truncate64, const char __user *, pathname, COMPAT_SYSCALL_DEFINE4(aarch32_ftruncate64, unsigned int, fd, u32, __pad, arg_u32p(length)) { - return ksys_ftruncate(fd, arg_u64(length)); + return ksys_ftruncate(fd, arg_u64(length), FTRUNCATE_LFS); } COMPAT_SYSCALL_DEFINE5(aarch32_readahead, int, fd, u32, __pad, diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index a0c0a7a654e9..fe9a787db569 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -60,7 +60,7 @@ SYSCALL_DEFINE4(32_truncate64, const char __user *, path, SYSCALL_DEFINE4(32_ftruncate64, unsigned long, fd, unsigned long, __dummy, unsigned long, a2, unsigned long, a3) { - return ksys_ftruncate(fd, merge_64(a2, a3)); + return ksys_ftruncate(fd, merge_64(a2, a3), FTRUNCATE_LFS); } SYSCALL_DEFINE5(32_llseek, unsigned int, fd, unsigned int, offset_high, diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c index b2cdbb8a12b1..fcb0d8069139 100644 --- a/arch/parisc/kernel/sys_parisc.c +++ b/arch/parisc/kernel/sys_parisc.c @@ -216,7 +216,7 @@ asmlinkage long parisc_truncate64(const char __user * path, asmlinkage long parisc_ftruncate64(unsigned int fd, unsigned int high, unsigned int low) { - return ksys_ftruncate(fd, (long)high << 32 | low); + return ksys_ftruncate(fd, (long)high << 32 | low, FTRUNCATE_LFS); } /* stubs for the benefit of the syscall_table since truncate64 and truncate @@ -227,7 +227,7 @@ asmlinkage long sys_truncate64(const char __user * path, unsigned long length) } asmlinkage long sys_ftruncate64(unsigned int fd, unsigned long length) { - return ksys_ftruncate(fd, length); + return ksys_ftruncate(fd, length, FTRUNCATE_LFS); } asmlinkage long sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) { diff --git a/arch/powerpc/kernel/sys_ppc32.c b/arch/powerpc/kernel/sys_ppc32.c index d451a8229223..03fa487f2614 100644 --- a/arch/powerpc/kernel/sys_ppc32.c +++ b/arch/powerpc/kernel/sys_ppc32.c @@ -101,7 +101,7 @@ PPC32_SYSCALL_DEFINE4(ppc_ftruncate64, unsigned int, fd, u32, reg4, unsigned long, len1, unsigned long, len2) { - return ksys_ftruncate(fd, merge_64(len1, len2)); + return ksys_ftruncate(fd, merge_64(len1, len2), FTRUNCATE_LFS); } PPC32_SYSCALL_DEFINE6(ppc32_fadvise64, diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c index f84a02ab6bf9..04432b82b9e3 100644 --- a/arch/sparc/kernel/sys_sparc32.c +++ b/arch/sparc/kernel/sys_sparc32.c @@ -58,7 +58,7 @@ COMPAT_SYSCALL_DEFINE3(truncate64, const char __user *, path, u32, high, u32, lo COMPAT_SYSCALL_DEFINE3(ftruncate64, unsigned int, fd, u32, high, u32, low) { - return ksys_ftruncate(fd, ((u64)high << 32) | low); + return ksys_ftruncate(fd, ((u64)high << 32) | low, FTRUNCATE_LFS); } static int cp_compat_stat64(struct kstat *stat, diff --git a/arch/x86/kernel/sys_ia32.c b/arch/x86/kernel/sys_ia32.c index 6cf65397d225..610a1c2f4519 100644 --- a/arch/x86/kernel/sys_ia32.c +++ b/arch/x86/kernel/sys_ia32.c @@ -61,7 +61,8 @@ SYSCALL_DEFINE3(ia32_truncate64, const char __user *, filename, SYSCALL_DEFINE3(ia32_ftruncate64, unsigned int, fd, unsigned long, offset_low, unsigned long, offset_high) { - return ksys_ftruncate(fd, ((loff_t) offset_high << 32) | offset_low); + return ksys_ftruncate(fd, ((loff_t) offset_high << 32) | offset_low, + FTRUNCATE_LFS); } /* warning: next two assume little endian */ diff --git a/fs/internal.h b/fs/internal.h index cbc384a1aa09..52e4c354e7a4 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -198,8 +198,7 @@ extern struct open_how build_open_how(int flags, umode_t mode); extern int build_open_flags(const struct open_how *how, struct open_flags *op); struct file *file_close_fd_locked(struct files_struct *files, unsigned fd); -int do_ftruncate(struct file *file, loff_t length, int small); -int do_sys_ftruncate(unsigned int fd, loff_t length, int small); +int do_ftruncate(struct file *file, loff_t length, unsigned int flags); int chmod_common(const struct path *path, umode_t mode); int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); diff --git a/fs/open.c b/fs/open.c index 91f1139591ab..681d405bc61e 100644 --- a/fs/open.c +++ b/fs/open.c @@ -126,7 +126,7 @@ mnt_drop_write_and_out: } EXPORT_SYMBOL_GPL(vfs_truncate); -int do_sys_truncate(const char __user *pathname, loff_t length) +int ksys_truncate(const char __user *pathname, loff_t length) { unsigned int lookup_flags = LOOKUP_FOLLOW; struct path path; @@ -151,33 +151,31 @@ retry: SYSCALL_DEFINE2(truncate, const char __user *, path, long, length) { - return do_sys_truncate(path, length); + return ksys_truncate(path, length); } #ifdef CONFIG_COMPAT COMPAT_SYSCALL_DEFINE2(truncate, const char __user *, path, compat_off_t, length) { - return do_sys_truncate(path, length); + return ksys_truncate(path, length); } #endif -int do_ftruncate(struct file *file, loff_t length, int small) +int do_ftruncate(struct file *file, loff_t length, unsigned int flags) { - struct inode *inode; - struct dentry *dentry; + struct dentry *dentry = file->f_path.dentry; + struct inode *inode = dentry->d_inode; int error; - /* explicitly opened as large or we are on 64-bit box */ - if (file->f_flags & O_LARGEFILE) - small = 0; - - dentry = file->f_path.dentry; - inode = dentry->d_inode; if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE)) return -EINVAL; - /* Cannot ftruncate over 2^31 bytes without large file support */ - if (small && length > MAX_NON_LFS) + /* + * Cannot ftruncate over 2^31 bytes without large file support, either + * through opening with O_LARGEFILE or by using ftruncate64(). + */ + if (length > MAX_NON_LFS && + !(file->f_flags & O_LARGEFILE) && !(flags & FTRUNCATE_LFS)) return -EINVAL; /* Check IS_APPEND on real upper inode */ @@ -197,7 +195,7 @@ int do_ftruncate(struct file *file, loff_t length, int small) ATTR_MTIME | ATTR_CTIME, file); } -int do_sys_ftruncate(unsigned int fd, loff_t length, int small) +int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags) { if (length < 0) return -EINVAL; @@ -205,18 +203,18 @@ int do_sys_ftruncate(unsigned int fd, loff_t length, int small) if (fd_empty(f)) return -EBADF; - return do_ftruncate(fd_file(f), length, small); + return do_ftruncate(fd_file(f), length, flags); } SYSCALL_DEFINE2(ftruncate, unsigned int, fd, off_t, length) { - return do_sys_ftruncate(fd, length, 1); + return ksys_ftruncate(fd, length, 0); } #ifdef CONFIG_COMPAT COMPAT_SYSCALL_DEFINE2(ftruncate, unsigned int, fd, compat_off_t, length) { - return do_sys_ftruncate(fd, length, 1); + return ksys_ftruncate(fd, length, 0); } #endif @@ -224,12 +222,12 @@ COMPAT_SYSCALL_DEFINE2(ftruncate, unsigned int, fd, compat_off_t, length) #if BITS_PER_LONG == 32 SYSCALL_DEFINE2(truncate64, const char __user *, path, loff_t, length) { - return do_sys_truncate(path, length); + return ksys_truncate(path, length); } SYSCALL_DEFINE2(ftruncate64, unsigned int, fd, loff_t, length) { - return do_sys_ftruncate(fd, length, 0); + return ksys_ftruncate(fd, length, FTRUNCATE_LFS); } #endif /* BITS_PER_LONG == 32 */ @@ -245,7 +243,7 @@ COMPAT_SYSCALL_DEFINE3(truncate64, const char __user *, pathname, COMPAT_SYSCALL_DEFINE3(ftruncate64, unsigned int, fd, compat_arg_u64_dual(length)) { - return ksys_ftruncate(fd, compat_arg_u64_glue(length)); + return ksys_ftruncate(fd, compat_arg_u64_glue(length), FTRUNCATE_LFS); } #endif diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 02bd6ddb6278..f5639d5ac331 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1283,19 +1283,9 @@ static inline long ksys_lchown(const char __user *filename, uid_t user, AT_SYMLINK_NOFOLLOW); } -int do_sys_ftruncate(unsigned int fd, loff_t length, int small); - -static inline long ksys_ftruncate(unsigned int fd, loff_t length) -{ - return do_sys_ftruncate(fd, length, 1); -} - -int do_sys_truncate(const char __user *pathname, loff_t length); - -static inline long ksys_truncate(const char __user *pathname, loff_t length) -{ - return do_sys_truncate(pathname, length); -} +#define FTRUNCATE_LFS (1u << 0) /* allow truncating > 32-bit */ +int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags); +int ksys_truncate(const char __user *pathname, loff_t length); static inline unsigned int ksys_personality(unsigned int personality) { diff --git a/io_uring/truncate.c b/io_uring/truncate.c index 487baf23b44e..c88d8bd8d20e 100644 --- a/io_uring/truncate.c +++ b/io_uring/truncate.c @@ -41,7 +41,7 @@ int io_ftruncate(struct io_kiocb *req, unsigned int issue_flags) WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); - ret = do_ftruncate(req->file, ft->len, 1); + ret = do_ftruncate(req->file, ft->len, 0); io_req_set_res(req, ret, 0); return IOU_COMPLETE; |
