| From a53765bc5442e2352ea9272dd3ce4a454440bae8 Mon Sep 17 00:00:00 2001 |
| From: Eric Sandeen <sandeen@redhat.com> |
| Date: Wed, 2 Oct 2019 16:17:54 -0500 |
| Subject: [PATCH] vfs: Fix EOVERFLOW testing in put_compat_statfs64 |
| |
| commit cc3a7bfe62b947b423fcb2cfe89fcba92bf48fa3 upstream. |
| |
| 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> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| 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 *, |
| static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf) |
| { |
| struct compat_statfs64 buf; |
| - if (sizeof(ubuf->f_bsize) == 4) { |
| - if ((kbuf->f_type | kbuf->f_bsize | kbuf->f_namelen | |
| - kbuf->f_frsize | kbuf->f_flags) & 0xffffffff00000000ULL) |
| - return -EOVERFLOW; |
| - /* f_files and f_ffree may be -1; it's okay |
| - * to stuff that into 32 bits */ |
| - if (kbuf->f_files != 0xffffffffffffffffULL |
| - && (kbuf->f_files & 0xffffffff00000000ULL)) |
| - return -EOVERFLOW; |
| - if (kbuf->f_ffree != 0xffffffffffffffffULL |
| - && (kbuf->f_ffree & 0xffffffff00000000ULL)) |
| - return -EOVERFLOW; |
| - } |
| + |
| + if ((kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL) |
| + return -EOVERFLOW; |
| + |
| memset(&buf, 0, sizeof(struct compat_statfs64)); |
| buf.f_type = kbuf->f_type; |
| buf.f_bsize = kbuf->f_bsize; |
| -- |
| 2.7.4 |
| |