On 10/14/26 17:46, Konstantin Khorenko wrote:
> Where arch_needs_pgtable_deposit() is true, every huge PMD needs a
> deposited page table. ppc64 in hash mode keeps the hash slot array of
> the huge page in it, so vmf_insert_pfn_pmd() deposits one for each
> special PMD it maps.
> 
> copy_huge_pmd() copies a special PMD into the child without a table.
> The child then dereferences a NULL table:
> 
> - on the first hash fault on the PMD, in __hash_page_thp();
> - on the zap at munmap() or exit, in hash__pmdp_huge_get_and_clear().
> 
> Allocate a table before taking the page table locks and deposit it
> with the copy, as insert_pmd() does.
> 
> Fixes: d1503aa9ab805 ("powerpc/64s: Add support for huge pfnmaps")
> Reported-by: Sashiko <[email protected]>
> Closes: 
> https://sashiko.dev/#/patchset/20261014-b4-prep-vstor-149847-copy_huge_pmd-recheck-special-pmd-v1-1-d225c60bc...@virtuozzo.com?part=1
> Cc: [email protected]
> Assisted-by: LLM
> Signed-off-by: Konstantin Khorenko <[email protected]>
> ---
>  mm/huge_memory.c | 11 +++++++++++
>  1 file changed, 11 insertions(+)
> 
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index bf788bb231691..2380e4e877f82 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -1961,6 +1961,11 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct 
> mm_struct *src_mm,
>       pmd = pmdp_get_lockless(src_pmd);
>       if (unlikely(pmd_present(pmd) && pmd_special(pmd) &&
>                    !is_huge_zero_pmd(pmd))) {
> +             if (arch_needs_pgtable_deposit()) {
> +                     pgtable = pte_alloc_one(dst_mm);
> +                     if (unlikely(!pgtable))
> +                             goto out;
> +             }
>               dst_ptl = pmd_lock(dst_mm, dst_pmd);
>               src_ptl = pmd_lockptr(src_mm, src_pmd);
>               spin_lock_nested(src_ptl, SINGLE_DEPTH_NESTING);
> @@ -1974,6 +1979,8 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct 
> mm_struct *src_mm,
>               if (unlikely(pmd_none(pmd))) {
>                       spin_unlock(src_ptl);
>                       spin_unlock(dst_ptl);
> +                     if (pgtable)
> +                             pte_free(dst_mm, pgtable);
>                       return -EAGAIN;
>               }
>               /*
> @@ -1983,6 +1990,10 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct 
> mm_struct *src_mm,
>                * wrongly write to the backend MMIO.
>                */
>               VM_WARN_ON_ONCE(vma_is_cow_mapping(src_vma) && pmd_write(pmd));
> +             if (pgtable) {
> +                     mm_inc_nr_ptes(dst_mm);
> +                     pgtable_trans_huge_deposit(dst_mm, dst_pmd, pgtable);
> +             }
>               goto set_pmd;

It's another sign that the pmd_special() is just entirely misplaced.

Let's get that sorted and it will just naturally fix the other case.

-- 
Cheers,

David

Reply via email to