From patchwork Thu Feb 20 17:59:27 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Theodore Ts'o X-Patchwork-Id: 322293 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 64B5C2C0267 for ; Fri, 21 Feb 2014 04:59:36 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753636AbaBTR7f (ORCPT ); Thu, 20 Feb 2014 12:59:35 -0500 Received: from imap.thunk.org ([74.207.234.97]:60521 "EHLO imap.thunk.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753098AbaBTR7e (ORCPT ); Thu, 20 Feb 2014 12:59:34 -0500 Received: from root (helo=closure.thunk.org) by imap.thunk.org with local-esmtp (Exim 4.80) (envelope-from ) id 1WGXuG-0008GI-9o; Thu, 20 Feb 2014 17:59:32 +0000 Received: by closure.thunk.org (Postfix, from userid 15806) id 194A65802AF; Thu, 20 Feb 2014 12:59:29 -0500 (EST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=thunk.org; s=mail; t=1392919169; bh=YCMlMsRDYEenfm+QODNnYEdvfqRb35L5Re53ZNuJFWA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sBtI8dIOgl07yFZn5rt/7XAQ7+DfHv4gFcfzAgIy+NY88RBoocv/urtfTtpbWwkfR fgCtLeL0dvTOvxwNjwOZEKYLpAB1wdT6+JxQ8z6J4HKx750IzEfJME8a1S8dkEY4SS Wqp91nAGkdbyG4o2ZAUKsL2bXmLYPvKnJXxoshtY= From: Theodore Ts'o To: Ext4 Developers List Cc: Theodore Ts'o Subject: [PATCH -v2] ext4: avoid possible overflow in ext4_map_blocks() Date: Thu, 20 Feb 2014 12:59:27 -0500 Message-Id: <1392919167-1282-1-git-send-email-tytso@mit.edu> X-Mailer: git-send-email 1.9.0 In-Reply-To: <143F882D-0091-4F04-9A2D-882581305CC8@dilger.ca> References: <143F882D-0091-4F04-9A2D-882581305CC8@dilger.ca> X-SA-Exim-Connect-IP: X-SA-Exim-Mail-From: tytso@thunk.org X-SA-Exim-Scanned: No (on imap.thunk.org); SAEximRunCond expanded to false Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org The ext4_map_blocks() function returns the number of blocks which satisfying the caller's request. This number of blocks requested by the caller is specified by an unsigned integer, but the return value of ext4_map_blocks() is a signed integer (to accomodate error codes per the kernel's standard error signalling convention). Historically, overflows could never happen since mballoc() will refuse to allocate more than 2048 blocks at a time (which is something we should fix), and if the blocks were already allocated, the fact that there would be some number of intervening metadata blocks pretty much guaranteed that there could never be a contiguous region of data blocks that was greater than 2**31 blocks. However, this is now possible if there is a file system which is a bit bigger than 8TB, and is created using the new mke2fs hugeblock feature, which can create a perfectly contiguous file. In that case, if a userspace program attempted to call fallocate() on this already fully allocated file, it's possible that ext4_map_blocks() could return a number large enough that it would overflow a signed integer, resulting in a ext4 thinking that the ext4_map_blocks() call had failed with some strange error code. Since ext4_map_blocks() is always free to return a smaller number of blocks than what was requested by the caller, fix this by capping the number of blocks that ext4_map_blocks() will ever try to map to 2**31 - 1. In practice this should never get hit, except by someone deliberately trying to provke the above-described bug. Thanks to the PaX team for asking whethre this could possibly happen in some off-line discussions about using some static code checking technology they are developing to find bugs in kernel code. Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 6e39895..113458c 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -514,6 +514,12 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, "logical block %lu\n", inode->i_ino, flags, map->m_len, (unsigned long) map->m_lblk); + /* + * ext4_map_blocks returns an int, and m_len is an unsigned int + */ + if (unlikely(map->m_len > INT_MAX)) + map->m_len = INT_MAX; + /* Lookup extent status tree firstly */ if (ext4_es_lookup_extent(inode, map->m_lblk, &es)) { ext4_es_lru_add(inode);