From: David Herrmann <dh.herrmann@gmail.com>
To: linux-kernel@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org,
Linus Torvalds <torvalds@linux-foundation.org>,
Andrew Morton <akpm@linux-foundation.org>,
David Herrmann <dh.herrmann@gmail.com>,
Al Viro <viro@zeniv.linux.org.uk>,
David Howells <dhowells@redhat.com>,
Oleg Nesterov <oleg@redhat.com>, <stable@vger.kernel.org>
Subject: [PATCH] fs: fix i_writecount on shmem and friends
Date: Mon, 3 Mar 2014 16:16:38 +0100 [thread overview]
Message-ID: <1393859798-1407-1-git-send-email-dh.herrmann@gmail.com> (raw)
VM_DENYWRITE currently relies on i_writecount. Unless there's an active
writable reference to an inode, VM_DENYWRITE is not allowed.
Unfortunately, alloc_file() does not increase i_writecount, therefore,
does not prevent a following VM_DENYWRITE even though the new file might
have been opened with FMODE_WRITE. However, callers of alloc_file() expect
the file object to be fully instantiated so they can call fput() on it. We
could now either fix all callers to do an get_write_access() if opened
with FMODE_WRITE, or simply fix alloc_file() to do that. I chose the
latter.
Note that this bug allows some rather subtle misbehavior. The following
sequence of calls should work just fine, but currently fails:
int p[2], orig, ro, rw;
char buf[128];
pipe(p);
sprintf(buf, "/proc/self/fd/%d", p[1]);
ro = open("/proc/self/fd/$orig", O_RDONLY);
close(p[1]);
rw = open("/proc/self/fd/$ro", O_RDWR);
The final open() cannot succeed as close(p[1]) caused an integer underflow
on i_writecount, effectively causing VM_DENYWRITE on the inode. The open
will fail with -ETXTBUSY.
It's a rather odd sequence of calls and given that open() doesn't use
alloc_file() (and thus not affected by this bug), it's rather unlikely
that this is a serious issue. But stuff like anon_inode shares a *single*
inode across a huge set of interfaces. If any of these is broken like
pipe(), it will affect all of these (ranging from dma-buf to epoll).
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: David Howells <dhowells@redhat.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
fs/file_table.c | 27 ++++++++++++++++++---------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/fs/file_table.c b/fs/file_table.c
index 5fff903..e3c8dd0 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -167,6 +167,7 @@ struct file *alloc_file(struct path *path, fmode_t mode,
const struct file_operations *fop)
{
struct file *file;
+ int error;
file = get_empty_filp();
if (IS_ERR(file))
@@ -178,15 +179,23 @@ struct file *alloc_file(struct path *path, fmode_t mode,
file->f_mode = mode;
file->f_op = fop;
- /*
- * These mounts don't really matter in practice
- * for r/o bind mounts. They aren't userspace-
- * visible. We do this for consistency, and so
- * that we can do debugging checks at __fput()
- */
- if ((mode & FMODE_WRITE) && !special_file(path->dentry->d_inode->i_mode)) {
- file_take_write(file);
- WARN_ON(mnt_clone_write(path->mnt));
+ if (mode & FMODE_WRITE) {
+ error = get_write_access(path->dentry->d_inode);
+ if (error) {
+ put_filp(file);
+ return ERR_PTR(error);
+ }
+
+ /*
+ * These mounts don't really matter in practice
+ * for r/o bind mounts. They aren't userspace-
+ * visible. We do this for consistency, and so
+ * that we can do debugging checks at __fput()
+ */
+ if (!special_file(path->dentry->d_inode->i_mode)) {
+ file_take_write(file);
+ WARN_ON(mnt_clone_write(path->mnt));
+ }
}
if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
i_readcount_inc(path->dentry->d_inode);
--
1.9.0
next reply other threads:[~2014-03-03 15:16 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-03-03 15:16 David Herrmann [this message]
2014-03-11 19:05 ` [PATCH] fs: fix i_writecount on shmem and friends Linus Torvalds
2014-03-12 18:19 ` Al Viro
2014-03-12 22:30 ` David Herrmann
2014-03-13 0:37 ` Al Viro
2014-03-13 0:37 ` Al Viro
2014-03-13 11:03 ` David Herrmann
2014-03-20 11:13 ` David Herrmann
2014-03-13 4:08 ` NeilBrown
2014-03-13 4:29 ` Al Viro
2014-03-13 5:55 ` NeilBrown
2014-03-14 4:51 ` Al Viro
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=1393859798-1407-1-git-send-email-dh.herrmann@gmail.com \
--to=dh.herrmann@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=dhowells@redhat.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=oleg@redhat.com \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.