From: Zhiling Zou page_table_check_set() and page_table_check_clear() store per-page anonymous and file map counts in signed atomic_t counters. A page can have more than INT_MAX read-only mappings while still being mapped legally. The global zero page is one example: repeated read faults on private anonymous mappings install read-only PTEs that are accounted as file mappings. Once file_map_count wraps, the next set or clear observes a negative count and trips BUG_ON(), allowing an unprivileged user to panic a kernel with CONFIG_PAGE_TABLE_CHECK enabled. Use atomic64_t for both counters so page_table_check can keep the same type-conflict checks without overflowing at INT_MAX mappings. Fixes: df4e817b7108 ("mm: page table check") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zhiling Zou Reviewed-by: Ren Wei --- mm/page_table_check.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/mm/page_table_check.c b/mm/page_table_check.c index 53a8997ec043..edd9d9edf6fc 100644 --- a/mm/page_table_check.c +++ b/mm/page_table_check.c @@ -14,8 +14,8 @@ #define pr_fmt(fmt) "page_table_check: " fmt struct page_table_check { - atomic_t anon_map_count; - atomic_t file_map_count; + atomic64_t anon_map_count; + atomic64_t file_map_count; }; static bool __page_table_check_enabled __initdata = @@ -79,11 +79,11 @@ static void page_table_check_clear(unsigned long pfn, unsigned long pgcnt) struct page_table_check *ptc = get_page_table_check(page_ext); if (anon) { - BUG_ON(atomic_read(&ptc->file_map_count)); - BUG_ON(atomic_dec_return(&ptc->anon_map_count) < 0); + BUG_ON(atomic64_read(&ptc->file_map_count)); + BUG_ON(atomic64_dec_return(&ptc->anon_map_count) < 0); } else { - BUG_ON(atomic_read(&ptc->anon_map_count)); - BUG_ON(atomic_dec_return(&ptc->file_map_count) < 0); + BUG_ON(atomic64_read(&ptc->anon_map_count)); + BUG_ON(atomic64_dec_return(&ptc->file_map_count) < 0); } } rcu_read_unlock(); @@ -114,11 +114,11 @@ static void page_table_check_set(unsigned long pfn, unsigned long pgcnt, struct page_table_check *ptc = get_page_table_check(page_ext); if (anon) { - BUG_ON(atomic_read(&ptc->file_map_count)); - BUG_ON(atomic_inc_return(&ptc->anon_map_count) > 1 && rw); + BUG_ON(atomic64_read(&ptc->file_map_count)); + BUG_ON(atomic64_inc_return(&ptc->anon_map_count) > 1 && rw); } else { - BUG_ON(atomic_read(&ptc->anon_map_count)); - BUG_ON(atomic_inc_return(&ptc->file_map_count) < 0); + BUG_ON(atomic64_read(&ptc->anon_map_count)); + BUG_ON(atomic64_inc_return(&ptc->file_map_count) < 0); } } rcu_read_unlock(); @@ -139,8 +139,8 @@ void __page_table_check_zero(struct page *page, unsigned int order) for_each_page_ext(page, 1 << order, page_ext, iter) { struct page_table_check *ptc = get_page_table_check(page_ext); - BUG_ON(atomic_read(&ptc->anon_map_count)); - BUG_ON(atomic_read(&ptc->file_map_count)); + BUG_ON(atomic64_read(&ptc->anon_map_count)); + BUG_ON(atomic64_read(&ptc->file_map_count)); } rcu_read_unlock(); } -- 2.43.0