CVE-2026-89493
nicheOut-of-bounds access in Linux kernel ocfs2 refcount block validation
The ocfs2 filesystem's refcount block validator (ocfs2_validate_refcount_block) fails to check the on-disk rl_used and rl_count fields against the block's fixed record capacity before refcount/reflink/CoW operations walk the record array. A crafted or corrupted refcount block with rl_used set to 0xffff causes ocfs2_find_refcount_rec_in_rl() to read far past the 4K metadata block and drives a memmove() of up to roughly 1 MiB from out-of-bounds memory in ocfs2_insert_refcount_rec(), an out-of-bounds read/write reachable via an ordinary reflink (FICLONE) against a malicious image. The attacker model is local: a user with CAP_SYS_ADMIN mounting a crafted ocfs2 image, or a raw write to the block device backing a mounted ocfs2 filesystem, so exploitation requires prior privileged local access and typically implies kernel memory corruption or a crash (with high confidentiality/integrity/impact potential per the assigned CVSS 8.8). Only systems that actually use the ocfs2 clustered filesystem are affected; the sibling extent-block validator already closes this class of gap, and the fix adds equivalent bounds checks to the refcount block validator. No public proof of concept is known, the flaw is not in CISA's KEV, and there is no evidence of exploitation in the wild.
What to do: Apply kernel updates that include the new rl_count/rl_used bounds checks in ocfs2_validate_refcount_block() once your vendor ships them. On hosts that never use OCFS2, blacklist or unload the ocfs2 module to eliminate the attack surface. Continue restricting mount privileges to trusted administrators, since the flaw requires CAP_SYS_ADMIN to mount a crafted image or raw write access to the backing device of a mounted ocfs2 filesystem.
| Linux kernel (ocfs2 filesystem) | — |
Order-of-magnitude estimate by the model from install counts, market share and public scan data it knows; verify before quoting.
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 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 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.
- Vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
In the news0 stories
No ingested article mentions this CVE yet.