From: Arnav Kansal <rnv@google.com>
To: Andrew Morton <akpm@linux-foundation.org>,
Mike Rapoport <rppt@kernel.org>
Cc: Luis Chamberlain <mcgrof@kernel.org>,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
Arnav Kansal <rnv@google.com>,
stable@vger.kernel.org, Rohan Kumar <rohakum@google.com>
Subject: [PATCH] mm/execmem: only free ROX cache vm_struct when fully unused
Date: Wed, 14 Oct 2026 16:30:58 -0700 [thread overview]
Message-ID: <20261014233058.1410692-1-rnv@google.com> (raw)
execmem_cache_clean() currently uses:
IS_ALIGNED(size, PMD_SIZE) && IS_ALIGNED(mas.index, PMD_SIZE)
as a proxy to determine whether a free range in execmem_cache.free_areas
spans an entire unused vm_struct that can be returned to vmalloc.
This heuristic is broken in both directions:
1. Premature vfree() of multi-PMD vm_structs (Use-After-Free):
When execmem_cache_populate_alloc() services a request larger than
PMD_SIZE (for example, a module with > 6 MiB of .text),
alloc_size = round_up(size, PMD_SIZE) creates a single multi-PMD
vm_struct (e.g., 8 MiB) whose remaining tail space is handed out to
subsequent module allocations (e.g., kvm.ko). When a module inside
that vm_struct unloads and leaves a PMD_SIZE-aligned free sub-range
(such as a 2 MiB or 4 MiB chunk at the start of the 8 MiB vm_struct),
execmem_cache_clean() passes the IS_ALIGNED() check and calls
vfree(area), which unmaps and frees the entire enclosing multi-PMD
vm_struct while sibling modules are still live:
BUG: unable to handle page fault for address: ffffffffc07dc030
#PF: supervisor instruction fetch in kernel mode
#PF: error_code(0x0010) - not-present page
PGD 2c33067 P4D 2c33067 PUD 2c35067 PMD 0
Oops: Oops: 0010 [#1] SMP NOPTI
RIP: 0010:dummy_exit+0x0/0xfd0 [tailm]
2. Failure to reclaim fallback vm_structs:
After commit 3bd4e0ac61b2 ("execmem: add fallback for failures in
vmalloc(VM_ALLOW_HUGE_VMAP)") and commit 4afb85f5e359 ("mm: remove
PMD alignment constraint in execmem_vmalloc()"), ROX cache vm_structs
allocated via the base-page or exact-size fallback paths are only
PAGE_SIZE-aligned in address and/or size, so they never satisfy the
PMD_SIZE alignment check even when completely unused.
Because vmalloc allocations include a trailing guard page, separate
vm_struct allocations can never coalesce into a single contiguous entry
in execmem_cache.free_areas. Replace the PMD_SIZE alignment check with
an explicit check that the free range starts at vm->addr and spans the
entire get_vm_area_size(vm).
Fixes: 2e45474ab14f ("execmem: add support for cache of large ROX pages")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Co-developed-by: Rohan Kumar <rohakum@google.com>
Signed-off-by: Rohan Kumar <rohakum@google.com>
Signed-off-by: Arnav Kansal <rnv@google.com>
---
mm/execmem.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/mm/execmem.c b/mm/execmem.c
index 74a178a87e75..3a1da0498fe2 100644
--- a/mm/execmem.c
+++ b/mm/execmem.c
@@ -166,11 +166,9 @@ static void execmem_cache_clean(struct work_struct *work)
mutex_lock(mutex);
mas_for_each(&mas, area, ULONG_MAX) {
size_t size = mas_range_len(&mas);
+ struct vm_struct *vm = find_vm_area(area);
- if (IS_ALIGNED(size, PMD_SIZE) &&
- IS_ALIGNED(mas.index, PMD_SIZE)) {
- struct vm_struct *vm = find_vm_area(area);
-
+ if (vm->addr == area && get_vm_area_size(vm) == size) {
execmem_set_direct_map_valid(vm, true);
mas_store_gfp(&mas, NULL, GFP_KERNEL);
vfree(area);
--
2.47.0
next reply other threads:[~2026-10-14 23:32 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-14 23:30 Arnav Kansal [this message]
2026-10-15 8:10 ` [PATCH] mm/execmem: only free ROX cache vm_struct when fully unused Mike Rapoport
2026-10-15 13:37 ` Arnav Kansal
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261014233058.1410692-1-rnv@google.com \
--to=rnv@google.com \
--cc=akpm@linux-foundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mcgrof@kernel.org \
--cc=rohakum@google.com \
--cc=rppt@kernel.org \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.