From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pg1-f170.google.com (mail-pg1-f170.google.com [209.85.215.170]) (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 A93403CBE7E for ; Wed, 15 Jul 2026 21:20:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.170 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784150441; cv=none; b=UMbsQw+4D8Ss8iQ6D3HQIGNnjO68nbUNH/ULxfqFKJYgTlfARz3EcDWBcS6QswOjWFGIkU+lodjJhXknhD1FDzwGGvKOrLer3HfkAZGGRuGOsAVkRW9HXVb+PYlu8UBHfkWRCPzaovpxx+lDgmCmYX17Rl1B0CObEqjcFG569RI= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784150441; c=relaxed/simple; bh=YxpUUfszQqKmsSbH4HBUPv0948rlEJyJRm4hYyF3F6c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=toza5SPpMCrEgKM2Hwgl/Py3QDEDT0w5m4Dl/Zlo/YawwD+F8o9Af8RBS0jPPLP+lmKJKjQyOBDoGDRPQaFirrv/7o3BIdc4hzqGIFqUWP9L1JVSHcTdYJ2x2CO9lR9/FSPDvGdoZ2I7Y/4b6ItFe9sOKr4Dqr2euoe2xAfujis= 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=j3eCqpbH; arc=none smtp.client-ip=209.85.215.170 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="j3eCqpbH" Received: by mail-pg1-f170.google.com with SMTP id 41be03b00d2f7-ca80d708489so1840009a12.1 for ; Wed, 15 Jul 2026 14:20:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784150438; x=1784755238; 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:content-type; bh=I4/w1PFAlDDnNDEoj+FOvtFg06IZcqV0NzLqb9Lu6qM=; b=j3eCqpbHTxCn+LJCgV19vO384DlWSPkYstk0xSPMVyMSY1xCdSoZ7AQ3pi4L+q3sGk ghKpb2tfk3OfLkidlkkH+RIM3ubXQnYieaCWZQ1SymRpyw36gSscZ5LkaQD76v7mCvWV BpnubS9x3wekmPrceF9BpAA4UwDpyzhqEyO5QiHPDztPErOG4ZiEJwUgAar3EKlQVGmM Aop/S7kqoCH39zX1wsy353/XPy65ImSROffue1tn5q6pUiI+rSsi/LpV9vhz/Et3bRwf naqfAuZDSt726j0Ml3LmGcMpkjBcxmz/XrmIpYNisv3492k5d7m3BvZCmXhAhlce/F7/ 7Vwg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784150438; x=1784755238; 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:content-type; bh=I4/w1PFAlDDnNDEoj+FOvtFg06IZcqV0NzLqb9Lu6qM=; b=lUTgepdAdPjGfemeVP3FIN4YHDwDSwf9vT1uH+UiVOogcxDMEgLSLbtyxtzfuKXn5+ aPrJHk9U33hrKoIsTAGcsN2tEuc6zhPY1asyjRLydK0I/iQrlDXnw80ENDwvxRkbjT/s +aemUF3o88AWlYo4l0k8T1Z6YzOfW0svbLWi8XQctIlcf4bf3blhy6/LSMm5aivKLDZw gsrfzfxQPCRfMRr3fHZnYVp87metOgPrqzp7a0EJR7UDR0bBHoR8S/aPSiYJBCrZ86fE Cf55FM/JBnHphGGmumu635C9qyjWojHUZPbIZvj67gTiLDMUBtyMWQFwFRFOx4mUfNDI 0mcg== X-Forwarded-Encrypted: i=1; AHgh+RqcDx7wbPfcSBs8s+04Lun6nkPIVN2XY4x9BEVb18qPWK/hKbAFfojcVyIFEUN8Yv/oXhoJY1vpl72zcDA=@vger.kernel.org X-Gm-Message-State: AOJu0Yz1lHxkApGO9iqniF4UlqR3tpUy/cVHFE2vR9ju/ZxUjlDLlB8l rKeSDQhfAIZ06zuTd4bsFBP9uMUsVGMXF+BDhmQQUNLDM/VEeWyRHTTi X-Gm-Gg: AfdE7ck3LVE9G34TtCcdMPsIFYbPj1w0XIXM4z+pa7zu7T3z7JS0gp+LGpnXDKIYHTX ZuWSY2k/T5+YCRBqflKMbnPH1Vgtt/jfIApT7uagRJPsLY4rZAUsRZyzVvZoS/95o0/YrZaY5V1 83jhtwnDLpXWe1Vq8n75T+hYqjdIopsLi9Rt02gHTZe/lF9BKz9qI5aywuMF6f7G83EoBGsup1F 9pYHo5/+vOIEdkB2Ob7PCOeV496r2sXwXfLGEyg1tSt6NKocO80qbMmZ6ITxJ9BNoe1TEh7Vsbl BNDowTIzgI81BLIuLsIX4OzubzlmtXL45CcfXfdrKCRFwixnGpP9JnFLOFvbkE4wH14AcmN27wP NtLqcV+Gx8FE/diSeccsoAoY3Z9F0wEZqfKyPvZjboe/8VZLvg4ccnzYB2ba8sbKr9Liu3DA3iz +kTRYm4RLf6kXP57PU22PQx9X1txbFVFnggi8MH/lWfxG3yLZ5yo353yZ5Bg== X-Received: by 2002:a05:6a21:4508:b0:3b4:8c67:51d4 with SMTP id adf61e73a8af0-3c384e1a96bmr1002656637.39.1784150437690; Wed, 15 Jul 2026 14:20:37 -0700 (PDT) Received: from pop-os.tail4adac7.ts.net ([129.210.115.107]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-13cd403a7f7sm4126844c88.0.2026.07.15.14.20.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Jul 2026 14:20:37 -0700 (PDT) From: Cong Wang To: Andy Lutomirski Cc: Kees Cook , linux-kernel@vger.kernel.org, Will Drewry , Christian Brauner , Andrew Morton , linux-mm@kvack.org, Cong Wang Subject: [PATCH v6 2/8] mm: add __do_mmap() and vm_mmap_remote()/vm_munmap_remote() Date: Wed, 15 Jul 2026 14:20:01 -0700 Message-ID: <20260715212007.2382846-3-xiyou.wangcong@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260715212007.2382846-1-xiyou.wangcong@gmail.com> References: <20260715212007.2382846-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 From: Cong Wang 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 wrapper passing current->mm and keeps the current-task READ_IMPLIES_EXEC handling. mmap_region()/__mmap_region() gain an mm argument so the target mm reaches VMA insertion. On top of that, add vm_mmap_remote() and vm_munmap_remote() in mm/util.c: entry points that install / remove a mapping in a caller-specified mm without target-side cooperation. The intended user is the seccomp unotify syscall redirect. Kernel-chosen placement resolves through __get_unmapped_area() against the target mm, so (via the previous patch) the file's own ->get_unmapped_area() runs and file-specific placement is honored. The result is bounds-checked against the target's mm->task_size, and the munmap machinery is likewise threaded with the target mm so counter bookkeeping and range checks apply to it rather than current->mm. vm_munmap_remote() delivers the target's UFFD_EVENT_UNMAP. Both are MMU-only and guarded with CONFIG_MMU, with -EOPNOTSUPP stubs so NOMMU keeps linking. LSM/fsnotify hooks run against current, the supervisor, paralleling pidfd_getfd(); cross-task authorization is left to the caller. Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Cong Wang --- include/linux/mm.h | 19 ++++++++ mm/internal.h | 6 +++ mm/mmap.c | 49 +++++++++++++-------- mm/mprotect.c | 2 +- mm/nommu.c | 22 +++++++--- mm/util.c | 106 +++++++++++++++++++++++++++++++++++++++++++++ mm/vma.c | 46 ++++++++++++-------- mm/vma.h | 12 ++--- 8 files changed, 212 insertions(+), 50 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 560bc369a54c..fa1d96bc379d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -4155,6 +4155,25 @@ 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); +#ifdef CONFIG_MMU +unsigned long vm_mmap_remote(struct mm_struct *mm, struct file *file, + unsigned long addr, unsigned long len, unsigned long prot, + unsigned long flags, unsigned long pgoff, vm_flags_t vm_flags); +int vm_munmap_remote(struct mm_struct *mm, unsigned long start, size_t len); +#else +static inline unsigned long vm_mmap_remote(struct mm_struct *mm, + struct file *file, unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, unsigned long pgoff, + vm_flags_t vm_flags) +{ + return -EOPNOTSUPP; +} +static inline int vm_munmap_remote(struct mm_struct *mm, unsigned long start, + size_t len) +{ + return -EOPNOTSUPP; +} +#endif 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 181e79f1d6a2..fd3d39f6b4c4 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -1436,6 +1436,12 @@ 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 54915ac478ab..f379a065a8e6 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; @@ -349,16 +351,6 @@ unsigned long do_mmap(struct file *file, unsigned long addr, if (!len) return -EINVAL; - /* - * Does the application expect PROT_READ to imply PROT_EXEC? - * - * (the exception is when the underlying filesystem is noexec - * mounted, in which case we don't add PROT_EXEC.) - */ - if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC)) - if (!(file && path_noexec(&file->f_path))) - prot |= PROT_EXEC; - /* force arch specific MAP_FIXED handling in get_unmapped_area */ if (flags & MAP_FIXED_NOREPLACE) flags |= MAP_FIXED; @@ -557,7 +549,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 +557,25 @@ 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) +{ + /* + * Does the application expect PROT_READ to imply PROT_EXEC? + * + * (the exception is when the underlying filesystem is noexec + * mounted, in which case we don't add PROT_EXEC.) + */ + if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC)) + if (!(file && path_noexec(&file->f_path))) + prot |= PROT_EXEC; + + 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/mprotect.c b/mm/mprotect.c index 9cbf932b028c..02e90577db25 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -939,7 +939,7 @@ static int do_mprotect_pkey(unsigned long start, size_t len, break; } - if (map_deny_write_exec(&vma->flags, &new_vma_flags)) { + if (map_deny_write_exec(vma->vm_mm, &vma->flags, &new_vma_flags)) { error = -EACCES; break; } diff --git a/mm/nommu.c b/mm/nommu.c index d6393bd2844e..15390ef5b784 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, @@ -1024,7 +1025,7 @@ unsigned long do_mmap(struct file *file, struct rb_node *rb; unsigned long capabilities, result; int ret; - VMA_ITERATOR(vmi, current->mm, 0); + VMA_ITERATOR(vmi, mm, 0); *populate = 0; @@ -1049,7 +1050,7 @@ unsigned long do_mmap(struct file *file, if (!region) goto error_getting_region; - vma = vm_area_alloc(current->mm); + vma = vm_area_alloc(mm); if (!vma) goto error_getting_vma; @@ -1191,7 +1192,7 @@ unsigned long do_mmap(struct file *file, /* okay... we have a mapping; now we have to register it */ result = vma->vm_start; - current->mm->total_vm += len >> PAGE_SHIFT; + mm->total_vm += len >> PAGE_SHIFT; share: BUG_ON(!vma->vm_region); @@ -1199,8 +1200,8 @@ unsigned long do_mmap(struct file *file, if (vma_iter_prealloc(&vmi, vma)) goto error_just_free; - setup_vma_to_mm(vma, current->mm); - current->mm->map_count++; + setup_vma_to_mm(vma, mm); + mm->map_count++; /* add the VMA to the tree */ vma_iter_store_new(&vmi, vma); @@ -1247,6 +1248,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 af2c2103f0d9..2b5254106d15 100644 --- a/mm/util.c +++ b/mm/util.c @@ -588,6 +588,112 @@ unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr, return ret; } +#ifdef CONFIG_MMU +/** + * vm_mmap_remote - install a file (or anonymous) mapping into @mm, without + * target-side cooperation. + * @mm: Target mm; caller holds a reference (e.g. get_task_mm()). + * @file: Backing file, or NULL for an anonymous mapping. + * @addr: Page-aligned address. If non-zero it is used as given (honoring + * MAP_FIXED/MAP_FIXED_NOREPLACE in @flags); if zero, the kernel + * chooses a free area in @mm and returns it. + * @len: Length in bytes. + * @prot: PROT_* protection bits. + * @flags: MAP_* flags. + * @pgoff: Page offset into @file. + * @vm_flags: Extra VMA flags to OR in (e.g. VM_SEALED), or 0. + * + * The general form of the remote install primitive: a supervisor places a + * mapping into a remote task's address space. LSM/fsnotify hooks run against + * %current (the installer), paralleling pidfd_getfd()'s cross-task install; + * cross-task authorization is the caller's responsibility. The foreign mm is + * not mm_populate()d. + * + * Kernel-chosen placement (@addr == 0) resolves through + * __get_unmapped_area() against @mm, so the file's own + * ->get_unmapped_area() runs and file-specific placement (hugetlb page + * size, THP PMD alignment, SHMLBA cache coloring, device dax alignment) + * is honored exactly as for a local mmap(). + * + * Returns the mapped address on success, or a negative errno. + */ +unsigned long vm_mmap_remote(struct mm_struct *mm, struct file *file, + unsigned long addr, unsigned long len, unsigned long prot, + unsigned long flags, unsigned long pgoff, vm_flags_t vm_flags) +{ + loff_t off = (loff_t)pgoff << PAGE_SHIFT; + unsigned long aligned_len = PAGE_ALIGN(len); + unsigned long ret; + unsigned long populate; + LIST_HEAD(uf); + + if (WARN_ON_ONCE(!mm)) + return -EINVAL; + + ret = security_mmap_file(file, prot, flags); + if (!ret) + ret = fsnotify_mmap_perm(file, prot, off, len); + if (ret) + return ret; + + if (mmap_write_lock_killable(mm)) + return -EINTR; + + /* + * Resolve kernel-chosen placement up front and pin it with + * MAP_FIXED, so the mm->task_size check below runs before the + * mapping is installed. The file's own ->get_unmapped_area() + * performs the search against @mm. + */ + if (!addr) { + addr = __get_unmapped_area(mm, file, 0, aligned_len, pgoff, + flags, vm_flags); + if (IS_ERR_VALUE(addr)) { + ret = addr; + goto unlock; + } + } + + if (aligned_len > mm->task_size || addr > mm->task_size - aligned_len) { + ret = -ENOMEM; + goto unlock; + } + + if (!(flags & MAP_FIXED_NOREPLACE)) + flags |= MAP_FIXED; + + ret = __do_mmap(mm, file, addr, len, prot, flags, vm_flags, + pgoff, &populate, &uf); +unlock: + mmap_write_unlock(mm); + userfaultfd_unmap_complete(mm, &uf); + return ret; +} + +/** + * vm_munmap_remote - unmap a range from @mm, mirroring vm_munmap() but + * targeting an explicit mm. + * @mm: Target mm; caller holds a reference (e.g. get_task_mm()). + * @start: Start address of the range to unmap. + * @len: Length in bytes. + * + * Returns 0 on success, or a negative errno. + */ +int vm_munmap_remote(struct mm_struct *mm, unsigned long start, size_t len) +{ + VMA_ITERATOR(vmi, mm, start); + LIST_HEAD(uf); + int ret; + + if (mmap_write_lock_killable(mm)) + return -EINTR; + ret = do_vmi_munmap(&vmi, mm, start, len, &uf, false); + mmap_write_unlock(mm); + userfaultfd_unmap_complete(mm, &uf); + return ret; +} +#endif /* CONFIG_MMU */ + /* * 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 9a6cb0338ed8..788c2a0f35ec 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -1333,7 +1333,7 @@ static void vms_complete_munmap_vmas(struct vma_munmap_struct *vms, struct vm_area_struct *vma; struct mm_struct *mm; - mm = current->mm; + mm = vms->mm; mm->map_count -= vms->vma_count; mm->locked_vm -= vms->locked_vm; if (vms->unlock) @@ -1544,10 +1544,11 @@ static int vms_gather_munmap_vmas(struct vma_munmap_struct *vms, * @unlock: Unlock after the operation. Only unlocked on success */ static void init_vma_munmap(struct vma_munmap_struct *vms, - struct vma_iterator *vmi, struct vm_area_struct *vma, - unsigned long start, unsigned long end, struct list_head *uf, - bool unlock) + struct mm_struct *mm, struct vma_iterator *vmi, + struct vm_area_struct *vma, unsigned long start, + unsigned long end, struct list_head *uf, bool unlock) { + vms->mm = mm; vms->vmi = vmi; vms->vma = vma; if (vma) { @@ -1591,7 +1592,7 @@ int do_vmi_align_munmap(struct vma_iterator *vmi, struct vm_area_struct *vma, struct vma_munmap_struct vms; int error; - init_vma_munmap(&vms, vmi, vma, start, end, uf, unlock); + init_vma_munmap(&vms, mm, vmi, vma, start, end, uf, unlock); error = vms_gather_munmap_vmas(&vms, &mas_detach); if (error) goto gather_failed; @@ -1634,7 +1635,8 @@ int do_vmi_munmap(struct vma_iterator *vmi, struct mm_struct *mm, unsigned long end; struct vm_area_struct *vma; - if ((offset_in_page(start)) || start > TASK_SIZE || len > TASK_SIZE-start) + if ((offset_in_page(start)) || start > mm->task_size || + len > mm->task_size - start) return -EINVAL; end = start + PAGE_ALIGN(len); @@ -2426,7 +2428,7 @@ static int __mmap_setup(struct mmap_state *map, struct vm_area_desc *desc, /* Find the first overlapping VMA and initialise unmap state. */ vms->vma = vma_find(vmi, map->end); - init_vma_munmap(vms, vmi, vms->vma, map->addr, map->end, uf, + init_vma_munmap(vms, map->mm, vmi, vms->vma, map->addr, map->end, uf, /* unlock = */ false); /* OK, we have overlapping VMAs - prepare to unmap them. */ @@ -2731,11 +2733,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); @@ -2809,14 +2810,16 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr, /** * mmap_region() - Actually perform the userland mapping of a VMA into - * current->mm with known, aligned and overflow-checked @addr and @len, and + * @mm with known, aligned and overflow-checked @addr and @len, and * correctly determined VMA flags @vm_flags and page offset @pgoff. * * This is an internal memory management function, and should not be used * directly. * - * The caller must write-lock current->mm->mmap_lock. + * The caller must write-lock @mm->mmap_lock. * + * @mm: The mm_struct to install the mapping into. The caller must hold a + * reference and write-lock its mmap_lock. * @file: If a file-backed mapping, a pointer to the struct file describing the * file to be mapped, otherwise NULL. * @addr: The page-aligned address at which to perform the mapping. @@ -2830,18 +2833,19 @@ 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)) + if (map_deny_write_exec(mm, &vma_flags, &vma_flags)) return -EACCES; /* Allow architectures to sanity-check the vm_flags. */ @@ -2857,13 +2861,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; } @@ -2983,6 +2987,8 @@ unsigned long unmapped_area(struct mm_struct *mm, if (low_limit < mmap_min_addr) low_limit = mmap_min_addr; high_limit = info->high_limit; + if (mm != current->mm) + high_limit = min(high_limit, mm->task_size); retry: if (vma_iter_area_lowest(&vmi, low_limit, high_limit, length)) return -ENOMEM; @@ -3043,6 +3049,8 @@ unsigned long unmapped_area_topdown(struct mm_struct *mm, if (low_limit < mmap_min_addr) low_limit = mmap_min_addr; high_limit = info->high_limit; + if (mm != current->mm) + high_limit = min(high_limit, mm->task_size); retry: if (vma_iter_area_highest(&vmi, low_limit, high_limit, length)) return -ENOMEM; diff --git a/mm/vma.h b/mm/vma.h index d0a45718deb9..0c0b9d9c5248 100644 --- a/mm/vma.h +++ b/mm/vma.h @@ -32,6 +32,7 @@ struct unlink_vma_file_batch { * vma munmap operation */ struct vma_munmap_struct { + struct mm_struct *mm; /* The mm being operated on */ struct vma_iterator *vmi; struct vm_area_struct *vma; /* The first vma to munmap */ struct vm_area_struct *prev; /* vma before the munmap area */ @@ -459,9 +460,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, @@ -732,11 +733,12 @@ int relocate_vma_down(struct vm_area_struct *vma, unsigned long shift); * * Return: false if proposed change is OK, true if not ok and should be denied. */ -static inline bool map_deny_write_exec(const vma_flags_t *old, +static inline bool map_deny_write_exec(struct mm_struct *mm, + const vma_flags_t *old, const vma_flags_t *new) { /* If MDWE is disabled, we have nothing to deny. */ - if (!mm_flags_test(MMF_HAS_MDWE, current->mm)) + if (!mm_flags_test(MMF_HAS_MDWE, mm)) return false; /* If the new VMA is not executable, we have nothing to deny. */ -- 2.43.0