Re: xfs: XLOG_WAS_CONT_TRANS on a transaction with no buffered regions reads ri_buf[-1] - null deref Oops during mount log recovery
From: Guikun Yang
Date: Sun Nov 08 2026 - 19:01:53 EST
Hi,
Another one from the region-collection stage that runs before any of
the item handlers - this one does not involve any item type at all.
## 7. ilf_size is read from a region smaller than the format header
xlog_recover_add_to_trans() allocates the region buffer with the
attacker-controlled oh_len and then reads the inode log format from
it (fs/xfs/xfs_log_recover.c):
ptr = xlog_kvmalloc(len);
memcpy(ptr, dp, len);
in_f = (struct xfs_inode_log_format *)ptr;
...
if (item->ri_total == 0) {
if (in_f->ilf_size == 0 ||
in_f->ilf_size > XLOG_MAX_REGIONS_IN_ITEM) {
in_f->ilf_size is at offset 2 of struct xfs_inode_log_format, so the
read needs len >= 4 - nothing checks that. len is the raw oh_len from
the operation header.
Reachability is clean: this branch only requires the item list to be
non-empty with no regions collected yet, which is exactly the state
left by a complete 16-byte transaction header (len ==
sizeof(struct xfs_trans_header) makes xlog_recover_add_item() hang an
empty item). A normal transaction header followed by a region
operation with oh_len = 1 reaches the read with a 1-byte allocation:
BUG: KASAN: slab-out-of-bounds in xlog_recover_add_to_trans+0x799/0x830
Read of size 2 at addr ...
xlog_recover_add_to_trans
xlog_recovery_process_trans
xlog_recover_process_ophdr
xlog_recover_process_data
...
xfs_fs_fill_super
The read is of size 2 - in_f->ilf_size itself.
## Reproducer
Same shape as the previous reports: a real dirty-log image, the region
operation header's oh_len changed from 0x14 to 0x01 plus the four CRC
bytes of the log record. The unmodified image recovers and mounts
cleanly; the modified one hits the KASAN report at mount(2), 3/3.
Unchanged in mainline as of 2026-11-08.
## Suggested fix
Check the region length before interpreting it as a log format - the
header magic is the only field this stage may assume:
if (len < sizeof(struct xfs_inode_log_format_32) /* or 4 */) {
xfs_warn(...);
return -EFSCORRUPTED;
}
before the ilf_size read; the later xfs_inode_item_format_convert()
already handles short legacy formats properly, so the check belongs at
this stage.
The reproducer (image builder, ready-made image, serial log) is
available on request.
Thanks,
Guikun Yang