linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH AUTOSEL 4.19 23/26] vfs: Fix EOVERFLOW testing in put_compat_statfs64
       [not found] <20191009170558.32517-1-sashal@kernel.org>
@ 2019-10-09 17:05 ` Sasha Levin
  2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 26/26] Make filldir[64]() verify the directory entry filename is valid Sasha Levin
  1 sibling, 0 replies; 4+ messages in thread
From: Sasha Levin @ 2019-10-09 17:05 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Eric Sandeen, Linus Torvalds, Sasha Levin, linux-fsdevel

From: Eric Sandeen <sandeen@redhat.com>

[ Upstream commit cc3a7bfe62b947b423fcb2cfe89fcba92bf48fa3 ]

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: Sasha Levin <sashal@kernel.org>
---
 fs/statfs.c | 17 ++++-------------
 1 file changed, 4 insertions(+), 13 deletions(-)

diff --git a/fs/statfs.c b/fs/statfs.c
index f0216629621d6..56f655f757ffb 100644
--- a/fs/statfs.c
+++ b/fs/statfs.c
@@ -304,19 +304,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.20.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH AUTOSEL 4.19 26/26] Make filldir[64]() verify the directory entry filename is valid
       [not found] <20191009170558.32517-1-sashal@kernel.org>
  2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 23/26] vfs: Fix EOVERFLOW testing in put_compat_statfs64 Sasha Levin
@ 2019-10-09 17:05 ` Sasha Levin
  2019-10-09 17:56   ` Linus Torvalds
  1 sibling, 1 reply; 4+ messages in thread
From: Sasha Levin @ 2019-10-09 17:05 UTC (permalink / raw)
  To: linux-kernel, stable
  Cc: Linus Torvalds, Alexander Viro, Jann Horn, Eric W . Biederman,
	Sasha Levin, linux-fsdevel

From: Linus Torvalds <torvalds@linux-foundation.org>

[ Upstream commit 8a23eb804ca4f2be909e372cf5a9e7b30ae476cd ]

This has been discussed several times, and now filesystem people are
talking about doing it individually at the filesystem layer, so head
that off at the pass and just do it in getdents{64}().

This is partially based on a patch by Jann Horn, but checks for NUL
bytes as well, and somewhat simplified.

There's also commentary about how it might be better if invalid names
due to filesystem corruption don't cause an immediate failure, but only
an error at the end of the readdir(), so that people can still see the
filenames that are ok.

There's also been discussion about just how much POSIX strictly speaking
requires this since it's about filesystem corruption.  It's really more
"protect user space from bad behavior" as pointed out by Jann.  But
since Eric Biederman looked up the POSIX wording, here it is for context:

 "From readdir:

   The readdir() function shall return a pointer to a structure
   representing the directory entry at the current position in the
   directory stream specified by the argument dirp, and position the
   directory stream at the next entry. It shall return a null pointer
   upon reaching the end of the directory stream. The structure dirent
   defined in the <dirent.h> header describes a directory entry.

  From definitions:

   3.129 Directory Entry (or Link)

   An object that associates a filename with a file. Several directory
   entries can associate names with the same file.

  ...

   3.169 Filename

   A name consisting of 1 to {NAME_MAX} bytes used to name a file. The
   characters composing the name may be selected from the set of all
   character values excluding the slash character and the null byte. The
   filenames dot and dot-dot have special meaning. A filename is
   sometimes referred to as a 'pathname component'."

Note that I didn't bother adding the checks to any legacy interfaces
that nobody uses.

Also note that if this ends up being noticeable as a performance
regression, we can fix that to do a much more optimized model that
checks for both NUL and '/' at the same time one word at a time.

We haven't really tended to optimize 'memchr()', and it only checks for
one pattern at a time anyway, and we really _should_ check for NUL too
(but see the comment about "soft errors" in the code about why it
currently only checks for '/')

See the CONFIG_DCACHE_WORD_ACCESS case of hash_name() for how the name
lookup code looks for pathname terminating characters in parallel.

Link: https://lore.kernel.org/lkml/20190118161440.220134-2-jannh@google.com/
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Jann Horn <jannh@google.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/readdir.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/fs/readdir.c b/fs/readdir.c
index d97f548e63233..91a28ddf50942 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -64,6 +64,40 @@ int iterate_dir(struct file *file, struct dir_context *ctx)
 }
 EXPORT_SYMBOL(iterate_dir);
 
+/*
+ * POSIX says that a dirent name cannot contain NULL or a '/'.
+ *
+ * It's not 100% clear what we should really do in this case.
+ * The filesystem is clearly corrupted, but returning a hard
+ * error means that you now don't see any of the other names
+ * either, so that isn't a perfect alternative.
+ *
+ * And if you return an error, what error do you use? Several
+ * filesystems seem to have decided on EUCLEAN being the error
+ * code for EFSCORRUPTED, and that may be the error to use. Or
+ * just EIO, which is perhaps more obvious to users.
+ *
+ * In order to see the other file names in the directory, the
+ * caller might want to make this a "soft" error: skip the
+ * entry, and return the error at the end instead.
+ *
+ * Note that this should likely do a "memchr(name, 0, len)"
+ * check too, since that would be filesystem corruption as
+ * well. However, that case can't actually confuse user space,
+ * which has to do a strlen() on the name anyway to find the
+ * filename length, and the above "soft error" worry means
+ * that it's probably better left alone until we have that
+ * issue clarified.
+ */
+static int verify_dirent_name(const char *name, int len)
+{
+	if (WARN_ON_ONCE(!len))
+		return -EIO;
+	if (WARN_ON_ONCE(memchr(name, '/', len)))
+		return -EIO;
+	return 0;
+}
+
 /*
  * Traditional linux readdir() handling..
  *
@@ -173,6 +207,9 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen,
 	int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
 		sizeof(long));
 
+	buf->error = verify_dirent_name(name, namlen);
+	if (unlikely(buf->error))
+		return buf->error;
 	buf->error = -EINVAL;	/* only used if we fail.. */
 	if (reclen > buf->count)
 		return -EINVAL;
@@ -259,6 +296,9 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,
 	int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
 		sizeof(u64));
 
+	buf->error = verify_dirent_name(name, namlen);
+	if (unlikely(buf->error))
+		return buf->error;
 	buf->error = -EINVAL;	/* only used if we fail.. */
 	if (reclen > buf->count)
 		return -EINVAL;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH AUTOSEL 4.19 26/26] Make filldir[64]() verify the directory entry filename is valid
  2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 26/26] Make filldir[64]() verify the directory entry filename is valid Sasha Levin
@ 2019-10-09 17:56   ` Linus Torvalds
  2019-10-09 20:51     ` Sasha Levin
  0 siblings, 1 reply; 4+ messages in thread
From: Linus Torvalds @ 2019-10-09 17:56 UTC (permalink / raw)
  To: Sasha Levin
  Cc: Linux Kernel Mailing List, stable, Alexander Viro, Jann Horn,
	Eric W . Biederman, linux-fsdevel

On Wed, Oct 9, 2019 at 10:24 AM Sasha Levin <sashal@kernel.org> wrote:
>
> From: Linus Torvalds <torvalds@linux-foundation.org>
>
> [ Upstream commit 8a23eb804ca4f2be909e372cf5a9e7b30ae476cd ]

I didn't mark this for stable because I expect things to still change
- particularly the WARN_ON_ONCE() should be removed before final 5.4,
I just wanted to see if anybody could trigger it with testing etc.

(At least syzbot did trigger it).

If you do want to take it, take it without the WARN_ON_ONCE() calls
and note that in the commit message..

           Linus

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH AUTOSEL 4.19 26/26] Make filldir[64]() verify the directory entry filename is valid
  2019-10-09 17:56   ` Linus Torvalds
@ 2019-10-09 20:51     ` Sasha Levin
  0 siblings, 0 replies; 4+ messages in thread
From: Sasha Levin @ 2019-10-09 20:51 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Linux Kernel Mailing List, stable, Alexander Viro, Jann Horn,
	Eric W . Biederman, linux-fsdevel

On Wed, Oct 09, 2019 at 10:56:56AM -0700, Linus Torvalds wrote:
>On Wed, Oct 9, 2019 at 10:24 AM Sasha Levin <sashal@kernel.org> wrote:
>>
>> From: Linus Torvalds <torvalds@linux-foundation.org>
>>
>> [ Upstream commit 8a23eb804ca4f2be909e372cf5a9e7b30ae476cd ]
>
>I didn't mark this for stable because I expect things to still change
>- particularly the WARN_ON_ONCE() should be removed before final 5.4,
>I just wanted to see if anybody could trigger it with testing etc.
>
>(At least syzbot did trigger it).
>
>If you do want to take it, take it without the WARN_ON_ONCE() calls
>and note that in the commit message..

I'll take both when you send a patch to remove it.

--
Thanks,
Sasha

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2019-10-09 20:51 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20191009170558.32517-1-sashal@kernel.org>
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 23/26] vfs: Fix EOVERFLOW testing in put_compat_statfs64 Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 26/26] Make filldir[64]() verify the directory entry filename is valid Sasha Levin
2019-10-09 17:56   ` Linus Torvalds
2019-10-09 20:51     ` Sasha Levin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).