KASAN: slab-use-after-free Read in finish_fault

Status: upstream: reported C repro on 2024/06/13 11:38
Subsystems: mm
Labels: prio:high
[Documentation on labels]
Reported-by: syzbot+d6e5c328862b5ae6cbfe@syzkaller.appspotmail.com
Fix commit: mm-memory-extend-finish_fault-to-support-large-folio-fix
Patched on: [ci-upstream-linux-next-kasan-gce-root], missing on: [ci-qemu-gce-upstream-auto ci-qemu-native-arm64-kvm ci-qemu-upstream ci-qemu-upstream-386 ci-qemu2-arm32 ci-qemu2-arm64 ci-qemu2-arm64-compat ci-qemu2-arm64-mte ci-qemu2-riscv64 ci-snapshot-upstream-root ci-upstream-bpf-kasan-gce ci-upstream-bpf-next-kasan-gce ci-upstream-gce-arm64 ci-upstream-gce-leak ci-upstream-kasan-badwrites-root ci-upstream-kasan-gce ci-upstream-kasan-gce-386 ci-upstream-kasan-gce-root ci-upstream-kasan-gce-selinux-root ci-upstream-kasan-gce-smack-root ci-upstream-kmsan-gce-386-root ci-upstream-kmsan-gce-root ci-upstream-net-kasan-gce ci-upstream-net-this-kasan-gce ci-upstream-rust-kasan-gce ci2-upstream-fs ci2-upstream-kcsan-gce ci2-upstream-usb]
First crash: 885d, last: 879d
Cause bisection: introduced by (bisect log) :
commit 1c05047ad01693ad92bdf8347fad3b5c2b25e8bb
Author: Baolin Wang <baolin.wang@linux.alibaba.com>
Date: Tue Jun 4 10:17:45 2024 +0000

  mm: memory: extend finish_fault() to support large folio

Crash: KASAN: slab-use-after-free Read in finish_fault (log)
Repro: C syz .config
  
✨ AI-generated root cause summary:

In finish_fault() (mm/memory.c), when handling a fault for a shared anonymous shmem VMA (vma_is_anon_shmem(vma)) backed by a large folio (nr_pages > 1), the kernel attempts to map the entire large folio at once through PTEs starting at addr = vmf->address - idx * PAGE_SIZE. While finish_fault() verifies that the folio fits within VMA limits (vma_pages(vma)), it fails to verify that the virtual address range stays within the PMD and page table page boundaries (i.e. pte_index(addr) + nr_pages <= PTRS_PER_PTE). If an anonymous shmem file has a large folio (e.g. collapsed via MADV_COLLAPSE) mapped at an address or page offset unaligned to the PMD boundary (e.g. via remap_file_pages()), finish_fault() calculates a non-zero PTE offset in vmf->pmd's page table page and iterates nr_pages entries. This causes pte_range_none() and set_pte_range() to access PTEs beyond the 4KB boundary of the page table page into adjacent physical memory, resulting in an out-of-bounds read/write (reported as a use-after-free by KASAN).

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit d35b2284e966:

Technical Breakdown & Root Cause

1. Involved Files, Functions, Structs, and Fields

  • Source Files:
  • Structs & Fields:
    • struct vm_fault: vmf->address, vmf->pmd, vmf->pte, vmf->ptl, vmf->pgoff, vmf->page, vmf->vma
    • struct vm_area_struct: vma->vm_start, vma->vm_end, vma->vm_pgoff, vma->vm_flags
    • struct folio: folio_nr_pages(folio) (nr_pages), folio_page_idx(folio, page) (idx)

2. Invariant and Lifecycle Rule Violated

In the Linux page fault handling architecture, a page table page (allocated for a specific PMD entry via pte_alloc()) consists of exactly one 4KB physical page containing PTRS_PER_PTE entries (512 entries of 8 bytes on x86-64), covering a contiguous virtual address range of size PMD_SIZE (2MB).

As stated in the documentation and context requirements of set_ptes() (include/linux/pgtable.h):

Context: The caller holds the page table lock. The pages all belong to the same folio. The PTEs are all in the same PMD.

A single call to pte_offset_map_lock(mm, vmf->pmd, addr, &vmf->ptl) returns a pointer pte_t * into the single page table page associated with vmf->pmd. Any subsequent operation that accesses or updates nr_pages consecutive PTEs starting from this pointer (pte_range_none() or set_pte_range()) MUST NOT exceed the end of that page table page: $$\text{pte_index}(addr) + nr_pages \le \text{PTRS_PER_PTE}$$ If this invariant is violated, pointer arithmetic on pte + i advances past the 4096-byte boundary of the page table page into the next contiguous physical page in the direct physical mapping.


3. How the Bug is Triggered and Mechanics of the Crash

  1. Large Folio Creation in Anonymous Shmem: A process creates a shared anonymous mapping via mmap(..., MAP_SHARED | MAP_ANONYMOUS), which is backed by tmpfs/shmem (vma_is_anon_shmem(vma) == true). It invokes madvise(..., MADV_COLLAPSE), producing a PMD-sized large folio (HPAGE_PMD_ORDER, 512 pages / 2MB) in the shmem file at pgoff = 0.

  2. Unaligned Mapping: The process maps file offset 0 (pgoff = 0) to an address that is not PMD-alignedβ€”for instance, via remap_file_pages() mapping to address 0x2000002ec000 with pgoff = 0. Here, the starting virtual address within the PMD has a page offset of 0xec (PTE index 236).

  3. PMD Fault Fallback: When a fault occurs on the VMA, finish_fault() is invoked. It first attempts to map the PMD as a huge page via do_set_pmd(). However, do_set_pmd() checks thp_vma_suitable_order(), which fails because the virtual start address of the VMA is not PMD-aligned ((vma->vm_start >> PAGE_SHIFT) - vma->vm_pgoff is not aligned to HPAGE_PMD_NR). do_set_pmd() returns VM_FAULT_FALLBACK.

  4. Faulting Large Folio across PTEs without PMD Boundary Checks: After do_set_pmd() falls back, finish_fault() allocates a standard PTE page for vmf->pmd and inspects the folio:

    folio = page_folio(page);
    nr_pages = folio_nr_pages(folio); // nr_pages = 512
    ...
    } else if (nr_pages > 1) {
        pgoff_t idx = folio_page_idx(folio, page); // idx = 0
        pgoff_t vma_off = vmf->pgoff - vmf->vma->vm_pgoff;
    
        if (unlikely(vma_off < idx ||
                 vma_off + (nr_pages - idx) > vma_pages(vma))) {
            nr_pages = 1;
        } else {
            addr = vmf->address - idx * PAGE_SIZE; // addr = 0x2000002ec000
            page = &folio->page;
        }
    }
    

    finish_fault() checks only whether [addr, addr + nr_pages * PAGE_SIZE) stays within the VMA's boundaries (vma_pages(vma)). It does not check whether the range stays within the single PMD covered by vmf->pmd, nor does it verify that pte_index(addr) + nr_pages <= PTRS_PER_PTE.

  5. Out-of-Bounds Access in pte_range_none() and set_pte_range(): finish_fault() locks the page table:

    vmf->pte = pte_offset_map_lock(vma->vm_mm, vmf->pmd, addr, &vmf->ptl);
    

    Because addr has page offset 0xec = 236, vmf->pte points to entry 236 of the page table page (pfn: 0x7bfb6, virtual 0xffff88807bfb6000). Only $512 - 236 = 276$ PTE entries remain in this 4KB page.

    Next, finish_fault() checks:

    else if (nr_pages > 1 && !pte_range_none(vmf->pte, nr_pages))
    

    pte_range_none() loops i from 0 to 511 (nr_pages):

    for (i = 0; i < nr_pages; i++) {
        if (!pte_none(ptep_get_lockless(pte + i)))
            return false;
    }
    

    When i reaches 276, pte + 276 accesses offset 236 * 8 + 276 * 8 = 4096 bytes from the base of the page table page, crossing into the base of the next physical page (pfn: 0x7bfb7, virtual address 0xffff88807bfb7000). Because that adjacent page happens to be free, KASAN immediately detects a use-after-free read of size 8 at 0xffff88807bfb7000.

▶ ▼ ✨ AI Jobs (10)
ID Workflow Result Correct Bug Created Started Finished Revision
eeef2183 assessment-security DenialOfService: βœ… Exploitable: βœ… FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: βœ… NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: βœ… UserNamespace: βœ… VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ KASAN: slab-use-after-free Read in finish_fault 2026/11/09 00:04 2026/11/09 00:04 2026/11/09 00:33 5cecaf1f
7c8570c0 assessment-security πŸ’₯ KASAN: slab-use-after-free Read in finish_fault 2026/10/09 01:36 2026/10/09 01:36 2026/10/09 01:54 723be09b
9fba6275 assessment-security πŸ’₯ KASAN: slab-use-after-free Read in finish_fault 2026/09/09 01:16 2026/09/09 01:16 2026/09/09 01:46 243b72f1
18a19ac1 assessment-security πŸ’₯ KASAN: slab-use-after-free Read in finish_fault 2026/08/09 12:49 2026/08/09 12:49 2026/08/09 13:14 5d63f80b
cfba9342 assessment-security πŸ’₯ KASAN: slab-use-after-free Read in finish_fault 2026/07/08 01:03 2026/07/08 01:03 2026/07/08 01:27 93f57954
3f8c886c assessment-security πŸ’₯ KASAN: slab-use-after-free Read in finish_fault 2026/06/22 00:58 2026/06/22 00:58 2026/06/22 01:20 43bfcdb0
8f364e03 assessment-security πŸ’₯ KASAN: slab-use-after-free Read in finish_fault 2026/06/14 00:43 2026/06/14 00:43 2026/06/14 00:56 4f06757f
cb5a4614 assessment-security πŸ’₯ KASAN: slab-use-after-free Read in finish_fault 2026/06/09 15:25 2026/06/09 15:25 2026/06/09 15:48 c36c07f6
bb950f39 assessment-security πŸ’₯ KASAN: slab-use-after-free Read in finish_fault 2026/06/02 08:52 2026/06/02 08:52 2026/06/02 09:21 1095583b
da0d3bb6 assessment-security πŸ’₯ KASAN: slab-use-after-free Read in finish_fault 2026/05/23 20:49 2026/05/23 20:49 2026/05/23 21:11 c69befb3
▶ ▼ Discussions (1)
Title Replies (including bot) Last reply
[syzbot] [mm?] KASAN: slab-use-after-free Read in finish_fault 2 (3) 2024/06/13 13:03

Sample crash report:
==================================================================
BUG: KASAN: use-after-free in ptep_get include/linux/pgtable.h:317 [inline]
BUG: KASAN: use-after-free in ptep_get_lockless include/linux/pgtable.h:581 [inline]
BUG: KASAN: use-after-free in pte_range_none mm/memory.c:4409 [inline]
BUG: KASAN: use-after-free in finish_fault+0xf87/0x1460 mm/memory.c:4905
Read of size 8 at addr ffff88807bfb7000 by task syz-executor149/5117

CPU: 0 PID: 5117 Comm: syz-executor149 Not tainted 6.10.0-rc2-next-20240607-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/02/2024
Call Trace:
 <TASK>
 __dump_stack lib/dump_stack.c:91 [inline]
 dump_stack_lvl+0x241/0x360 lib/dump_stack.c:117
 print_address_description mm/kasan/report.c:377 [inline]
 print_report+0x169/0x550 mm/kasan/report.c:488
 kasan_report+0x143/0x180 mm/kasan/report.c:601
 ptep_get include/linux/pgtable.h:317 [inline]
 ptep_get_lockless include/linux/pgtable.h:581 [inline]
 pte_range_none mm/memory.c:4409 [inline]
 finish_fault+0xf87/0x1460 mm/memory.c:4905
 do_read_fault mm/memory.c:5052 [inline]
 do_fault mm/memory.c:5178 [inline]
 do_pte_missing mm/memory.c:3948 [inline]
 handle_pte_fault+0x3db5/0x7130 mm/memory.c:5502
 __handle_mm_fault mm/memory.c:5645 [inline]
 handle_mm_fault+0x10df/0x1ba0 mm/memory.c:5810
 faultin_page mm/gup.c:1339 [inline]
 __get_user_pages+0x6ef/0x1590 mm/gup.c:1638
 populate_vma_page_range+0x264/0x330 mm/gup.c:2078
 __mm_populate+0x27a/0x460 mm/gup.c:2181
 mm_populate include/linux/mm.h:3442 [inline]
 __do_sys_remap_file_pages mm/mmap.c:3177 [inline]
 __se_sys_remap_file_pages+0x7a1/0x9a0 mm/mmap.c:3103
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7ff1cbb3e399
Code: 28 00 00 00 75 05 48 83 c4 28 c3 e8 51 18 00 00 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007ff1cbad8238 EFLAGS: 00000246 ORIG_RAX: 00000000000000d8
RAX: ffffffffffffffda RBX: 00007ff1cbbc8318 RCX: 00007ff1cbb3e399
RDX: 0000000000000000 RSI: 0000000000200000 RDI: 00000000202ec000
RBP: 00007ff1cbbc8310 R08: 0000000000000000 R09: 00007ff1cbad86c0
R10: 0000000000000000 R11: 0000000000000246 R12: 00007ff1cbbc831c
R13: 0000000000000000 R14: 00007ffcff8e0350 R15: 00007ffcff8e0438
 </TASK>

The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x7bfb7
flags: 0xfff00000000000(node=0|zone=1|lastcpupid=0x7ff)
raw: 00fff00000000000 ffffea00007ed408 ffff8880b94448a0 0000000000000000
raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as freed
page last allocated via order 0, migratetype Unmovable, gfp_mask 0x440dc0(GFP_KERNEL_ACCOUNT|__GFP_COMP|__GFP_ZERO), pid 5097, tgid 5088 (syz-executor149), ts 55842453591, free_ts 55925905863
 set_page_owner include/linux/page_owner.h:32 [inline]
 post_alloc_hook+0x1f3/0x230 mm/page_alloc.c:1470
 prep_new_page mm/page_alloc.c:1478 [inline]
 get_page_from_freelist+0x2cbd/0x2d70 mm/page_alloc.c:3457
 __alloc_pages_noprof+0x256/0x6c0 mm/page_alloc.c:4715
 alloc_pages_mpol_noprof+0x3e8/0x680 mm/mempolicy.c:2263
 pagetable_alloc_noprof include/linux/mm.h:2881 [inline]
 __pte_alloc_one_noprof include/asm-generic/pgalloc.h:70 [inline]
 pte_alloc_one+0x88/0x5d0 arch/x86/mm/pgtable.c:33
 do_fault_around mm/memory.c:5000 [inline]
 do_read_fault mm/memory.c:5039 [inline]
 do_fault mm/memory.c:5178 [inline]
 do_pte_missing mm/memory.c:3948 [inline]
 handle_pte_fault+0x2ba7/0x7130 mm/memory.c:5502
 __handle_mm_fault mm/memory.c:5645 [inline]
 handle_mm_fault+0x10df/0x1ba0 mm/memory.c:5810
 faultin_page mm/gup.c:1339 [inline]
 __get_user_pages+0x6ef/0x1590 mm/gup.c:1638
 populate_vma_page_range+0x264/0x330 mm/gup.c:2078
 __mm_populate+0x27a/0x460 mm/gup.c:2181
 mm_populate include/linux/mm.h:3442 [inline]
 __do_sys_remap_file_pages mm/mmap.c:3177 [inline]
 __se_sys_remap_file_pages+0x7a1/0x9a0 mm/mmap.c:3103
 do_syscall_x64 arch/x86/entry/common.c:52 [inline]
 do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
page last free pid 5097 tgid 5088 stack trace:
 reset_page_owner include/linux/page_owner.h:25 [inline]
 free_pages_prepare mm/page_alloc.c:1089 [inline]
 free_unref_folios+0x103a/0x1b00 mm/page_alloc.c:2669
 folios_put_refs+0x76e/0x860 mm/swap.c:1020
 free_pages_and_swap_cache+0x5c8/0x690 mm/swap_state.c:335
 __tlb_batch_free_encoded_pages mm/mmu_gather.c:136 [inline]
 tlb_batch_pages_flush mm/mmu_gather.c:149 [inline]
 tlb_flush_mmu_free mm/mmu_gather.c:366 [inline]
 tlb_flush_mmu+0x3a3/0x680 mm/mmu_gather.c:373
 tlb_finish_mmu+0xd4/0x200 mm/mmu_gather.c:465
 exit_mmap+0x44f/0xc80 mm/mmap.c:3395
 __mmput+0x115/0x390 kernel/fork.c:1341
 exit_mm+0x220/0x310 kernel/exit.c:565
 do_exit+0x9aa/0x28e0 kernel/exit.c:861
 do_group_exit+0x207/0x2c0 kernel/exit.c:1023
 get_signal+0x16a1/0x1740 kernel/signal.c:2909
 arch_do_signal_or_restart+0x96/0x830 arch/x86/kernel/signal.c:310
 exit_to_user_mode_loop kernel/entry/common.c:111 [inline]
 exit_to_user_mode_prepare include/linux/entry-common.h:328 [inline]
 __syscall_exit_to_user_mode_work kernel/entry/common.c:207 [inline]
 syscall_exit_to_user_mode+0xc9/0x370 kernel/entry/common.c:218
 do_syscall_64+0x100/0x230 arch/x86/entry/common.c:89
 entry_SYSCALL_64_after_hwframe+0x77/0x7f

Memory state around the buggy address:
 ffff88807bfb6f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ffff88807bfb6f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff88807bfb7000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
                   ^
 ffff88807bfb7080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
 ffff88807bfb7100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
==================================================================

Crashes (8):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2024/06/09 14:36 linux-next d35b2284e966 82c05ab8 .config console log report syz / log C [disk image] [vmlinux] [kernel image] ci-upstream-linux-next-kasan-gce-root KASAN: use-after-free Read in finish_fault
2024/06/15 13:06 linux-next a957267fa7e9 f429ab00 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-linux-next-kasan-gce-root KASAN: slab-use-after-free Read in finish_fault
2024/06/09 11:28 linux-next d35b2284e966 82c05ab8 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-linux-next-kasan-gce-root KASAN: slab-use-after-free Read in finish_fault
2024/06/15 13:57 linux-next a957267fa7e9 f429ab00 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-linux-next-kasan-gce-root KASAN: use-after-free Read in finish_fault
2024/06/15 03:11 linux-next a957267fa7e9 8d849073 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-linux-next-kasan-gce-root KASAN: use-after-free Read in finish_fault
2024/06/12 13:09 linux-next a957267fa7e9 4d75f4f7 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-linux-next-kasan-gce-root KASAN: use-after-free Read in finish_fault
2024/06/12 12:09 linux-next a957267fa7e9 4d75f4f7 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-linux-next-kasan-gce-root KASAN: use-after-free Read in finish_fault
2024/06/09 13:28 linux-next d35b2284e966 82c05ab8 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-linux-next-kasan-gce-root KASAN: use-after-free Read in finish_fault
* Struck through repros no longer work on HEAD.