From patchwork Wed Aug 23 07:57:58 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qu Wenruo X-Patchwork-Id: 9916801 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 5FABE60327 for ; Wed, 23 Aug 2017 07:58:21 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 521B9288BD for ; Wed, 23 Aug 2017 07:58:21 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 46C6028948; Wed, 23 Aug 2017 07:58:21 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,FREEMAIL_FROM, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B71E2288BD for ; Wed, 23 Aug 2017 07:58:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753502AbdHWH6P (ORCPT ); Wed, 23 Aug 2017 03:58:15 -0400 Received: from mout.gmx.net ([212.227.17.22]:54584 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753386AbdHWH6L (ORCPT ); Wed, 23 Aug 2017 03:58:11 -0400 Received: from localhost.localdomain ([45.32.39.184]) by mail.gmx.com (mrgmx103 [212.227.17.174]) with ESMTPSA (Nemesis) id 0LxxNo-1dWHJs2XAK-015EwD; Wed, 23 Aug 2017 09:58:09 +0200 From: Qu Wenruo To: linux-btrfs@vger.kernel.org Cc: dsterba@suse.cz Subject: [PATCH v2 3/4] btrfs: Add sanity check for EXTENT_DATA when reading out leaf Date: Wed, 23 Aug 2017 16:57:58 +0900 Message-Id: <20170823075759.13982-4-quwenruo.btrfs@gmx.com> X-Mailer: git-send-email 2.13.3 In-Reply-To: <20170823075759.13982-1-quwenruo.btrfs@gmx.com> References: <20170823075759.13982-1-quwenruo.btrfs@gmx.com> X-Provags-ID: V03:K0:ySLxlk+34J0lJEVYxarClQ6xLofg9oR9qXdydKg1736GOnoi51V 7r0ztXoGFQz1hTfbeP+0sScenVmL+4DFBMje0uQVPFPhq0IFEn0gv1Bb6Csor8WYHSPVhQu ZTYqOIaREvmpP1H8NoZgBMMa6EeVY7UJZ1nyAGBMfMxRhmRSUP0RbupQWyvkS4Qz9FjyTez Gn3YVc8VuUCPrhh8yBJ7w== X-UI-Out-Filterresults: notjunk:1; V01:K0:4dGpwIQTldM=:1N5j7A7fiyP3XMucv7JvjM rDyF1LSJANYy8SiL9v42hq3x9xkx1YI0YjbkLZZEfte3c1EqpboX2+mNnf84ciomIZLMelYQX 5crxzHeEGma8sTUgFl3Hw59gi4flfvD7kbzguW+8Gl7vatkT1KyJPASmU7Pb/olEg/mYQU6tp FimX9WjFTxOmRkM65rs8rjA/TXm58kUMmeG31CVp+9lqlU0wudaPlfLEigbJirSwtbCjtZ7W1 bAfwLCAxmzu1dxIny9TN19DL/M9nVMaKotj5ERcBoBYIAigWFrYXp2EnPTxH6wqgQDMMf0ekY OXCylO9lcWNTnAGFxT7LnCSAQEYYF2+gXsJex7F6hSEU5O584yjknzaRP1wPq7RDmepFBtSa0 wbo7os0MMecD9LsrLuRDjLkGkRgloh5chxbzMDGqhgXNICcb9mObkZh7Mki69yZk4ZGYVZrEu 1rQTiHphz777CGh7IEAUsRXOYUN4FR2UClecr2ZH5wc3wSmza1tKyd/PZUAXEBk+xARgT6Vrz EwYZDVj/KnWLq1/dwj5i6H30qF1bN2DAFy2JyElWrf9bgcp1zqy9dyi5fJQEExrFPYaOp0T+N uRi5z2v5cVmlpO88TrY5voxs6C5mJ3x410tnCNa8wZaxecd51z/suLifxEfgGqfK5sOS9D1/7 EPo/2pKFfff/XUwqqPzAVVOdGcyWruaP8S3uR5Ju8HlnqEyJPYX5vmnYXjOO40vfLpJ/4TIE4 L2tWp6RWgUrTPw9ZVAcqUmqD4ogDElJz2VnPiD1BPuuu+2o39aST/KWr2QTu7HQQR6yevwAd6 MIOkSwDc9r1wDgbasPK+buebvnZuA== Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add extra checker for item with EXTENT_DATA type. This checks the following thing: 0) Key offset All key offset must be aligned to sectorsize. Inline extent must have 0 for key offset. 1) Item size Plain text inline file extent size must match item size. (compressed inline file extent has no info about its on-disk size) Regular/preallocated file extent size must be a fixed value. 2) Every member of regular file extent item Including alignment for bytenr and offset, possible value for compression/encryption/type. 3) Type/compression/encode must be one of the valid values. This should be the most comprehensive and restrict check in the context of btrfs_item for EXTENT_DATA. Signed-off-by: Qu Wenruo --- fs/btrfs/disk-io.c | 108 ++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/btrfs_tree.h | 1 + 2 files changed, 109 insertions(+) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index e034d08bd036..b92296c6a698 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -549,6 +549,103 @@ static int check_tree_block_fsid(struct btrfs_fs_info *fs_info, btrfs_header_level(eb) == 0 ? "leaf" : "node", \ reason, btrfs_header_bytenr(eb), root->objectid, slot) +static int check_extent_data_item(struct btrfs_root *root, + struct extent_buffer *leaf, + struct btrfs_key *key, int slot) +{ + struct btrfs_file_extent_item *fi; + u32 sectorsize = root->fs_info->sectorsize; + u32 item_size = btrfs_item_size_nr(leaf, slot); + + if (!IS_ALIGNED(key->offset, sectorsize)) { + CORRUPT("unaligned key offset for file extent", + leaf, root, slot); + return -EUCLEAN; + } + + fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); + + if (btrfs_file_extent_type(leaf, fi) >= BTRFS_FILE_EXTENT_LAST_TYPE) { + CORRUPT("invalid file extent type", leaf, root, slot); + return -EUCLEAN; + } + + /* + * Support for new compression/encrption must introduce incompat flag, + * and must be caught in open_ctree(). + */ + if (btrfs_file_extent_compression(leaf, fi) >= BTRFS_COMPRESS_LAST) { + CORRUPT("invalid file extent compression", leaf, root, slot); + return -EUCLEAN; + } + if (btrfs_file_extent_encryption(leaf, fi)) { + CORRUPT("invalid file extent encryption", leaf, root, slot); + return -EUCLEAN; + } + if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) { + /* Inline extent must have 0 as key offset */ + if (key->offset) { + CORRUPT("inline extent has non-zero key offset", + leaf, root, slot); + return -EUCLEAN; + } + + /* Compressed inline extent has no on-disk size, skip it */ + if (btrfs_file_extent_compression(leaf, fi) != + BTRFS_COMPRESS_NONE) + return 0; + + /* Plaintext inline extent size must match item size */ + if (item_size != BTRFS_FILE_EXTENT_INLINE_DATA_START + + btrfs_file_extent_ram_bytes(leaf, fi)) { + CORRUPT("plaintext inline extent has invalid size", + leaf, root, slot); + return -EUCLEAN; + } + return 0; + } + + + /* regular or preallocated extent has fixed item size */ + if (item_size != sizeof(*fi)) { + CORRUPT( + "regluar or preallocated extent data item size is invalid", + leaf, root, slot); + return -EUCLEAN; + } + if (!IS_ALIGNED(btrfs_file_extent_ram_bytes(leaf, fi), sectorsize) || + !IS_ALIGNED(btrfs_file_extent_disk_bytenr(leaf, fi), sectorsize) || + !IS_ALIGNED(btrfs_file_extent_disk_num_bytes(leaf, fi), + sectorsize) || + !IS_ALIGNED(btrfs_file_extent_offset(leaf, fi), sectorsize) || + !IS_ALIGNED(btrfs_file_extent_num_bytes(leaf, fi), sectorsize)) { + CORRUPT( + "regular or preallocated extent data item has unaligned value", + leaf, root, slot); + return -EUCLEAN; + } + + return 0; +} + +static int check_leaf_item(struct btrfs_root *root, + struct extent_buffer *leaf, + struct btrfs_key *key, int slot) +{ + int ret = 0; + + /* + * Considering how overcrowded the code will be inside the switch, + * complex verification is better to moved its own function. + */ + switch (key->type) { + case BTRFS_EXTENT_DATA_KEY: + ret = check_extent_data_item(root, leaf, key, slot); + break; + } + return ret; +} + static noinline int check_leaf(struct btrfs_root *root, struct extent_buffer *leaf) { @@ -605,9 +702,13 @@ static noinline int check_leaf(struct btrfs_root *root, * 1) key order * 2) item offset and size * No overlap, no hole, all inside the leaf. + * 3) item content + * If possible, do comprehensive sanity check. + * NOTE: All check must only rely on the item data itself. */ for (slot = 0; slot < nritems; slot++) { u32 item_end_expected; + int ret; btrfs_item_key_to_cpu(leaf, &key, slot); @@ -650,6 +751,13 @@ static noinline int check_leaf(struct btrfs_root *root, return -EUCLEAN; } + /* + * Check if the item size and content meets other limitation + */ + ret = check_leaf_item(root, leaf, &key, slot); + if (ret < 0) + return ret; + prev_key.objectid = key.objectid; prev_key.type = key.type; prev_key.offset = key.offset; diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index 10689e1fdf11..3aadbb74a024 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -732,6 +732,7 @@ struct btrfs_balance_item { #define BTRFS_FILE_EXTENT_INLINE 0 #define BTRFS_FILE_EXTENT_REG 1 #define BTRFS_FILE_EXTENT_PREALLOC 2 +#define BTRFS_FILE_EXTENT_LAST_TYPE 3 struct btrfs_file_extent_item { /*