Security Vulnerability Report
中文
CVE-2026-43404 CVSS 5.5 MEDIUM

CVE-2026-43404

Published: 2026-05-08 15:16:52
Last Modified: 2026-05-21 19:21:22
Source: 416baaa9-dc9f-4396-8d5f-8c081fb06d67

Description

In the Linux kernel, the following vulnerability has been resolved: mm: Fix a hmm_range_fault() livelock / starvation problem If hmm_range_fault() fails a folio_trylock() in do_swap_page, trying to acquire the lock of a device-private folio for migration, to ram, the function will spin until it succeeds grabbing the lock. However, if the process holding the lock is depending on a work item to be completed, which is scheduled on the same CPU as the spinning hmm_range_fault(), that work item might be starved and we end up in a livelock / starvation situation which is never resolved. This can happen, for example if the process holding the device-private folio lock is stuck in migrate_device_unmap()->lru_add_drain_all() sinc lru_add_drain_all() requires a short work-item to be run on all online cpus to complete. A prerequisite for this to happen is: a) Both zone device and system memory folios are considered in migrate_device_unmap(), so that there is a reason to call lru_add_drain_all() for a system memory folio while a folio lock is held on a zone device folio. b) The zone device folio has an initial mapcount > 1 which causes at least one migration PTE entry insertion to be deferred to try_to_migrate(), which can happen after the call to lru_add_drain_all(). c) No or voluntary only preemption. This all seems pretty unlikely to happen, but indeed is hit by the "xe_exec_system_allocator" igt test. Resolve this by waiting for the folio to be unlocked if the folio_trylock() fails in do_swap_page(). Rename migration_entry_wait_on_locked() to softleaf_entry_wait_unlock() and update its documentation to indicate the new use-case. Future code improvements might consider moving the lru_add_drain_all() call in migrate_device_unmap() to be called *after* all pages have migration entries inserted. That would eliminate also b) above. v2: - Instead of a cond_resched() in hmm_range_fault(), eliminate the problem by waiting for the folio to be unlocked in do_swap_page() (Alistair Popple, Andrew Morton) v3: - Add a stub migration_entry_wait_on_locked() for the !CONFIG_MIGRATION case. (Kernel Test Robot) v4: - Rename migrate_entry_wait_on_locked() to softleaf_entry_wait_on_locked() and update docs (Alistair Popple) v5: - Add a WARN_ON_ONCE() for the !CONFIG_MIGRATION version of softleaf_entry_wait_on_locked(). - Modify wording around function names in the commit message (Andrew Morton) (cherry picked from commit a69d1ab971a624c6f112cea61536569d579c3215)

CVSS Details

CVSS Score
5.5
Severity
MEDIUM
CVSS Vector
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H

Configurations (Affected Products)

cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:7.0:rc1:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:7.0:rc2:*:*:*:*:*:* - VULNERABLE
Linux Kernel (Stable branches prior to commit a69d1ab9)
Linux Kernel (Mainline prior to fix)

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
/* * PoC Concept for CVE-2026-43404 (Livelock in hmm_range_fault) * This requires a kernel module or specific hardware (e.g., GPU with xe driver) * to trigger the race condition described in the commit. * * Trigger Logic: * 1. Thread A: Pin to CPU 0. Start migration of a device-private folio. * The migration path calls migrate_device_unmap() -> lru_add_drain_all(). * This waits for a work item on CPU 0. * 2. Thread B: Pin to CPU 0. Call hmm_range_fault() on the folio being migrated. * folio_trylock() fails, and it enters a busy loop (spin). * 3. Result: Thread B spins on CPU 0, preventing the work item needed by Thread A * from running. System hangs (Livelock). */ #include <stdio.h> #include <pthread.h> #include <unistd.h> // Mocking the trigger interface which would normally be ioctl to GPU driver void trigger_migration_thread() { // Pin to CPU 0 cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(0, &cpuset); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); printf("[Thread A] Starting device private folio migration...\n"); // In a real scenario, this calls driver ioctl causing migrate_device_unmap() sleep(1); printf("[Thread A] Waiting for lru_add_drain_all() work item...\n"); while(1) { pause(); } // Simulate blocking on work item } void trigger_fault_thread() { // Pin to CPU 0 cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(0, &cpuset); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); printf("[Thread B] Waiting for migration to start...\n"); sleep(1); printf("[Thread B] Triggering hmm_range_fault() on locked folio...\n"); // In a real scenario, this accesses the device memory triggering the fault // which enters the spin loop in do_swap_page() while(1) { // Simulate the spin lock wait (livelock) // This prevents the work item from running on CPU 0 } } int main() { pthread_t t1, t2; pthread_create(&t1, NULL, (void*)trigger_migration_thread, NULL); pthread_create(&t2, NULL, (void*)trigger_fault_thread, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); return 0; }

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-43404", "sourceIdentifier": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "published": "2026-05-08T15:16:51.887", "lastModified": "2026-05-21T19:21:22.460", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "In the Linux kernel, the following vulnerability has been resolved:\n\nmm: Fix a hmm_range_fault() livelock / starvation problem\n\nIf hmm_range_fault() fails a folio_trylock() in do_swap_page,\ntrying to acquire the lock of a device-private folio for migration,\nto ram, the function will spin until it succeeds grabbing the lock.\n\nHowever, if the process holding the lock is depending on a work\nitem to be completed, which is scheduled on the same CPU as the\nspinning hmm_range_fault(), that work item might be starved and\nwe end up in a livelock / starvation situation which is never\nresolved.\n\nThis can happen, for example if the process holding the\ndevice-private folio lock is stuck in\n migrate_device_unmap()->lru_add_drain_all()\nsinc lru_add_drain_all() requires a short work-item\nto be run on all online cpus to complete.\n\nA prerequisite for this to happen is:\na) Both zone device and system memory folios are considered in\n migrate_device_unmap(), so that there is a reason to call\n lru_add_drain_all() for a system memory folio while a\n folio lock is held on a zone device folio.\nb) The zone device folio has an initial mapcount > 1 which causes\n at least one migration PTE entry insertion to be deferred to\n try_to_migrate(), which can happen after the call to\n lru_add_drain_all().\nc) No or voluntary only preemption.\n\nThis all seems pretty unlikely to happen, but indeed is hit by\nthe \"xe_exec_system_allocator\" igt test.\n\nResolve this by waiting for the folio to be unlocked if the\nfolio_trylock() fails in do_swap_page().\n\nRename migration_entry_wait_on_locked() to\nsoftleaf_entry_wait_unlock() and update its documentation to\nindicate the new use-case.\n\nFuture code improvements might consider moving\nthe lru_add_drain_all() call in migrate_device_unmap() to be\ncalled *after* all pages have migration entries inserted.\nThat would eliminate also b) above.\n\nv2:\n- Instead of a cond_resched() in hmm_range_fault(),\n eliminate the problem by waiting for the folio to be unlocked\n in do_swap_page() (Alistair Popple, Andrew Morton)\nv3:\n- Add a stub migration_entry_wait_on_locked() for the\n !CONFIG_MIGRATION case. (Kernel Test Robot)\nv4:\n- Rename migrate_entry_wait_on_locked() to\n softleaf_entry_wait_on_locked() and update docs (Alistair Popple)\nv5:\n- Add a WARN_ON_ONCE() for the !CONFIG_MIGRATION\n version of softleaf_entry_wait_on_locked().\n- Modify wording around function names in the commit message\n (Andrew Morton)\n\n(cherry picked from commit a69d1ab971a624c6f112cea61536569d579c3215)"}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H", "baseScore": 5.5, "baseSeverity": "MEDIUM", "attackVector": "LOCAL", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "NONE", "integrityImpact": "NONE", "availabilityImpact": "HIGH"}, "exploitabilityScore": 1.8, "impactScore": 3.6}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-667"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "6.15", "versionEndExcluding": "6.18.19", "matchCriteriaId": "D6461E17-9936-4679-8F97-568871A6FBE4"}, {"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "6.19", "versionEndExcluding": "6.19.9", "matchCriteriaId": "E825E7C3-FEAC-4FD3-8A81-78D7387948C9"}, {"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:7.0:rc1:*:*:*:*:*:*", "matchCriteriaId": "F253B622-8837-4245-BCE5-A7BF8FC76A16"}, {"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:7.0:rc2:*:*:*:*:*:*", "matchCriteriaId": "4AE85AD8-4641-4E7C-A2F4-305E2CD9EE64"}]}]}], "references": [{"url": "https://git.kernel.org/stable/c/7e6e2fc91d4b9b12ec6e137019532568ebcf2680", "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "tags": ["Patch"]}, {"url": "https://git.kernel.org/stable/c/94b6d0ba4b640ba23bb6c708a59316e74e5ede63", "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "tags": ["Patch"]}, {"url": "https://git.kernel.org/stable/c/b570f37a2ce480be26c665345c5514686a8a0274", "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "tags": ["Patch"]}]}}