From nobody Sat Jul 25 17:34:50 2026 Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1370E435A8A for ; Wed, 15 Jul 2026 12:06:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784117188; cv=none; b=PbigOLDTz1Bp6PkwZjzBl+HmTAlN9QkjU/2AVX4GKblcxuegc2tLe4hnfCcC8C43FjwutBp3NKfsLQZ1J1NaEstgeEOr51auxG+M0B1fesz8zfGvyQdV1dpmrQa0b/KUG6Gpt+orq2MYm8QPhUViP77ani4d0Sv7406WfbdI0dk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784117188; c=relaxed/simple; bh=5chy9DLs53u/9RawX16/HaFqd0e3eUNquSrQlw9FA7Y=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=E5qn4GFlLSRJ1kErdYzGc1gOZM5w/xDA9ZGHgOrxTpf4TV6sgxRI4fXOYY7iRifzxrupOdsJxX2weVUcorFTH/weziaePf9tUnNhCezMsIm8FUSFdrYWnn2B1Jky2gNAME6wEVgqjuJmgjrEJ4tWgYlKegvtMdofjDE2g9fPo1k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=sTUGK1GA; arc=none smtp.client-ip=209.85.214.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="sTUGK1GA" Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-2cc73e322dbso64345445ad.1 for ; Wed, 15 Jul 2026 05:06:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784117184; x=1784721984; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=QBCR6V8aARkLeXQtpzy2HKAP8PmYdJT3CmWSOmYdG+o=; b=sTUGK1GA0aGJ962kTRHbQpezY8ptuvfqXeYwGVSRByq/UPen8cG7eblCcgD3T0zTSk nmgiEQUHzvkHvqQsO6qdnhjdEPxr2PZaCl6RSgq4Aqn3GLE6Rj40B20ebhTBSzjEfe9f CJZpdBEpo0V2GowIR7SmSxTB0Ch7IxPCYzckRDwOpw+OIwYSVfX24O/cHzKyDitu31N/ oHkF2jHlTGI+LVqjGgQp6IpuSYv6ct1eU2N5xO1h9DY69y7MQzUAJRX+k+q0/jN0GnNr K7fqpS3FvMnhoiTUz6U8et8ww/Lf60nND4GcyvgwJUt7G8IKaULofbLlNJyLWxpFWvRW rOkA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784117184; x=1784721984; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=QBCR6V8aARkLeXQtpzy2HKAP8PmYdJT3CmWSOmYdG+o=; b=IUvdJ/mnkIX1BSPASXQDFYK98C+ewGhs50fuCOkiV1dYLCa1eOWsJWZ14FIKiPAdEv VFLBQIn9PtMoXskuHCv+RHRnwwLSZ9rYhBPeJq005Pg1sGoshsuS9mwkIiyXo3f0ECAt 3zt+ogDRnojUsQDdFSOYgsdBxawPe93dNGhGkGNcl9PWv/U1zQv9SwgVmjsjE3Aafb3S H1PSj2eRhwBDPL3ZYzivHpYLs3omXidFEkLSzCaxAfFvw1HcsbhEXoYEzEvS5ktTKGc7 xplY5D6j6zF2JnpU4m9tOtfqsqWH+o/7A3K+zKXPSAUI06zXB9rXP6d+0r2fXBaHqrxY aP4Q== X-Forwarded-Encrypted: i=1; AHgh+Ro2a8UAiSN8OApwCxF1jB/NE3dLngIzH+F5GAjvV/hV1oF5zXc5pJtWkObikn2ho8Jtx02e/cHysHdzJCU=@vger.kernel.org X-Gm-Message-State: AOJu0Yxc5ZZqlpASrcWpo3r4rD7S+vRdhbIqevBeDrgzq096AqXsahPz BAsYqZnxgqBBeNGVF4MI9//JYPnxbScUUMpj9A8YviCF/efEhzyO0poD X-Gm-Gg: AfdE7ckXR/z4zixt8YF7lPhjfJ2o/sIemFI/15TkIVUuZZIGeAJtY813rqCO9jZeB0W DdCV0/gYAL6OAj91eQbK58TXnO+81qw63+PbjHPZXG6fQ4mPbpk4yt4SgIvYeCV0d+dc9wEz5gn QD/hbw0ra7S4bqXIE/zoIGmOFTIiX1JT2bGlAOz+criUH1FZFa4YbQ1RTGrEp6dSfZl2hXXF7Qn 5JpHANnK0EuOZap/0NoJFO03LmPgH8WrN/dtftCzc0Jwsye0tDLkZPQhvK2QYgemD+12Nq/hSEC jKCbtoNjVTnNQS/UgfoOq+nhrtMQATYzTsfr5NnS0dEEooF9zNbz1E1OExv14eSdjpatsC2+2mZ 6vNX0w7E/blO/obA1hQ+Q3z12ckyDOKWF5HOT6jY/kHsAzOrNQja0JwTiPf9wBTfwwQT8v1FfDk icYZI3hosAd9DTUsM4e3qcrh2GBwk5QGtOjOXnmd5sX8V9mc8E7be+ X-Received: by 2002:a17:903:11ce:b0:2c9:da58:17dd with SMTP id d9443c01a7336-2cf03d93685mr22145255ad.34.1784117183596; Wed, 15 Jul 2026 05:06:23 -0700 (PDT) Received: from lipengfei28-ThinkStation-P368.mioffice.cn ([2408:8607:1b00:8:d1d2:d044:5a81:884]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2cf106e6fe6sm1147105ad.73.2026.07.15.05.06.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Jul 2026 05:06:23 -0700 (PDT) From: Li Pengfei X-Google-Original-From: Li Pengfei To: Andreas Gruenbacher Cc: gfs2@lists.linux.dev, linux-kernel@vger.kernel.org, lipengfei28@xiaomi.com, syzbot+8921d5debffa05b33b27@syzkaller.appspotmail.com Subject: [PATCH] gfs2: validate metadata block numbers before reading them Date: Wed, 15 Jul 2026 20:06:12 +0800 Message-Id: <20260715120612.3084326-1-lipengfei28@xiaomi.com> X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Pengfei Li When mounting a crafted/corrupted gfs2 image, an inode's indirect block may contain a block pointer that lies outside the filesystem. Both the metadata tree walk (__fillup_metapath -> gfs2_meta_buffer) and the metadata read-ahead (gfs2_metapath_ra) take such a pointer straight from disk and pass it to gfs2_getbuf() without any bounds check. gfs2_getbuf() turns the block number into a page index via "index =3D blkno >> shift". A pointer of 0xffffffffffffffff therefore inserts a metadata folio at index ULONG_MAX in the glock's metadata mapping. When the glock is put, __gfs2_glock_put() calls truncate_inode_pages_final(), but that only scans the range [0, ULONG_MAX-1] (truncate_inode_pages_range() passes end - 1 as the inclusive last index), so the folio at ULONG_MAX is never removed. The withdraw path in __gfs2_glock_put() then skips the GLOCK_BUG_ON(!mapping_empty()) assertion, and the glock -- with its embedded address_space still holding the stray folio -- is freed via RCU. Later, when a per-cpu LRU batch is drained, folio_evictable() dereferences the now-freed mapping, producing a slab-use-after-free that surfaces far from gfs2 (e.g. in lru_add during an unrelated task's shmem/fault path). Reject block pointers that fall outside the block device before using them. In the tree-walk path flag the inode as inconsistent (which withdraws the filesystem); in the read-ahead path, which is advisory, just skip the bogus pointer. Reported-by: syzbot+8921d5debffa05b33b27@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3D8921d5debffa05b33b27 Signed-off-by: Pengfei Li --- fs/gfs2/bmap.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 51ac1fd44..032acb964 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -291,8 +291,25 @@ static void clone_metapath(struct metapath *clone, str= uct metapath *mp) get_bh(clone->mp_bh[hgt]); } =20 +/* + * On a corrupted filesystem, an indirect block may contain a pointer that + * lies outside the filesystem. Feeding such a block number to gfs2_getbu= f() + * creates a page far beyond the end of the metadata mapping (at an index = up + * to ULONG_MAX), which truncate_inode_pages_final() never reaches; the pa= ge + * is then left dangling when the glock is freed. Reject pointers that are + * outside the block device. + */ +static bool gfs2_meta_blk_out_of_range(const struct gfs2_sbd *sdp, u64 blk) +{ + u64 blocks =3D bdev_nr_bytes(sdp->sd_vfs->s_bdev) >> + sdp->sd_sb.sb_bsize_shift; + + return blk >=3D blocks; +} + static void gfs2_metapath_ra(struct gfs2_glock *gl, __be64 *start, __be64 = *end) { + struct gfs2_sbd *sdp =3D glock_sbd(gl); const __be64 *t; =20 for (t =3D start; t < end; t++) { @@ -300,6 +317,8 @@ static void gfs2_metapath_ra(struct gfs2_glock *gl, __b= e64 *start, __be64 *end) =20 if (!*t) continue; + if (gfs2_meta_blk_out_of_range(sdp, be64_to_cpu(*t))) + continue; =20 rabh =3D gfs2_getbuf(gl, be64_to_cpu(*t), CREATE); if (trylock_buffer(rabh)) { @@ -325,6 +344,8 @@ metapath_dibh(struct metapath *mp) static int __fillup_metapath(struct gfs2_inode *ip, struct metapath *mp, unsigned int x, unsigned int h) { + struct gfs2_sbd *sdp =3D GFS2_SB(&ip->i_inode); + for (; x < h; x++) { __be64 *ptr =3D metapointer(x, mp); u64 dblock =3D be64_to_cpu(*ptr); @@ -332,6 +353,10 @@ static int __fillup_metapath(struct gfs2_inode *ip, st= ruct metapath *mp, =20 if (!dblock) break; + if (gfs2_meta_blk_out_of_range(sdp, dblock)) { + gfs2_consist_inode(ip); + return -EIO; + } ret =3D gfs2_meta_buffer(ip, GFS2_METATYPE_IN, dblock, &mp->mp_bh[x + 1]= ); if (ret) return ret; --=20 2.34.1