Linux filesystem development
 help / color / mirror / Atom feed
* [PATCH v2 0/4] udf: fix double allocation from the unallocated space table
@ 2026-10-30 14:05 Matthias Goergens
  2026-10-30 14:05 ` [PATCH v2 1/4] udf: don't let the extent type hide an exhausted free-space table extent Matthias Goergens
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Matthias Goergens @ 2026-10-30 14:05 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-fsdevel, linux-kernel, syzbot+799a0e744ac47f928024,
	syzbot+43fc5ba6dcb33e3261ca, syzkaller-bugs

Hi Honza,

Patch 3 now fixes udf_discard_prealloc() the way you suggested, and
udf_next_aext() is left alone.  Its Fixes: tag now names the commit that
added the peek into eloc and elen there.  The two other users that v1
covered through udf_next_aext(), both walks of the unallocated space
table, are fixed directly in patch 4.  The code in patch 1 is unchanged,
and patch 2 only rewords one code comment.  The messages now make the
mount and stable-backport consequences explicit.

A filesystem made with "mkudffs --space=unalloctable" hands out blocks
that are still in use.

Patch 1: udf_table_new_block() keeps the extent type in the length
word, so an exhausted type-1 table extent (the type mkudffs writes)
survives as a zero-length extent pointing at an in-use block, and the
next allocation from it underflows into about a gigabyte of "free"
space.  Filling a fresh 1 MiB image with empty files is enough to
overwrite the reserve VDS and the backup anchor.  This is the double
allocation behind the two syzbot reports linked in the patch.

Patch 2 checks the table once at mount and refuses read-write access
if it is damaged, for example by the bug in patch 1.

Patches 1 and 2 should be taken together.  Patch 1 prevents the
allocator from creating new empty extents.  Patch 2 rejects an empty
entry left by an old kernel before it can be consumed again.  It also
rejects extents outside the partition.  It cannot detect every possible
overlap in a table that is already damaged.

Patch 3: when a file's extent list ends in an empty allocation extent,
udf_discard_prealloc() frees that allocation extent's block a second
time instead of the preallocated blocks.  On a table the block is then
handed out twice, and fsx, as run by generic/091 and generic/263,
reads back bad data, with or without patches 1 and 2.  On a bitmap the
preallocated blocks leak.

Patch 4: udf_table_prealloc_blocks() can hand out the block of an
empty allocation extent at the end of the table.  With patch 3 alone,
the reproducer below patch 3 passes, but generic/091 or 263 failed in
8 of 34 runs on the 2 October base.  udf_table_free_blocks() can
write a partition reference it never set; that one I have not
reproduced.

One more user is left as it is: udf_fiiter_add_entry() takes the last
extent of a directory from iter->eloc and iter->elen after inode_bmap()
has returned 0 in udf_fiiter_init().  That goes wrong only if the
directory's extent list ends in an empty allocation extent.  The kernel
does not create one for a directory: udf_bread() passes
UDF_MAP_NOPREALLOC when it allocates directory blocks.  Normal updates
to a live directory do not shrink its extent list.

On the 2 October base (4ad934e7a85e), with the whole series, one run of
fstests -g quick per allocation type (2 GiB images made with
--space=unalloctable or --space=unallocbitmap; KASAN, UBSAN and lockdep
enabled) passed generic/091 and generic/263 on a table.  The remaining
failures (generic/131, 360, 563, 634 and 777, and a hang in generic/346)
also appeared without the series and on a bitmap, where the series
changed no result in those runs.  The reproducers are below the --- of
patches 1 and 3, and the runs behind patch 4 below its own.

Thanks,
Matthias


---
v1:
https://lore.kernel.org/r/20261002042627.1677228-1-matthias.goergens@gmail.com/

For stable: patches 2, 3 and 4 build on the int return of udf_next_aext()
(b405c1e58b73, v6.12, also backported to 6.6.y) and apply as they are
to 6.6.y and later.  Patches 1 and 2 should be backported together.

Refreshed onto jack/linux-fs for_next as of 30 October.  The four code
diffs are unchanged; the messages include the clarifications above.

I repeated the inline fsx reproducer on this base and the whole series,
with KASAN, UBSAN and lockdep enabled.  On a table, both fresh base
VMs read back bad data at operation 9852; both fixed VMs completed all
10000 operations.  On a bitmap, one fresh VM with each kernel completed
all operations.  In one fresh-image generic/091 and generic/263 group
per kernel, both tests read back bad data on base and both passed with
the series.  No kernel report appeared in these runs.

On the 30 September base, the unchanged patch 2 code also rejected a
crafted looping allocation-extent chain, forced read-only fallback when
the table location held a file entry, and let SIGKILL interrupt the walk
when a test-only knob disabled cycle detection.  Those malformed-image
checks were not repeated on the 30 October base.


Matthias Goergens (4):
  udf: don't let the extent type hide an exhausted free-space table
    extent
  udf: check the unallocated space table when it is loaded
  udf: fix preallocation discarding before an empty allocation extent
  udf: don't use eloc after the end of the unallocated space table

 fs/udf/balloc.c   | 11 ++++---
 fs/udf/super.c    | 80 ++++++++++++++++++++++++++++++++++++++++++++++-
 fs/udf/truncate.c |  7 +++--
 3 files changed, 90 insertions(+), 8 deletions(-)


base-commit: a344542d9f26d44a5ebf597220ccac84c536467e
-- 
2.56.0


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

* [PATCH v2 1/4] udf: don't let the extent type hide an exhausted free-space table extent
  2026-10-30 14:05 [PATCH v2 0/4] udf: fix double allocation from the unallocated space table Matthias Goergens
@ 2026-10-30 14:05 ` Matthias Goergens
  2026-10-30 14:05 ` [PATCH v2 2/4] udf: check the unallocated space table when it is loaded Matthias Goergens
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Matthias Goergens @ 2026-10-30 14:05 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-fsdevel, linux-kernel, syzbot+799a0e744ac47f928024,
	syzbot+43fc5ba6dcb33e3261ca, syzkaller-bugs

udf_table_new_block() takes the first block of the free-space table
extent closest to the goal.  It keeps that extent's type and length
together in goal_elen, subtracts one block, and deletes the extent only
if goal_elen then reaches zero.  UDF 2.60 section 2.3.7.1 requires the
free-space extents of an Unallocated Space Entry to be of type 1
(allocated but not recorded), and mkudffs writes them that way.  For
such an extent the type bits keep goal_elen non-zero, so taking its last
block leaves a type-1 extent of length zero behind, starting at the
block after the extent, which is in use.

The next allocation that picks this empty extent returns that in-use
block.  Subtracting a block from 0x40000000 then borrows from the type
bits, and the extent becomes type 0 with a length of 2^30 - blocksize:
about a gigabyte of "free" space overlapping live metadata, the other
table extents and whatever lies behind the partition.  From then on
blocks are handed out twice, or past the end of the partition.

Extents that udf_table_free_blocks() adds are type 0, where the check
works, so only tables written by mkudffs or by another implementation
are affected.  Nothing more than filling such a filesystem is needed:
on a fresh 1 MiB "mkudffs --space=unalloctable" image, creating empty
files until the partition is full writes file entries over the reserve
volume descriptor sequence and the backup anchor, and df then reports a
negative amount of used space.

On syzbot's images the same double allocation is what the two reports
below trip over.  In the first, ftruncate() extends a new file with
enough hole extents to need a chain of allocation extent descriptors;
one AED is placed on another inode's file entry, and a later one is
placed on the block of the AED currently being filled, which
udf_setup_indirect_aext() zeroes, so __udf_add_aext() finds
lengthAllocDescs out of step with its cursor.  In the second, an AED
is placed past the end of the device, sb_getblk() fails, and the error
path of udf_do_extend_file() calls udf_truncate_extents() on an extent
list that no longer covers i_size.

Keep the type separately from the length, as udf_table_prealloc_blocks()
already does.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Reported-by: syzbot+799a0e744ac47f928024@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=799a0e744ac47f928024
Reported-by: syzbot+43fc5ba6dcb33e3261ca@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=43fc5ba6dcb33e3261ca
Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com>
---
Reproducer, with mkudffs from udftools:

  truncate --size=1M udf.img
  mkudffs --blocksize=512 --space=unalloctable udf.img
  mount -t udf -o loop udf.img /mnt
  mkdir /mnt/d
  i=0; while touch /mnt/d/f$i 2> /dev/null; do i=$((i + 1)); done
  df /mnt
  umount /mnt
  dd if=udf.img bs=512 skip=2047 count=1 | od -A n -t u2 -N 2

Without this patch df shows a negative used count, and the last block,
the backup anchor (tag identifier 2), now holds an extended file entry
(266).  With it, file creation stops when the partition is full, df
shows it 100% used, and the anchor is intact.

 fs/udf/balloc.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c
index 30cec56..2ec577b 100644
--- a/fs/udf/balloc.c
+++ b/fs/udf/balloc.c
@@ -572,7 +572,7 @@ static udf_pblk_t udf_table_new_block(struct super_block *sb,
 	uint32_t elen, goal_elen = 0;
 	struct kernel_lb_addr eloc, goal_eloc;
 	struct extent_position epos, goal_epos;
-	int8_t etype;
+	int8_t etype, goal_etype = 0;
 	struct udf_inode_info *iinfo = UDF_I(table);
 	int ret = 0;
 
@@ -623,7 +623,8 @@ static udf_pblk_t udf_table_new_block(struct super_block *sb,
 			goal_epos.block = epos.block;
 			goal_epos.offset = epos.offset - adsize;
 			goal_eloc = eloc;
-			goal_elen = (etype << 30) | elen;
+			goal_elen = elen;
+			goal_etype = etype;
 		}
 	}
 
@@ -647,7 +648,8 @@ static udf_pblk_t udf_table_new_block(struct super_block *sb,
 	goal_elen -= sb->s_blocksize;
 
 	if (goal_elen)
-		udf_write_aext(table, &goal_epos, &goal_eloc, goal_elen, 1);
+		udf_write_aext(table, &goal_epos, &goal_eloc,
+			       (goal_etype << 30) | goal_elen, 1);
 	else
 		udf_delete_aext(table, goal_epos, &freed);
 	brelse(goal_epos.bh);
-- 
2.56.0


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

* [PATCH v2 2/4] udf: check the unallocated space table when it is loaded
  2026-10-30 14:05 [PATCH v2 0/4] udf: fix double allocation from the unallocated space table Matthias Goergens
  2026-10-30 14:05 ` [PATCH v2 1/4] udf: don't let the extent type hide an exhausted free-space table extent Matthias Goergens
@ 2026-10-30 14:05 ` Matthias Goergens
  2026-10-30 14:05 ` [PATCH v2 3/4] udf: fix preallocation discarding before an empty allocation extent Matthias Goergens
  2026-10-30 14:05 ` [PATCH v2 4/4] udf: don't use eloc after the end of the unallocated space table Matthias Goergens
  3 siblings, 0 replies; 5+ messages in thread
From: Matthias Goergens @ 2026-10-30 14:05 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-fsdevel, linux-kernel, syzbot+799a0e744ac47f928024,
	syzbot+43fc5ba6dcb33e3261ca, syzkaller-bugs

udf_table_new_block() hands out the first block of the extent closest
to its goal and trusts that the extent covers at least one whole block
inside the partition.  Nothing checks that.  A zero-length extent makes
it return the block the extent points at, which need not be free, and
the subtraction of one block then underflows the length into the type
bits, turning the entry into a bogus continuation.  An extent running
past the end of the partition makes it return blocks behind the
partition.

Such tables are not only found on crafted images: until the previous
commit, udf_table_new_block() itself left zero-length extents behind
in tables written by mkudffs, and then converted them into extents
running past the partition.

Check every extent once when the table is loaded, and refuse
read-write access if one is empty, is not a whole number of blocks, or
does not lie inside the partition, in the same way as for other
allocation information the kernel cannot use.  Check first that the
table is an Unallocated Space Entry at all: the walk starts at the
offset of the allocation descriptors in one, and for a file entry that
offset lies before the inode's in-memory copy of the descriptors.
Treat a chain of allocation extent descriptors that leads back to
itself the same way: the walk would otherwise return the same extents
forever, and the mount would never finish.  Let a fatal signal
interrupt the walk and fail the mount.

Refusing read-write access to an empty extent is deliberate.  It shows
that an old kernel took the last block but left the entry behind.  If
consumed again, the entry returns an in-use block and turns into a huge
bogus extent.  Refusing the table before that
happens avoids this second allocation; the filesystem can still be
mounted read-only.  The check cannot detect every overlap that may
already exist in a damaged table.

This patch should be backported together with the previous one: that
patch stops creating new empty extents, and this one rejects existing
ones before they are consumed again.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com>
---
 fs/udf/super.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 79 insertions(+), 1 deletion(-)

diff --git a/fs/udf/super.c b/fs/udf/super.c
index 2335c31..a81a850 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -1106,6 +1106,73 @@ force_ro:
 	return 0;
 }
 
+/*
+ * Check the Unallocated Space Table once when it is loaded: the allocator
+ * hands out blocks from the start of each extent and trusts that every
+ * extent covers at least one whole block inside the partition.
+ */
+static int udf_check_unalloc_table(struct super_block *sb,
+				   struct inode *table, u32 partition_len)
+{
+	struct extent_position epos = {
+		.block = UDF_I(table)->i_location,
+		.offset = sizeof(struct unallocSpaceEntry),
+	};
+	struct kernel_lb_addr eloc;
+	uint32_t elen, blocks;
+	struct kernel_lb_addr seen_block = {};
+	uint32_t seen_offset = 0;
+	u64 steps = 0, period = 1;
+	int8_t etype;
+	int ret;
+
+	/* The walk below assumes the layout of an Unallocated Space Entry */
+	if (!UDF_I(table)->i_use) {
+		udf_err(sb, "unallocated space table is not an unallocated space entry\n");
+		return -EFSCORRUPTED;
+	}
+
+	while ((ret = udf_next_aext(table, &epos, &eloc, &elen, &etype, 1)) > 0) {
+		blocks = elen >> sb->s_blocksize_bits;
+		if (!blocks || (elen & (sb->s_blocksize - 1)) ||
+		    eloc.logicalBlockNum >= partition_len ||
+		    blocks > partition_len - eloc.logicalBlockNum) {
+			udf_err(sb, "invalid unallocated space table extent (block %u, length %u)\n",
+				eloc.logicalBlockNum, elen);
+			ret = -EFSCORRUPTED;
+			break;
+		}
+		/*
+		 * A chain of allocation extents can lead back to itself, and
+		 * then the walk returns the same extents forever.  Remember a
+		 * position at doubling intervals; a walk that comes back to it
+		 * is in a loop.
+		 */
+		if (epos.block.logicalBlockNum == seen_block.logicalBlockNum &&
+		    epos.block.partitionReferenceNum ==
+				seen_block.partitionReferenceNum &&
+		    epos.offset == seen_offset) {
+			udf_err(sb, "unallocated space table loops back on itself\n");
+			ret = -EFSCORRUPTED;
+			break;
+		}
+		if (++steps == period) {
+			seen_block = epos.block;
+			seen_offset = epos.offset;
+			steps = 0;
+			period <<= 1;
+		}
+		if (fatal_signal_pending(current)) {
+			udf_err(sb, "interrupted while checking the unallocated space table\n");
+			ret = -EINTR;
+			break;
+		}
+		cond_resched();
+	}
+	brelse(epos.bh);
+	return ret;
+}
+
 static int udf_fill_partdesc_info(struct super_block *sb,
 		struct partitionDesc *p, int p_index)
 {
@@ -1166,6 +1233,16 @@ static int udf_fill_partdesc_info(struct super_block *sb,
 				  p_index);
 			return PTR_ERR(inode);
 		}
+		err = udf_check_unalloc_table(sb, inode, map->s_partition_len);
+		if (err) {
+			iput(inode);
+			if (err == -EINTR)
+				return err;
+			if (!sb_rdonly(sb))
+				return -EACCES;
+			UDF_SET_FLAG(sb, UDF_FLAG_RW_INCOMPAT);
+			return 0;
+		}
 		map->s_uspace.s_table = inode;
 		map->s_partition_flags |= UDF_PART_FLAG_UNALLOC_TABLE;
 		udf_debug("unallocSpaceTable (part %d) @ %llu\n",
@@ -2243,8 +2320,9 @@ static int udf_fill_super(struct super_block *sb, struct fs_context *fc)
 				/*
 				 * EACCES is special - we want to propagate to
 				 * upper layers that we cannot handle RW mount.
+				 * EINTR means that a fatal signal is pending.
 				 */
-				if (ret == -EACCES)
+				if (ret == -EACCES || ret == -EINTR)
 					break;
 			} else
 				break;
-- 
2.56.0


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

* [PATCH v2 3/4] udf: fix preallocation discarding before an empty allocation extent
  2026-10-30 14:05 [PATCH v2 0/4] udf: fix double allocation from the unallocated space table Matthias Goergens
  2026-10-30 14:05 ` [PATCH v2 1/4] udf: don't let the extent type hide an exhausted free-space table extent Matthias Goergens
  2026-10-30 14:05 ` [PATCH v2 2/4] udf: check the unallocated space table when it is loaded Matthias Goergens
@ 2026-10-30 14:05 ` Matthias Goergens
  2026-10-30 14:05 ` [PATCH v2 4/4] udf: don't use eloc after the end of the unallocated space table Matthias Goergens
  3 siblings, 0 replies; 5+ messages in thread
From: Matthias Goergens @ 2026-10-30 14:05 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-fsdevel, linux-kernel, syzbot+799a0e744ac47f928024,
	syzbot+43fc5ba6dcb33e3261ca, syzkaller-bugs

udf_discard_prealloc() finds the last extent of a file by peeking at
the next one with udf_next_aext() and stopping when that returns 0.
The peek reads into eloc and elen, and the code then uses them as the
last extent.  But when udf_next_aext() returns 0 they hold no defined
value: if the extent list ends in a continuation descriptor that leads
to an empty allocation extent, they describe that continuation, i.e.
the block of the empty allocation extent, one block long.

The kernel creates such lists itself: when udf_delete_aext() removes
the only descriptor of the last allocation extent in a list, it leaves
that allocation extent empty and the continuation to it in place.

If the last extent is a preallocation, udf_discard_prealloc() deletes
it with udf_delete_aext(), which finds the following allocation extent
empty, so it also removes the continuation and frees that allocation
extent's block.  udf_discard_prealloc() then frees eloc and elen: that
block a second time, instead of the preallocated blocks.  On a space
bitmap the preallocated blocks leak and the free block count drifts.
On an unallocated space table the second free adds a second free
extent for the same block, which is later handed out twice: fsx as run
by generic/091 and generic/263 ends up with two parts of its test file
in one block and reads back bad data.

Peek into separate variables, so that eloc and elen keep the last
extent that udf_next_aext() returned with 1.

Fixes: cfe4c1b25dd6 ("udf: Fix preallocation discarding at indirect extent boundary")
Cc: stable@vger.kernel.org
Suggested-by: Jan Kara <jack@suse.cz>
Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com>
---
Reproducer, with fsx from fstests and mkudffs from udftools:

  truncate --size=2G udf.img
  mkudffs --blocksize=512 --space=unalloctable udf.img
  mount -t udf -o loop udf.img /mnt
  fsx -N 10000 -l 500000 -r 4096 -t 512 -w 512 -Z -R -W /mnt/junk

On the 2 October for_next base (4ad934e7a85e), fsx stopped with READ
BAD DATA after 9852 operations in five runs out of five; with this
patch alone, all 10000 operations completed in five runs out of
five.  With --space=unallocbitmap fsx completed either way.

 fs/udf/truncate.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c
index 0990f94..75151a5 100644
--- a/fs/udf/truncate.c
+++ b/fs/udf/truncate.c
@@ -124,8 +124,8 @@ void udf_discard_prealloc(struct inode *inode)
 {
 	struct extent_position epos = {};
 	struct extent_position prev_epos = {};
-	struct kernel_lb_addr eloc;
-	uint32_t elen;
+	struct kernel_lb_addr eloc, tmpeloc;
+	uint32_t elen, tmpelen;
 	uint64_t lbcount = 0;
 	int8_t etype = -1;
 	struct udf_inode_info *iinfo = UDF_I(inode);
@@ -141,7 +141,8 @@ void udf_discard_prealloc(struct inode *inode)
 
 	/* Find the last extent in the file */
 	while (1) {
-		ret = udf_next_aext(inode, &epos, &eloc, &elen, &tmpetype, 0);
+		ret = udf_next_aext(inode, &epos, &tmpeloc, &tmpelen,
+				    &tmpetype, 0);
 		if (ret < 0)
 			goto out;
 		if (ret == 0)
-- 
2.56.0


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

* [PATCH v2 4/4] udf: don't use eloc after the end of the unallocated space table
  2026-10-30 14:05 [PATCH v2 0/4] udf: fix double allocation from the unallocated space table Matthias Goergens
                   ` (2 preceding siblings ...)
  2026-10-30 14:05 ` [PATCH v2 3/4] udf: fix preallocation discarding before an empty allocation extent Matthias Goergens
@ 2026-10-30 14:05 ` Matthias Goergens
  3 siblings, 0 replies; 5+ messages in thread
From: Matthias Goergens @ 2026-10-30 14:05 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-fsdevel, linux-kernel, syzbot+799a0e744ac47f928024,
	syzbot+43fc5ba6dcb33e3261ca, syzkaller-bugs

When udf_next_aext() returns 0, its eloc, elen and etype outputs hold
no defined value.  Two walks of the unallocated space table still use
eloc after the walk has reached the end of the table.

udf_table_prealloc_blocks() looks for a free extent starting at
first_block and decides whether it found one by comparing eloc with
first_block after the walk.  If the table's list ends in a continuation
descriptor that leads to an empty allocation extent, eloc is then the
block of that allocation extent, which is in use, and if that is
first_block, the block is handed out as preallocation.  Once
udf_discard_prealloc() no longer frees the wrong block, this is what
still makes fsx in generic/091 and generic/263 read back bad data on a
table, in about one run in four.  Only take the extent if the walk
stopped at it.

When udf_table_free_blocks() cannot merge the freed blocks into an
existing extent, it appends a new one and sets only its block number,
so the partition reference comes from whatever eloc held after the
walk, and with long allocation descriptors it is written to disk.  For
a table without extents, eloc was never set.  Take the partition
reference from the freed blocks instead.  Short allocation descriptors
do not store the partition reference, which is why this half remains a
latent long-descriptor fix rather than a reproduced failure.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com>
---
Measured on 2-3 October on the for_next base 4ad934e7a85e by running
fstests generic/075, 091 and 263 as a group, each run on a fresh 2 GiB
"mkudffs --space=unalloctable" test filesystem.  With patch 3 alone, 8
of 34 runs read back bad data in 091 or 263.  In 32 of these runs a
temporary debug message (not part of this series) reported when the
comparison above matched after the walk had reached the end of the
table; it fired in all 7 failing runs among them, each time on a
continuation descriptor (type 3, one block long), and in one passing
run.  With patches 3 and 4, none of 18 runs failed.  The
udf_table_free_blocks() case has not been reproduced.

 fs/udf/balloc.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c
index 2ec577b..7512418 100644
--- a/fs/udf/balloc.c
+++ b/fs/udf/balloc.c
@@ -457,6 +457,7 @@ static void udf_table_free_blocks(struct super_block *sb,
 
 		int adsize;
 
+		eloc.partitionReferenceNum = bloc->partitionReferenceNum;
 		eloc.logicalBlockNum = start;
 		elen = EXT_RECORDED_ALLOCATED |
 			(count << sb->s_blocksize_bits);
@@ -532,7 +533,7 @@ static int udf_table_prealloc_blocks(struct super_block *sb,
 			  eloc.logicalBlockNum, elen, first_block);
 	}
 
-	if (first_block == eloc.logicalBlockNum) {
+	if (ret > 0 && first_block == eloc.logicalBlockNum) {
 		epos.offset -= adsize;
 
 		alloc_count = (elen >> sb->s_blocksize_bits);
-- 
2.56.0


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

end of thread, other threads:[~2026-10-30 14:05 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-30 14:05 [PATCH v2 0/4] udf: fix double allocation from the unallocated space table Matthias Goergens
2026-10-30 14:05 ` [PATCH v2 1/4] udf: don't let the extent type hide an exhausted free-space table extent Matthias Goergens
2026-10-30 14:05 ` [PATCH v2 2/4] udf: check the unallocated space table when it is loaded Matthias Goergens
2026-10-30 14:05 ` [PATCH v2 3/4] udf: fix preallocation discarding before an empty allocation extent Matthias Goergens
2026-10-30 14:05 ` [PATCH v2 4/4] udf: don't use eloc after the end of the unallocated space table Matthias Goergens

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