mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [Linux Kernel Bug] KASAN: use-after-free Write in ocfs2_local_release_dquot
@ 2026-09-13 14:27 Jiaming Zhang
  2026-09-14  3:49 ` [PATCH] ocfs2: fix chunk number of the first chunk in a local quota file Jiaming Zhang
  0 siblings, 1 reply; 6+ messages in thread
From: Jiaming Zhang @ 2026-09-13 14:27 UTC (permalink / raw)
  To: jlbec, joseph.qi, mark, ocfs2-devel; +Cc: linux-kernel, syzkaller, r772577952

Dear Linux kernel developers and maintainers,

We are writing to report an issue discovered in the OCFS2 subsystem.  The
issue is reproducible on the latest version of linux (v7.3-rc2, commit
df2908090cda368b01ff43709f51890076c56157).  Below is the kernel report:

==================================================================
BUG: KASAN: use-after-free in instrument_write
include/linux/instrumented.h:41 [inline]
BUG: KASAN: use-after-free in ___clear_bit
include/asm-generic/bitops/instrumented-non-atomic.h:44 [inline]
BUG: KASAN: use-after-free in __clear_bit_le
include/asm-generic/bitops/le.h:41 [inline]
BUG: KASAN: use-after-free in _ocfs2_clear_bit fs/ocfs2/ocfs2.h:936 [inline]
BUG: KASAN: use-after-free in ocfs2_clear_bit_unaligned
fs/ocfs2/ocfs2.h:967 [inline]
BUG: KASAN: use-after-free in ocfs2_local_release_dquot+0x44c/0x6a0
fs/ocfs2/quota_local.c:1298
Write of size 8 at addr ffff8880569ef400 by task kworker/u10:1/37

CPU: 1 UID: 0 PID: 37 Comm: kworker/u10:1 Not tainted 7.3.0-rc2 #47
PREEMPT(full)
Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix,
1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Workqueue: quota_events_unbound quota_release_workfn
Call Trace:
 <TASK>
 __dump_stack lib/dump_stack.c:94 [inline]
 dump_stack_lvl+0x10e/0x190 lib/dump_stack.c:120
 print_address_description mm/kasan/report.c:378 [inline]
 print_report+0x153/0x7e0 mm/kasan/report.c:482
 kasan_report+0x147/0x180 mm/kasan/report.c:595
 check_region_inline mm/kasan/generic.c:-1 [inline]
 kasan_check_range+0x2b0/0x2c0 mm/kasan/generic.c:200
 instrument_write include/linux/instrumented.h:41 [inline]
 ___clear_bit include/asm-generic/bitops/instrumented-non-atomic.h:44 [inline]
 __clear_bit_le include/asm-generic/bitops/le.h:41 [inline]
 _ocfs2_clear_bit fs/ocfs2/ocfs2.h:936 [inline]
 ocfs2_clear_bit_unaligned fs/ocfs2/ocfs2.h:967 [inline]
 ocfs2_local_release_dquot+0x44c/0x6a0 fs/ocfs2/quota_local.c:1298
 ocfs2_release_dquot+0x626/0xc70 fs/ocfs2/quota_global.c:793
 quota_release_workfn+0x35f/0x610 fs/quota/dquot.c:867
 process_one_work kernel/workqueue.c:3396 [inline]
 process_scheduled_works+0xc85/0x18f0 kernel/workqueue.c:3479
 worker_thread+0x8a3/0xda0 kernel/workqueue.c:3560
 kthread+0x38c/0x480 kernel/kthread.c:436
 ret_from_fork+0x509/0xb70 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
 </TASK>

The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x569ef
flags: 0x4fff00000000000(node=1|zone=1|lastcpupid=0x7ff)
raw: 04fff00000000000 ffffea00015a7bc8 ffffea00015a7bc8 0000000000000000
raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
page dumped because: kasan: bad access detected
page_owner info is not present (never set?)

Memory state around the buggy address:
 ffff8880569ef300: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
 ffff8880569ef380: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
>ffff8880569ef400: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
                   ^
 ffff8880569ef480: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
 ffff8880569ef500: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
==================================================================

Following is the root cause analysis for this issue, note that the
analysis is performed with the assistance of LLM, but we try our best to
ensure the accuracy.

When ocfs2 releases a quota structure, it clears the structure's bit in the
bitmap of the local quota file chunk that holds it, and the position of
that bit is computed from the chunk's number.  A bogus chunk number can
push that position far outside the bitmap, so the write lands on unrelated
memory.

The chunk number is wrong because ocfs2_local_quota_add_chunk() appends the
new chunk to the list first, and then numbers it one past the entry before
it:

list_add_tail(&chunk->qc_chunk, &oinfo->dqi_chunk);
chunk->qc_num = list_entry(chunk->qc_chunk.prev,
  struct ocfs2_quota_chunk,
  qc_chunk)->qc_num + 1;

If the list was empty, the entry before it is the list head itself.  The
head is a member of struct ocfs2_mem_dqinfo, not a chunk, so the "previous
number" it reads is part of the dqi_gqinode pointer stored next to the
head.  A crafted image gets a first chunk numbered with part of a kernel
pointer instead of 0.

ol_dqblk_off() turns the chunk number into a file offset by shifting a
32-bit block number left by the block size bits, and a block number built
from such a large chunk number loses its top bits there.  When the entry is
released, the offset is converted back into a bit position using the full
chunk number, so the lost bits push that position far outside the chunk,
and clearing it corrupts unrelated memory.  Depending on where the write
lands, testing also triggered the following reports:

- BUG: unable to handle kernel paging request in ocfs2_local_release_dquot
- KASAN: slab-out-of-bounds Write in ocfs2_local_release_dquot
- KASAN: slab-use-after-free Write in ocfs2_local_release_dquot
- KFENCE: use-after-free write in ocfs2_local_release_dquot

To fix this issue, the chunk number should be computed before the chunk is
added to the list, and set to 0 when the list is empty:

diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index f55810c59b1b..d351cda9211f 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -1071,10 +1071,13 @@ static struct ocfs2_quota_chunk
*ocfs2_local_quota_add_chunk(
  goto out;
  }

+ if (list_empty(&oinfo->dqi_chunk))
+ chunk->qc_num = 0;
+ else
+ chunk->qc_num = list_entry(oinfo->dqi_chunk.prev,
+   struct ocfs2_quota_chunk,
+   qc_chunk)->qc_num + 1;
  list_add_tail(&chunk->qc_chunk, &oinfo->dqi_chunk);
- chunk->qc_num = list_entry(chunk->qc_chunk.prev,
-   struct ocfs2_quota_chunk,
-   qc_chunk)->qc_num + 1;
  chunk->qc_headerbh = bh;
  *offset = 0;
  return chunk;

After applying the patch, the reproducer no longer triggers the issue on
our machine.  If this solution is acceptable, we are happy to submit a
formal patch.

The kernel console output, kernel config, syzkaller reproducer, and C
reproducer are available at google drive:
https://drive.google.com/drive/folders/1-LzPTgOALEc3eOjmgM6oOfRKkYXJ-bnO?usp=drive_link

Please let us know if any further information is required.

Best Regards,
Jiaming Zhang

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-17  4:23 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-13 14:27 [Linux Kernel Bug] KASAN: use-after-free Write in ocfs2_local_release_dquot Jiaming Zhang
2026-09-14  3:49 ` [PATCH] ocfs2: fix chunk number of the first chunk in a local quota file Jiaming Zhang
2026-09-15  9:26   ` Joseph Qi
2026-09-16  1:07   ` Andrew Morton
2026-09-16  4:58     ` Jiaming Zhang
2026-09-17  4:23       ` Andrew Morton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®