CVE-2025-21932

Severity CVSS v4.0:
Pending analysis
Type:
Unavailable / Other
Publication date:
01/04/2025
Last modified:
01/04/2025

Description

In the Linux kernel, the following vulnerability has been resolved:<br /> <br /> mm: abort vma_modify() on merge out of memory failure<br /> <br /> The remainder of vma_modify() relies upon the vmg state remaining pristine<br /> after a merge attempt.<br /> <br /> Usually this is the case, however in the one edge case scenario of a merge<br /> attempt failing not due to the specified range being unmergeable, but<br /> rather due to an out of memory error arising when attempting to commit the<br /> merge, this assumption becomes untrue.<br /> <br /> This results in vmg-&gt;start, end being modified, and thus the proceeding<br /> attempts to split the VMA will be done with invalid start/end values.<br /> <br /> Thankfully, it is likely practically impossible for us to hit this in<br /> reality, as it would require a maple tree node pre-allocation failure that<br /> would likely never happen due to it being &amp;#39;too small to fail&amp;#39;, i.e. the<br /> kernel would simply keep retrying reclaim until it succeeded.<br /> <br /> However, this scenario remains theoretically possible, and what we are<br /> doing here is wrong so we must correct it.<br /> <br /> The safest option is, when this scenario occurs, to simply give up the<br /> operation. If we cannot allocate memory to merge, then we cannot allocate<br /> memory to split either (perhaps moreso!).<br /> <br /> Any scenario where this would be happening would be under very extreme<br /> (likely fatal) memory pressure, so it&amp;#39;s best we give up early.<br /> <br /> So there is no doubt it is appropriate to simply bail out in this<br /> scenario.<br /> <br /> However, in general we must if at all possible never assume VMG state is<br /> stable after a merge attempt, since merge operations update VMG fields. <br /> As a result, additionally also make this clear by storing start, end in<br /> local variables.<br /> <br /> The issue was reported originally by syzkaller, and by Brad Spengler (via<br /> an off-list discussion), and in both instances it manifested as a<br /> triggering of the assert:<br /> <br /> VM_WARN_ON_VMG(start &gt;= end, vmg);<br /> <br /> In vma_merge_existing_range().<br /> <br /> It seems at least one scenario in which this is occurring is one in which<br /> the merge being attempted is due to an madvise() across multiple VMAs<br /> which looks like this:<br /> <br /> start end<br /> ||<br /> |----------|------|<br /> | vma | next |<br /> |----------|------|<br /> <br /> When madvise_walk_vmas() is invoked, we first find vma in the above<br /> (determining prev to be equal to vma as we are offset into vma), and then<br /> enter the loop.<br /> <br /> We determine the end of vma that forms part of the range we are<br /> madvise()&amp;#39;ing by setting &amp;#39;tmp&amp;#39; to this value:<br /> <br /> /* Here vma-&gt;vm_start vm_end;<br /> <br /> We then invoke the madvise() operation via visit(), letting prev get<br /> updated to point to vma as part of the operation:<br /> <br /> /* Here vma-&gt;vm_start start, end get set to perhaps<br /> unintuitive values - we intended to shrink the middle VMA and expand the<br /> next.<br /> <br /> This means vmg-&gt;start, end are set to... vma-&gt;vm_start, start.<br /> <br /> Now the commit_merge() fails, and vmg-&gt;start, end are left like this. <br /> This means we return to the rest of vma_modify() with vmg-&gt;start, end<br /> (here denoted as start&amp;#39;, end&amp;#39;) set as:<br /> <br /> start&amp;#39; end&amp;#39;<br /> ||<br /> |----------|------|<br /> | vma | next |<br /> |----------|------|<br /> <br /> So we now erroneously try to split accordingly. This is where the<br /> unfortunate<br /> ---truncated---

Impact