Skip to content

udf: Fix zero-length extent handling in udf_table_new_block() - #2954

Open
vfsci-bot[bot] wants to merge 1 commit into
vfs.base.cifrom
pw/1191331/vfs.base.ci
Open

vfsci-bot[bot] wants to merge 1 commit into
vfs.base.cifrom
pw/1191331/vfs.base.ci

Conversation

@vfsci-bot

@vfsci-bot vfsci-bot Bot commented Oct 22, 2026

Copy link
Copy Markdown

Series: https://patchwork.kernel.org/project/linux-fsdevel/list/?series=1191331
Submitter: Zhihao Cheng
Version: 1
Patches: 1/1
Message-ID: <20261022132603.2464065-1-chengzhihao1@huawei.com>
Base: vfs.base.ci
Lore: https://lore.kernel.org/linux-fsdevel/20261022132603.2464065-1-chengzhihao1@huawei.com


Automated by ml2pr

udf_table_new_block() keeps the remaining extent length together with the
extent type in goal_elen and then uses the whole value to decide whether
the extent is empty:

	goal_elen = (etype << 30) | elen;
	...
	goal_elen -= sb->s_blocksize;
	if (goal_elen)
		udf_write_aext(...);
	else
		udf_delete_aext(...);

On a table-managed partition, free extents may have non-zero type bits.
If such an extent has exactly one block left, subtracting the block
leaves only the type bits (e.g. 0x40000000 for type 1).  The test is
still true, so instead of deleting the extent the function writes a
zero-length extent at the next block back into the unallocated space
table.

Consider a free extent with type 1 and 9 blocks:

	lb=55, len=9 blocks (55..63)

A new file is allocated from it at lb=55, so the extent becomes
"lb=56, len=8 blocks".  A huge write then extends the file with a hole.
After the in-ICB allocation descriptor area fills, AED blocks 56..63 are
allocated from this extent.  When lb=63 is allocated, goal_elen is
0x40000200; after subtracting one block it becomes 0x40000000, so the
extent is rewritten as

	lb=64, len=0, type=1

instead of being deleted.

The next allocation with goal=63 prefers this zero-length extent
(distance 1) over the real next extent "lb=65, len=31 blocks" (distance
2) and allocates lb=64 from it.  Subtracting a block from the zero-length
value borrows from the type field:

	0x40000000 - 0x200 = 0x3FFFFE00

and writes back a bogus ~1GB extent:

	lb=65, len=0x3FFFFE00

which now overlaps the real free extent "lb=65, len=31 blocks".  The next
allocation with goal=64 picks the bogus extent first (both start at 65,
the bogus one is encountered first and the search keeps the first
minimum), so lb=65 is handed out and the bogus extent advances to lb=66.

lb=65 is now the file's current AED.  After 60 short_ad descriptors are
appended to it, epos->offset is 504 and aed->lengthAllocDescs is 480.
The next append enters udf_setup_indirect_aext().  The allocation goal is
65, and now the real extent "lb=65, len=31 blocks" wins: the goal lies
inside it (distance 0), while the bogus extent has advanced to lb=66
(distance 1).  udf_new_block() therefore returns 65 again, i.e. the block
that is already the current AED.

udf_setup_indirect_aext() then does:

	sb_getblk() returns the buffer of the current AED
	memset(bh->b_data, 0, sb->s_blocksize)
	  -> clears aed->lengthAllocDescs

Because epos->offset is still 504, the block is not considered full
(504 + 8 <= 512) and the else branch calls __udf_add_aext(), which hits

	WARN_ON(aed->lengthAllocDescs !=
		epos->offset - sizeof(struct allocExtDesc))

with lengthAllocDescs == 0 and offset == 504, i.e. 0 != 480.  The AED
contents are destroyed and the filesystem is corrupted.

This is reachable by mounting a table-based UDF image with
adinicb,shortad and writing at a very large offset: the write creates a
huge hole, fills the in-ICB allocation descriptor area and then
allocates many AED blocks from the unallocated space table.

Reproducer is https://syzkaller.appspot.com/x/repro.c?x=16562e75180000.

Fix it by testing only the extent length field.

Fixes: 1da177e ("Linux-2.6.12-rc2")
Reported-by: syzbot+799a0e744ac47f928024@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=799a0e744ac47f928024
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants