From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from userp1040.oracle.com ([156.151.31.81]:33366 "EHLO userp1040.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751076AbcLGAc0 (ORCPT ); Tue, 6 Dec 2016 19:32:26 -0500 Date: Tue, 6 Dec 2016 16:32:22 -0800 From: "Darrick J. Wong" To: Al Viro , Linus Torvalds Cc: linux-fsdevel Subject: [PATCH] vfs: reject inodes with negative size to prevent kernel hang Message-ID: <20161207003222.GI16807@birch.djwong.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Sender: linux-fsdevel-owner@vger.kernel.org List-ID: Due to insufficient input validation, various filesystem drivers can load an inode with a negative size from a maliciously crafted fs image. If this happens, a subsequent write-append operation can cause integer overflows in the writeback code, causing the kernel to lock up. Therefore, if we catch anyone trying to link a dentry to a garbage inode, reject the whole attempt. Signed-off-by: Darrick J. Wong --- The regression tests for this bug are {ext4,xfs}/40[01] in the patch "xfs/ext4: check negative inode size" that I just sent to fstests@vger. I realize it's /very/ late in the 4.9 cycle, but this seemed like the most general way to fix this problem. Perhaps a better fix is to strengthen the _iget verification in every filesystem, which I'm working on for 4.10. But we should at the very least bonk suspicious looking activity. :) --- fs/dcache.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/fs/dcache.c b/fs/dcache.c index 5c7cc95..6a253a4 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2524,6 +2524,17 @@ static inline void __d_add(struct dentry *dentry, struct inode *inode) n = start_dir_add(dir); __d_lookup_done(dentry); } + + /* + * Someone fed us an inode with negative size?! This can cause + * integer overflows in other parts of the VFS, so reject this. + */ + if (inode && i_size_read(inode) < 0) { + WARN_ON_ONCE(1); + iput(inode); + inode = NULL; + } + if (inode) { unsigned add_flags = d_flags_for_inode(inode); hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); @@ -2946,6 +2957,16 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) if (!inode) goto out; + /* + * Someone fed us an inode with negative size?! This can cause + * integer overflows in other parts of the VFS, so reject this. + */ + if (i_size_read(inode) < 0) { + WARN_ON_ONCE(1); + iput(inode); + return ERR_PTR(-EUCLEAN); + } + security_d_instantiate(dentry, inode); spin_lock(&inode->i_lock); if (S_ISDIR(inode->i_mode)) {