Re: [PATCH 2/3] mm/mseal: limit scope of mseal address zero to address zero

From: Pedro Falcato

Date: Thu Jul 16 2026 - 12:07:33 EST


On Thu, Jul 16, 2026 at 02:43:10PM +0100, Lorenzo Stoakes (ARM) wrote:
> Commit 44f65d900698 ("binfmt_elf: mseal address zero") unconditionally
> provided do_mseal() to any internal kernel caller in order to address a
> corner case slated for possible removal.
>
> It also incorrectly attempts to mseal() without checking to see whether the
> mapping even succeeded.
>
> Restrict the scope to the corner case by providing mseal_mmap_page_zero()
> which asserts the MMAP_PAGE_ZERO personality.
>
> Avoid unnecessary checks in the start, end range by abstracting the actual
> mseal()'ing to mseal() and have mseal_mmap_page_zero() call that instead.
>
> Only try to seal the VMA if we mapped the VMA.
>
> Signed-off-by: Lorenzo Stoakes (ARM) <ljs@xxxxxxxxxx>
> ---
> fs/binfmt_elf.c | 7 ++-----
> include/linux/mm.h | 8 ++------
> mm/mseal.c | 48 +++++++++++++++++++++++++++++++++++-------------
> 3 files changed, 39 insertions(+), 24 deletions(-)
>
> diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
> index 16a56b6b3f6c..e3131a311995 100644
> --- a/fs/binfmt_elf.c
> +++ b/fs/binfmt_elf.c
> @@ -1353,11 +1353,8 @@ static int load_elf_binary(struct linux_binprm *bprm)
> emulate the SVr4 behavior. Sigh. */
> error = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC,
> MAP_FIXED | MAP_PRIVATE, 0);

I think pedantically load_elf_binary() should error out on mmap error, no?
Not that this personality is used in the big 2026 however...

> -
> - retval = do_mseal(0, PAGE_SIZE, 0);
> - if (retval)
> - pr_warn_ratelimited("pid=%d, couldn't seal address 0, ret=%d.\n",
> - task_pid_nr(current), retval);
> + if (!error)
> + mseal_mmap_page_zero();
> }
>
> regs = current_pt_regs();
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 550fb92957d1..87feaa5a2b78 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -5291,13 +5291,9 @@ int reserve_mem_find_by_name(const char *name, phys_addr_t *start, phys_addr_t *
> int reserve_mem_release_by_name(const char *name);
>
> #ifdef CONFIG_64BIT
> -int do_mseal(unsigned long start, size_t len_in, unsigned long flags);
> +void mseal_mmap_page_zero(void);
> #else
> -static inline int do_mseal(unsigned long start, size_t len_in, unsigned long flags)
> -{
> - /* noop on 32 bit */
> - return 0;
> -}
> +static inline void mseal_mmap_page_zero(void) {}
> #endif
>
> /*
> diff --git a/mm/mseal.c b/mm/mseal.c
> index 207fea89c61e..5930551d84f2 100644
> --- a/mm/mseal.c
> +++ b/mm/mseal.c
> @@ -32,7 +32,7 @@ static bool range_contains_unmapped(unsigned long start, unsigned long end)
> return prev_end < end;
> }
>
> -static int mseal_apply(unsigned long start, unsigned long end)
> +static int __mseal(unsigned long start, unsigned long end)

Why? I think the previous name is perfectly cromulent.

> {
> struct vm_area_struct *vma, *prev;
> VMA_ITERATOR(vmi, current->mm, start);
> @@ -66,6 +66,38 @@ static int mseal_apply(unsigned long start, unsigned long end)
> return 0;
> }
>
> +static int mseal(unsigned long start, unsigned long end)

I don't like that you go start - end on a function called "mseal". The actual
system call goes start - len. It just looks confusing :) So either rename it
to mseal_range(), or make it take a start, length pair.

The overall spirit of the change LGTM however.

--
Pedro