linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sasha Levin <sashal@kernel.org>
To: linux-kernel@vger.kernel.org, stable@vger.kernel.org
Cc: Linus Torvalds <torvalds@linux-foundation.org>,
	Alexander Viro <viro@zeniv.linux.org.uk>,
	Jann Horn <jannh@google.com>,
	"Eric W . Biederman" <ebiederm@xmission.com>,
	Sasha Levin <sashal@kernel.org>,
	linux-fsdevel@vger.kernel.org
Subject: [PATCH AUTOSEL 4.19 26/26] Make filldir[64]() verify the directory entry filename is valid
Date: Wed,  9 Oct 2019 13:05:58 -0400	[thread overview]
Message-ID: <20191009170558.32517-26-sashal@kernel.org> (raw)
In-Reply-To: <20191009170558.32517-1-sashal@kernel.org>

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


  parent reply	other threads:[~2019-10-09 17:29 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-10-09 17:05 [PATCH AUTOSEL 4.19 01/26] KVM: arm/arm64: vgic: Use the appropriate TRACE_INCLUDE_PATH Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 02/26] nvme-pci: Fix a race in controller removal Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 03/26] scsi: ufs: skip shutdown if hba is not powered Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 04/26] scsi: megaraid: disable device when probe failed after enabled device Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 05/26] scsi: qla2xxx: Fix unbound sleep in fcport delete path Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 06/26] ARM: OMAP2+: Fix missing reset done flag for am3 and am43 Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 07/26] ARM: OMAP2+: Fix warnings with broken omap2_set_init_voltage() Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 08/26] kvm: x86: Improve emulation of CPUID leaves 0BH and 1FH Sasha Levin
2019-10-09 20:58   ` Paolo Bonzini
2019-10-09 22:41     ` Sasha Levin
2019-10-09 22:49       ` Paolo Bonzini
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 09/26] kvm: x86: Use AMD CPUID semantics for AMD vCPUs Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 10/26] ieee802154: ca8210: prevent memory leak Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 11/26] ARM: dts: am4372: Set memory bandwidth limit for DISPC Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 12/26] net: dsa: qca8k: Use up to 7 ports for all operations Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 13/26] MIPS: dts: ar9331: fix interrupt-controller size Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 14/26] xen/efi: Set nonblocking callbacks Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 15/26] kvm: vmx: Limit guest PMCs to those supported on the host Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 16/26] nl80211: fix null pointer dereference Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 17/26] mac80211: fix txq " Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 18/26] netfilter: nft_connlimit: disable bh on garbage collection Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 19/26] net: dsa: rtl8366rb: add missing of_node_put after calling of_get_child_by_name Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 20/26] mips: Loongson: Fix the link time qualifier of 'serial_exit()' Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 21/26] net: hisilicon: Fix usage of uninitialized variable in function mdio_sc_cfg_reg_write() Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 22/26] lib: textsearch: fix escapes in example code Sasha Levin
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 24/26] r8152: Set macpassthru in reset_resume callback Sasha Levin
2019-10-09 17:05 ` [PATCH AUTOSEL 4.19 25/26] namespace: fix namespace.pl script to support relative paths Sasha Levin
2019-10-09 17:05 ` Sasha Levin [this message]
2019-10-09 17:56   ` [PATCH AUTOSEL 4.19 26/26] Make filldir[64]() verify the directory entry filename is valid Linus Torvalds
2019-10-09 20:51     ` Sasha Levin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191009170558.32517-26-sashal@kernel.org \
    --to=sashal@kernel.org \
    --cc=ebiederm@xmission.com \
    --cc=jannh@google.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=torvalds@linux-foundation.org \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).