From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pj1-f41.google.com (mail-pj1-f41.google.com [209.85.216.41]) (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 7A2313C942A for ; Thu, 23 Apr 2026 03:09:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.41 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776913764; cv=none; b=oyfzsKeBY/CnjCR2kFJak1jzXTOYYFVI+whuD7HkVSmiedD02hiL7MP1ExMkDik9ysd5SX2a2owElPSwZIB2XX36coUbmV2ur03PmTNsVn0IdgB9U/BewtkHoYnwdGRQoq7Lu8dppcKGWFcRKBl4qNqJf/zSBxnkbeysohbfLzI= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776913764; c=relaxed/simple; bh=MLH66ojpKVhwLhBTehIr39sCh+EmmhiE41yyw5U6QAA=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=nUwgI/sGs2Xo/jRXMGA+SgFDc59RfWI4UAWLy3w/yfCECAnbnG0hLNzWoJoV2ZBwtKLPo8kqNABov488fnb03uQahoaBMLCeR/C32c/kQzEGdMtVDcHyfwtLBI9iBVp2V9eQd18Svr/Sz/6Oot/0fLvsG0FlP87Osv4B/jhYZko= 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=lpBBCh+y; arc=none smtp.client-ip=209.85.216.41 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="lpBBCh+y" Received: by mail-pj1-f41.google.com with SMTP id 98e67ed59e1d1-35d99031e4eso4272113a91.1 for ; Wed, 22 Apr 2026 20:09:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776913762; x=1777518562; 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; bh=3VW1kTSn+x/uimseDbthu+BoZ7rGPr/N04/pCvrInfM=; b=lpBBCh+ycnkqT6F7z3JCKapwIM+SC5nhpz8rtLr/j4juopIiw+q3oIzyUwWHToWkde J5W+8Tjk4bXplEK4NjB8upfBMIWTDgG2MvoAjTzi17zRkxqhSFb7ii8Y1OFrz7CtUrOv n3yGkH6cW7V+IRi3FblHVjRY57z4/l1FAvXbDOOAQxm6Wm8470cnY9K85N5L8/t0Z77C uhfCyZY1vrDoqlFrpTVk8BRoqF6X36pNPy++tEz7yF8JHofC5kWLm4gyEgEJ/8BGi/Hh kg5YAECKtVsltxYiIiGngR8xrs08yR+qax74ayQVSzoRNVWmL2WeLcqyvuP81K5rHE6H 1sXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776913762; x=1777518562; 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; bh=3VW1kTSn+x/uimseDbthu+BoZ7rGPr/N04/pCvrInfM=; b=aBdx2koHNZoZgT1nco+9F2A0ZrBREu0BuX2BR4Z7Ik5PpZ5RTmgT2GApp/xoXC7M98 aTO8cSH1N8jftXPN+M2SG3TF7PrBX9ut//Hr1nxsrd6uLglZ51LIobUjW7Q5IUFJ2iyY A8UyXdZyRaneZTmawuytoXP+cul0sNDT1lCJasADsxWFCeTu6l7MxCczkfl/mvDtsjPk GMs3yUAuJCWlZ7ts0X/oehBH1euemZxRmbmTnTXZTfVPYNJHSI952Z8XK8mrTyznx5in oLSsuWQ3Swg9NK0fBuwcJgXAAHNz0HTDggzyOZkhjR9XjyrBBVcFZ8CVBs8IlDGP3Vmz 8lSg== X-Forwarded-Encrypted: i=1; AFNElJ9zx2ZbjB+2Ujm+dWVGwPPwrOfOO42SCWoUmJH2raXVkjrj0fM+7NbK9mXnjusXU5qvqfUbTkw519VNLHg=@vger.kernel.org X-Gm-Message-State: AOJu0YxMmlZjJE3WyH3CzUWS1tiLrOlk9rJej4laqnDAiZqM85ldP9qi qIiUOwe+E4eQ76Qn3ieVYws/zdhYSHBo8JbygCb42kcoG8XSugYP///V X-Gm-Gg: AeBDietAqhmCqsOV5SPF2ZwoKxR02YRPBAN2PFCkig4jUHCEW+suZejXeR8Nd5KwzNc HM9eMUcFWilXNS/6J8fbXUd//11D3R4D8UBDUrzoLiZN2OXAzoI8dQHJqCm8v6TrInfWJgVTfp6 hzQAeoF6CQaVT4hhcBJHazeQPD5mWpa10MS51aZqOXlKbWMA8dfVE0N7O4QMWF+QRTAxC93LAME VDAjN+pN34jpI7XrkW0AjTHqTBh0qd+NWP4wllpKlCZpfrOaIRmPqE8tkJ27ZlAiYx4hGy85x1+ 9iTXhWilJP/O9aH9XZFkbLgqXtBotXlwnSrVSxFB3kJQ1rpbDvgkPqcGImXAAyNpxo0+PeAJD// ZckI+OhS8YFhVDk+e7xyN9jqCqT6Qlv/LOyym4mWFgQmtU9jds4Hwh/FrliS+vyv6lwEPL70snw jT1WSbK/QYh539qdaov9vJRVLIRHTM5Q40uJHPjFFSm4WI1OXuuI8N8Gouzs2UqbOl4cON6aJt7 IEu6LOAGA== X-Received: by 2002:a17:90b:3d83:b0:35f:c1e1:a263 with SMTP id 98e67ed59e1d1-36140478347mr25261231a91.19.1776913761492; Wed, 22 Apr 2026 20:09:21 -0700 (PDT) Received: from kernel-fuzz.. ([103.172.182.26]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-361410b998fsm18469310a91.13.2026.04.22.20.09.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 Apr 2026 20:09:20 -0700 (PDT) From: ZhengYuan Huang To: mark@fasheh.com, jlbec@evilplan.org, joseph.qi@linux.alibaba.com Cc: ocfs2-devel@lists.linux.dev, linux-kernel@vger.kernel.org, baijiaju1990@gmail.com, r33s3n6@gmail.com, zzzccc427@gmail.com, ZhengYuan Huang Subject: [PATCH v2] ocfs2: validate dx entry list counts on read Date: Thu, 23 Apr 2026 11:09:11 +0800 Message-ID: <20260423030911.3532135-1-gality369@gmail.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit [BUG] A corrupt indexed directory dx leaf can carry de_num_used larger than de_count. When unlinkat() removes an indexed entry, that corrupt count reaches ocfs2_dx_list_remove_entry() and drives an oversized memmove(): BUG: KASAN: use-after-free in ocfs2_dx_list_remove_entry fs/ocfs2/dir.c:1246 [inline] BUG: KASAN: use-after-free in ocfs2_delete_entry_dx fs/ocfs2/dir.c:1363 [inline] BUG: KASAN: use-after-free in ocfs2_delete_entry+0xb47/0x1070 fs/ocfs2/dir.c:1417 Read of size 401648 at addr ffff888020b8bd20 by task syz.0.1/347 Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0xbe/0x130 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xd1/0x650 mm/kasan/report.c:482 kasan_report+0xfb/0x140 mm/kasan/report.c:595 check_region_inline mm/kasan/generic.c:186 [inline] kasan_check_range+0x11c/0x200 mm/kasan/generic.c:200 __asan_memmove+0x23/0x80 mm/kasan/shadow.c:94 ocfs2_dx_list_remove_entry fs/ocfs2/dir.c:1246 [inline] ocfs2_delete_entry_dx fs/ocfs2/dir.c:1363 [inline] ocfs2_delete_entry+0xb47/0x1070 fs/ocfs2/dir.c:1417 ocfs2_unlink+0xb79/0x1900 fs/ocfs2/namei.c:991 vfs_unlink+0x2c2/0x900 fs/namei.c:4673 do_unlinkat+0x474/0x680 fs/namei.c:4737 __do_sys_unlinkat fs/namei.c:4778 [inline] __se_sys_unlinkat fs/namei.c:4771 [inline] __x64_sys_unlinkat+0xae/0x130 fs/namei.c:4771 ... [CAUSE] DX leaf and inline dx root blocks validate signatures and ECC, but they do not validate the geometry of struct ocfs2_dx_entry_list. Corrupt de_count/de_num_used values therefore survive block read and are later used as traversal bounds and memmove/memset lengths in lookup, delete and rebalance paths. In the unlink case, lookup can still find the target entry before the bad bound matters, and the same corrupt de_num_used then reaches ocfs2_dx_list_remove_entry(). [FIX] Validate dx entry-list geometry in the metadata read callbacks. de_count must match the block layout for the current superblock, and de_num_used must not exceed de_count. Do this for both dx leaves and inline dx roots so corrupted metadata is rejected before any caller walks or rewrites de_entries[]. Signed-off-by: ZhengYuan Huang --- No Fixes tag is included because this is a missing metadata validation check, not a regression attributable to a single introducing commit. v2: - Reframe the bug report around the unlinkat-triggered UAF - Keep the code change unchanged because it fixes the same root cause --- fs/ocfs2/dir.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 8e6b03238327..a89634e20b32 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -578,6 +578,29 @@ static int ocfs2_read_dir_block_direct(struct inode *dir, u64 phys, return ret; } +static int ocfs2_validate_dx_entry_list(struct super_block *sb, u64 blkno, + struct ocfs2_dx_entry_list *entry_list, + unsigned int expected, + const char *owner) +{ + unsigned int count = le16_to_cpu(entry_list->de_count); + unsigned int num_used = le16_to_cpu(entry_list->de_num_used); + + if (count != expected) + return ocfs2_error(sb, + "%s # %llu has invalid de_count %u (expected %u)\n", + owner, (unsigned long long)blkno, count, + expected); + + if (num_used > count) + return ocfs2_error(sb, + "%s # %llu has invalid de_num_used %u (de_count %u)\n", + owner, (unsigned long long)blkno, num_used, + count); + + return 0; +} + static int ocfs2_validate_dx_root(struct super_block *sb, struct buffer_head *bh) { @@ -604,7 +625,14 @@ static int ocfs2_validate_dx_root(struct super_block *sb, goto bail; } - if (!(dx_root->dr_flags & OCFS2_DX_FLAG_INLINE)) { + if (dx_root->dr_flags & OCFS2_DX_FLAG_INLINE) { + ret = ocfs2_validate_dx_entry_list(sb, bh->b_blocknr, + &dx_root->dr_entries, + ocfs2_dx_entries_per_root(sb), + "Dir Index Root"); + if (ret) + goto bail; + } else { struct ocfs2_extent_list *el = &dx_root->dr_list; if (le16_to_cpu(el->l_count) != ocfs2_extent_recs_per_dx_root(sb)) { @@ -660,13 +688,21 @@ static int ocfs2_validate_dx_leaf(struct super_block *sb, mlog(ML_ERROR, "Checksum failed for dir index leaf block %llu\n", (unsigned long long)bh->b_blocknr); - return ret; + goto bail; } if (!OCFS2_IS_VALID_DX_LEAF(dx_leaf)) { ret = ocfs2_error(sb, "Dir Index Leaf has bad signature %.*s\n", 7, dx_leaf->dl_signature); + goto bail; } + ret = ocfs2_validate_dx_entry_list(sb, bh->b_blocknr, + &dx_leaf->dl_list, + ocfs2_dx_entries_per_leaf(sb), + "Dir Index Leaf"); + +bail: + return ret; } -- 2.43.0