* fix architecture-specific compat_ftruncate64 implementations
@ 2026-03-23 7:01 Christoph Hellwig
2026-03-23 7:01 ` [PATCH 1/3] fs: fix archiecture-specific compat_ftruncate64 Christoph Hellwig
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Christoph Hellwig @ 2026-03-23 7:01 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner
Cc: Jan Kara, x86, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, sparclinux, linux-fsdevel, linux-api
Hi all,
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.
Diffstat:
arch/arm64/kernel/sys32.c | 2 +-
arch/mips/kernel/linux32.c | 2 +-
arch/parisc/kernel/sys_parisc.c | 4 ++--
arch/powerpc/kernel/sys_ppc32.c | 2 +-
arch/sparc/kernel/sys_sparc32.c | 2 +-
arch/x86/kernel/sys_ia32.c | 3 ++-
fs/internal.h | 3 +--
fs/open.c | 40 +++++++++++++++++++---------------------
include/linux/syscalls.h | 16 +++-------------
io_uring/truncate.c | 2 +-
10 files changed, 32 insertions(+), 44 deletions(-)
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/3] fs: fix archiecture-specific compat_ftruncate64
2026-03-23 7:01 fix architecture-specific compat_ftruncate64 implementations Christoph Hellwig
@ 2026-03-23 7:01 ` Christoph Hellwig
2026-03-23 10:27 ` Jan Kara
2026-03-23 7:01 ` [PATCH 2/3] fs: pass on FTRUNCATE_* flags to do_truncate Christoph Hellwig
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Christoph Hellwig @ 2026-03-23 7:01 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner
Cc: Jan Kara, x86, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, sparclinux, linux-fsdevel, linux-api
The "small" argument to do_sys_ftruncate indicates if > 32-bit size
should be reject, but all the arch-specific compat ftruncate64
implementations get this wrong. Merge do_sys_ftruncate and
ksys_ftruncate, replace the integer as boolean small flag with a
descriptive one about LFS semantics, and use it correctly in the
architecture-specific ftruncate64 implementations.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Fixes: 3dd681d944f6 ("arm64: 32-bit (compat) applications support")
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
arch/arm64/kernel/sys32.c | 2 +-
arch/mips/kernel/linux32.c | 2 +-
arch/parisc/kernel/sys_parisc.c | 4 ++--
arch/powerpc/kernel/sys_ppc32.c | 2 +-
arch/sparc/kernel/sys_sparc32.c | 2 +-
arch/x86/kernel/sys_ia32.c | 3 ++-
fs/internal.h | 1 -
fs/open.c | 12 ++++++------
include/linux/syscalls.h | 8 ++------
9 files changed, 16 insertions(+), 20 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..2663823e273a 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -199,7 +199,6 @@ 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 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..412d0d6fbaa7 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -197,7 +197,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 +205,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 & FTRUNCATE_LFS));
}
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
@@ -229,7 +229,7 @@ SYSCALL_DEFINE2(truncate64, const char __user *, path, loff_t, 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 +245,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..8787b3511c86 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -1283,12 +1283,8 @@ 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);
-}
+#define FTRUNCATE_LFS (1u << 0) /* allow truncating > 32-bit */
+int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags);
int do_sys_truncate(const char __user *pathname, loff_t length);
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/3] fs: pass on FTRUNCATE_* flags to do_truncate
2026-03-23 7:01 fix architecture-specific compat_ftruncate64 implementations Christoph Hellwig
2026-03-23 7:01 ` [PATCH 1/3] fs: fix archiecture-specific compat_ftruncate64 Christoph Hellwig
@ 2026-03-23 7:01 ` Christoph Hellwig
2026-03-23 10:28 ` Jan Kara
2026-03-23 7:01 ` [PATCH 3/3] fs: remove do_sys_truncate Christoph Hellwig
2026-03-23 11:44 ` fix architecture-specific compat_ftruncate64 implementations Christian Brauner
3 siblings, 1 reply; 8+ messages in thread
From: Christoph Hellwig @ 2026-03-23 7:01 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner
Cc: Jan Kara, x86, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, sparclinux, linux-fsdevel, linux-api
Pass the flags one level down to replace the somewhat confusing small
argument, and clean up do_truncate as a result.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
fs/internal.h | 2 +-
fs/open.c | 22 ++++++++++------------
io_uring/truncate.c | 2 +-
3 files changed, 12 insertions(+), 14 deletions(-)
diff --git a/fs/internal.h b/fs/internal.h
index 2663823e273a..52e4c354e7a4 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -198,7 +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_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 412d0d6fbaa7..181c1597e73c 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -161,23 +161,21 @@ COMPAT_SYSCALL_DEFINE2(truncate, const char __user *, path, compat_off_t, 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 */
@@ -205,7 +203,7 @@ int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags)
if (fd_empty(f))
return -EBADF;
- return do_ftruncate(fd_file(f), length, !(flags & FTRUNCATE_LFS));
+ return do_ftruncate(fd_file(f), length, flags);
}
SYSCALL_DEFINE2(ftruncate, unsigned int, fd, off_t, length)
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;
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/3] fs: remove do_sys_truncate
2026-03-23 7:01 fix architecture-specific compat_ftruncate64 implementations Christoph Hellwig
2026-03-23 7:01 ` [PATCH 1/3] fs: fix archiecture-specific compat_ftruncate64 Christoph Hellwig
2026-03-23 7:01 ` [PATCH 2/3] fs: pass on FTRUNCATE_* flags to do_truncate Christoph Hellwig
@ 2026-03-23 7:01 ` Christoph Hellwig
2026-03-23 10:31 ` Jan Kara
2026-03-23 11:44 ` fix architecture-specific compat_ftruncate64 implementations Christian Brauner
3 siblings, 1 reply; 8+ messages in thread
From: Christoph Hellwig @ 2026-03-23 7:01 UTC (permalink / raw)
To: Alexander Viro, Christian Brauner
Cc: Jan Kara, x86, linux-arm-kernel, linux-mips, linux-parisc,
linuxppc-dev, sparclinux, linux-fsdevel, linux-api
do_sys_truncate ist only used to implement ksys_truncate and the native
truncate syscalls. Merge do_sys_truncate into ksys_truncate and return
int from it as it only returns 0 or negative errnos.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
fs/open.c | 8 ++++----
include/linux/syscalls.h | 8 +-------
2 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/fs/open.c b/fs/open.c
index 181c1597e73c..681d405bc61e 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -126,7 +126,7 @@ int vfs_truncate(const struct path *path, loff_t length)
}
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,13 +151,13 @@ int do_sys_truncate(const char __user *pathname, loff_t length)
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
@@ -222,7 +222,7 @@ 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)
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 8787b3511c86..f5639d5ac331 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -1285,13 +1285,7 @@ static inline long ksys_lchown(const char __user *filename, uid_t user,
#define FTRUNCATE_LFS (1u << 0) /* allow truncating > 32-bit */
int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags);
-
-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);
-}
+int ksys_truncate(const char __user *pathname, loff_t length);
static inline unsigned int ksys_personality(unsigned int personality)
{
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/3] fs: fix archiecture-specific compat_ftruncate64
2026-03-23 7:01 ` [PATCH 1/3] fs: fix archiecture-specific compat_ftruncate64 Christoph Hellwig
@ 2026-03-23 10:27 ` Jan Kara
0 siblings, 0 replies; 8+ messages in thread
From: Jan Kara @ 2026-03-23 10:27 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Alexander Viro, Christian Brauner, Jan Kara, x86,
linux-arm-kernel, linux-mips, linux-parisc, linuxppc-dev,
sparclinux, linux-fsdevel, linux-api
On Mon 23-03-26 08:01:44, Christoph Hellwig wrote:
> The "small" argument to do_sys_ftruncate indicates if > 32-bit size
> should be reject, but all the arch-specific compat ftruncate64
> implementations get this wrong. Merge do_sys_ftruncate and
> ksys_ftruncate, replace the integer as boolean small flag with a
> descriptive one about LFS semantics, and use it correctly in the
> architecture-specific ftruncate64 implementations.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Fixes: 3dd681d944f6 ("arm64: 32-bit (compat) applications support")
> Signed-off-by: Christoph Hellwig <hch@lst.de>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> arch/arm64/kernel/sys32.c | 2 +-
> arch/mips/kernel/linux32.c | 2 +-
> arch/parisc/kernel/sys_parisc.c | 4 ++--
> arch/powerpc/kernel/sys_ppc32.c | 2 +-
> arch/sparc/kernel/sys_sparc32.c | 2 +-
> arch/x86/kernel/sys_ia32.c | 3 ++-
> fs/internal.h | 1 -
> fs/open.c | 12 ++++++------
> include/linux/syscalls.h | 8 ++------
> 9 files changed, 16 insertions(+), 20 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..2663823e273a 100644
> --- a/fs/internal.h
> +++ b/fs/internal.h
> @@ -199,7 +199,6 @@ 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 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..412d0d6fbaa7 100644
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -197,7 +197,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 +205,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 & FTRUNCATE_LFS));
> }
>
> 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
>
> @@ -229,7 +229,7 @@ SYSCALL_DEFINE2(truncate64, const char __user *, path, loff_t, 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 +245,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..8787b3511c86 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -1283,12 +1283,8 @@ 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);
> -}
> +#define FTRUNCATE_LFS (1u << 0) /* allow truncating > 32-bit */
> +int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags);
>
> int do_sys_truncate(const char __user *pathname, loff_t length);
>
> --
> 2.47.3
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] fs: pass on FTRUNCATE_* flags to do_truncate
2026-03-23 7:01 ` [PATCH 2/3] fs: pass on FTRUNCATE_* flags to do_truncate Christoph Hellwig
@ 2026-03-23 10:28 ` Jan Kara
0 siblings, 0 replies; 8+ messages in thread
From: Jan Kara @ 2026-03-23 10:28 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Alexander Viro, Christian Brauner, Jan Kara, x86,
linux-arm-kernel, linux-mips, linux-parisc, linuxppc-dev,
sparclinux, linux-fsdevel, linux-api
On Mon 23-03-26 08:01:45, Christoph Hellwig wrote:
> Pass the flags one level down to replace the somewhat confusing small
> argument, and clean up do_truncate as a result.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/internal.h | 2 +-
> fs/open.c | 22 ++++++++++------------
> io_uring/truncate.c | 2 +-
> 3 files changed, 12 insertions(+), 14 deletions(-)
>
> diff --git a/fs/internal.h b/fs/internal.h
> index 2663823e273a..52e4c354e7a4 100644
> --- a/fs/internal.h
> +++ b/fs/internal.h
> @@ -198,7 +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_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 412d0d6fbaa7..181c1597e73c 100644
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -161,23 +161,21 @@ COMPAT_SYSCALL_DEFINE2(truncate, const char __user *, path, compat_off_t, 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 */
> @@ -205,7 +203,7 @@ int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags)
> if (fd_empty(f))
> return -EBADF;
>
> - return do_ftruncate(fd_file(f), length, !(flags & FTRUNCATE_LFS));
> + return do_ftruncate(fd_file(f), length, flags);
> }
>
> SYSCALL_DEFINE2(ftruncate, unsigned int, fd, off_t, length)
> 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;
> --
> 2.47.3
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3] fs: remove do_sys_truncate
2026-03-23 7:01 ` [PATCH 3/3] fs: remove do_sys_truncate Christoph Hellwig
@ 2026-03-23 10:31 ` Jan Kara
0 siblings, 0 replies; 8+ messages in thread
From: Jan Kara @ 2026-03-23 10:31 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Alexander Viro, Christian Brauner, Jan Kara, x86,
linux-arm-kernel, linux-mips, linux-parisc, linuxppc-dev,
sparclinux, linux-fsdevel, linux-api
On Mon 23-03-26 08:01:46, Christoph Hellwig wrote:
> do_sys_truncate ist only used to implement ksys_truncate and the native
> truncate syscalls. Merge do_sys_truncate into ksys_truncate and return
> int from it as it only returns 0 or negative errnos.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/open.c | 8 ++++----
> include/linux/syscalls.h | 8 +-------
> 2 files changed, 5 insertions(+), 11 deletions(-)
>
> diff --git a/fs/open.c b/fs/open.c
> index 181c1597e73c..681d405bc61e 100644
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -126,7 +126,7 @@ int vfs_truncate(const struct path *path, loff_t length)
> }
> 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,13 +151,13 @@ int do_sys_truncate(const char __user *pathname, loff_t length)
>
> 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
>
> @@ -222,7 +222,7 @@ 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)
> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
> index 8787b3511c86..f5639d5ac331 100644
> --- a/include/linux/syscalls.h
> +++ b/include/linux/syscalls.h
> @@ -1285,13 +1285,7 @@ static inline long ksys_lchown(const char __user *filename, uid_t user,
>
> #define FTRUNCATE_LFS (1u << 0) /* allow truncating > 32-bit */
> int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags);
> -
> -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);
> -}
> +int ksys_truncate(const char __user *pathname, loff_t length);
>
> static inline unsigned int ksys_personality(unsigned int personality)
> {
> --
> 2.47.3
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: fix architecture-specific compat_ftruncate64 implementations
2026-03-23 7:01 fix architecture-specific compat_ftruncate64 implementations Christoph Hellwig
` (2 preceding siblings ...)
2026-03-23 7:01 ` [PATCH 3/3] fs: remove do_sys_truncate Christoph Hellwig
@ 2026-03-23 11:44 ` Christian Brauner
3 siblings, 0 replies; 8+ messages in thread
From: Christian Brauner @ 2026-03-23 11:44 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Christian Brauner, Jan Kara, x86, linux-arm-kernel, linux-mips,
linux-parisc, linuxppc-dev, sparclinux, linux-fsdevel, linux-api,
Alexander Viro
On Mon, 23 Mar 2026 08:01:43 +0100, Christoph Hellwig wrote:
> 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.
>
> Diffstat:
> arch/arm64/kernel/sys32.c | 2 +-
> arch/mips/kernel/linux32.c | 2 +-
> arch/parisc/kernel/sys_parisc.c | 4 ++--
> arch/powerpc/kernel/sys_ppc32.c | 2 +-
> arch/sparc/kernel/sys_sparc32.c | 2 +-
> arch/x86/kernel/sys_ia32.c | 3 ++-
> fs/internal.h | 3 +--
> fs/open.c | 40 +++++++++++++++++++---------------------
> include/linux/syscalls.h | 16 +++-------------
> io_uring/truncate.c | 2 +-
> 10 files changed, 32 insertions(+), 44 deletions(-)
>
> [...]
VFS CI: https://github.com/linux-fsdevel/vfs/actions/runs/23425735066
x86_64 (gcc, debian, ovl-fstests) pass
x86_64 (gcc, debian, selftests) pass
x86_64 (gcc, debian, xfstests) pass
x86_64 (gcc, fedora, ovl-fstests) pass
x86_64 (gcc, fedora, selftests) pass
x86_64 (gcc, fedora, xfstests) pass
---
Applied to the vfs-7.1.misc branch of the vfs/vfs.git tree.
Patches in the vfs-7.1.misc branch should appear in linux-next soon.
Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.
It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.
Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.
tree: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs-7.1.misc
[1/3] fs: fix archiecture-specific compat_ftruncate64
https://git.kernel.org/vfs/vfs/c/e43dce8a0bc0
[2/3] fs: pass on FTRUNCATE_* flags to do_truncate
https://git.kernel.org/vfs/vfs/c/0924f6b80d4a
[3/3] fs: remove do_sys_truncate
https://git.kernel.org/vfs/vfs/c/e8767a3134ca
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-03-23 11:44 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-23 7:01 fix architecture-specific compat_ftruncate64 implementations Christoph Hellwig
2026-03-23 7:01 ` [PATCH 1/3] fs: fix archiecture-specific compat_ftruncate64 Christoph Hellwig
2026-03-23 10:27 ` Jan Kara
2026-03-23 7:01 ` [PATCH 2/3] fs: pass on FTRUNCATE_* flags to do_truncate Christoph Hellwig
2026-03-23 10:28 ` Jan Kara
2026-03-23 7:01 ` [PATCH 3/3] fs: remove do_sys_truncate Christoph Hellwig
2026-03-23 10:31 ` Jan Kara
2026-03-23 11:44 ` fix architecture-specific compat_ftruncate64 implementations Christian Brauner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox