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