From patchwork Wed Sep 2 19:41:37 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: 1356079 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 4BhZBQ5b4zz9sTm; Thu, 3 Sep 2020 05:43:58 +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 1kDYfi-0003sh-0O; Wed, 02 Sep 2020 19:43:54 +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 1kDYfe-0003qy-So for kernel-team@lists.ubuntu.com; Wed, 02 Sep 2020 19:43:50 +0000 Received: from mail-qt1-f197.google.com ([209.85.160.197]) by youngberry.canonical.com with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.86_2) (envelope-from ) id 1kDYfe-0004nN-Ht for kernel-team@lists.ubuntu.com; Wed, 02 Sep 2020 19:43:50 +0000 Received: by mail-qt1-f197.google.com with SMTP id a14so542411qtp.15 for ; Wed, 02 Sep 2020 12:43:50 -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=nfldywba2z9X9lT/thOlscX1il39C4H1Aik1psLypNo=; b=PdL4L/A01oiGAmS8IX2DMOKWsgpt6+SMuxsjcqwwkoc99UDTttTJfi95UsnDJoV64D PV20+cpxNyKeoPwzUdJkdPxjdVnFa304fRB+su3WH1W005aHQUDwwRtW/n7stRJhRgcb yIGY60M6DSZQNznozbSGCRDdD+7DlG3siij2ymMRS4NdmfIj0DpB6LoLZ9JBBaz4tGCy OksMZgg2oXPbIKKj05pgdgZ1XhJXrBUIZnhUXSSbNPHmSLynwiTQP5aDz6lVTV3yS3K8 3HaBi3jxpkpoPae0/hB9XHapfs+7+QFou9OQUH88SnBD7umbvMvvKd0amhl0pWw30Sd8 jRQg== X-Gm-Message-State: AOAM5300uhtLDCdWhlrO7h93an75xP/g/WgMy8Q0D9PUzrKaK4W3+dXe G3WVUBhuiBKDHU7Gck8Daigj7+dtGWKTSSOQn2QULj58RuNS+YOpAe8Y+ZmL5+hLFpUYkJhXKu5 QsgBTLG5+76p4BwfEBb7eupdfjgUrLR/L8Qvx407/rg== X-Received: by 2002:a0c:ee41:: with SMTP id m1mr8460240qvs.214.1599075829310; Wed, 02 Sep 2020 12:43:49 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyJbwWi3EHO8T7LswQRMcQ3hc7Cr+yKWpHeSbNGSokfaPHlDxKJeNokEsaqSpznGDpfdPb+0w== X-Received: by 2002:a0c:ee41:: with SMTP id m1mr8460224qvs.214.1599075829012; 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.48 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 02 Sep 2020 12:43:48 -0700 (PDT) From: William Breathitt Gray To: kernel-team@lists.ubuntu.com Subject: [SRU][BIONIC][CVE-2018-10322][PATCH v2 3/4] xfs: move inode fork verifiers to xfs_dinode_verify Date: Wed, 2 Sep 2020 15:41:37 -0400 Message-Id: <20200902194139.67480-4-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 | 72 ++++++++++++++++++++++++++-- fs/xfs/libxfs/xfs_inode_fork.c | 86 ---------------------------------- 2 files changed, 69 insertions(+), 89 deletions(-) diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index 6b7989038d75..dfda5ff936b6 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -390,12 +390,14 @@ xfs_dinode_verify( uint16_t mode; uint16_t flags; uint64_t flags2; + 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); @@ -403,9 +405,70 @@ xfs_dinode_verify( return false; /* 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; @@ -420,7 +483,6 @@ xfs_dinode_verify( if (!uuid_equal(&dip->di_uuid, &mp->m_sb.sb_meta_uuid)) return false; - flags = be16_to_cpu(dip->di_flags); flags2 = be64_to_cpu(dip->di_flags2); /* don't allow reflink/cowextsize if we don't have reflink */ @@ -428,6 +490,10 @@ xfs_dinode_verify( !xfs_sb_version_hasreflink(&mp->m_sb)) return false; + /* only regular files get reflink */ + if ((flags2 & XFS_DIFLAG2_REFLINK) && (mode & S_IFMT) != S_IFREG) + return false; + /* don't let reflink and realtime mix */ if ((flags2 & XFS_DIFLAG2_REFLINK) && (flags & XFS_DIFLAG_REALTIME)) return false; diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c index c79a1616b79d..fd88cbe8c264 100644 --- a/fs/xfs/libxfs/xfs_inode_fork.c +++ b/fs/xfs/libxfs/xfs_inode_fork.c @@ -62,69 +62,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; - } - - if (unlikely(xfs_is_reflink_inode(ip) && !S_ISREG(inode->i_mode))) { - xfs_warn(ip->i_mount, - "corrupt dinode %llu, wrong file type for reflink.", - ip->i_ino); - XFS_CORRUPTION_ERROR("xfs_iformat(reflink)", - XFS_ERRLEVEL_LOW, ip->i_mount, dip); - return -EFSCORRUPTED; - } - - if (unlikely(xfs_is_reflink_inode(ip) && - (ip->i_d.di_flags & XFS_DIFLAG_REALTIME))) { - xfs_warn(ip->i_mount, - "corrupt dinode %llu, has reflink+realtime flag set.", - ip->i_ino); - XFS_CORRUPTION_ERROR("xfs_iformat(reflink)", - XFS_ERRLEVEL_LOW, ip->i_mount, dip); - return -EFSCORRUPTED; - } - switch (inode->i_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; inode->i_rdev = xfs_to_linux_dev_t(xfs_dinode_get_rdev(dip)); break; @@ -134,32 +76,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; @@ -170,14 +87,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)