Skip to content

Commit e30cbb5

Browse files
committed
fix: avoid page_table_check BUG on time namespace VVAR page
Backport a kernel patch to stop page_table_check from tracking special (PFN-mapped) PTEs. The vDSO "[vvar]" mapping is VM_PFNMAP and its pages are installed with vmf_insert_pfn(), producing special PTEs. pte_user_accessible_page() on x86/arm64 does not exclude special PTEs, so page_table_check accounts these PFN mappings. The time-namespace VVAR page is a real alloc_page() that is freed in free_time_ns() when the last task in a time namespace exits; the unbalanced accounting leaves a non-zero map count and trips the BUG_ON() in __page_table_check_zero(): kernel BUG at mm/page_table_check.c:143! __page_table_check_zero / __free_frozen_pages / free_time_ns / free_nsproxy / do_exit This is hit under heavy container/CI churn (CLONE_NEWTIME via runc / docker-init / tini) on both amd64 and arm64, since the Talos kernel enables CONFIG_PAGE_TABLE_CHECK. Mainline sidesteps it in v7.0 by switching the mapping to VM_MIXEDMAP + vmf_insert_page(), but 6.18.y still uses the PFNMAP path. See: siderolabs/talos#13496 Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
1 parent f2850d1 commit e30cbb5

2 files changed

Lines changed: 81 additions & 0 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
From: Andrey Smirnov <andrey.smirnov@siderolabs.com>
2+
Date: Mon, 8 Jun 2026 00:00:00 +0000
3+
Subject: [PATCH] mm/page_table_check: do not track special (PFN-mapped) PTEs
4+
5+
The vDSO data store ("[vvar]") special mapping is created as a VM_PFNMAP
6+
mapping and its pages are installed into userspace with vmf_insert_pfn(),
7+
which produces *special* PTEs (pte_special()). On x86 and arm64 (and
8+
riscv) pte_user_accessible_page() only tests the PRESENT/USER bits and
9+
does not exclude special PTEs, so page_table_check accounts these PFN
10+
mappings in the per-page anon/file map counters even though they are not
11+
rmap-managed pages (vm_normal_page() returns NULL for them).
12+
13+
Most of these data pages live in the kernel image and are never freed, so
14+
the stray accounting is invisible. The time-namespace VVAR page is the
15+
exception: it is a real alloc_page() page that is released with
16+
__free_page() in free_time_ns() when the last task of a time namespace
17+
exits. Across the map / unmap / vdso_join_timens() zap transitions the
18+
special-PTE accounting is not balanced for this page, so a non-zero
19+
file_map_count survives to the free path and trips:
20+
21+
kernel BUG at mm/page_table_check.c:143!
22+
__page_table_check_zero+...
23+
__free_frozen_pages+...
24+
free_time_ns+...
25+
free_nsproxy+...
26+
do_exit / do_group_exit
27+
28+
This reproduces under heavy container/CI churn (rapid creation and
29+
teardown of time namespaces via CLONE_NEWTIME, e.g. runc / docker-init /
30+
tini) on x86_64 and arm64, and was independently reported by syzbot on
31+
riscv. It only manifests when CONFIG_PAGE_TABLE_CHECK is active.
32+
33+
Special PTEs have no struct-page rmap semantics and must never have been
34+
tracked by page table check. Skip them in both the set and clear paths so
35+
the counters stay balanced (always zero) for PFN-mapped pages, regardless
36+
of how the architecture defines pte_user_accessible_page(). pte_special()
37+
is available generically (a no-op returning false on architectures
38+
without ARCH_HAS_PTE_SPECIAL), so this is a single, arch-independent fix.
39+
40+
Mainline sidesteps this since commit 05988dba1179 ("vdso/datastore:
41+
Allocate data pages dynamically", v7.0) switched the mapping to
42+
VM_MIXEDMAP + vmf_insert_page() with balanced struct-page accounting, but
43+
6.18.y still uses the PFNMAP path and needs this fix.
44+
45+
Reported-by: syzbot+2b5fe617654be3d8848b@syzkaller.appspotmail.com
46+
Link: https://github.com/siderolabs/talos/issues/13496
47+
Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
48+
---
49+
mm/page_table_check.c | 12 ++++++++++--
50+
1 file changed, 10 insertions(+), 2 deletions(-)
51+
52+
--- a/mm/page_table_check.c
53+
+++ b/mm/page_table_check.c
54+
@@ -150,7 +150,15 @@
55+
if (&init_mm == mm)
56+
return;
57+
58+
- if (pte_user_accessible_page(pte)) {
59+
+ /*
60+
+ * PFN-mapped (special) PTEs - e.g. the vDSO/time-namespace "[vvar]"
61+
+ * mapping installed via vmf_insert_pfn() - are not rmap-managed and
62+
+ * must not be tracked here. Tracking them can leave a non-zero map
63+
+ * count on a struct page that is later freed (the time namespace VVAR
64+
+ * page in free_time_ns()), tripping the BUG_ON() in
65+
+ * __page_table_check_zero().
66+
+ */
67+
+ if (pte_user_accessible_page(pte) && !pte_special(pte)) {
68+
page_table_check_clear(pte_pfn(pte), PAGE_SIZE >> PAGE_SHIFT);
69+
}
70+
}
71+
@@ -205,7 +213,7 @@
72+
73+
for (i = 0; i < nr; i++)
74+
__page_table_check_pte_clear(mm, ptep_get(ptep + i));
75+
- if (pte_user_accessible_page(pte))
76+
+ if (pte_user_accessible_page(pte) && !pte_special(pte))
77+
page_table_check_set(pte_pfn(pte), nr, pte_write(pte));
78+
}
79+
EXPORT_SYMBOL(__page_table_check_ptes_set);

kernel/build/patches/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@
44
| `0002-net-macb-insert-PCIe-read-barrier-before-TX-completi.patch` | macb: insert non-destructive PCIe read barrier (`queue_readl(queue, IMR)`) before `macb_tx_complete_pending()` in `macb_tx_poll()`. Replaces the v1 ISR-read form which was destructive on read-clear silicon (RP1) — that read silently consumed RCOMP / ROVR / TXUBR bits, causing silent RX-completion loss at moderate-to-heavy load | v2 submitted to netdev | [v2 thread](https://lore.kernel.org/netdev/20260514215459.36109-1-lukasz@raczylo.com/T/) |
55
| `0003-net-macb-add-TX-stall-watchdog-to-recover-from-lost-.patch` | macb: per-queue `delayed_work` watchdog that calls `macb_tx_restart()` if tx_tail hasn't advanced. v2 uses a `bool tx_stall_tail_moved` flag (pelwell-suggested form) instead of a tx_tail snapshot, gates the check on `netif_carrier_ok()` to eliminate a boot-time false positive, and wraps the stall-warn in `if (printk_ratelimit()) netdev_warn(...)` so events stay observable while bounded | v2 submitted to netdev | [v2 thread](https://lore.kernel.org/netdev/20260514215459.36109-1-lukasz@raczylo.com/T/) · [v2 patch 3 build-fix](https://lore.kernel.org/netdev/20260515095336.92237-1-lukasz@raczylo.com/T/) |
66
| `0004-PCI-prevent-shrink-bridge-window.patch` | PCI: prevent `adjust_bridge_window()` from shrinking a bridge window below the size required by `pbus_size_mem()` — fixes large-BAR / eGPU resource starvation | Merged to mainline v6.19, candidate for 6.18.y stable backport | [lore patch](https://patch.msgid.link/20260219153951.68869-1-ilpo.jarvinen@linux.intel.com) |
7+
| `0005-slab-backport-flex-allocator-helpers.patch` | Incomplete backport to 6.18.x breaking the DRBD build | Cherry-picked from mainline, drop when upgrading ||
8+
| `006-mm-page_table_check-do-not-track-special-PFN-mapped-PTEs.patch` | mm/page_table_check: do not track special (PFN-mapped) PTEs | Linux 7.0 is not affected, but 6.18.x. is ||

0 commit comments

Comments
 (0)