* [RFC PATCH 0/2] kexec,mm/cma: do not run the next kernel from a fixed-base CMA area
@ 2026-10-14 14:57 Radhey Shyam Pandey
2026-10-14 14:57 ` [RFC PATCH 1/2] kexec: do not place the next kernel in a fixed-address " Radhey Shyam Pandey
2026-10-14 14:57 ` [RFC PATCH 2/2] mm/cma: reject CMA areas that overlap the kernel image Radhey Shyam Pandey
0 siblings, 2 replies; 5+ messages in thread
From: Radhey Shyam Pandey @ 2026-10-14 14:57 UTC (permalink / raw)
To: akpm, david, ljs, liam, vbabka, rppt, surenb, mhocko, baoquan.he,
pasha.tatashin, pratyush, graf
Cc: linux-mm, linux-kernel, kexec, Radhey Shyam Pandey
Since commit 07d24902977e ("kexec: enable CMA based contiguous
allocation"), kexec_locate_mem_hole() tries kexec_alloc_contig() before
the memblock hole search. On success the CMA address becomes the final
destination of the next kernel rather than a staging buffer, so the
image is run in place instead of being copied during the hot phase.
That is unsafe when the default CMA area has a fixed base. CMA
allocation state is purely runtime: the outgoing kernel marks the pages
busy in its own bitmap, but that bitmap is not carried across kexec.
The incoming kernel builds its CMA areas afresh from the same device
tree, which describes the pool's extent - the one thing both kernels
agree on exactly - and has no way to learn that part of the pool holds
the image it is executing from. cma_activate_area() hands the area to
init_cma_reserved_pageblock(), which clears PageReserved on every page
and frees them into MIGRATE_CMA, overriding the memblock_reserve() of
[_text, _end) the architecture installed.
On a VEK385 (arm64, three device-tree CMA pools, 14 GiB total) this
reproduces within ~300ms of /sbin/init starting, as a copy-on-write
fault writing to the read-only linear alias of .text after kernel text
had been handed out as a movable page:
Unable to handle kernel write to read-only memory at virtual address ffff00084030c000
Call trace:
copy_page+0x48/0xc4 (P)
copy_user_highpage+0x14/0x30
__handle_mm_fault+0x318/0x1340
Patch 1 is the fix. It records whether an area was placed at an address
CMA chose or one dictated from outside, and refuses the latter for the
kexec payload, leaving kexec_locate_mem_hole() to fall back to the
memblock hole search as it did before CMA placement existed. Areas
placed by __cma_declare_contiguous_nid() without a fixed base are still
used, because the next kernel repeats that search against its own
memblock state and so cannot land on the same extent. "cma=nn" keeps
working and "cma=nn@ss" does not, which is the intended distinction.
Patch 2 adds a check in cma_init_reserved_mem() so that a caller passing
a base overlapping the kernel image is refused rather than silently
corrupting it. It is hardening rather than the fix, and deliberately
carries no Fixes: tag - see the open question below.
Open question for patch 2
=========================
Returning an error is not sufficient on its own for a device-tree pool.
fdt_init_reserved_mem_node() responds to an error from a non-nomap
region by calling memblock_phys_free() on the whole range, which drops
the kernel image's own reservation along with it, so the pages are still
released - as plain free memory instead of MIGRATE_CMA. powerpc's
fadump_cma_init() is the one caller that keeps its reservation on
failure and is therefore fully protected.
Should of_reserved_mem avoid freeing a region whose node_init failed, or
should cma_init_reserved_mem() signal this case differently? I have left
patch 2 as a diagnostic and a second line of defence behind patch 1
rather than guess at the right answer. Patch 1 stands on its own and
does not depend on how patch 2 is resolved.
Testing
=======
With patch 1 applied, kexec_file_load() on the VEK385 declines the
default pool (4 GiB at 0x800000000) and places the Image through the
memblock search at 0x9c00000 size 0x1de0000. The kernel that boots from
it reports its image as [0x09c00000-0x0b9e0000] and 14680064K
cma-reserved, i.e. all three pools present and none of them overlapping
the running kernel. Both segments report being copied rather than run in
place. Without patch 1, the payload is placed inside the default pool
and the trace above follows.
Radhey Shyam Pandey (2):
kexec: do not place the next kernel in a fixed-address CMA area
mm/cma: reject CMA areas that overlap the kernel image
include/linux/cma.h | 10 ++++++++++
kernel/kexec_file.c | 12 ++++++++++++
mm/cma.c | 39 +++++++++++++++++++++++++++++++++++++++
mm/cma.h | 2 ++
4 files changed, 63 insertions(+)
base-commit: 1fffefb00cc0b0da1f2ae24e3d33364530ea6515
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC PATCH 1/2] kexec: do not place the next kernel in a fixed-address CMA area
2026-10-14 14:57 [RFC PATCH 0/2] kexec,mm/cma: do not run the next kernel from a fixed-base CMA area Radhey Shyam Pandey
@ 2026-10-14 14:57 ` Radhey Shyam Pandey
2026-10-14 15:07 ` sashiko-bot
2026-10-14 14:57 ` [RFC PATCH 2/2] mm/cma: reject CMA areas that overlap the kernel image Radhey Shyam Pandey
1 sibling, 1 reply; 5+ messages in thread
From: Radhey Shyam Pandey @ 2026-10-14 14:57 UTC (permalink / raw)
To: akpm, david, ljs, liam, vbabka, rppt, surenb, mhocko, baoquan.he,
pasha.tatashin, pratyush, graf
Cc: linux-mm, linux-kernel, kexec, Radhey Shyam Pandey
kexec_alloc_contig() picks the destination of the next kernel out of the
default CMA area, and the next kernel then runs from there in place
instead of being copied out. That is only safe if the area will not exist
at the same address in the kernel being loaded.
For an area whose base came from the device tree, it will. The loaded
kernel parses the same description, declares the range it is executing
from as a shared-dma-pool, and cma_activate_area() releases the pages
holding its own image to the page allocator.
Distinguish the two cases. An area placed by cma_declare_contiguous_nid()
without a fixed base lands wherever this kernel found room, and the next
kernel repeats that search against its own memblock state, so it cannot
collide. Record the difference as CMA_FIXED_BASE and refuse to use such
an area for the payload, which leaves kexec_locate_mem_hole() to fall
back to the memblock hole search as it did before CMA placement existed.
This is what makes the KEXEC_FILE_NO_CMA flag unnecessary on platforms
that describe their CMA pools in the device tree.
Tested on a AMD Versal AI Edge Series Gen 2 VEK385 Evaluation Kit whose
device tree declares three CMA pools, the first of which is
linux,cma-default:
cma_reserved@800000000 4 GiB at 0x800000000 (default)
cma_reserved@50000000000 6 GiB at 0x50000000000
cma_reserved@60000000000 4 GiB at 0x60000000000
All three have a fixed base, so kexec_alloc_contig() declines the default
area and kexec_locate_mem_hole() falls through to the memblock search,
which places the Image segment at 0x9c00000 size 0x1de0000. The kernel
that boots from it reports its image as [0x09c00000-0x0b9e0000] and
14680064K cma-reserved, i.e. all three pools still present and none of
them overlapping the running kernel.
Fixes: 07d24902977e ("kexec: enable CMA based contiguous allocation")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
---
include/linux/cma.h | 10 ++++++++++
kernel/kexec_file.c | 12 ++++++++++++
mm/cma.c | 16 ++++++++++++++++
mm/cma.h | 2 ++
4 files changed, 40 insertions(+)
diff --git a/include/linux/cma.h b/include/linux/cma.h
index 8555d38a97b1..2fe66377e5d3 100644
--- a/include/linux/cma.h
+++ b/include/linux/cma.h
@@ -61,4 +61,14 @@ extern int cma_for_each_area(int (*it)(struct cma *cma, void *data), void *data)
extern bool cma_intersects(struct cma *cma, unsigned long start, unsigned long end);
extern void cma_reserve_pages_on_error(struct cma *cma);
+
+#ifdef CONFIG_CMA
+bool cma_has_fixed_base(const struct cma *cma);
+#else
+static inline bool cma_has_fixed_base(const struct cma *cma)
+{
+ return false;
+}
+#endif
+
#endif
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index b668ce8d68ee..10e770bcee0b 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -28,6 +28,7 @@
#include <linux/vmalloc.h>
#include <linux/dma-map-ops.h>
#include <linux/kexec_handover.h>
+#include <linux/cma.h>
#include "kexec_internal.h"
#ifdef CONFIG_KEXEC_SIG
@@ -691,6 +692,7 @@ static int kexec_walk_resources(struct kexec_buf *kbuf,
static int kexec_alloc_contig(struct kexec_buf *kbuf)
{
size_t nr_pages = kbuf->memsz >> PAGE_SHIFT;
+ struct cma *cma;
unsigned long mem;
struct page *p;
@@ -702,6 +704,16 @@ static int kexec_alloc_contig(struct kexec_buf *kbuf)
if (kbuf->image->type == KEXEC_TYPE_CRASH)
return -EPERM;
+ /*
+ * The next kernel runs from here rather than being copied out, so it
+ * would declare this area again from the same description and release
+ * the pages holding its own image to the page allocator. Only an area
+ * this kernel placed itself is safe to use.
+ */
+ cma = dev_get_cma_area(NULL);
+ if (!cma || cma_has_fixed_base(cma))
+ return -EOPNOTSUPP;
+
p = dma_alloc_from_contiguous(NULL, nr_pages, get_order(kbuf->buf_align), true);
if (!p)
return -ENOMEM;
diff --git a/mm/cma.c b/mm/cma.c
index a10ea37a261d..1f4d3d48a3bb 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -55,6 +55,19 @@ const char *cma_get_name(const struct cma *cma)
}
EXPORT_SYMBOL_GPL(cma_get_name);
+/**
+ * cma_has_fixed_base() - was the area placed at an address it did not choose?
+ * @cma: Contiguous memory region
+ *
+ * Such an area is described outside the kernel, typically by a device tree
+ * reserved-memory node, and so lands at the same address in every kernel
+ * booted with that description.
+ */
+bool cma_has_fixed_base(const struct cma *cma)
+{
+ return test_bit(CMA_FIXED_BASE, &cma->flags);
+}
+
static unsigned long cma_bitmap_aligned_mask(const struct cma *cma,
unsigned int align_order)
{
@@ -304,6 +317,7 @@ int __init cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
cma->ranges[0].count = cma->count;
cma->nranges = 1;
cma->nid = NUMA_NO_NODE;
+ set_bit(CMA_FIXED_BASE, &cma->flags);
*res_cma = cma;
@@ -517,6 +531,8 @@ static int __init __cma_declare_contiguous_nid(phys_addr_t *basep,
}
(*res_cma)->nid = nid;
+ if (!fixed)
+ clear_bit(CMA_FIXED_BASE, &(*res_cma)->flags);
*basep = base;
return 0;
diff --git a/mm/cma.h b/mm/cma.h
index 68e574b0f951..1bab24033284 100644
--- a/mm/cma.h
+++ b/mm/cma.h
@@ -80,6 +80,8 @@ enum cma_flags {
CMA_ZONES_VALID,
CMA_ZONES_INVALID,
CMA_ACTIVATED,
+ /* Base address was dictated by the caller, not picked by CMA. */
+ CMA_FIXED_BASE,
};
extern struct cma cma_areas[MAX_CMA_AREAS];
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [RFC PATCH 2/2] mm/cma: reject CMA areas that overlap the kernel image
2026-10-14 14:57 [RFC PATCH 0/2] kexec,mm/cma: do not run the next kernel from a fixed-base CMA area Radhey Shyam Pandey
2026-10-14 14:57 ` [RFC PATCH 1/2] kexec: do not place the next kernel in a fixed-address " Radhey Shyam Pandey
@ 2026-10-14 14:57 ` Radhey Shyam Pandey
2026-10-14 15:09 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Radhey Shyam Pandey @ 2026-10-14 14:57 UTC (permalink / raw)
To: akpm, david, ljs, liam, vbabka, rppt, surenb, mhocko, baoquan.he,
pasha.tatashin, pratyush, graf
Cc: linux-mm, linux-kernel, kexec, Radhey Shyam Pandey
cma_init_reserved_mem() takes a base address chosen by its caller and
requires that the range already be reserved. That makes an overlap with
the kernel image indistinguishable from the normal case by looking at
memblock alone, yet such an area is fatal: cma_activate_area() hands it
to init_cma_reserved_pageblock(), which unconditionally clears
PageReserved on every page and frees them into MIGRATE_CMA, overriding
the memblock_reserve() of [_text, _end) that the architecture installed.
Those pages satisfy ordinary movable allocations from that point on, so
userspace is handed live kernel text.
The preceding patch closes the way this is reachable today, where a
kexec'd kernel runs from inside the default CMA area and then declares
the range it is executing from as a shared-dma-pool. On a VEK385 that
reproduced within ~300ms of /sbin/init starting, as a copy-on-write
fault writing to the read-only linear alias of .text:
Unable to handle kernel write to read-only memory at virtual address ffff00084030c000
Call trace:
copy_page+0x48/0xc4 (P)
copy_user_highpage+0x14/0x30
__handle_mm_fault+0x318/0x1340
Dynamically placed areas cannot hit this: __cma_declare_contiguous_nid()
either rejects a fixed base that is already reserved, or picks an address
itself and so avoids the kernel image. Only a caller that passes a base
chosen elsewhere can, so check for the overlap explicitly rather than
leave the next such caller to corrupt the kernel silently.
Note that failing here is not by itself sufficient for a device-tree
pool. fdt_init_reserved_mem_node() responds to an error from a non-nomap
region by calling memblock_phys_free() on the whole range, which drops
the kernel image's own reservation along with it, so the pages are still
released - as plain free memory rather than MIGRATE_CMA. Callers that
keep their reservation on failure, such as powerpc's fadump_cma_init(),
are fully protected. For the device-tree path this is a diagnostic and a
second line of defence behind the preceding patch.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
---
mm/cma.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/mm/cma.c b/mm/cma.c
index 1f4d3d48a3bb..32b799a3f5c8 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -31,6 +31,8 @@
#include <linux/kmemleak.h>
#include <trace/events/cma.h>
+#include <asm/sections.h>
+
#include "internal.h"
#include "cma.h"
#include "mm_init.h"
@@ -68,6 +70,11 @@ bool cma_has_fixed_base(const struct cma *cma)
return test_bit(CMA_FIXED_BASE, &cma->flags);
}
+static bool cma_overlaps_kernel_image(phys_addr_t base, phys_addr_t size)
+{
+ return base < __pa_symbol(_end) && __pa_symbol(_text) < base + size;
+}
+
static unsigned long cma_bitmap_aligned_mask(const struct cma *cma,
unsigned int align_order)
{
@@ -295,6 +302,22 @@ int __init cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
if (!size || !memblock_is_region_reserved(base, size))
return -EINVAL;
+ /*
+ * The area being already reserved is expected here, so an overlap with
+ * the kernel image cannot be told apart from the normal case by looking
+ * at memblock alone. Check for it explicitly: activating such an area
+ * clears PageReserved on the kernel's own text and data and releases
+ * them to the page allocator.
+ */
+ if (cma_overlaps_kernel_image(base, size)) {
+ phys_addr_t text = __pa_symbol(_text);
+ phys_addr_t end = __pa_symbol(_end);
+
+ pr_err("area %s (%pa, size %pa) overlaps kernel image [%pa-%pa]\n",
+ name ?: "(unnamed)", &base, &size, &text, &end);
+ return -EBUSY;
+ }
+
/*
* CMA uses CMA_MIN_ALIGNMENT_BYTES as alignment requirement which
* needs pageblock_order to be initialized. Let's enforce it.
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [RFC PATCH 1/2] kexec: do not place the next kernel in a fixed-address CMA area
2026-10-14 14:57 ` [RFC PATCH 1/2] kexec: do not place the next kernel in a fixed-address " Radhey Shyam Pandey
@ 2026-10-14 15:07 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-14 15:07 UTC (permalink / raw)
To: Radhey Shyam Pandey, akpm, david, ljs, liam, vbabka, rppt, surenb,
mhocko, baoquan.he, pasha.tatashin, pratyush, graf
Cc: kexec
> kexec_alloc_contig() picks the destination of the next kernel out of the
> default CMA area, and the next kernel then runs from there in place
> instead of being copied out. That is only safe if the area will not exist
> at the same address in the kernel being loaded.
>
> For an area whose base came from the device tree, it will. The loaded
> kernel parses the same description, declares the range it is executing
> from as a shared-dma-pool, and cma_activate_area() releases the pages
> holding its own image to the page allocator.
>
> Distinguish the two cases. An area placed by cma_declare_contiguous_nid()
> without a fixed base lands wherever this kernel found room, and the next
> kernel repeats that search against its own memblock state, so it cannot
> collide. Record the difference as CMA_FIXED_BASE and refuse to use such
> an area for the payload, which leaves kexec_locate_mem_hole() to fall
> [ ... ]
> them overlapping the running kernel.
>
> Fixes: 07d24902977e ("kexec: enable CMA based contiguous allocation")
> Assisted-by: Claude:claude-opus-5
> Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261014145731.3431527-1-radhey.shyam.pandey@amd.com?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC PATCH 2/2] mm/cma: reject CMA areas that overlap the kernel image
2026-10-14 14:57 ` [RFC PATCH 2/2] mm/cma: reject CMA areas that overlap the kernel image Radhey Shyam Pandey
@ 2026-10-14 15:09 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-14 15:09 UTC (permalink / raw)
To: Radhey Shyam Pandey, akpm, david, ljs, liam, vbabka, rppt, surenb,
mhocko, baoquan.he, pasha.tatashin, pratyush, graf
Cc: kexec
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] mm: integer overflow in CMA kernel overlap check bypasses protection
--
commit af398e11f427511d4d7335b0e8810e798dcdd794
Author: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
mm/cma: reject CMA areas that overlap the kernel image
This commit adds an explicit check in cma_init_reserved_mem() to ensure
that initialized CMA areas do not overlap with the kernel image. This
prevents scenarios where an overlap clears PageReserved on the kernel's
own text and data, releasing them to the page allocator.
> diff --git a/mm/cma.c b/mm/cma.c
> index 1f4d3d48a3bb9..32b799a3f5c8f 100644
> --- a/mm/cma.c
> +++ b/mm/cma.c
[ ... ]
> @@ -68,6 +70,11 @@ bool cma_has_fixed_base(const struct cma *cma)
> return test_bit(CMA_FIXED_BASE, &cma->flags);
> }
>
> +static bool cma_overlaps_kernel_image(phys_addr_t base, phys_addr_t size)
> +{
> + return base < __pa_symbol(_end) && __pa_symbol(_text) < base + size;
[Severity: Critical]
Can a maliciously large size cause an integer overflow here?
If a malformed huge size is passed down from an untrusted source (like a
device tree overlay), base + size could wrap around in a 32-bit phys_addr_t.
This would cause __pa_symbol(_text) < base + size to evaluate to false,
completely bypassing the kernel image protection.
> +}
> +
> static unsigned long cma_bitmap_aligned_mask(const struct cma *cma,
> unsigned int align_order)
> {
> @@ -295,6 +302,22 @@ int __init cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
> if (!size || !memblock_is_region_reserved(base, size))
> return -EINVAL;
[Severity: Critical]
Does memblock_is_region_reserved() cap the size strictly internally?
Since size is passed by value, any capping inside memblock_is_region_reserved()
or memblock_reserve() leaves the caller's uncapped size unchanged.
The unmodified huge size then continues down to cma_overlaps_kernel_image(),
leading to the overflow described above. This could bypass the check entirely,
allowing subsequent processing to clear PageReserved on live kernel text pages.
>
> + /*
> + * The area being already reserved is expected here, so an overlap with
> + * the kernel image cannot be told apart from the normal case by looking
> + * at memblock alone. Check for it explicitly: activating such an area
> + * clears PageReserved on the kernel's own text and data and releases
> + * them to the page allocator.
> + */
> + if (cma_overlaps_kernel_image(base, size)) {
> + phys_addr_t text = __pa_symbol(_text);
> + phys_addr_t end = __pa_symbol(_end);
> +
> + pr_err("area %s (%pa, size %pa) overlaps kernel image [%pa-%pa]\n",
> + name ?: "(unnamed)", &base, &size, &text, &end);
> + return -EBUSY;
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261014145731.3431527-1-radhey.shyam.pandey@amd.com?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-14 15:09 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-14 14:57 [RFC PATCH 0/2] kexec,mm/cma: do not run the next kernel from a fixed-base CMA area Radhey Shyam Pandey
2026-10-14 14:57 ` [RFC PATCH 1/2] kexec: do not place the next kernel in a fixed-address " Radhey Shyam Pandey
2026-10-14 15:07 ` sashiko-bot
2026-10-14 14:57 ` [RFC PATCH 2/2] mm/cma: reject CMA areas that overlap the kernel image Radhey Shyam Pandey
2026-10-14 15:09 ` sashiko-bot
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.