On Mon, 5 Aug 2024 at 11:11, Jeff Xu <jeffxu@xxxxxxxxxx> wrote: > > One thing that you can't walk around is that can_modify_mm must be > called prior to arch_unmap, that means in-place check for the munmap > is not possible. Actually, we should move 'arch_unmap()'. There is only one user of it, and it's pretty pointless. (Ok, there are two users - x86 also has an 'arch_unmap()', but it's empty). The reason I say that the current user of arch_unmap() is pointless is because this is what the powerpc user does: static inline void arch_unmap(struct mm_struct *mm, unsigned long start, unsigned long end) { unsigned long vdso_base = (unsigned long)mm->context.vdso; if (start <= vdso_base && vdso_base < end) mm->context.vdso = NULL; } and that would make sense if we didn't have an actual 'vma' that matched the vdso. But we do. I think this code may predate the whole "create a vma for the vdso" code. Or maybe it was just always confused. Anyway, what the code *should* do is that we should just have a ->close() function for special mappings, and call that in special_mapping_close(). This is an ENTIRELY UNTESTED patch that gets rid of this horrendous wart. Michael / Nick / Christophe? Note that I didn't even compile-test this on x86-64, much less on powerpc. So please consider this a "maybe something like this" patch, but that 'arch_unmap()' really is pretty nasty. Oh, and there was a bug in the error path of the powerpc vdso setup code anyway. The patch fixes that too, although considering the entirely untested nature of it, the "fixes" is laughably optimistic. Linus
arch/powerpc/include/asm/mmu_context.h | 9 --------- arch/powerpc/kernel/vdso.c | 12 +++++++++++- arch/x86/include/asm/mmu_context.h | 5 ----- include/asm-generic/mm_hooks.h | 11 +++-------- include/linux/mm_types.h | 2 ++ mm/mmap.c | 15 ++++++--------- 6 files changed, 22 insertions(+), 32 deletions(-) diff --git a/arch/powerpc/include/asm/mmu_context.h b/arch/powerpc/include/asm/mmu_context.h index 37bffa0f7918..a334a1368848 100644 --- a/arch/powerpc/include/asm/mmu_context.h +++ b/arch/powerpc/include/asm/mmu_context.h @@ -260,15 +260,6 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, extern void arch_exit_mmap(struct mm_struct *mm); -static inline void arch_unmap(struct mm_struct *mm, - unsigned long start, unsigned long end) -{ - unsigned long vdso_base = (unsigned long)mm->context.vdso; - - if (start <= vdso_base && vdso_base < end) - mm->context.vdso = NULL; -} - #ifdef CONFIG_PPC_MEM_KEYS bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write, bool execute, bool foreign); diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c index 7a2ff9010f17..4de8af43f920 100644 --- a/arch/powerpc/kernel/vdso.c +++ b/arch/powerpc/kernel/vdso.c @@ -81,12 +81,20 @@ static int vdso64_mremap(const struct vm_special_mapping *sm, struct vm_area_str return vdso_mremap(sm, new_vma, &vdso64_end - &vdso64_start); } +static int vvar_close(const struct vm_special_mapping *sm, + struct vm_area_struct *vma) +{ + struct mm_struct *mm = vma->vm_mm; + mm->context.vdso = NULL; +} + static vm_fault_t vvar_fault(const struct vm_special_mapping *sm, struct vm_area_struct *vma, struct vm_fault *vmf); static struct vm_special_mapping vvar_spec __ro_after_init = { .name = "[vvar]", .fault = vvar_fault, + .close = vvar_close, }; static struct vm_special_mapping vdso32_spec __ro_after_init = { @@ -207,8 +215,10 @@ static int __arch_setup_additional_pages(struct linux_binprm *bprm, int uses_int vma = _install_special_mapping(mm, vdso_base, vvar_size, VM_READ | VM_MAYREAD | VM_IO | VM_DONTDUMP | VM_PFNMAP, &vvar_spec); - if (IS_ERR(vma)) + if (IS_ERR(vma)) { + mm->context.vdso = NULL; return PTR_ERR(vma); + } /* * our vma flags don't have VM_WRITE so by default, the process isn't diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index 8dac45a2c7fc..80f2a3187aa6 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -232,11 +232,6 @@ static inline bool is_64bit_mm(struct mm_struct *mm) } #endif -static inline void arch_unmap(struct mm_struct *mm, unsigned long start, - unsigned long end) -{ -} - /* * We only want to enforce protection keys on the current process * because we effectively have no access to PKRU for other diff --git a/include/asm-generic/mm_hooks.h b/include/asm-generic/mm_hooks.h index 4dbb177d1150..6eea3b3c1e65 100644 --- a/include/asm-generic/mm_hooks.h +++ b/include/asm-generic/mm_hooks.h @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Define generic no-op hooks for arch_dup_mmap, arch_exit_mmap - * and arch_unmap to be included in asm-FOO/mmu_context.h for any - * arch FOO which doesn't need to hook these. + * Define generic no-op hooks for arch_dup_mmap and arch_exit_mmap + * to be included in asm-FOO/mmu_context.h for any arch FOO which + * doesn't need to hook these. */ #ifndef _ASM_GENERIC_MM_HOOKS_H #define _ASM_GENERIC_MM_HOOKS_H @@ -17,11 +17,6 @@ static inline void arch_exit_mmap(struct mm_struct *mm) { } -static inline void arch_unmap(struct mm_struct *mm, - unsigned long start, unsigned long end) -{ -} - static inline bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write, bool execute, bool foreign) { diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 485424979254..ef32d87a3adc 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -1313,6 +1313,8 @@ struct vm_special_mapping { int (*mremap)(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma); + void (*close)(const struct vm_special_mapping *sm, + struct vm_area_struct *vma); }; enum tlb_flush_reason { diff --git a/mm/mmap.c b/mm/mmap.c index d0dfc85b209b..adaaf1ef197a 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2789,7 +2789,7 @@ do_vmi_align_munmap(struct vma_iterator *vmi, struct vm_area_struct *vma, * * This function takes a @mas that is either pointing to the previous VMA or set * to MA_START and sets it up to remove the mapping(s). The @len will be - * aligned and any arch_unmap work will be preformed. + * aligned. * * Return: 0 on success and drops the lock if so directed, error and leaves the * lock held otherwise. @@ -2809,16 +2809,12 @@ int do_vmi_munmap(struct vma_iterator *vmi, struct mm_struct *mm, return -EINVAL; /* - * Check if memory is sealed before arch_unmap. - * Prevent unmapping a sealed VMA. + * Check if memory is sealed, prevent unmapping a sealed VMA. * can_modify_mm assumes we have acquired the lock on MM. */ if (unlikely(!can_modify_mm(mm, start, end))) return -EPERM; - /* arch_unmap() might do unmaps itself. */ - arch_unmap(mm, start, end); - /* Find the first overlapping VMA */ vma = vma_find(vmi, end); if (!vma) { @@ -3232,14 +3228,12 @@ int do_vma_munmap(struct vma_iterator *vmi, struct vm_area_struct *vma, struct mm_struct *mm = vma->vm_mm; /* - * Check if memory is sealed before arch_unmap. - * Prevent unmapping a sealed VMA. + * Check if memory is sealed, prevent unmapping a sealed VMA. * can_modify_mm assumes we have acquired the lock on MM. */ if (unlikely(!can_modify_mm(mm, start, end))) return -EPERM; - arch_unmap(mm, start, end); return do_vmi_align_munmap(vmi, vma, mm, start, end, uf, unlock); } @@ -3624,6 +3618,9 @@ static vm_fault_t special_mapping_fault(struct vm_fault *vmf); */ static void special_mapping_close(struct vm_area_struct *vma) { + const struct vm_special_mapping *sm = vma->vm_private_data; + if (sm->close) + sm->close(sm, vma); } static const char *special_mapping_name(struct vm_area_struct *vma)