From: Eiichi Tsukata <devel@etsukata.com>
To: andi@firstfloor.org, tytso@mit.edu, adilger.kernel@dilger.ca,
linux-ext4@vger.kernel.org, linux-fsdevel@vger.kernel.org
Cc: Eiichi Tsukata <devel@etsukata.com>
Subject: [RFC PATCH 1/1] ext4: fix race between llseek SEEK_END and write
Date: Fri, 16 Nov 2018 17:37:37 +0900 [thread overview]
Message-ID: <20181116083737.10596-2-devel@etsukata.com> (raw)
In-Reply-To: <20181116083737.10596-1-devel@etsukata.com>
The commit ef3d0fd27e90 ("vfs: do (nearly) lockless generic_file_llseek")
removed almost all locks in llseek() including SEEK_END. It based on the
idea that write() updates size atomically. But in fact, write() can be
divided into two or more parts in generic_perform_write() when pos
straddles over the PAGE_SIZE, which results in updating size multiple
times in one write(). It means that llseek() can see the size being
updated during write().
This race changes behavior of some applications. 'tail' is one of those
applications. It reads range [pos, pos_end] where pos_end is obtained
via llseek() SEEK_END. Sometimes, a read line could be broken.
reproducer:
$ while true; do echo 123456 >> out; done
$ while true; do tail out | grep -v 123456 ; done
example output(take 30 secs):
12345
1
1234
1
12
1234
Signed-off-by: Eiichi Tsukata <devel@etsukata.com>
---
fs/ext4/file.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 69d65d49837b..6479f3066043 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -477,6 +477,16 @@ loff_t ext4_llseek(struct file *file, loff_t offset, int whence)
default:
return generic_file_llseek_size(file, offset, whence,
maxbytes, i_size_read(inode));
+ case SEEK_END:
+ /*
+ * protects against inode size race with write so that llseek
+ * doesn't see inode size being updated in generic_perform_write
+ */
+ inode_lock_shared(inode);
+ offset = generic_file_llseek_size(file, offset, whence,
+ maxbytes, i_size_read(inode));
+ inode_unlock_shared(inode);
+ return offset;
case SEEK_HOLE:
inode_lock_shared(inode);
offset = iomap_seek_hole(inode, offset, &ext4_iomap_ops);
--
2.19.1
next prev parent reply other threads:[~2018-11-16 23:02 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-11-16 8:37 [RFC PATCH 0/1] fs: fix race between llseek SEEK_END and write Eiichi Tsukata
2018-11-16 8:37 ` Eiichi Tsukata [this message]
2018-11-16 15:44 ` Andi Kleen
2018-11-17 7:29 ` Eiichi Tsukata
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=20181116083737.10596-2-devel@etsukata.com \
--to=devel@etsukata.com \
--cc=adilger.kernel@dilger.ca \
--cc=andi@firstfloor.org \
--cc=linux-ext4@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=tytso@mit.edu \
/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).