linux/mm/mseal.c
Lorenzo Stoakes (ARM) 1b300f679a mm/mseal: remove further superfluous comments, do_mseal()
There's no need to abstract do_mseal() any longer so put the system call
implementation in the system call declaration.

The comment around do_mseal() is strangely formatted, overly long and adds
a lot of superfluous information that the code already provides, so boil
it down to the essentials.

Link: https://lore.kernel.org/20260717-mseal-fixups-v2-3-0daa0014b813@kernel.org
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Pedro Falcato <pfalcato@suse.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Jann Horn <jannh@google.com>
Cc: Kees Cook <kees@kernel.org>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2026-08-06 18:57:12 -07:00

144 lines
3.2 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Implement mseal() syscall.
*
* Copyright (c) 2023,2024 Google, Inc.
*
* Author: Jeff Xu <jeffxu@chromium.org>
*/
#include <linux/mempolicy.h>
#include <linux/minmax.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/mm_inline.h>
#include <linux/syscalls.h>
#include <linux/sched.h>
#include "internal.h"
static bool range_contains_unmapped(unsigned long start, unsigned long end)
{
VMA_ITERATOR(vmi, current->mm, start);
unsigned long prev_end = start;
struct vm_area_struct *vma;
for_each_vma_range(vmi, vma, end) {
if (vma->vm_start > prev_end)
return true;
prev_end = vma->vm_end;
}
return prev_end < end;
}
static int __mseal_range(unsigned long start, unsigned long end)
{
VMA_ITERATOR(vmi, current->mm, start);
struct vm_area_struct *vma, *prev;
/* We know there are no gaps so this will be non-NULL. */
vma = vma_iter_load(&vmi);
prev = vma_prev(&vmi);
if (start > vma->vm_start)
prev = vma;
for_each_vma_range(vmi, vma, end) {
const unsigned long curr_start = max(vma->vm_start, start);
const unsigned long curr_end = min(vma->vm_end, end);
if (!vma_test(vma, VMA_SEALED_BIT)) {
vma_flags_t vma_flags = vma->flags;
vma_flags_set(&vma_flags, VMA_SEALED_BIT);
vma = vma_modify_flags(&vmi, prev, vma, curr_start,
curr_end, &vma_flags);
if (IS_ERR(vma))
return PTR_ERR(vma);
vma_start_write(vma);
vma_set_flags(vma, VMA_SEALED_BIT);
}
prev = vma;
}
return 0;
}
static int mseal_range(unsigned long start, unsigned long end)
{
int err;
err = mmap_write_lock_killable(current->mm);
if (err)
return err;
if (range_contains_unmapped(start, end))
err = -ENOMEM;
else
err = __mseal_range(start, end);
mmap_write_unlock(current->mm);
return err;
}
/**
* mseal_mmap_page_zero() - If the MMAP_PAGE_ZERO personality is set, mseal()
* the page mapped at address zero.
*/
void mseal_mmap_page_zero(void)
{
int err;
if (WARN_ON_ONCE(!(current->personality & MMAP_PAGE_ZERO)))
return;
err = mseal_range(0, PAGE_SIZE);
if (err)
pr_warn_ratelimited("pid=%d, couldn't seal address 0, ret=%d.\n",
task_pid_nr(current), err);
}
/*
* Seal VMAs in the specified input range to prevent an attacker replacing what
* is mapped in the range with something else.
*
* Disallows:
* - VMA unmapping, remapping or shrinking.
* - Overwriting the VMA with another one via mmap(), mremap() or similar.
* - Alteration of properties via mprotect()/pkey_mprotect().
* - Destructive madvise() behaviours (like MADV_DONTNEED) on anonymous read-only
* ranges.
*
* Since unmapped ranges can be mapped at any time, the input range must span
* mapped ranges only.
*
* The flags parameter is currently reserved.
*/
SYSCALL_DEFINE3(mseal, unsigned long, start, size_t, len, unsigned long, flags)
{
size_t len_aligned;
unsigned long end;
/* Verify flags not set. */
if (flags)
return -EINVAL;
start = untagged_addr(start);
if (!PAGE_ALIGNED(start))
return -EINVAL;
len_aligned = PAGE_ALIGN(len);
/* Check to see whether len was rounded up from small -ve to zero. */
if (len && !len_aligned)
return -EINVAL;
end = start + len_aligned;
if (end < start)
return -EINVAL;
if (end == start)
return 0;
return mseal_range(start, end);
}