aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Sandeen <sandeen@redhat.com>2019-10-02 17:17:54 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-10-03 17:21:35 -0400
commitcc3a7bfe62b947b423fcb2cfe89fcba92bf48fa3 (patch)
tree35fc83c3f92553cce868f9e1f31305a353485c98
parentc1053cd122b23519322c8256ca24487e3b9695ce (diff)
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>
-rw-r--r--fs/statfs.c17
1 files changed, 4 insertions, 13 deletions
diff --git a/fs/statfs.c b/fs/statfs.c
index eea7af6f2f22..2616424012ea 100644
--- a/fs/statfs.c
+++ b/fs/statfs.c
@@ -318,19 +318,10 @@ COMPAT_SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct compat_statfs __user *,
318static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf) 318static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf)
319{ 319{
320 struct compat_statfs64 buf; 320 struct compat_statfs64 buf;
321 if (sizeof(ubuf->f_bsize) == 4) { 321
322 if ((kbuf->f_type | kbuf->f_bsize | kbuf->f_namelen | 322 if ((kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL)
323 kbuf->f_frsize | kbuf->f_flags) & 0xffffffff00000000ULL) 323 return -EOVERFLOW;
324 return -EOVERFLOW; 324
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 }
334 memset(&buf, 0, sizeof(struct compat_statfs64)); 325 memset(&buf, 0, sizeof(struct compat_statfs64));
335 buf.f_type = kbuf->f_type; 326 buf.f_type = kbuf->f_type;
336 buf.f_bsize = kbuf->f_bsize; 327 buf.f_bsize = kbuf->f_bsize;