In the Linux kernel, the following vulnerability has been resolved:
mm: userfaultfd: fix race of userfaultfd_move and swap cache
This commit fixes two kinds of races, they may have different results:
Barry reported a BUG_ON in commit c50f8e6053b0, we may see the same BUG_ON if the filemap lookup returned NULL and folio is added to swap cache after that.
If another kind of race is triggered (folio changed after lookup) we may see RSS counter is corrupted:
[ 406.893936] BUG: Bad rss-counter state mm:ffff0000c5a9ddc0 type:MM_ANONPAGES val:-1 [ 406.894071] BUG: Bad rss-counter state mm:ffff0000c5a9ddc0 type:MM_SHMEMPAGES val:1
Because the folio is being accounted to the wrong VMA.
I'm not sure if there will be any data corruption though, seems no. The issues above are critical already.
On seeing a swap entry PTE, userfaultfd_move does a lockless swap cache lookup, and tries to move the found folio to the faulting vma. Currently, it relies on checking the PTE value to ensure that the moved folio still belongs to the src swap entry and that no new folio has been added to the swap cache, which turns out to be unreliable.
While working and reviewing the swap table series with Barry, following existing races are observed and reproduced [1]:
In the example below, move_pages_pte is moving src_pte to dst_pte, where src_pte is a swap entry PTE holding swap entry S1, and S1 is not in the swap cache:
CPU1 CPU2 userfaultfd_move move_pages_pte() entry = pte_to_swp_entry(orig_src_pte); // Here it got entry = S1 ... < interrupted> ... <swapin src_pte, alloc and use folio A> // folio A is a new allocated folio // and get installed into src_pte <frees swap entry S1> // src_pte now points to folio A, S1 // has swap count == 0, it can be freed // by folio_swap_swap or swap // allocator's reclaim. <try to swap out another folio B> // folio B is a folio in another VMA. <put folio B to swap cache using S1 > // S1 is freed, folio B can use it // for swap out with no problem. ... folio = filemap_get_folio(S1) // Got folio B here !!! ... < interrupted again> ... <swapin folio B and free S1> // Now S1 is free to be used again. <swapout src_pte & folio A using S1> // Now src_pte is a swap entry PTE // holding S1 again. folio_trylock(folio) move_swap_pte double_pt_lock is_pte_pages_stable // Check passed because src_pte == S1 folio_move_anon_rmap(...) // Moved invalid folio B here !!!
The race window is very short and requires multiple collisions of multiple rare events, so it's very unlikely to happen, but with a deliberately constructed reproducer and increased time window, it can be reproduced easily.
This can be fixed by checking if the folio returned by filemap is the valid swap cache folio after acquiring the folio lock.
Another similar race is possible: filemap_get_folio may return NULL, but folio (A) could be swapped in and then swapped out again using the same swap entry after the lookup. In such a case, folio (A) may remain in the swap cache, so it must be moved too:
CPU1 CPU2 userfaultfd_move move_pages_pte() entry = pte_to_swp_entry(orig_src_pte); // Here it got entry = S1, and S1 is not in swap cache folio = filemap_get ---truncated---
CVSS Details
- CVSS 3.1 Base Score: 7.8
- CVSS 3.1 Vector: (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)
Covered by Rapid7
| Product | Vendor Advisory | Solution File | Added | Published |
|---|---|---|---|---|
| Amazon_linux_2023 | — | Upgrade kernel6.12-tools-develUpgrade perf6.12-debuginfoUpgrade kernel6.12-debuginfoUpgrade kernel6.12-develUpgrade kernel-livepatch-6.12.37-61.105Upgrade kernel6.12-debuginfo-common-x86_64Upgrade kernel6.12-headersUpgrade python3-perf6.12Upgrade kernel6.12-toolsUpgrade perf6.12Upgrade kernel6.12-modules-extraUpgrade kernel6.12-debuginfo-common-aarch64Upgrade kernel6.12-libbpfUpgrade bpftool6.12Upgrade python3-perf6.12-debuginfoUpgrade kernel6.12Upgrade kernel6.12-libbpf-staticUpgrade kernel6.12-modules-extra-commonUpgrade kernel6.12-libbpf-debuginfoUpgrade bpftool6.12-debuginfoUpgrade kernel6.12-tools-debuginfoUpgrade kernel6.12-libbpf-devel | Sep 9, 2025 | Jul 9, 2025 |
| Debian | — | Upgrade linux | Jul 23, 2026 | Jul 23, 2026 |
| Suse | — | Upgrade dtb-marvellUpgrade kernel-64kb-optionalUpgrade dlm-kmp-64kbUpgrade dtb-hisiliconUpgrade ocfs2-kmp-rtUpgrade kernel-sourceUpgrade gfs2-kmp-rtUpgrade kselftests-kmp-64kbUpgrade kernel-docs-htmlUpgrade dtb-apmUpgrade dtb-freescaleUpgrade kernel-zfcpdumpUpgrade kernel-obs-qaUpgrade kernel-64kbUpgrade dtb-alteraUpgrade dtb-amlogicUpgrade ocfs2-kmp-defaultUpgrade kernel-macrosUpgrade dtb-renesasUpgrade kernel-rt-optionalUpgrade dtb-amazonUpgrade dtb-amdUpgrade kernel-default-livepatchUpgrade gfs2-kmp-64kbUpgrade kernel-default-livepatch-develUpgrade dtb-caviumUpgrade kernel-kvmsmallUpgrade kernel-rt-livepatch-develUpgrade ocfs2-kmp-64kbUpgrade dtb-lgUpgrade kernel-symsUpgrade kernel-default-develUpgrade dtb-broadcomUpgrade kernel-64kb-develUpgrade kernel-default-extraUpgrade dtb-exynosUpgrade dtb-allwinnerUpgrade kernel-kvmsmall-vdsoUpgrade dtb-socionextUpgrade dtb-appleUpgrade kernel-kvmsmall-develUpgrade dtb-nvidiaUpgrade cluster-md-kmp-64kbUpgrade dtb-qcomUpgrade cluster-md-kmp-rtUpgrade kernel-rtUpgrade kernel-develUpgrade kselftests-kmp-defaultUpgrade gfs2-kmp-defaultUpgrade kernel-rt-extraUpgrade kernel-obs-buildUpgrade dtb-mediatekUpgrade kernel-rt-livepatchUpgrade kernel-rt-develUpgrade kernel-source-vanillaUpgrade kselftests-kmp-rtUpgrade kernel-defaultUpgrade dtb-xilinxUpgrade dtb-rockchipUpgrade dlm-kmp-defaultUpgrade cluster-md-kmp-defaultUpgrade kernel-rt-vdsoUpgrade dlm-kmp-rtUpgrade kernel-default-optionalUpgrade kernel-64kb-extraUpgrade dtb-sprdUpgrade dtb-armUpgrade kernel-docsUpgrade kernel-default-vdso | Dec 5, 2025 | Nov 6, 2025 |
| Ubuntu | — | Upgrade linux-image-6.14.0-1014-azureUpgrade linux-image-oem-24.04cUpgrade linux-image-oracle-6.14Upgrade linux-image-generic-64k-6.14Upgrade linux-image-aws-64k-6.14Upgrade linux-image-6.14.0-1016-raspiUpgrade linux-image-generic-hwe-24.04Upgrade linux-image-oem-24.04aUpgrade linux-image-oracleUpgrade linux-image-oracle-64k-6.14Upgrade linux-image-generic-64k-hwe-24.04Upgrade linux-image-6.14.0-1018-gcpUpgrade linux-image-nvidia-tegra-rt-6.8Upgrade linux-image-6.14.0-1015-aws-64kUpgrade linux-image-awsUpgrade linux-image-azure-nvidia-6.14Upgrade linux-image-gcp-6.14Upgrade linux-image-generic-6.14Upgrade linux-image-gcpUpgrade linux-image-6.14.0-1015-awsUpgrade linux-image-oracle-64kUpgrade linux-image-6.8.0-1035-nvidia-tegraUpgrade linux-image-virtual-6.14Upgrade linux-image-realtimeUpgrade linux-image-realtime-6.14Upgrade linux-image-aws-6.14Upgrade linux-image-virtualUpgrade linux-image-gcp-64kUpgrade linux-image-gcp-64k-6.14Upgrade linux-image-realtime-hwe-24.04Upgrade linux-image-azure-6.14Upgrade linux-image-nvidia-tegraUpgrade linux-image-6.14.0-1014-realtimeUpgrade linux-image-azure-nvidiaUpgrade linux-image-oem-6.14Upgrade linux-image-6.14.0-1015-oracle-64kUpgrade linux-image-6.14.0-34-genericUpgrade linux-image-6.14.0-34-generic-64kUpgrade linux-image-6.14.0-1014-oemUpgrade linux-image-6.14.0-1007-azure-nvidiaUpgrade linux-image-raspi-6.14Upgrade linux-image-6.8.0-1035-nvidia-tegra-rtUpgrade linux-image-virtual-hwe-24.04Upgrade linux-image-nvidia-tegra-rtUpgrade linux-image-aws-64kUpgrade linux-image-6.14.0-1018-gcp-64kUpgrade linux-image-raspiUpgrade linux-image-nvidia-tegra-6.8Upgrade linux-image-azureUpgrade linux-image-oem-24.04bUpgrade linux-image-6.14.0-1015-oracleUpgrade linux-image-oem-24.04Upgrade linux-image-generic-64kUpgrade linux-image-generic | Jul 14, 2025 | Jul 9, 2025 |
| Vmware Photon_os | — | Use 'tdnf update' to upgrade all packages to the latest version. | Jul 6, 2026 | Jul 9, 2025 |
Prioritise with Active Threat Intelligence
With curated Threat Intelligence, you can see which vulnerabilities truly put you at risk, prioritize what matters most, and act before attackers do.
Explore Intelligence Hub