{"id":21415,"date":"2026-09-12T04:31:09","date_gmt":"2026-09-11T20:31:09","guid":{"rendered":"https:\/\/www.54iter.cn\/?vuln=cve-2026-89493"},"modified":"2026-09-12T04:31:09","modified_gmt":"2026-09-11T20:31:09","slug":"cve-2026-89493","status":"publish","type":"vuln","link":"https:\/\/www.54iter.cn\/?vuln=cve-2026-89493","title":{"rendered":"CVE-2026-89493 &#8211; In the Linux kernel, the following vulnerability has been resolved:\n\nocfs2: validate rl_us"},"content":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>ocfs2: validate rl_used against rl_count in refcount block validator<\/p>\n<p>ocfs2_find_refcount_rec_in_rl() walks the on-disk refcount record array<br \/>\nwith:<\/p>\n<p>\tfor (; i rf_records.rl_used); i++) {<br \/>\n\t\trec = &amp;rb-&gt;rf_records.rl_recs[i];<br \/>\n\t\t&#8230;<\/p>\n<p>rl_recs[] lives in a single metadata block (4096 bytes on the common<br \/>\nconfiguration), so its real capacity is fixed by<br \/>\nocfs2_refcount_recs_per_rb(sb) (247 records for a 4K block with the<br \/>\n16-byte ocfs2_refcount_rec).  rl_used and rl_count are both read directly<br \/>\noff disk by ocfs2_validate_refcount_block() and are never checked against<br \/>\nthat capacity, nor against each other, before any refcount\/reflink\/CoW<br \/>\noperation walks the array.<\/p>\n<p>A crafted (or corrupted) refcount block with rl_used == 0xffff makes the<br \/>\nloop above walk far past the end of the block, dereferencing rl_recs[i]<br \/>\nfor i up to 65534.  The resulting index is then handed to the sibling<br \/>\nocfs2_insert_refcount_rec(), whose insert-shift does:<\/p>\n<p>\tif (index rl_used))<br \/>\n\t\tmemmove(&amp;rf_list-&gt;rl_recs[index + 1],<br \/>\n\t\t\t&amp;rf_list-&gt;rl_recs[index],<br \/>\n\t\t\t(le16_to_cpu(rf_list-&gt;rl_used) &#8211; index) *<br \/>\n\t\t\t sizeof(struct ocfs2_refcount_rec));<\/p>\n<p>i.e.  a memmove() of up to (0xffff &#8211; index) * 16 bytes (~1 MiB) from an<br \/>\noffset already past the block.  This is reachable from an ordinary reflink<br \/>\n(FICLONE) against a crafted\/corrupted ocfs2 image: attaching an extent<br \/>\nwhose cpos sorts past every real record in the leaf forces the lookup to<br \/>\nrun off the end instead of returning early on a match.  The attacker model<br \/>\nis local: CAP_SYS_ADMIN mounting a crafted or corrupted ocfs2 image, or a<br \/>\nraw write to the block device backing an already-mounted ocfs2 filesystem.<\/p>\n<p>ocfs2_validate_refcount_block() already validates the block&#8217;s ECC,<br \/>\nsignature, rf_blkno and rf_fs_generation, but never rl_count\/rl_used<br \/>\nagainst the block&#8217;s actual on-disk capacity.  This is the same class of<br \/>\ngap that ocfs2_validate_extent_block() (fs\/ocfs2\/alloc.c) already closes<br \/>\nfor the sibling extent-list header, which checks both the record capacity<br \/>\nand the &#8220;used&#8221; bound before any code walks h_list.l_recs[]:<\/p>\n<p>\tif (le16_to_cpu(eb-&gt;h_list.l_count) != ocfs2_extent_recs_per_eb(sb)) {<br \/>\n\t\trc = ocfs2_error(&#8230;);<br \/>\n\t\tgoto bail;<br \/>\n\t}<\/p>\n<p>\tif (le16_to_cpu(eb-&gt;h_list.l_next_free_rec) &gt;<br \/>\n\t    le16_to_cpu(eb-&gt;h_list.l_count)) {<br \/>\n\t\trc = ocfs2_error(&#8230;);<br \/>\n\t\tgoto bail;<br \/>\n\t}<\/p>\n<p>Add the equivalent pair of checks to ocfs2_validate_refcount_block():<br \/>\nreject a refcount block whose rl_count does not match the fixed per-block<br \/>\ncapacity returned by ocfs2_refcount_recs_per_rb(), and reject rl_used &gt;<br \/>\nrl_count.  Both checks are skipped when OCFS2_REFCOUNT_TREE_FL is set,<br \/>\nbecause in that case the same union bytes hold an ocfs2_extent_list<br \/>\n(rf_list), not the refcount record list (rf_records) &#8212; that layout is<br \/>\nalready validated separately by ocfs2_validate_extent_block() when the<br \/>\nreferenced extent block is read.  This mirrors the existing<br \/>\n&#8220;!(rb-&gt;rf_flags &amp; OCFS2_REFCOUNT_TREE_FL)&#8221; guard used elsewhere in this<br \/>\nfile (e.g.  ocfs2_get_refcount_rec()) to decide whether rf_records or<br \/>\nrf_list is the live member of the union.<\/p>\n<p>With this in place, a forged rl_used\/rl_count is caught at block<br \/>\nvalidation time (ocfs2_error()), consistent with every other corruption<br \/>\ncheck in this function, instead of driving an out-of-bounds read in<br \/>\nocfs2_find_refcount_rec_in_rl() and a subsequent out-of-bounds memmove()<br \/>\nin ocfs2_insert_refcount_rec().<\/p>\n<p>Verified against a crafted image on a v6.19 KASAN (KASAN_GENERIC) build:<br \/>\nreplaying the same reflink (FICLONE) reliably hit a KASAN report in<br \/>\n__ocfs2_increase_refcount()\/ocfs2_insert_refcount_rec() before this patch,<br \/>\nand triggers no report once ocfs2_validate_refcount_block() rejects the<br \/>\nforged rl_used\/rl_count.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>ocfs2: validate rl_used against rl_count in refcount block validator<\/p>\n<p>ocfs2_find_refcount_rec_in_rl() walks the on-disk refcount re<\/p>\n","protected":false},"template":"","meta":[],"categories":[],"tags":[],"class_list":["post-21415","vuln","type-vuln","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln\/21415","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln"}],"about":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/types\/vuln"}],"wp:attachment":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=21415"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=21415"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=21415"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}