Re: [PATCH] fs/proc/task_mmu: pin mm_users in do_pagemap_scan()
From: David Hildenbrand (Arm)
Date: Sun Oct 25 2026 - 06:56:28 EST
On 10/20/26 02:46, Andrew Morton wrote:
> On Wed, 14 Oct 2026 16:55:40 +0700 Ngoc Quy <ngocquy0700@xxxxxxxxx> wrote:
>
>> The PAGEMAP_SCAN ioctl takes mmap_read_lock() on an mm obtained from
>> /proc/<pid>/pagemap without holding a reference to mm_users.
>>
>> proc_mem_open() deliberately grabs only mm_count, so a consumer that
>> touches the address space has to call mmget_not_zero() first. Every
>> other consumer in this file does -- m_start(), do_procmap_query(),
>> show_smaps_rollup() and pagemap_read() -- but do_pagemap_scan() does
>> not.
>>
>> Without that reference the target can reach exit_mmap() concurrently,
>> and exit_mmap() calls unmap_vmas() while holding mmap_lock only for
>> reading, which does not exclude the scanner. For ordinary VMAs this is
>> harmless, as free_pgtables() and remove_vma() run afterwards under
>> mmap_write_lock. A shared hugetlb VMA is different: __hugetlb_zap_end()
>> frees the VMA's hugetlb_vma_lock inside the read-locked phase.
>>
>> walk_hugetlb_range() can therefore load vma->vm_private_data, have the
>> lock freed underneath it, and then down_read() a freed rw_semaphore.
>> The load happens before the freeing side clears the pointer, so this is
>> a use-after-free and not a NULL dereference. Any unprivileged user can
>> reach it with no huge pages configured, as hugetlb_reserve_pages()
>> allocates the lock before the VM_NORESERVE early return.
>>
>> Pin mm_users across the scan, the way the other consumers do. mmput()
>> from the exiting task can then no longer reach exit_mmap() while a walk
>> is in progress.
>
> All looks good and checks out for me, thanks.
>
> Except...
>
>> --- a/fs/proc/task_mmu.c
>> +++ b/fs/proc/task_mmu.c
>> @@ -3231,15 +3231,18 @@ static long do_pagemap_scan(struct mm_struct *mm, unsigned long uarg)
>> size_t n_ranges_out = 0;
>> int ret;
>>
>> + if (!mm || !mmget_not_zero(mm))
>> + return -ESRCH;
>
> Why the new test for !mm? The current call chain guarantees this is a
> cant-happen and the current do_pagemap_scan will cheerfully oops if
> mm==NULL.
I'd just keep it for consistency with all the other checks in this file?
--
Cheers,
David