CVE-2024-26960

Severity CVSS v4.0:
Pending analysis
Type:
CWE-362 Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')
Publication date:
01/05/2024
Last modified:
20/03/2025

Description

In the Linux kernel, the following vulnerability has been resolved:<br /> <br /> mm: swap: fix race between free_swap_and_cache() and swapoff()<br /> <br /> There was previously a theoretical window where swapoff() could run and<br /> teardown a swap_info_struct while a call to free_swap_and_cache() was<br /> running in another thread. This could cause, amongst other bad<br /> possibilities, swap_page_trans_huge_swapped() (called by<br /> free_swap_and_cache()) to access the freed memory for swap_map.<br /> <br /> This is a theoretical problem and I haven&amp;#39;t been able to provoke it from a<br /> test case. But there has been agreement based on code review that this is<br /> possible (see link below).<br /> <br /> Fix it by using get_swap_device()/put_swap_device(), which will stall<br /> swapoff(). There was an extra check in _swap_info_get() to confirm that<br /> the swap entry was not free. This isn&amp;#39;t present in get_swap_device()<br /> because it doesn&amp;#39;t make sense in general due to the race between getting<br /> the reference and swapoff. So I&amp;#39;ve added an equivalent check directly in<br /> free_swap_and_cache().<br /> <br /> Details of how to provoke one possible issue (thanks to David Hildenbrand<br /> for deriving this):<br /> <br /> --8try_to_unuse() will stop as soon as soon as si-&gt;inuse_pages==0.<br /> <br /> So the question is: could someone reclaim the folio and turn<br /> si-&gt;inuse_pages==0, before we completed swap_page_trans_huge_swapped().<br /> <br /> Imagine the following: 2 MiB folio in the swapcache. Only 2 subpages are<br /> still references by swap entries.<br /> <br /> Process 1 still references subpage 0 via swap entry.<br /> Process 2 still references subpage 1 via swap entry.<br /> <br /> Process 1 quits. Calls free_swap_and_cache().<br /> -&gt; count == SWAP_HAS_CACHE<br /> [then, preempted in the hypervisor etc.]<br /> <br /> Process 2 quits. Calls free_swap_and_cache().<br /> -&gt; count == SWAP_HAS_CACHE<br /> <br /> Process 2 goes ahead, passes swap_page_trans_huge_swapped(), and calls<br /> __try_to_reclaim_swap().<br /> <br /> __try_to_reclaim_swap()-&gt;folio_free_swap()-&gt;delete_from_swap_cache()-&gt;<br /> put_swap_folio()-&gt;free_swap_slot()-&gt;swapcache_free_entries()-&gt;<br /> swap_entry_free()-&gt;swap_range_free()-&gt;<br /> ...<br /> WRITE_ONCE(si-&gt;inuse_pages, si-&gt;inuse_pages - nr_entries);<br /> <br /> What stops swapoff to succeed after process 2 reclaimed the swap cache<br /> but before process1 finished its call to swap_page_trans_huge_swapped()?<br /> <br /> --8

Vulnerable products and versions

CPE From Up to
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 4.11 (including) 5.10.215 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 5.11 (including) 5.15.154 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 5.16 (including) 6.1.84 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 6.2 (including) 6.6.24 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 6.7 (including) 6.7.12 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 6.8 (including) 6.8.3 (excluding)
cpe:2.3:o:debian:debian_linux:10.0:*:*:*:*:*:*:*