From patchwork Wed Sep 2 19:41:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Breathitt Gray X-Patchwork-Id: 1356080 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.ubuntu.com (client-ip=91.189.94.19; helo=huckleberry.canonical.com; envelope-from=kernel-team-bounces@lists.ubuntu.com; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=canonical.com Received: from huckleberry.canonical.com (huckleberry.canonical.com [91.189.94.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4BhZBR1xT9z9sTh; Thu, 3 Sep 2020 05:43:59 +1000 (AEST) Received: from localhost ([127.0.0.1] helo=huckleberry.canonical.com) by huckleberry.canonical.com with esmtp (Exim 4.86_2) (envelope-from ) id 1kDYfj-0003tf-9t; Wed, 02 Sep 2020 19:43:55 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by huckleberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kDYff-0003rP-Lf for kernel-team@lists.ubuntu.com; Wed, 02 Sep 2020 19:43:51 +0000 Received: from mail-qv1-f69.google.com ([209.85.219.69]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kDYff-0004nS-Bi for kernel-team@lists.ubuntu.com; Wed, 02 Sep 2020 19:43:51 +0000 Received: by mail-qv1-f69.google.com with SMTP id y2so435802qvs.14 for ; Wed, 02 Sep 2020 12:43:51 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ziKB3lFBSUKeGfpAneVtG2Gc1LwAc1Or1tcIJGCtYMI=; b=OJFVXYM9OiRmw4Mv+BF65CzVCuPyTTMBXCvEWHeRa5YZuuqPLgIQ3LuZdg9IRiXosX GGObDs8JbdeC9zWzWHGmWwmBi2nUPdhZDvYNDJTOVvOsw4nH6rqnYQhwxty61qM9MblI c2nZFu06MDj4wNW+wghom8koq14/+rWrsc4AgMWz3tk0Q53XxSyG3e3FiqCw44pyW75g glfv84VdEXgWTDOfdNI17yHAQE7BSt0o0kWW0vkUlO+dRowRBXIMWSiwhTnBgmqA88Ng nUVb/Mq+JFsl3hW7nYLPuqox7in/TYI6nW+yXmPW0SAVV6C2NFeWEWZsy+A/OpCygNda /aNQ== X-Gm-Message-State: AOAM530f3QalX6jW4oj/OZmRNH4GwY1phPdk65pdXW+0Nf5ZCYQguhKv 2JqnIC+NzQ/h0oqCXFmVnW2ym8GTZcbBaJP3G/CWmSvYdwpUY9s4ca+K+y/vlV59ATyLcPoqToa yTZHyn0J0z6W0mPYdr96SGgSoGN8Tatf+kWkroDc8cA== X-Received: by 2002:a0c:cb83:: with SMTP id p3mr8480492qvk.121.1599075830174; Wed, 02 Sep 2020 12:43:50 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyXC66thUIXs/CtEZiEHePAPQxOY2S930WGilhxpujJ09wURBSeY5kMQhxgdcbj6vUT6lGzSw== X-Received: by 2002:a0c:cb83:: with SMTP id p3mr8480473qvk.121.1599075829906; Wed, 02 Sep 2020 12:43:49 -0700 (PDT) Received: from localhost.localdomain (072-189-064-225.res.spectrum.com. [72.189.64.225]) by smtp.gmail.com with ESMTPSA id k63sm478635qkf.33.2020.09.02.12.43.49 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 02 Sep 2020 12:43:49 -0700 (PDT) From: William Breathitt Gray To: kernel-team@lists.ubuntu.com Subject: [SRU][XENIAL][CVE-2018-10322][PATCH v2 3/4] xfs: move inode fork verifiers to xfs_dinode_verify Date: Wed, 2 Sep 2020 15:41:38 -0400 Message-Id: <20200902194139.67480-5-william.gray@canonical.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200902194139.67480-1-william.gray@canonical.com> References: <20200902194139.67480-1-william.gray@canonical.com> MIME-Version: 1.0 X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.20 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: kernel-team-bounces@lists.ubuntu.com Sender: "kernel-team" From: Darrick J. Wong Consolidate the fork size and format verifiers to xfs_dinode_verify so that we can reject bad inodes earlier and in a single place. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner CVE-2018-10322 (backported from commit 71493b839e294065ba63bd6f8d07263f3afee8c6) [ vilhelmgray: context adjustments ] Signed-off-by: William Breathitt Gray --- fs/xfs/libxfs/xfs_inode_buf.c | 68 +++++++++++++++++++++++++++++++++- fs/xfs/libxfs/xfs_inode_fork.c | 67 --------------------------------- 2 files changed, 66 insertions(+), 69 deletions(-) diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index 20fcace5fecc..936a72e420b1 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -297,20 +297,84 @@ xfs_dinode_verify( struct xfs_dinode *dip) { uint16_t mode; + uint16_t flags; + uint64_t di_size; if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC)) return false; /* don't allow invalid i_size */ - if (be64_to_cpu(dip->di_size) & (1ULL << 63)) + di_size = be64_to_cpu(dip->di_size); + if (di_size & (1ULL << 63)) return false; mode = be16_to_cpu(dip->di_mode); /* No zero-length symlinks/dirs. */ - if ((S_ISLNK(mode) || S_ISDIR(mode)) && dip->di_size == 0) + if ((S_ISLNK(mode) || S_ISDIR(mode)) && di_size == 0) return false; + /* Fork checks carried over from xfs_iformat_fork */ + if (mode && + be32_to_cpu(dip->di_nextents) + be16_to_cpu(dip->di_anextents) > + be64_to_cpu(dip->di_nblocks)) + return false; + + if (mode && XFS_DFORK_BOFF(dip) > mp->m_sb.sb_inodesize) + return false; + + flags = be16_to_cpu(dip->di_flags); + + if (mode && (flags & XFS_DIFLAG_REALTIME) && !mp->m_rtdev_targp) + return false; + + /* Do we have appropriate data fork formats for the mode? */ + switch (mode & S_IFMT) { + case S_IFIFO: + case S_IFCHR: + case S_IFBLK: + case S_IFSOCK: + if (dip->di_format != XFS_DINODE_FMT_DEV) + return false; + break; + case S_IFREG: + case S_IFLNK: + case S_IFDIR: + switch (dip->di_format) { + case XFS_DINODE_FMT_LOCAL: + /* + * no local regular files yet + */ + if (S_ISREG(mode)) + return false; + if (di_size > XFS_DFORK_DSIZE(dip, mp)) + return false; + /* fall through */ + case XFS_DINODE_FMT_EXTENTS: + case XFS_DINODE_FMT_BTREE: + break; + default: + return false; + } + break; + case 0: + /* Uninitialized inode ok. */ + break; + default: + return false; + } + + if (XFS_DFORK_Q(dip)) { + switch (dip->di_aformat) { + case XFS_DINODE_FMT_LOCAL: + case XFS_DINODE_FMT_EXTENTS: + case XFS_DINODE_FMT_BTREE: + break; + default: + return false; + } + } + /* only version 3 or greater inodes are extensively verified here */ if (dip->di_version < 3) return true; diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c index 0defbd02f62d..06ebfc25e974 100644 --- a/fs/xfs/libxfs/xfs_inode_fork.c +++ b/fs/xfs/libxfs/xfs_inode_fork.c @@ -86,50 +86,11 @@ xfs_iformat_fork( int error = 0; xfs_fsize_t di_size; - if (unlikely(be32_to_cpu(dip->di_nextents) + - be16_to_cpu(dip->di_anextents) > - be64_to_cpu(dip->di_nblocks))) { - xfs_warn(ip->i_mount, - "corrupt dinode %Lu, extent total = %d, nblocks = %Lu.", - (unsigned long long)ip->i_ino, - (int)(be32_to_cpu(dip->di_nextents) + - be16_to_cpu(dip->di_anextents)), - (unsigned long long) - be64_to_cpu(dip->di_nblocks)); - XFS_CORRUPTION_ERROR("xfs_iformat(1)", XFS_ERRLEVEL_LOW, - ip->i_mount, dip); - return -EFSCORRUPTED; - } - - if (unlikely(dip->di_forkoff > ip->i_mount->m_sb.sb_inodesize)) { - xfs_warn(ip->i_mount, "corrupt dinode %Lu, forkoff = 0x%x.", - (unsigned long long)ip->i_ino, - dip->di_forkoff); - XFS_CORRUPTION_ERROR("xfs_iformat(2)", XFS_ERRLEVEL_LOW, - ip->i_mount, dip); - return -EFSCORRUPTED; - } - - if (unlikely((ip->i_d.di_flags & XFS_DIFLAG_REALTIME) && - !ip->i_mount->m_rtdev_targp)) { - xfs_warn(ip->i_mount, - "corrupt dinode %Lu, has realtime flag set.", - ip->i_ino); - XFS_CORRUPTION_ERROR("xfs_iformat(realtime)", - XFS_ERRLEVEL_LOW, ip->i_mount, dip); - return -EFSCORRUPTED; - } - switch (ip->i_d.di_mode & S_IFMT) { case S_IFIFO: case S_IFCHR: case S_IFBLK: case S_IFSOCK: - if (unlikely(dip->di_format != XFS_DINODE_FMT_DEV)) { - XFS_CORRUPTION_ERROR("xfs_iformat(3)", XFS_ERRLEVEL_LOW, - ip->i_mount, dip); - return -EFSCORRUPTED; - } ip->i_d.di_size = 0; ip->i_df.if_u2.if_rdev = xfs_dinode_get_rdev(dip); break; @@ -139,32 +100,7 @@ xfs_iformat_fork( case S_IFDIR: switch (dip->di_format) { case XFS_DINODE_FMT_LOCAL: - /* - * no local regular files yet - */ - if (unlikely(S_ISREG(be16_to_cpu(dip->di_mode)))) { - xfs_warn(ip->i_mount, - "corrupt inode %Lu (local format for regular file).", - (unsigned long long) ip->i_ino); - XFS_CORRUPTION_ERROR("xfs_iformat(4)", - XFS_ERRLEVEL_LOW, - ip->i_mount, dip); - return -EFSCORRUPTED; - } - di_size = be64_to_cpu(dip->di_size); - if (unlikely(di_size < 0 || - di_size > XFS_DFORK_DSIZE(dip, ip->i_mount))) { - xfs_warn(ip->i_mount, - "corrupt inode %Lu (bad size %Ld for local inode).", - (unsigned long long) ip->i_ino, - (long long) di_size); - XFS_CORRUPTION_ERROR("xfs_iformat(5)", - XFS_ERRLEVEL_LOW, - ip->i_mount, dip); - return -EFSCORRUPTED; - } - size = (int)di_size; error = xfs_iformat_local(ip, dip, XFS_DATA_FORK, size); break; @@ -175,14 +111,11 @@ xfs_iformat_fork( error = xfs_iformat_btree(ip, dip, XFS_DATA_FORK); break; default: - XFS_ERROR_REPORT("xfs_iformat(6)", XFS_ERRLEVEL_LOW, - ip->i_mount); return -EFSCORRUPTED; } break; default: - XFS_ERROR_REPORT("xfs_iformat(7)", XFS_ERRLEVEL_LOW, ip->i_mount); return -EFSCORRUPTED; } if (error) {