>From 8beef54a334bb244574506491472e1c955388198 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Fri, 1 Aug 2025 08:47:24 -0400 Subject: [PATCH 1/2] coccinelle: add semantic patch to detect kmap_local LIFO violations Add a Coccinelle semantic patch to detect violations of kmap_local's Last-In-First-Out (LIFO) ordering requirement. When using kmap_local_page(), kmap_atomic(), pte_offset_map(), or pte_offset_map_rw_nolock() variants, the mappings must be unmapped in reverse order to maintain correct highmem slot ordering. This semantic patch identifies patterns where: - Multiple kmap operations are unmapped in the wrong order - The same pattern applies to pte_offset_map() and kmap_atomic() - Conditional unmapping patterns that violate LIFO ordering These violations can cause warnings on CONFIG_HIGHPTE systems: WARNING: CPU: 0 PID: 604 at mm/highmem.c:622 kunmap_local_indexed+0x178/0x17c addr \!= __fix_to_virt(FIX_KMAP_BEGIN + idx) Co-developed-by: Claude claude-opus-4-20250514 Signed-off-by: Sasha Levin --- scripts/coccinelle/api/kmap_local_lifo.cocci | 114 +++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 scripts/coccinelle/api/kmap_local_lifo.cocci diff --git a/scripts/coccinelle/api/kmap_local_lifo.cocci b/scripts/coccinelle/api/kmap_local_lifo.cocci new file mode 100644 index 000000000000..e6ba780753de --- /dev/null +++ b/scripts/coccinelle/api/kmap_local_lifo.cocci @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 +/// Detect violations of kmap_local LIFO ordering +/// +/// kmap_local_page() and pte_offset_map() operations must follow +/// Last-In-First-Out (LIFO) ordering. This means if you map A then B, +/// you must unmap B then A. +/// +// Confidence: High +// Copyright: (C) 2025 Sasha Levin + +virtual report + +// Pattern 1: kmap_local_page() followed by kunmap_local() in wrong order +@kmap_lifo@ +expression E1, E2; +identifier ptr1 != ptr2; +identifier ptr2; +position p1, p2, p3, p4; +@@ + +ptr1 = kmap_local_page(E1)@p1; +... when != kunmap_local(ptr1) +ptr2 = kmap_local_page(E2)@p2; +... when != kunmap_local(ptr1) + when != kunmap_local(ptr2) +kunmap_local(ptr1)@p3; +... when != kunmap_local(ptr2) +kunmap_local(ptr2)@p4; + +@script:python depends on report@ +p1 << kmap_lifo.p1; +p2 << kmap_lifo.p2; +p3 << kmap_lifo.p3; +ptr1 << kmap_lifo.ptr1; +ptr2 << kmap_lifo.ptr2; +@@ + +coccilib.report.print_report(p3[0], "WARNING: kmap_local LIFO violation - %s mapped before %s but unmapped first" % (ptr1, ptr2)) + +// Pattern 2: pte_offset_map() followed by pte_unmap() in wrong order +@pte_lifo@ +expression E1, E2, E3, E4; +identifier ptr1 != ptr2; +identifier ptr2; +position p1, p2, p3, p4; +@@ + +ptr1 = pte_offset_map(E1, E2)@p1; +... when != pte_unmap(ptr1) +ptr2 = pte_offset_map(E3, E4)@p2; +... when != pte_unmap(ptr1) + when != pte_unmap(ptr2) +pte_unmap(ptr1)@p3; +... when != pte_unmap(ptr2) +pte_unmap(ptr2)@p4; + +@script:python depends on report@ +p1 << pte_lifo.p1; +p2 << pte_lifo.p2; +p3 << pte_lifo.p3; +ptr1 << pte_lifo.ptr1; +ptr2 << pte_lifo.ptr2; +@@ + +coccilib.report.print_report(p3[0], "WARNING: pte_offset_map LIFO violation - %s mapped before %s but unmapped first" % (ptr1, ptr2)) + +// Pattern 3: kmap_atomic() followed by kunmap_atomic() in wrong order +@atomic_lifo@ +expression E1, E2; +identifier ptr1 != ptr2; +identifier ptr2; +position p1, p2, p3, p4; +@@ + +ptr1 = kmap_atomic(E1)@p1; +... when != kunmap_atomic(ptr1) +ptr2 = kmap_atomic(E2)@p2; +... when != kunmap_atomic(ptr1) + when != kunmap_atomic(ptr2) +kunmap_atomic(ptr1)@p3; +... when != kunmap_atomic(ptr2) +kunmap_atomic(ptr2)@p4; + +@script:python depends on report@ +p1 << atomic_lifo.p1; +p2 << atomic_lifo.p2; +p3 << atomic_lifo.p3; +ptr1 << atomic_lifo.ptr1; +ptr2 << atomic_lifo.ptr2; +@@ + +coccilib.report.print_report(p3[0], "WARNING: kmap_atomic LIFO violation - %s mapped before %s but unmapped first" % (ptr1, ptr2)) + +// Pattern 4: Specific pattern for userfaultfd conditional unmapping +@userfault_pattern@ +identifier dst_pte, src_pte; +position p1, p2, p3, p4; +@@ + +dst_pte@p1 = pte_offset_map_rw_nolock(...); +... when exists +src_pte@p2 = pte_offset_map_rw_nolock(...); +... when exists +when any +if (dst_pte) + pte_unmap(dst_pte)@p3; +if (src_pte) + pte_unmap(src_pte)@p4; + +@script:python depends on report@ +p3 << userfault_pattern.p3; +@@ + +coccilib.report.print_report(p3[0], "WARNING: pte_offset_map_rw_nolock LIFO violation - dst_pte mapped before src_pte but unmapped first") -- 2.39.5