Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vfs: Fix EOVERFLOW testing in put_compat_statfs64

Today, put_compat_statfs64() disallows nearly any field value over
2^32 if f_bsize is only 32 bits, but that makes no sense.
compat_statfs64 is there for the explicit purpose of providing 64-bit
fields for f_files, f_ffree, etc. And f_bsize is always only 32 bits.

As a result, 32-bit userspace gets -EOVERFLOW for i.e. large file
counts even with -D_FILE_OFFSET_BITS=64 set.

In reality, only f_bsize and f_frsize can legitimately overflow
(fields like f_type and f_namelen should never be large), so test
only those fields.

This bug was discussed at length some time ago, and this is the proposal
Al suggested at https://lkml.org/lkml/2018/8/6/640. It seemed to get
dropped amid the discussion of other related changes, but this
part seems obviously correct on its own, so I've picked it up and
sent it, for expediency.

Fixes: 64d2ab32efe3 ("vfs: fix put_compat_statfs64() does not handle errors")
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Eric Sandeen and committed by
Linus Torvalds
cc3a7bfe c1053cd1

+4 -13
+4 -13
fs/statfs.c
··· 318 318 static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf) 319 319 { 320 320 struct compat_statfs64 buf; 321 - if (sizeof(ubuf->f_bsize) == 4) { 322 - if ((kbuf->f_type | kbuf->f_bsize | kbuf->f_namelen | 323 - kbuf->f_frsize | kbuf->f_flags) & 0xffffffff00000000ULL) 324 - return -EOVERFLOW; 325 - /* f_files and f_ffree may be -1; it's okay 326 - * to stuff that into 32 bits */ 327 - if (kbuf->f_files != 0xffffffffffffffffULL 328 - && (kbuf->f_files & 0xffffffff00000000ULL)) 329 - return -EOVERFLOW; 330 - if (kbuf->f_ffree != 0xffffffffffffffffULL 331 - && (kbuf->f_ffree & 0xffffffff00000000ULL)) 332 - return -EOVERFLOW; 333 - } 321 + 322 + if ((kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL) 323 + return -EOVERFLOW; 324 + 334 325 memset(&buf, 0, sizeof(struct compat_statfs64)); 335 326 buf.f_type = kbuf->f_type; 336 327 buf.f_bsize = kbuf->f_bsize;