From nobody Tue Sep 29 09:09:01 2026 Received: from mail.ispras.ru (mail.ispras.ru [83.149.199.84]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 078863C1966 for ; Mon, 10 Aug 2026 11:37:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=83.149.199.84 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786361854; cv=none; b=Th9JI8I4F1SnBu12Wj2jdHcoCr6CK1p8tvP+MYO4y5Qjpt5uJXKipcViXqwlIvQAW/wlK0p1skXLwpjovTPU2LoqKNUqp7gaE54kiGof2SU0N6AZfhi5loMqMQ2nVKRZkk8yaPRtVnacrI4UA8ih3VpOk+JysXe/rOl1H9yScTY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786361854; c=relaxed/simple; bh=BL808TLAbJXlegrSu6qlFBP+3pDDYGgdxTqdEDRZ/cg=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=F8H5WiZAInyZ6+SzTyVdbUrFQgLQdurmST6HpSSViWWVkulT2Rw7bkbFjvhc0rCHHVOGmJP/Pf4nWzZWTlxyvqXgGVRi6I7mTrjGq3LwqY7Zn5BgG9Uqwl4YRigQk+IRyXtTI8jivgok+1i/hGo4dF5/FZNGitrVfHsN3l4Ig/0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ispras.ru; spf=pass smtp.mailfrom=ispras.ru; dkim=pass (1024-bit key) header.d=ispras.ru header.i=@ispras.ru header.b=AudqP9i7; arc=none smtp.client-ip=83.149.199.84 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ispras.ru Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ispras.ru Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ispras.ru header.i=@ispras.ru header.b="AudqP9i7" Received: from localhost.localdomain (unknown [95.54.231.191]) by mail.ispras.ru (Postfix) with ESMTPSA id 62142406E9BD; Mon, 10 Aug 2026 11:37:21 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.ispras.ru 62142406E9BD DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ispras.ru; s=default; t=1786361841; bh=KoY4YzgHWl+H55iWJRy6ez+AYUFA0HjjC6U4505ligg=; h=From:To:Cc:Subject:Date:From; b=AudqP9i7aRne1+etH+ZJfYihkCdYTvGlinXV0kayc6zdwdOBCHjq1GE+P/tDjA2ak ICr4JxdYYqwHbjCq5mmOIgXEWe5Cmam9iydG+dEunx5vviXDKrZ9d9yLG/q7hEegm1 +uh1lTIV/mcedpCEa1spvqDWQMNlLkJ/+ySH823s= From: Dmitry Morgun To: Mark Fasheh Cc: Dmitry Morgun , Joel Becker , Joseph Qi , ocfs2-devel@oss.oracle.com, linux-kernel@vger.kernel.org, lvc-project@linuxtesting.org Subject: [PATCH] ocfs2: validate la_size before ocfs2_clear_local_alloc() Date: Mon, 10 Aug 2026 11:35:08 +0000 Message-Id: <20260810113508.8513-1-d.morgun@ispras.ru> 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" la_size, like the dirty flag, is read from disk. If dirty !=3D 0, ocfs2_begin_local_alloc_recovery() is always called, which immediately invokes ocfs2_clear_local_alloc(). At this point, ocfs2_clear_local_alloc() uses la_size as the loop bound without validating it first. If la_size is corrupted, the loop writes past the end of la_bitmap and may eventually start writing into memory that has already been freed. BUG: KASAN: use-after-free in ocfs2_clear_local_alloc fs/ocfs2/localalloc.c= :919 [inline] BUG: KASAN: use-after-free in ocfs2_begin_local_alloc_recovery+0xb07/0xc00 = fs/ocfs2/localalloc.c:515 CPU: 1 PID: 2386 Comm: syz.2.190 Not tainted 6.1.174-syzkaller-00520-g10c50= 5401422 #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1= .17.0-1 04/01/2014 Call Trace: ocfs2_clear_local_alloc fs/ocfs2/localalloc.c:919 [inline] ocfs2_begin_local_alloc_recovery+0xb07/0xc00 fs/ocfs2/localalloc.c:515 ocfs2_check_volume fs/ocfs2/super.c:2448 [inline] ocfs2_mount_volume fs/ocfs2/super.c:1819 [inline] ocfs2_fill_super+0x2033/0x3dc0 fs/ocfs2/super.c:1082 mount_bdev+0x356/0x410 fs/super.c:1443 legacy_get_tree+0x108/0x220 fs/fs_context.c:632 vfs_get_tree+0x8e/0x300 fs/super.c:1573 do_new_mount fs/namespace.c:3078 [inline] path_mount+0x6af/0x1f70 fs/namespace.c:3408 do_mount fs/namespace.c:3421 [inline] __do_sys_mount fs/namespace.c:3629 [inline] __se_sys_mount fs/namespace.c:3606 [inline] __x64_sys_mount+0x283/0x300 fs/namespace.c:3606 do_syscall_x64 arch/x86/entry/common.c:46 [inline] do_syscall_64+0x35/0x80 fs/namespace.c:76 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 The validation of la_size currently exists only in ocfs2_load_local_alloc(), but that function is called after ocfs2_clear_local_alloc(). As a result, memory corruption occurs before the invalid value is detected and -EINVAL is returned. Adding the same validation to ocfs2_begin_local_alloc_recovery() before calling ocfs2_clear_local_alloc() prevents the out-of-bounds write by failing the recovery early with -EINVAL. Found by Linux Verification Center (linuxtesting.org) with Syzkaller. Fixes: ccd979bdbce9 ("OCFS2: The Second Oracle Cluster Filesystem") Signed-off-by: Dmitry Morgun --- A similar issue also exists in ocfs2_complete_local_alloc_recovery(). The i_total and la_bm_off fields are also read from disk and used as loop bounds and offsets without prior validation. With a corrupted filesystem image, they could lead to similar out-of-bounds accesses during the completion of local alloc recovery. Therefore, a more complete solution would be to introduce a shared validation helper for all relevant on-disk local alloc fields (la_size, i_total, la_bm_off, and others) and invoke it before starting the recovery process. This patch fixes only the reported reproducer, triggered=20 by an invalid la_size. Other fields like i_total and la_bm_off are still unvalidated, so a similarly corrupted image could trigger=20 an analogous bug elsewhere. Maintainers' input would be welcome=20 on whether a shared validation helper is preferred over targeted=20 per-field fixes. fs/ocfs2/localalloc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/ocfs2/localalloc.c b/fs/ocfs2/localalloc.c index c4426d12a..ce03903ed 100644 --- a/fs/ocfs2/localalloc.c +++ b/fs/ocfs2/localalloc.c @@ -481,6 +481,7 @@ int ocfs2_begin_local_alloc_recovery(struct ocfs2_super= *osb, struct buffer_head *alloc_bh =3D NULL; struct inode *inode =3D NULL; struct ocfs2_dinode *alloc; + struct ocfs2_local_alloc *la; =20 trace_ocfs2_begin_local_alloc_recovery(slot_num); =20 @@ -512,6 +513,16 @@ int ocfs2_begin_local_alloc_recovery(struct ocfs2_supe= r *osb, memcpy((*alloc_copy), alloc_bh->b_data, alloc_bh->b_size); =20 alloc =3D (struct ocfs2_dinode *) alloc_bh->b_data; + la =3D OCFS2_LOCAL_ALLOC(alloc); + + if ((la->la_size =3D=3D 0) || + (le16_to_cpu(la->la_size) > ocfs2_local_alloc_size(inode->i_sb))) { + mlog(ML_ERROR, "Local alloc size is invalid (la_size =3D %u)\n", + le16_to_cpu(la->la_size)); + status =3D -EINVAL; + goto bail; + } + ocfs2_clear_local_alloc(alloc); =20 ocfs2_compute_meta_ecc(osb->sb, alloc_bh->b_data, &alloc->i_check); --=20 2.34.1