linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 4.14 0/2] Backport readdir sanity checking patches
@ 2019-12-23 19:36 Siddharth Chandrasekaran
  2019-12-23 19:36 ` [PATCH 4.14 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
                   ` (5 more replies)
  0 siblings, 6 replies; 14+ messages in thread
From: Siddharth Chandrasekaran @ 2019-12-23 19:36 UTC (permalink / raw)
  To: torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth,
	Siddharth Chandrasekaran

Hello,

This patchset is a backport of upstream commits that makes getdents() and
getdents64() do sanity checking on the pathname that it gives to user
space.

Sid

Linus Torvalds (2):
  Make filldir[64]() verify the directory entry filename is valid
  filldir[64]: remove WARN_ON_ONCE() for bad directory entries

 fs/readdir.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

-- 
2.7.4


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

* [PATCH 4.14 1/2] Make filldir[64]() verify the directory entry filename is valid
  2019-12-23 19:36 [PATCH 4.14 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
@ 2019-12-23 19:36 ` Siddharth Chandrasekaran
  2019-12-23 19:36 ` [PATCH 4.14 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries Siddharth Chandrasekaran
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Siddharth Chandrasekaran @ 2019-12-23 19:36 UTC (permalink / raw)
  To: torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth,
	Alexander Viro, Eric W . Biederman, Siddharth Chandrasekaran

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: Siddharth Chandrasekaran <csiddharth@vmware.com>
---
 fs/readdir.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/fs/readdir.c b/fs/readdir.c
index d336db6..9a3dc66 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -66,6 +66,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..
  *
  * "count=1" is a special case, meaning that the buffer is one
@@ -174,6 +208,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;
@@ -260,6 +297,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.7.4


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

* [PATCH 4.14 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries
  2019-12-23 19:36 [PATCH 4.14 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
  2019-12-23 19:36 ` [PATCH 4.14 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
@ 2019-12-23 19:36 ` Siddharth Chandrasekaran
  2019-12-23 19:36 ` [PATCH 4.19 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Siddharth Chandrasekaran @ 2019-12-23 19:36 UTC (permalink / raw)
  To: torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth,
	Siddharth Chandrasekaran

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

[ Upstream commit b9959c7a347d6adbb558fba7e36e9fef3cba3b07 ]

This was always meant to be a temporary thing, just for testing and to
see if it actually ever triggered.

The only thing that reported it was syzbot doing disk image fuzzing, and
then that warning is expected.  So let's just remove it before -rc4,
because the extra sanity testing should probably go to -stable, but we
don't want the warning to do so.

Reported-by: syzbot+3031f712c7ad5dd4d926@syzkaller.appspotmail.com
Fixes: 8a23eb804ca4 ("Make filldir[64]() verify the directory entry filename is valid")
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Siddharth Chandrasekaran <csiddharth@vmware.com>
---
 fs/readdir.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/readdir.c b/fs/readdir.c
index 9a3dc66..0c35766 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -92,9 +92,9 @@ EXPORT_SYMBOL(iterate_dir);
  */
 static int verify_dirent_name(const char *name, int len)
 {
-	if (WARN_ON_ONCE(!len))
+	if (!len)
 		return -EIO;
-	if (WARN_ON_ONCE(memchr(name, '/', len)))
+	if (memchr(name, '/', len))
 		return -EIO;
 	return 0;
 }
-- 
2.7.4


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

* [PATCH 4.19 0/2] Backport readdir sanity checking patches
  2019-12-23 19:36 [PATCH 4.14 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
  2019-12-23 19:36 ` [PATCH 4.14 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
  2019-12-23 19:36 ` [PATCH 4.14 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries Siddharth Chandrasekaran
@ 2019-12-23 19:36 ` Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.19 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.19 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries Siddharth Chandrasekaran
  2019-12-23 19:36 ` [PATCH 4.4 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 14+ messages in thread
From: Siddharth Chandrasekaran @ 2019-12-23 19:36 UTC (permalink / raw)
  To: torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth,
	Siddharth Chandrasekaran

Hello,

This patchset is a backport of upstream commits that makes getdents() and
getdents64() do sanity checking on the pathname that it gives to user
space.

Sid

Linus Torvalds (2):
  Make filldir[64]() verify the directory entry filename is valid
  filldir[64]: remove WARN_ON_ONCE() for bad directory entries

 fs/readdir.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

-- 
2.7.4


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

* [PATCH 4.19 1/2] Make filldir[64]() verify the directory entry filename is valid
  2019-12-23 19:36 ` [PATCH 4.19 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
@ 2019-12-23 19:36   ` Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.19 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries Siddharth Chandrasekaran
  1 sibling, 0 replies; 14+ messages in thread
From: Siddharth Chandrasekaran @ 2019-12-23 19:36 UTC (permalink / raw)
  To: torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth,
	Alexander Viro, Eric W . Biederman, Siddharth Chandrasekaran

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: Siddharth Chandrasekaran <csiddharth@vmware.com>
---
 fs/readdir.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/fs/readdir.c b/fs/readdir.c
index 9d0212c..ace19d9 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..
  *
  * "count=1" is a special case, meaning that the buffer is one
@@ -172,6 +206,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;
@@ -258,6 +295,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.7.4


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

* [PATCH 4.19 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries
  2019-12-23 19:36 ` [PATCH 4.19 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.19 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
@ 2019-12-23 19:36   ` Siddharth Chandrasekaran
  1 sibling, 0 replies; 14+ messages in thread
From: Siddharth Chandrasekaran @ 2019-12-23 19:36 UTC (permalink / raw)
  To: torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth,
	Siddharth Chandrasekaran

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

[ Upstream commit b9959c7a347d6adbb558fba7e36e9fef3cba3b07 ]

This was always meant to be a temporary thing, just for testing and to
see if it actually ever triggered.

The only thing that reported it was syzbot doing disk image fuzzing, and
then that warning is expected.  So let's just remove it before -rc4,
because the extra sanity testing should probably go to -stable, but we
don't want the warning to do so.

Reported-by: syzbot+3031f712c7ad5dd4d926@syzkaller.appspotmail.com
Fixes: 8a23eb804ca4 ("Make filldir[64]() verify the directory entry filename is valid")
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Siddharth Chandrasekaran <csiddharth@vmware.com>
---
 fs/readdir.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/readdir.c b/fs/readdir.c
index ace19d9..1059f2a 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -90,9 +90,9 @@ EXPORT_SYMBOL(iterate_dir);
  */
 static int verify_dirent_name(const char *name, int len)
 {
-	if (WARN_ON_ONCE(!len))
+	if (!len)
 		return -EIO;
-	if (WARN_ON_ONCE(memchr(name, '/', len)))
+	if (memchr(name, '/', len))
 		return -EIO;
 	return 0;
 }
-- 
2.7.4


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

* [PATCH 4.4 0/2] Backport readdir sanity checking patches
  2019-12-23 19:36 [PATCH 4.14 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
                   ` (2 preceding siblings ...)
  2019-12-23 19:36 ` [PATCH 4.19 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
@ 2019-12-23 19:36 ` Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.4 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
                     ` (2 more replies)
  2019-12-23 19:36 ` [PATCH 4.9 " Siddharth Chandrasekaran
  2020-01-01 17:00 ` [PATCH 4.14 0/2] Backport readdir sanity checking patches Greg KH
  5 siblings, 3 replies; 14+ messages in thread
From: Siddharth Chandrasekaran @ 2019-12-23 19:36 UTC (permalink / raw)
  To: torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth,
	Siddharth Chandrasekaran

Hello,

This patchset is a backport of upstream commits that makes getdents() and
getdents64() do sanity checking on the pathname that it gives to user
space.

Sid

Linus Torvalds (2):
  Make filldir[64]() verify the directory entry filename is valid
  filldir[64]: remove WARN_ON_ONCE() for bad directory entries

 fs/readdir.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

-- 
2.7.4


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

* [PATCH 4.4 1/2] Make filldir[64]() verify the directory entry filename is valid
  2019-12-23 19:36 ` [PATCH 4.4 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
@ 2019-12-23 19:36   ` Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.4 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries Siddharth Chandrasekaran
  2020-01-07 22:09   ` [PATCH 4.4 0/2] Backport readdir sanity checking patches Ben Hutchings
  2 siblings, 0 replies; 14+ messages in thread
From: Siddharth Chandrasekaran @ 2019-12-23 19:36 UTC (permalink / raw)
  To: torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth,
	Alexander Viro, Eric W . Biederman, Siddharth Chandrasekaran

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: Siddharth Chandrasekaran <csiddharth@vmware.com>
---
 fs/readdir.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/fs/readdir.c b/fs/readdir.c
index ced6791..4c6ffe3 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -51,6 +51,40 @@ out:
 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..
  *
  * "count=1" is a special case, meaning that the buffer is one
@@ -159,6 +193,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;
@@ -243,6 +280,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.7.4


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

* [PATCH 4.4 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries
  2019-12-23 19:36 ` [PATCH 4.4 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.4 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
@ 2019-12-23 19:36   ` Siddharth Chandrasekaran
  2020-01-07 22:09   ` [PATCH 4.4 0/2] Backport readdir sanity checking patches Ben Hutchings
  2 siblings, 0 replies; 14+ messages in thread
From: Siddharth Chandrasekaran @ 2019-12-23 19:36 UTC (permalink / raw)
  To: torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth,
	Siddharth Chandrasekaran

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

[ Upstream commit b9959c7a347d6adbb558fba7e36e9fef3cba3b07 ]

This was always meant to be a temporary thing, just for testing and to
see if it actually ever triggered.

The only thing that reported it was syzbot doing disk image fuzzing, and
then that warning is expected.  So let's just remove it before -rc4,
because the extra sanity testing should probably go to -stable, but we
don't want the warning to do so.

Reported-by: syzbot+3031f712c7ad5dd4d926@syzkaller.appspotmail.com
Fixes: 8a23eb804ca4 ("Make filldir[64]() verify the directory entry filename is valid")
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Siddharth Chandrasekaran <csiddharth@vmware.com>
---
 fs/readdir.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/readdir.c b/fs/readdir.c
index 4c6ffe3..3494d7a 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -77,9 +77,9 @@ EXPORT_SYMBOL(iterate_dir);
  */
 static int verify_dirent_name(const char *name, int len)
 {
-	if (WARN_ON_ONCE(!len))
+	if (!len)
 		return -EIO;
-	if (WARN_ON_ONCE(memchr(name, '/', len)))
+	if (memchr(name, '/', len))
 		return -EIO;
 	return 0;
 }
-- 
2.7.4


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

* [PATCH 4.9 0/2] Backport readdir sanity checking patches
  2019-12-23 19:36 [PATCH 4.14 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
                   ` (3 preceding siblings ...)
  2019-12-23 19:36 ` [PATCH 4.4 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
@ 2019-12-23 19:36 ` Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.9 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.9 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries Siddharth Chandrasekaran
  2020-01-01 17:00 ` [PATCH 4.14 0/2] Backport readdir sanity checking patches Greg KH
  5 siblings, 2 replies; 14+ messages in thread
From: Siddharth Chandrasekaran @ 2019-12-23 19:36 UTC (permalink / raw)
  To: torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth,
	Siddharth Chandrasekaran

Hello,

This patchset is a backport of upstream commits that makes getdents() and
getdents64() do sanity checking on the pathname that it gives to user
space.

Sid

Linus Torvalds (2):
  Make filldir[64]() verify the directory entry filename is valid
  filldir[64]: remove WARN_ON_ONCE() for bad directory entries

 fs/readdir.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

-- 
2.7.4


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

* [PATCH 4.9 1/2] Make filldir[64]() verify the directory entry filename is valid
  2019-12-23 19:36 ` [PATCH 4.9 " Siddharth Chandrasekaran
@ 2019-12-23 19:36   ` Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.9 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries Siddharth Chandrasekaran
  1 sibling, 0 replies; 14+ messages in thread
From: Siddharth Chandrasekaran @ 2019-12-23 19:36 UTC (permalink / raw)
  To: torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth,
	Alexander Viro, Eric W . Biederman, Siddharth Chandrasekaran

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: Siddharth Chandrasekaran <csiddharth@vmware.com>
---
 fs/readdir.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/fs/readdir.c b/fs/readdir.c
index 9d0212c..ace19d9 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..
  *
  * "count=1" is a special case, meaning that the buffer is one
@@ -172,6 +206,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;
@@ -258,6 +295,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.7.4


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

* [PATCH 4.9 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries
  2019-12-23 19:36 ` [PATCH 4.9 " Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.9 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
@ 2019-12-23 19:36   ` Siddharth Chandrasekaran
  1 sibling, 0 replies; 14+ messages in thread
From: Siddharth Chandrasekaran @ 2019-12-23 19:36 UTC (permalink / raw)
  To: torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth,
	Siddharth Chandrasekaran

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

[ Upstream commit b9959c7a347d6adbb558fba7e36e9fef3cba3b07 ]

This was always meant to be a temporary thing, just for testing and to
see if it actually ever triggered.

The only thing that reported it was syzbot doing disk image fuzzing, and
then that warning is expected.  So let's just remove it before -rc4,
because the extra sanity testing should probably go to -stable, but we
don't want the warning to do so.

Reported-by: syzbot+3031f712c7ad5dd4d926@syzkaller.appspotmail.com
Fixes: 8a23eb804ca4 ("Make filldir[64]() verify the directory entry filename is valid")
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Siddharth Chandrasekaran <csiddharth@vmware.com>
---
 fs/readdir.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/readdir.c b/fs/readdir.c
index ace19d9..1059f2a 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -90,9 +90,9 @@ EXPORT_SYMBOL(iterate_dir);
  */
 static int verify_dirent_name(const char *name, int len)
 {
-	if (WARN_ON_ONCE(!len))
+	if (!len)
 		return -EIO;
-	if (WARN_ON_ONCE(memchr(name, '/', len)))
+	if (memchr(name, '/', len))
 		return -EIO;
 	return 0;
 }
-- 
2.7.4


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

* Re: [PATCH 4.14 0/2] Backport readdir sanity checking patches
  2019-12-23 19:36 [PATCH 4.14 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
                   ` (4 preceding siblings ...)
  2019-12-23 19:36 ` [PATCH 4.9 " Siddharth Chandrasekaran
@ 2020-01-01 17:00 ` Greg KH
  5 siblings, 0 replies; 14+ messages in thread
From: Greg KH @ 2020-01-01 17:00 UTC (permalink / raw)
  To: Siddharth Chandrasekaran
  Cc: torvalds, sashal, jannh, linux-kernel, stable, siddharth

On Tue, Dec 24, 2019 at 01:06:25AM +0530, Siddharth Chandrasekaran wrote:
> Hello,
> 
> This patchset is a backport of upstream commits that makes getdents() and
> getdents64() do sanity checking on the pathname that it gives to user
> space.
> 
> Sid
> 
> Linus Torvalds (2):
>   Make filldir[64]() verify the directory entry filename is valid
>   filldir[64]: remove WARN_ON_ONCE() for bad directory entries
> 
>  fs/readdir.c | 40 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 40 insertions(+)

All applied to all branches, thanks.

greg k-h

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

* Re: [PATCH 4.4 0/2] Backport readdir sanity checking patches
  2019-12-23 19:36 ` [PATCH 4.4 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.4 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
  2019-12-23 19:36   ` [PATCH 4.4 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries Siddharth Chandrasekaran
@ 2020-01-07 22:09   ` Ben Hutchings
  2 siblings, 0 replies; 14+ messages in thread
From: Ben Hutchings @ 2020-01-07 22:09 UTC (permalink / raw)
  To: Siddharth Chandrasekaran, torvalds
  Cc: gregkh, sashal, jannh, linux-kernel, stable, siddharth

[-- Attachment #1: Type: text/plain, Size: 680 bytes --]

On Tue, 2019-12-24 at 01:06 +0530, Siddharth Chandrasekaran wrote:
> Hello,
> 
> This patchset is a backport of upstream commits that makes getdents() and
> getdents64() do sanity checking on the pathname that it gives to user
> space.

These seem to be needed for 3.16, as well, so I've added them to my
queue.

Ben.

> Sid
> 
> Linus Torvalds (2):
>   Make filldir[64]() verify the directory entry filename is valid
>   filldir[64]: remove WARN_ON_ONCE() for bad directory entries
> 
>  fs/readdir.c | 40 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 40 insertions(+)
> 
-- 
Ben Hutchings
Larkinson's Law: All laws are basically false.



[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

end of thread, other threads:[~2020-01-07 22:09 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-23 19:36 [PATCH 4.14 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
2019-12-23 19:36 ` [PATCH 4.14 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
2019-12-23 19:36 ` [PATCH 4.14 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries Siddharth Chandrasekaran
2019-12-23 19:36 ` [PATCH 4.19 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
2019-12-23 19:36   ` [PATCH 4.19 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
2019-12-23 19:36   ` [PATCH 4.19 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries Siddharth Chandrasekaran
2019-12-23 19:36 ` [PATCH 4.4 0/2] Backport readdir sanity checking patches Siddharth Chandrasekaran
2019-12-23 19:36   ` [PATCH 4.4 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
2019-12-23 19:36   ` [PATCH 4.4 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries Siddharth Chandrasekaran
2020-01-07 22:09   ` [PATCH 4.4 0/2] Backport readdir sanity checking patches Ben Hutchings
2019-12-23 19:36 ` [PATCH 4.9 " Siddharth Chandrasekaran
2019-12-23 19:36   ` [PATCH 4.9 1/2] Make filldir[64]() verify the directory entry filename is valid Siddharth Chandrasekaran
2019-12-23 19:36   ` [PATCH 4.9 2/2] filldir[64]: remove WARN_ON_ONCE() for bad directory entries Siddharth Chandrasekaran
2020-01-01 17:00 ` [PATCH 4.14 0/2] Backport readdir sanity checking patches Greg KH

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).