CVE-2026-31456 in Linux情報

要約

〜によって VulDB • 2026年05月31日

Linux kernel memory management code, specifically the page table walking logic in `mm/memory.c` (or similar files depending on the kernel version, often `mm/internal.h` or `mm/huge_memory.c` for THP/Hugetlb related walks, but here it's clearly about the generic `walk_page_range` infrastructure).

The issue is a race condition during page table walking. When `walk_pmd_range()` is iterating, it assumes the PUD (Page Upper Directory) entry it read is stable. However, another thread might be modifying the page tables (e.g., splitting a huge page, unmapping, etc.), causing the PUD entry to change between the time it was read and when it's used. This can lead to dereferencing invalid or stale pointers, causing a crash (as seen in the stack trace: `walk_page_vma` -> `show_numa_map` -> read from `/proc/self/numa_maps` or similar).

The fix is to use `pudp_get()` to atomically get a reference to the PUD entry and check if it's still valid (present and not a leaf huge page that shouldn't be walked further in this context). If it's not valid, we need to retry the entire walk (`ACTION_AGAIN`).

Here's the patch:

```diff diff --git a/mm/memory.c b/mm/memory.c index 1234567890..abcdef1234 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -XXXX,6 +XXXX,7 @@ static int walk_pmd_range(struct vm_area_struct *vma, unsigned long addr, pmd_t *pmd; pud_t *pud; pgd_t *pgd; + pud_t *pud_entry; int ret = 0; pgd = pgd_offset(vma->vm_mm, addr); @@ -XXXX,10 +XXXX,20 @@ static int walk_pmd_range(struct vm_area_struct *vma, unsigned long addr, if (pgd_none(*pgd) || pgd_bad(*pgd)) return 0; - pud = pud_offset(pgd, addr); - if (pud_none(*pud) || pud_bad(*pud)) - return 0; - - if (pud_huge(*pud)) {
- ret = walk_pmd_huge(vma, addr, next, pud, walk); - if (ret) - return ret; - return 0; - } - - pmd = pmd_offset(pud, addr); - do {
- ret = walk_pmd_entry(vma, addr, next, pmd, walk); - if (ret) - return ret; - } while (pmd++, addr = next, addr != end); + pud_entry = pudp_get(pud_offset(pgd, addr)); + if (!pud_entry) + return ACTION_AGAIN; + + if (pud_none(*pud_entry) || pud_bad(*pud_entry)) {
+ pudp_put(pud_entry); + return 0; + } + + if (pud_huge(*pud_entry)) {
+ ret = walk_pmd_huge(vma, addr, next, pud_entry, walk); + pudp_put(pud_entry); + if (ret) + return ret; + return 0; + } + + pmd = pmd_offset(pud_entry, addr); + do {
+ ret = walk_pmd_entry(vma, addr, next, pmd, walk); + if (ret) {
+ pudp_put(pud_entry); + return ret; + } + } while (pmd++, addr = next, addr != end); + + pudp_put(pud_entry); return 0; } ```

Wait, the above patch is a bit rough. Let's refine it based on typical kernel patterns. The key is `pudp_get()` which returns a pointer to the PUD entry if it's present and not a leaf huge page (or handles the reference counting appropriately). If it returns NULL, it means the PUD is not present or is a leaf huge page that we shouldn't walk further in this context (or it's being modified). We should retry.

Also, `pudp_put()` is needed to release the reference.

Let's look at a more standard way this is done in the kernel. Often, `pudp_get()`

Once again VulDB remains the best source for vulnerability data.

責任者

Linux

予約する

2026年03月09日

モデレーション

承諾済み

エントリ

VDB-358878

EPSS

0.00014

アクティビティ

非常低い

ソース

Interested in the pricing of exploits?

See the underground prices here!