From: Nick Piggin Credit for these next 4 patches goes to Hugh. He found and fixed the problem, I just split them up and added a bit of a changelog and hopefully no new bugs. When running kbuild stress testing, it data corruptions on ext2 were noticed occasionally. The page being written to by write(2) was being unlocked in generic_write_end before updating i_size, and that renders an extending-write vulnerable to have its newly written data zeroed out if writepage comes at the wrong time and finds the page unlocked but i_size is not yet updated. Fortunately ext3 wasn't affected by this bug, but ext2 and others using generic_write_end would be. Signed-off-by: Hugh Dickins Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton --- fs/buffer.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff -puN fs/buffer.c~introduce-write_begin-write_end-aops-important-fix fs/buffer.c --- a/fs/buffer.c~introduce-write_begin-write_end-aops-important-fix +++ a/fs/buffer.c @@ -2039,19 +2039,22 @@ int generic_write_end(struct file *file, copied = block_write_end(file, mapping, pos, len, copied, page, fsdata); - unlock_page(page); - mark_page_accessed(page); /* XXX: put this in caller? */ - page_cache_release(page); - /* * No need to use i_size_read() here, the i_size * cannot change under us because we hold i_mutex. + * + * But it's important to update i_size while still holding page lock: + * page writeout could otherwise come in and zero beyond i_size. */ if (pos+copied > inode->i_size) { i_size_write(inode, pos+copied); mark_inode_dirty(inode); } + unlock_page(page); + mark_page_accessed(page); + page_cache_release(page); + return copied; } EXPORT_SYMBOL(generic_write_end); _