From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-dy1-f177.google.com (mail-dy1-f177.google.com [74.125.82.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4E39DC8CE for ; Sat, 13 Jun 2026 00:16:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.177 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781309763; cv=none; b=cpVwe14tzAXJxwQqtvcoDyxfg0Igr2Vk3P23Z4uI8AlcFbnrKbpdfVa6sum2+tK/+iHZ+O9oytux1xT/07RiZXnXeaMioPD08YSrJp/tsIrA4OwDq5tYMJz+Bi4PWOwOhm1lnOgmGR0lbyKFhmcTVBQ3oEbHzvXIGj9Qh9QPedU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781309763; c=relaxed/simple; bh=3mIrQv3WpvlY+EfkZB3CJZChLrBsS5D/rkzIVmJvoGM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Lf3Pp9Pj21q4BqZFsBhSm8tvOAFTvJJxpgCfRhX90KlkYuwJGHmTH8R1BBh+fprtf+WTRal4YRnujetHVqJpnyPQoQdqaV0C98maUyt0zBs7SzL83HnjlIHKmzBtPNQWkOcHfrSYbvT3gxHKrSod+SOIn742Y0zbWGVjNU6kpPg= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=iG2voEx4; arc=none smtp.client-ip=74.125.82.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="iG2voEx4" Received: by mail-dy1-f177.google.com with SMTP id 5a478bee46e88-304cf518c9dso2526625eec.1 for ; Fri, 12 Jun 2026 17:16:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781309761; x=1781914561; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=SuJzKaRSGxJkPHNvyqpPBYAgFRjs9AeFn6eOwOF+Ba8=; b=iG2voEx43FJ+CXShAnkMe0AQ63CKLQc1eKUy9pU1bwFSS3Rrg1eoVTjPg3PiPEfWzG lYMYIg5gbgMhez/gkkW+OJyIxeKWT84VCkCaLQSrPAOFmUcXpec7jVND06rnr+dClHKF NghiDIRGmylAgNpc5Nf8lWWYmhK2jMXQXbF05cj/+7OhyfhNpEcwzM6/stw6Q1xIIuhm x5Q9TMRTYow63JZzgKJXFzDLt1jpf6CCymypvyPkuBXkYC+C85i3MdBtE3bX+AS/RtgL LLk2szhgul597fIN41Q5gWF1xL+zJWsHAZkHk0mJZG5aQAlInxKNEPkqDUeCAGenoFCG 3hgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781309761; x=1781914561; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=SuJzKaRSGxJkPHNvyqpPBYAgFRjs9AeFn6eOwOF+Ba8=; b=RCrJOa/t1vVE5b/9bdXLe7I/bAdRhW3aQR60bOaFtq556oohF+kuWWVltD9iuhsC7D FMh0J4dXtfwKA4aA9HpqVNZM3hrXscy+ziSlRtR8VfZGN1HmQEZYBQN1OHtBwL5B90+l MZeFb4HNMLiNtUXGOMmvudQ5g9PQNBjQmg5ZsZrnUilSwed/1Pk4JcD0+5HcHjLgpz1u ujScFWC4QgcVI6rUQ2CaO7+n+RuFY7lSwkyc1g/xA+GxZu4xLJ1MXJmX0TrtlDWl39vd 3XtOFcSwZPdlzGDwigue5iPjU/cx8iYjdBoeKveMORCKYiEFtgFUVVAjMLHawU6kR856 z6ww== X-Forwarded-Encrypted: i=1; AFNElJ80cXhwh8pBQAMmKMWK1JSPokUI584l2EXVktcBneFAzDpcJCvOlnf/2WGe+ViQtvztU9I1c/jM7tEbp3M=@vger.kernel.org X-Gm-Message-State: AOJu0YxfYeIEwbxhEQ7C5cMWyU81t/fQPxM2FplsWwILAfWfi/bIEqur dILyJRzTnaCRWgPQViXCVpsm/OwxRBaTrZq0W+wMEFkxZY57/rB80Xry X-Gm-Gg: Acq92OFt5+PBHBj8nwDZl9/zbcIDjfpHDAEsjnIgaVc5GZBSNkp/GczzYsal6UkyfZx Aw4i+CUguIDDpV+SZly2ub4rgbHI7nfXS4fxrg6maoKGyFGhCU3DvDHEt1R90yTpOTLDQA/cjk5 VewynG43aSAR2bQgsl5eJ90KVbellJ43jgvvLY6iuKCgki2JDLEGk+f4shTzQ6/Wrqj4vALDq6v Ft9VqT8L3yH0AZIH0Da1Vs3Bp4N/6GQc09KWyy81M7YufzmiC9HXBGq1dNotyzz6K7JE6oXOfMB BD6RgRLn9V/PJvCaERHyQ+FJAjkC3LX4D+I72Cvb2lgo1nJUzQw4bTOSQkudzN8COMXawTmvsat 6y3swza/r8OdA1fkpnUxY8Rz4hHBvt9uJ+tclAvPQE5lv7vXCkP6ycAX+NQ4Txad2dULSx/u5dy jtzFXqWqP05zTdTVC0Gt5WtI/W9oNzUt0VPIyCdrVHf7ofRYRbqx2B15K5+pg9ScLKMMAZSd8bo LG7 X-Received: by 2002:a05:7300:5781:b0:304:4f23:542d with SMTP id 5a478bee46e88-30936861869mr1123012eec.11.1781309761254; Fri, 12 Jun 2026 17:16:01 -0700 (PDT) Received: from pop-os.scu.edu ([129.210.115.107]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3081e4898c0sm5710567eec.3.2026.06.12.17.16.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 12 Jun 2026 17:16:00 -0700 (PDT) From: Cong Wang To: Andy Lutomirski Cc: Kees Cook , linux-kernel@vger.kernel.org, Will Drewry , Christian Brauner Subject: [RFC PATCH v3 1/3] mm: add __do_mmap() and vm_mmap_seal_remote() Date: Fri, 12 Jun 2026 17:15:31 -0700 Message-ID: <20260613001533.314739-2-xiyou.wangcong@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260613001533.314739-1-xiyou.wangcong@gmail.com> References: <20260613001533.314739-1-xiyou.wangcong@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Add __do_mmap(), a variant of do_mmap() that installs the mapping into a caller-supplied mm rather than current->mm. do_mmap() becomes a thin wrapper that passes current->mm, so all existing callers and the public do_mmap() signature are unchanged; the same split is applied in the nommu do_mmap(). mmap_region()/__mmap_region() gain an mm argument (their sole caller is __do_mmap()) so the target mm flows down to where the VMA is inserted. __do_mmap() is mm-internal, declared in mm/internal.h. On top of that, add vm_mmap_seal_remote() in mm/util.c, a high-level entry point that installs a mapping into a caller-specified mm. The intended consumer is seccomp_unotify, where an unprivileged supervisor needs to install a sealed pinned memfd region in a supervised task's address space without target-side cooperation (the existing mseal-based pinned-memfd flow only worked if the target installed its own mmap+mseal during a trusted setup window, which is unavailable for fork+execve sandbox wrappers). LSM hooks (security_mmap_file, fsnotify_mmap_perm) run against current, the supervisor installing the mapping, not the target mm's owner. This matches the supervisor-installs-into-target mental model and parallels pidfd_getfd()'s cross-task fd install. Cross-task authorization is left to the caller; this primitive performs no ptrace_may_access check. The seccomp consumer gates on listener-fd ownership. Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Cong Wang --- include/linux/mm.h | 2 ++ mm/internal.h | 5 +++++ mm/mmap.c | 29 ++++++++++++++++++--------- mm/nommu.c | 12 ++++++++++- mm/util.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++ mm/vma.c | 18 ++++++++--------- mm/vma.h | 6 +++--- 7 files changed, 100 insertions(+), 22 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index fc2acedf0b76..dd14a32f76d3 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -4118,6 +4118,8 @@ extern unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, vm_flags_t vm_flags, unsigned long pgoff, unsigned long *populate, struct list_head *uf); +unsigned long vm_mmap_seal_remote(struct mm_struct *mm, struct file *file, + unsigned long addr, unsigned long len, unsigned long pgoff); extern int do_vmi_munmap(struct vma_iterator *vmi, struct mm_struct *mm, unsigned long start, size_t len, struct list_head *uf, bool unlock); diff --git a/mm/internal.h b/mm/internal.h index 5a2ddcf68e0b..897c4e08e0b1 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -1437,6 +1437,11 @@ extern unsigned long __must_check vm_mmap_pgoff(struct file *, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); +unsigned long __do_mmap(struct mm_struct *mm, struct file *file, + unsigned long addr, unsigned long len, unsigned long prot, + unsigned long flags, vm_flags_t vm_flags, unsigned long pgoff, + unsigned long *populate, struct list_head *uf); + extern void set_pageblock_order(void); unsigned long reclaim_pages(struct list_head *folio_list); unsigned int reclaim_clean_pages_from_list(struct zone *zone, diff --git a/mm/mmap.c b/mm/mmap.c index 5754d1c36462..c9e437effd9c 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -277,7 +277,7 @@ static inline bool file_mmap_ok(struct file *file, struct inode *inode, } /** - * do_mmap() - Perform a userland memory mapping into the current process + * __do_mmap() - Perform a userland memory mapping into @mm's * address space of length @len with protection bits @prot, mmap flags @flags * (from which VMA flags will be inferred), and any additional VMA flags to * apply @vm_flags. If this is a file-backed mapping then the file is specified @@ -307,8 +307,11 @@ static inline bool file_mmap_ok(struct file *file, struct inode *inode, * start of a VMA, rather only the start of a valid mapped range of length * @len bytes, rounded down to the nearest page size. * - * The caller must write-lock current->mm->mmap_lock. + * The caller must write-lock @mm->mmap_lock. do_mmap() is the common + * wrapper that targets current->mm. * + * @mm: The mm_struct to install the mapping into. The caller must hold a + * reference and write-lock its mmap_lock. * @file: An optional struct file pointer describing the file which is to be * mapped, if a file-backed mapping. * @addr: If non-zero, hints at (or if @flags has MAP_FIXED set, specifies) the @@ -333,13 +336,12 @@ static inline bool file_mmap_ok(struct file *file, struct inode *inode, * Returns: Either an error, or the address at which the requested mapping has * been performed. */ -unsigned long do_mmap(struct file *file, unsigned long addr, - unsigned long len, unsigned long prot, - unsigned long flags, vm_flags_t vm_flags, - unsigned long pgoff, unsigned long *populate, - struct list_head *uf) +unsigned long __do_mmap(struct mm_struct *mm, struct file *file, + unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + vm_flags_t vm_flags, unsigned long pgoff, + unsigned long *populate, struct list_head *uf) { - struct mm_struct *mm = current->mm; int pkey = 0; *populate = 0; @@ -557,7 +559,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr, vm_flags |= VM_NORESERVE; } - addr = mmap_region(file, addr, len, vm_flags, pgoff, uf); + addr = mmap_region(mm, file, addr, len, vm_flags, pgoff, uf); if (!IS_ERR_VALUE(addr) && ((vm_flags & VM_LOCKED) || (flags & (MAP_POPULATE | MAP_NONBLOCK)) == MAP_POPULATE)) @@ -565,6 +567,15 @@ unsigned long do_mmap(struct file *file, unsigned long addr, return addr; } +unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + vm_flags_t vm_flags, unsigned long pgoff, + unsigned long *populate, struct list_head *uf) +{ + return __do_mmap(current->mm, file, addr, len, prot, flags, + vm_flags, pgoff, populate, uf); +} + unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff) diff --git a/mm/nommu.c b/mm/nommu.c index ed3934bc2de4..7f2136129c72 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1009,7 +1009,8 @@ static int do_mmap_private(struct vm_area_struct *vma, /* * handle mapping creation for uClinux */ -unsigned long do_mmap(struct file *file, +unsigned long __do_mmap(struct mm_struct *mm, + struct file *file, unsigned long addr, unsigned long len, unsigned long prot, @@ -1246,6 +1247,15 @@ unsigned long do_mmap(struct file *file, return -ENOMEM; } +unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + vm_flags_t vm_flags, unsigned long pgoff, + unsigned long *populate, struct list_head *uf) +{ + return __do_mmap(current->mm, file, addr, len, prot, flags, + vm_flags, pgoff, populate, uf); +} + unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff) diff --git a/mm/util.c b/mm/util.c index 3cc949a0b7ed..ecc2087f744a 100644 --- a/mm/util.c +++ b/mm/util.c @@ -588,6 +588,56 @@ unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr, return ret; } +/** + * vm_mmap_seal_remote - install a sealed PROT_READ MAP_SHARED file mapping + * into @mm, without target-side cooperation. + * @mm: Target mm; caller holds a reference (e.g. get_task_mm()). + * @file: Backing file. + * @addr: Page-aligned address (MAP_FIXED_NOREPLACE: -EEXIST if occupied). + * @len: Length in bytes (page-aligned). + * @pgoff: Page offset into @file. + * + * The VMA is created VM_SEALED, so it is immediately immutable against the + * target mm's owner and its CLONE_VM peers. LSM/fsnotify hooks run against + * %current; cross-task authorization is the caller's responsibility (no + * ptrace_may_access check). + * + * Returns the mapped address on success, or a negative errno. + */ +unsigned long vm_mmap_seal_remote(struct mm_struct *mm, struct file *file, + unsigned long addr, unsigned long len, unsigned long pgoff) +{ + const unsigned long prot = PROT_READ; + const unsigned long flags = MAP_SHARED | MAP_FIXED_NOREPLACE; + loff_t off = (loff_t)pgoff << PAGE_SHIFT; + unsigned long ret; + unsigned long populate; + LIST_HEAD(uf); + + if (WARN_ON_ONCE(!mm)) + return -EINVAL; + if (!VM_SEALED) /* sealing unavailable (e.g. !CONFIG_64BIT) */ + return -EOPNOTSUPP; + + ret = security_mmap_file(file, prot, flags); + if (!ret) + ret = fsnotify_mmap_perm(file, prot, off, len); + if (!ret) { + if (mmap_write_lock_killable(mm)) + return -EINTR; + ret = __do_mmap(mm, file, addr, len, prot, flags, VM_SEALED, + pgoff, &populate, &uf); + mmap_write_unlock(mm); + userfaultfd_unmap_complete(mm, &uf); + /* + * Do not mm_populate() against a foreign mm; the target task + * will fault pages in on first access. + */ + } + return ret; +} +EXPORT_SYMBOL_GPL(vm_mmap_seal_remote); + /* * Perform a userland memory mapping into the current process address space. See * the comment for do_mmap() for more details on this operation in general. diff --git a/mm/vma.c b/mm/vma.c index d90791b00a7b..fdd14349f719 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -2729,11 +2729,10 @@ static bool can_set_ksm_flags_early(struct mmap_state *map) return false; } -static unsigned long __mmap_region(struct file *file, unsigned long addr, - unsigned long len, vma_flags_t vma_flags, +static unsigned long __mmap_region(struct mm_struct *mm, struct file *file, + unsigned long addr, unsigned long len, vma_flags_t vma_flags, unsigned long pgoff, struct list_head *uf) { - struct mm_struct *mm = current->mm; struct vm_area_struct *vma = NULL; bool have_mmap_prepare = file && file->f_op->mmap_prepare; VMA_ITERATOR(vmi, mm, addr); @@ -2827,15 +2826,16 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr, * Returns: Either an error, or the address at which the requested mapping has * been performed. */ -unsigned long mmap_region(struct file *file, unsigned long addr, - unsigned long len, vm_flags_t vm_flags, - unsigned long pgoff, struct list_head *uf) +unsigned long mmap_region(struct mm_struct *mm, struct file *file, + unsigned long addr, unsigned long len, + vm_flags_t vm_flags, unsigned long pgoff, + struct list_head *uf) { unsigned long ret; bool writable_file_mapping = false; const vma_flags_t vma_flags = legacy_to_vma_flags(vm_flags); - mmap_assert_write_locked(current->mm); + mmap_assert_write_locked(mm); /* Check to see if MDWE is applicable. */ if (map_deny_write_exec(&vma_flags, &vma_flags)) @@ -2854,13 +2854,13 @@ unsigned long mmap_region(struct file *file, unsigned long addr, writable_file_mapping = true; } - ret = __mmap_region(file, addr, len, vma_flags, pgoff, uf); + ret = __mmap_region(mm, file, addr, len, vma_flags, pgoff, uf); /* Clear our write mapping regardless of error. */ if (writable_file_mapping) mapping_unmap_writable(file->f_mapping); - validate_mm(current->mm); + validate_mm(mm); return ret; } diff --git a/mm/vma.h b/mm/vma.h index 8e4b61a7304c..4f5222ad2e9d 100644 --- a/mm/vma.h +++ b/mm/vma.h @@ -459,9 +459,9 @@ bool vma_wants_writenotify(struct vm_area_struct *vma, pgprot_t vm_page_prot); int mm_take_all_locks(struct mm_struct *mm); void mm_drop_all_locks(struct mm_struct *mm); -unsigned long mmap_region(struct file *file, unsigned long addr, - unsigned long len, vm_flags_t vm_flags, unsigned long pgoff, - struct list_head *uf); +unsigned long mmap_region(struct mm_struct *mm, struct file *file, + unsigned long addr, unsigned long len, vm_flags_t vm_flags, + unsigned long pgoff, struct list_head *uf); int do_brk_flags(struct vma_iterator *vmi, struct vm_area_struct *brkvma, unsigned long addr, unsigned long request, -- 2.43.0