PT-2026-90209 · Linux · Linux
CVE-2026-89493
·
Published
2026-09-11
·
Updated
2026-09-11
None
No severity ratings or metrics are available. When they are, we'll update the corresponding info on the page.
In the Linux kernel, the following vulnerability has been resolved:
ocfs2: validate rl used against rl count in refcount block validator
ocfs2 find refcount rec in rl() walks the on-disk refcount record array
with:
for (; i < le16 to cpu(rb->rf records.rl used); i++) {
rec = &rb->rf records.rl recs[i];
...rl recs[] lives in a single metadata block (4096 bytes on the common
configuration), so its real capacity is fixed by
ocfs2 refcount recs per rb(sb) (247 records for a 4K block with the
16-byte ocfs2 refcount rec). rl used and rl count are both read directly
off disk by ocfs2 validate refcount block() and are never checked against
that capacity, nor against each other, before any refcount/reflink/CoW
operation walks the array.
A crafted (or corrupted) refcount block with rl used == 0xffff makes the
loop above walk far past the end of the block, dereferencing rl recs[i]
for i up to 65534. The resulting index is then handed to the sibling
ocfs2 insert refcount rec(), whose insert-shift does:
if (index < le16 to cpu(rf list->rl used))
memmove(&rf list->rl recs[index + 1],
&rf list->rl recs[index],
(le16 to cpu(rf list->rl used) - index) *
sizeof(struct ocfs2 refcount rec));i.e. a memmove() of up to (0xffff - index) * 16 bytes (~1 MiB) from an
offset already past the block. This is reachable from an ordinary reflink
(FICLONE) against a crafted/corrupted ocfs2 image: attaching an extent
whose cpos sorts past every real record in the leaf forces the lookup to
run off the end instead of returning early on a match. The attacker model
is local: CAP SYS ADMIN mounting a crafted or corrupted ocfs2 image, or a
raw write to the block device backing an already-mounted ocfs2 filesystem.
ocfs2 validate refcount block() already validates the block's ECC,
signature, rf blkno and rf fs generation, but never rl count/rl used
against the block's actual on-disk capacity. This is the same class of
gap that ocfs2 validate extent block() (fs/ocfs2/alloc.c) already closes
for the sibling extent-list header, which checks both the record capacity
and the "used" bound before any code walks h list.l recs[]:
if (le16 to cpu(eb->h list.l count) != ocfs2 extent recs per eb(sb)) {
rc = ocfs2 error(...);
goto bail;
}
if (le16 to cpu(eb->h list.l next free rec) >
le16 to cpu(eb->h list.l count)) {
rc = ocfs2 error(...);
goto bail;
}Add the equivalent pair of checks to ocfs2 validate refcount block():
reject a refcount block whose rl count does not match the fixed per-block
capacity returned by ocfs2 refcount recs per rb(), and reject rl used >
rl count. Both checks are skipped when OCFS2 REFCOUNT TREE FL is set,
because in that case the same union bytes hold an ocfs2 extent list
(rf list), not the refcount record list (rf records) -- that layout is
already validated separately by ocfs2 validate extent block() when the
referenced extent block is read. This mirrors the existing
"!(rb->rf flags & OCFS2 REFCOUNT TREE FL)" guard used elsewhere in this
file (e.g. ocfs2 get refcount rec()) to decide whether rf records or
rf list is the live member of the union.
With this in place, a forged rl used/rl count is caught at block
validation time (ocfs2 error()), consistent with every other corruption
check in this function, instead of driving an out-of-bounds read in
ocfs2 find refcount rec in rl() and a subsequent out-of-bounds memmove()
in ocfs2 insert refcount rec().
Verified against a crafted image on a v6.19 KASAN (KASAN GENERIC) build:
replaying the same reflink (FICLONE) reliably hit a KASAN report in
ocfs2 increase refcount()/ocfs2 insert refcount rec() before this patch,
and triggers no report once ocfs2 validate refcount block() rejects the
forged rl used/rl count.
Found an issue in the description? Have something to add? Feel free to write us 👾
Related Identifiers
Affected Products
Linux