* [PATCH RFC 0/5] liveupdate: validate restored LUO metadata
@ 2026-05-01 9:46 Cris Jacob Maamor
2026-05-01 9:46 ` [PATCH RFC 1/5] kexec: handover: add helper to check preserved page ranges Cris Jacob Maamor
` (5 more replies)
0 siblings, 6 replies; 14+ messages in thread
From: Cris Jacob Maamor @ 2026-05-01 9:46 UTC (permalink / raw)
To: Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Dan Carpenter, Greg Kroah-Hartman,
kexec, linux-mm, linux-kernel
LUO restores metadata from KHO/FDT during liveupdate. The restored
metadata contains physical addresses and count fields used to access and
walk preserved session, file-set, and FLB arrays.
This series adds a non-consuming KHO preserved-range check and uses it
before phys_to_virt() on restored metadata addresses. It also rejects
restored counts above LUO_SESSION_MAX, LUO_FILE_MAX, and LUO_FLB_MAX
before traversal.
As far as I can tell, this is root/admin-only; I do not have evidence
that a normal unprivileged user can trigger it directly.
I have not reproduced this in a VM yet, so I may be missing a KHO
invariant or a preferred restore helper pattern. Feedback on the helper
semantics is welcome.
Cris Jacob Maamor (5):
kexec: handover: add helper to check preserved page ranges
liveupdate: validate restored LUO FDT before use
liveupdate: validate restored LUO session metadata
liveupdate: validate restored LUO file-set metadata
liveupdate: validate restored LUO FLB metadata
include/linux/kexec_handover.h | 6 +++++
kernel/liveupdate/kexec_handover.c | 35 ++++++++++++++++++++++++++++++
kernel/liveupdate/luo_core.c | 10 ++++++++-
kernel/liveupdate/luo_file.c | 14 ++++++++++--
kernel/liveupdate/luo_flb.c | 23 +++++++++++++++++++-
kernel/liveupdate/luo_session.c | 22 +++++++++++++++++--
6 files changed, 104 insertions(+), 6 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH RFC 1/5] kexec: handover: add helper to check preserved page ranges
2026-05-01 9:46 [PATCH RFC 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
@ 2026-05-01 9:46 ` Cris Jacob Maamor
2026-05-01 10:11 ` Greg Kroah-Hartman
2026-05-01 9:46 ` [PATCH RFC 2/5] liveupdate: validate restored LUO FDT before use Cris Jacob Maamor
` (4 subsequent siblings)
5 siblings, 1 reply; 14+ messages in thread
From: Cris Jacob Maamor @ 2026-05-01 9:46 UTC (permalink / raw)
To: Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Dan Carpenter, Greg Kroah-Hartman,
kexec, linux-mm, linux-kernel
Signed-off-by: Cris Jacob Maamor <crisjacobmaamor@gmail.com>
---
include/linux/kexec_handover.h | 6 +++++
kernel/liveupdate/kexec_handover.c | 35 ++++++++++++++++++++++++++++++
2 files changed, 41 insertions(+)
diff --git a/include/linux/kexec_handover.h b/include/linux/kexec_handover.h
index 8968c56d2d73..fb09943ab232 100644
--- a/include/linux/kexec_handover.h
+++ b/include/linux/kexec_handover.h
@@ -19,6 +19,7 @@ struct page;
#ifdef CONFIG_KEXEC_HANDOVER
bool kho_is_enabled(void);
bool is_kho_boot(void);
+bool kho_is_preserved(phys_addr_t phys, unsigned long nr_pages);
int kho_preserve_folio(struct folio *folio);
void kho_unpreserve_folio(struct folio *folio);
@@ -51,6 +52,11 @@ static inline bool is_kho_boot(void)
return false;
}
+static inline bool kho_is_preserved(phys_addr_t phys, unsigned long nr_pages)
+{
+ return false;
+}
+
static inline int kho_preserve_folio(struct folio *folio)
{
return -EOPNOTSUPP;
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 94762de1fe5f..fe9f11190705 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -10,6 +10,7 @@
#define pr_fmt(fmt) "KHO: " fmt
+#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/cma.h>
#include <linux/kmemleak.h>
@@ -429,6 +430,40 @@ static struct page *kho_restore_page(phys_addr_t phys, bool is_folio)
return page;
}
+/**
+ * kho_is_preserved - Verify that a physical page range belongs to KHO.
+ * @phys: physical address of the first page in the range.
+ * @nr_pages: number of pages that the caller expects to access.
+ *
+ * Use this before phys_to_virt() when a physical address comes from restored
+ * metadata. It checks that @phys starts a KHO-preserved allocation large
+ * enough to cover @nr_pages.
+ *
+ * This only checks the KHO marker. It does not restore, free, or take
+ * ownership of the pages.
+ *
+ * Return: true if @phys starts a preserved KHO allocation large enough to cover
+ * @nr_pages, false otherwise.
+ */
+bool kho_is_preserved(phys_addr_t phys, unsigned long nr_pages)
+{
+ struct page *page;
+ union kho_page_info info;
+
+ if (!nr_pages || !IS_ALIGNED(phys, PAGE_SIZE))
+ return false;
+
+ page = pfn_to_online_page(PHYS_PFN(phys));
+ if (!page)
+ return false;
+
+ info.page_private = page->private;
+ if (info.magic != KHO_PAGE_MAGIC || info.order >= BITS_PER_LONG)
+ return false;
+
+ return nr_pages <= BIT(info.order);
+}
+
/**
* kho_restore_folio - recreates the folio from the preserved memory.
* @phys: physical address of the folio.
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH RFC 2/5] liveupdate: validate restored LUO FDT before use
2026-05-01 9:46 [PATCH RFC 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
2026-05-01 9:46 ` [PATCH RFC 1/5] kexec: handover: add helper to check preserved page ranges Cris Jacob Maamor
@ 2026-05-01 9:46 ` Cris Jacob Maamor
2026-05-01 9:46 ` [PATCH RFC 3/5] liveupdate: validate restored LUO session metadata Cris Jacob Maamor
` (3 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Cris Jacob Maamor @ 2026-05-01 9:46 UTC (permalink / raw)
To: Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Dan Carpenter, Greg Kroah-Hartman,
kexec, linux-mm, linux-kernel
Signed-off-by: Cris Jacob Maamor <crisjacobmaamor@gmail.com>
---
kernel/liveupdate/luo_core.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
index 803f51c84275..633a16434164 100644
--- a/kernel/liveupdate/luo_core.c
+++ b/kernel/liveupdate/luo_core.c
@@ -82,6 +82,7 @@ early_param("liveupdate", early_liveupdate_param);
static int __init luo_early_startup(void)
{
+ size_t fdt_size;
phys_addr_t fdt_phys;
int err, ln_size;
const void *ptr;
@@ -94,7 +95,8 @@ static int __init luo_early_startup(void)
}
/* Retrieve LUO subtree, and verify its format. */
- err = kho_retrieve_subtree(LUO_FDT_KHO_ENTRY_NAME, &fdt_phys, NULL);
+ err = kho_retrieve_subtree(LUO_FDT_KHO_ENTRY_NAME, &fdt_phys,
+ &fdt_size);
if (err) {
if (err != -ENOENT) {
pr_err("failed to retrieve FDT '%s' from KHO: %pe\n",
@@ -105,6 +107,12 @@ static int __init luo_early_startup(void)
return 0;
}
+ if (!fdt_size || fdt_size > LUO_FDT_SIZE ||
+ !kho_is_preserved(fdt_phys, DIV_ROUND_UP(fdt_size, PAGE_SIZE))) {
+ pr_err("Invalid LUO FDT from KHO\n");
+ return -EINVAL;
+ }
+
luo_global.fdt_in = phys_to_virt(fdt_phys);
err = fdt_node_check_compatible(luo_global.fdt_in, 0,
LUO_FDT_COMPATIBLE);
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH RFC 3/5] liveupdate: validate restored LUO session metadata
2026-05-01 9:46 [PATCH RFC 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
2026-05-01 9:46 ` [PATCH RFC 1/5] kexec: handover: add helper to check preserved page ranges Cris Jacob Maamor
2026-05-01 9:46 ` [PATCH RFC 2/5] liveupdate: validate restored LUO FDT before use Cris Jacob Maamor
@ 2026-05-01 9:46 ` Cris Jacob Maamor
2026-05-01 9:46 ` [PATCH RFC 4/5] liveupdate: validate restored LUO file-set metadata Cris Jacob Maamor
` (2 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Cris Jacob Maamor @ 2026-05-01 9:46 UTC (permalink / raw)
To: Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Dan Carpenter, Greg Kroah-Hartman,
kexec, linux-mm, linux-kernel
Signed-off-by: Cris Jacob Maamor <crisjacobmaamor@gmail.com>
---
kernel/liveupdate/luo_session.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index a3327a28fc1f..0244c071936d 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -501,7 +501,18 @@ int __init luo_session_setup_incoming(void *fdt_in)
}
header_ser_pa = get_unaligned((u64 *)ptr);
+ if (!kho_is_preserved(header_ser_pa, LUO_SESSION_PGCNT)) {
+ pr_err("Session header is not KHO preserved: %#llx\n",
+ (unsigned long long)header_ser_pa);
+ return -EINVAL;
+ }
+
header_ser = phys_to_virt(header_ser_pa);
+ if (header_ser->count > LUO_SESSION_MAX) {
+ pr_err("Invalid session count: %llu\n",
+ (unsigned long long)header_ser->count);
+ return -EINVAL;
+ }
luo_session_global.incoming.header_ser = header_ser;
luo_session_global.incoming.ser = (void *)(header_ser + 1);
@@ -515,6 +526,7 @@ int luo_session_deserialize(void)
struct luo_session_header *sh = &luo_session_global.incoming;
static bool is_deserialized;
static int err;
+ u64 count;
/* If has been deserialized, always return the same error code */
if (is_deserialized)
@@ -524,6 +536,13 @@ int luo_session_deserialize(void)
if (!sh->active)
return 0;
+ count = sh->header_ser->count;
+ if (count > LUO_SESSION_MAX) {
+ pr_err("Invalid session count: %llu\n",
+ (unsigned long long)count);
+ return -EINVAL;
+ }
+
/*
* Note on error handling:
*
@@ -539,7 +558,7 @@ int luo_session_deserialize(void)
* userspace to detect the failure and trigger a reboot, which will
* reliably reset devices and reclaim memory.
*/
- for (int i = 0; i < sh->header_ser->count; i++) {
+ for (u64 i = 0; i < count; i++) {
struct luo_session *session;
session = luo_session_alloc(sh->ser[i].name);
@@ -606,4 +625,3 @@ int luo_session_serialize(void)
return err;
}
-
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH RFC 4/5] liveupdate: validate restored LUO file-set metadata
2026-05-01 9:46 [PATCH RFC 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
` (2 preceding siblings ...)
2026-05-01 9:46 ` [PATCH RFC 3/5] liveupdate: validate restored LUO session metadata Cris Jacob Maamor
@ 2026-05-01 9:46 ` Cris Jacob Maamor
2026-05-01 9:46 ` [PATCH RFC 5/5] liveupdate: validate restored LUO FLB metadata Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
5 siblings, 0 replies; 14+ messages in thread
From: Cris Jacob Maamor @ 2026-05-01 9:46 UTC (permalink / raw)
To: Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Dan Carpenter, Greg Kroah-Hartman,
kexec, linux-mm, linux-kernel
Signed-off-by: Cris Jacob Maamor <crisjacobmaamor@gmail.com>
---
kernel/liveupdate/luo_file.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index a0a419085e28..cde43d822f8f 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -783,11 +783,21 @@ int luo_file_deserialize(struct luo_file_set *file_set,
struct luo_file_ser *file_ser;
u64 i;
- if (!file_set_ser->files) {
- WARN_ON(file_set_ser->count);
+ if (!file_set_ser->count) {
+ if (file_set_ser->files)
+ return -EINVAL;
return 0;
}
+ if (file_set_ser->count > LUO_FILE_MAX)
+ return -EINVAL;
+
+ if (!file_set_ser->files)
+ return -EINVAL;
+
+ if (!kho_is_preserved(file_set_ser->files, LUO_FILE_PGCNT))
+ return -EINVAL;
+
file_set->count = file_set_ser->count;
file_set->files = phys_to_virt(file_set_ser->files);
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH RFC 5/5] liveupdate: validate restored LUO FLB metadata
2026-05-01 9:46 [PATCH RFC 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
` (3 preceding siblings ...)
2026-05-01 9:46 ` [PATCH RFC 4/5] liveupdate: validate restored LUO file-set metadata Cris Jacob Maamor
@ 2026-05-01 9:46 ` Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
5 siblings, 0 replies; 14+ messages in thread
From: Cris Jacob Maamor @ 2026-05-01 9:46 UTC (permalink / raw)
To: Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Dan Carpenter, Greg Kroah-Hartman,
kexec, linux-mm, linux-kernel
Signed-off-by: Cris Jacob Maamor <crisjacobmaamor@gmail.com>
---
kernel/liveupdate/luo_flb.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c
index 00f5494812c4..e80032669cea 100644
--- a/kernel/liveupdate/luo_flb.c
+++ b/kernel/liveupdate/luo_flb.c
@@ -162,6 +162,7 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb)
struct luo_flb_header *fh = &luo_flb_global.incoming;
struct liveupdate_flb_op_args args = {0};
bool found = false;
+ u64 count;
int err;
guard(mutex)(&private->incoming.lock);
@@ -175,7 +176,14 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb)
if (!fh->active)
return -ENODATA;
- for (int i = 0; i < fh->header_ser->count; i++) {
+ count = fh->header_ser->count;
+ if (count > LUO_FLB_MAX) {
+ pr_err("Invalid FLB count: %llu\n",
+ (unsigned long long)count);
+ return -EINVAL;
+ }
+
+ for (u64 i = 0; i < count; i++) {
if (!strcmp(fh->ser[i].name, flb->compatible)) {
private->incoming.data = fh->ser[i].data;
private->incoming.count = fh->ser[i].count;
@@ -620,7 +628,20 @@ int __init luo_flb_setup_incoming(void *fdt_in)
}
header_ser_pa = get_unaligned((u64 *)ptr);
+ if (!kho_is_preserved(header_ser_pa, LUO_FLB_PGCNT)) {
+ pr_err("FLB header is not KHO preserved: %#llx\n",
+ (unsigned long long)header_ser_pa);
+ return -EINVAL;
+ }
+
header_ser = phys_to_virt(header_ser_pa);
+ if (header_ser->pgcnt != LUO_FLB_PGCNT ||
+ header_ser->count > LUO_FLB_MAX) {
+ pr_err("Invalid FLB header: pgcnt %llu count %llu\n",
+ (unsigned long long)header_ser->pgcnt,
+ (unsigned long long)header_ser->count);
+ return -EINVAL;
+ }
luo_flb_global.incoming.header_ser = header_ser;
luo_flb_global.incoming.ser = (void *)(header_ser + 1);
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH RFC 1/5] kexec: handover: add helper to check preserved page ranges
2026-05-01 9:46 ` [PATCH RFC 1/5] kexec: handover: add helper to check preserved page ranges Cris Jacob Maamor
@ 2026-05-01 10:11 ` Greg Kroah-Hartman
0 siblings, 0 replies; 14+ messages in thread
From: Greg Kroah-Hartman @ 2026-05-01 10:11 UTC (permalink / raw)
To: Cris Jacob Maamor
Cc: Mike Rapoport, Pasha Tatashin, Pratyush Yadav, Alexander Graf,
Andrew Morton, Dan Carpenter, kexec, linux-mm, linux-kernel
On Fri, May 01, 2026 at 05:46:33PM +0800, Cris Jacob Maamor wrote:
> Signed-off-by: Cris Jacob Maamor <crisjacobmaamor@gmail.com>
For obvious reasons, we can't take patches without any changelog text
(nor do you want us to.)
And when a pathc is "RFC", what specific comments are you asking for?
Why don't you feel this is good enough to take now?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 0/5] liveupdate: validate restored LUO metadata
2026-05-01 9:46 [PATCH RFC 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
` (4 preceding siblings ...)
2026-05-01 9:46 ` [PATCH RFC 5/5] liveupdate: validate restored LUO FLB metadata Cris Jacob Maamor
@ 2026-05-01 17:30 ` Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 1/5] kexec: handover: add helper to check preserved page ranges Cris Jacob Maamor
` (5 more replies)
5 siblings, 6 replies; 14+ messages in thread
From: Cris Jacob Maamor @ 2026-05-01 17:30 UTC (permalink / raw)
To: Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Greg Kroah-Hartman, kexec,
linux-mm, linux-kernel
LUO restores metadata from KHO/FDT during liveupdate. The restored
metadata contains physical addresses and count fields used to access and
walk preserved session, file set, and FLB arrays.
This series adds a non-consuming KHO preserved-range check and uses it
before phys_to_virt() on restored metadata addresses. It also rejects
restored counts above LUO_SESSION_MAX, LUO_FILE_MAX, and LUO_FLB_MAX
before traversal.
As far as I can tell, this is root/admin-only; I do not have evidence
that a normal unprivileged user can trigger it directly.
Changes since v1:
- Dropped RFC marking.
- Added changelog text to each patch.
- No code changes.
Cris Jacob Maamor (5):
kexec: handover: add helper to check preserved page ranges
liveupdate: validate LUO FDT physical address before mapping
liveupdate: validate restored LUO session metadata
liveupdate: validate restored LUO file set metadata
liveupdate: validate restored LUO FLB metadata
include/linux/kexec_handover.h | 6 +++++
kernel/liveupdate/kexec_handover.c | 35 ++++++++++++++++++++++++++++++
kernel/liveupdate/luo_core.c | 10 ++++++++-
kernel/liveupdate/luo_file.c | 14 ++++++++++--
kernel/liveupdate/luo_flb.c | 23 +++++++++++++++++++-
kernel/liveupdate/luo_session.c | 22 +++++++++++++++++--
6 files changed, 104 insertions(+), 6 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 1/5] kexec: handover: add helper to check preserved page ranges
2026-05-01 17:30 ` [PATCH v2 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
@ 2026-05-01 17:30 ` Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 2/5] liveupdate: validate LUO FDT physical address before mapping Cris Jacob Maamor
` (4 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Cris Jacob Maamor @ 2026-05-01 17:30 UTC (permalink / raw)
To: Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Greg Kroah-Hartman, kexec,
linux-mm, linux-kernel
Restore code should not use phys_to_virt() on physical addresses from
restored metadata until it verifies that the range was preserved by KHO.
Add kho_is_preserved() so callers can check a preserved page range without
restoring, freeing, or taking ownership of the pages.
Signed-off-by: Cris Jacob Maamor <crisjacobmaamor@gmail.com>
---
include/linux/kexec_handover.h | 6 +++++
kernel/liveupdate/kexec_handover.c | 35 ++++++++++++++++++++++++++++++
2 files changed, 41 insertions(+)
diff --git a/include/linux/kexec_handover.h b/include/linux/kexec_handover.h
index 8968c56d2d73..fb09943ab232 100644
--- a/include/linux/kexec_handover.h
+++ b/include/linux/kexec_handover.h
@@ -19,6 +19,7 @@ struct page;
#ifdef CONFIG_KEXEC_HANDOVER
bool kho_is_enabled(void);
bool is_kho_boot(void);
+bool kho_is_preserved(phys_addr_t phys, unsigned long nr_pages);
int kho_preserve_folio(struct folio *folio);
void kho_unpreserve_folio(struct folio *folio);
@@ -51,6 +52,11 @@ static inline bool is_kho_boot(void)
return false;
}
+static inline bool kho_is_preserved(phys_addr_t phys, unsigned long nr_pages)
+{
+ return false;
+}
+
static inline int kho_preserve_folio(struct folio *folio)
{
return -EOPNOTSUPP;
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 94762de1fe5f..fe9f11190705 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -10,6 +10,7 @@
#define pr_fmt(fmt) "KHO: " fmt
+#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/cma.h>
#include <linux/kmemleak.h>
@@ -429,6 +430,40 @@ static struct page *kho_restore_page(phys_addr_t phys, bool is_folio)
return page;
}
+/**
+ * kho_is_preserved - Verify that a physical page range belongs to KHO.
+ * @phys: physical address of the first page in the range.
+ * @nr_pages: number of pages that the caller expects to access.
+ *
+ * Use this before phys_to_virt() when a physical address comes from restored
+ * metadata. It checks that @phys starts a KHO-preserved allocation large
+ * enough to cover @nr_pages.
+ *
+ * This only checks the KHO marker. It does not restore, free, or take
+ * ownership of the pages.
+ *
+ * Return: true if @phys starts a preserved KHO allocation large enough to cover
+ * @nr_pages, false otherwise.
+ */
+bool kho_is_preserved(phys_addr_t phys, unsigned long nr_pages)
+{
+ struct page *page;
+ union kho_page_info info;
+
+ if (!nr_pages || !IS_ALIGNED(phys, PAGE_SIZE))
+ return false;
+
+ page = pfn_to_online_page(PHYS_PFN(phys));
+ if (!page)
+ return false;
+
+ info.page_private = page->private;
+ if (info.magic != KHO_PAGE_MAGIC || info.order >= BITS_PER_LONG)
+ return false;
+
+ return nr_pages <= BIT(info.order);
+}
+
/**
* kho_restore_folio - recreates the folio from the preserved memory.
* @phys: physical address of the folio.
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 2/5] liveupdate: validate LUO FDT physical address before mapping
2026-05-01 17:30 ` [PATCH v2 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 1/5] kexec: handover: add helper to check preserved page ranges Cris Jacob Maamor
@ 2026-05-01 17:30 ` Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 3/5] liveupdate: validate restored LUO session metadata Cris Jacob Maamor
` (3 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Cris Jacob Maamor @ 2026-05-01 17:30 UTC (permalink / raw)
To: Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Greg Kroah-Hartman, kexec,
linux-mm, linux-kernel
LUO gets the restored FDT address from KHO and maps it with phys_to_virt().
Check the FDT size and make sure the address range is KHO-preserved before
mapping it. Reject empty or oversized FDT metadata.
Signed-off-by: Cris Jacob Maamor <crisjacobmaamor@gmail.com>
---
kernel/liveupdate/luo_core.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/kernel/liveupdate/luo_core.c b/kernel/liveupdate/luo_core.c
index 803f51c84275..633a16434164 100644
--- a/kernel/liveupdate/luo_core.c
+++ b/kernel/liveupdate/luo_core.c
@@ -82,6 +82,7 @@ early_param("liveupdate", early_liveupdate_param);
static int __init luo_early_startup(void)
{
+ size_t fdt_size;
phys_addr_t fdt_phys;
int err, ln_size;
const void *ptr;
@@ -94,7 +95,8 @@ static int __init luo_early_startup(void)
}
/* Retrieve LUO subtree, and verify its format. */
- err = kho_retrieve_subtree(LUO_FDT_KHO_ENTRY_NAME, &fdt_phys, NULL);
+ err = kho_retrieve_subtree(LUO_FDT_KHO_ENTRY_NAME, &fdt_phys,
+ &fdt_size);
if (err) {
if (err != -ENOENT) {
pr_err("failed to retrieve FDT '%s' from KHO: %pe\n",
@@ -105,6 +107,12 @@ static int __init luo_early_startup(void)
return 0;
}
+ if (!fdt_size || fdt_size > LUO_FDT_SIZE ||
+ !kho_is_preserved(fdt_phys, DIV_ROUND_UP(fdt_size, PAGE_SIZE))) {
+ pr_err("Invalid LUO FDT from KHO\n");
+ return -EINVAL;
+ }
+
luo_global.fdt_in = phys_to_virt(fdt_phys);
err = fdt_node_check_compatible(luo_global.fdt_in, 0,
LUO_FDT_COMPATIBLE);
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 3/5] liveupdate: validate restored LUO session metadata
2026-05-01 17:30 ` [PATCH v2 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 1/5] kexec: handover: add helper to check preserved page ranges Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 2/5] liveupdate: validate LUO FDT physical address before mapping Cris Jacob Maamor
@ 2026-05-01 17:30 ` Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 4/5] liveupdate: validate restored LUO file set metadata Cris Jacob Maamor
` (2 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Cris Jacob Maamor @ 2026-05-01 17:30 UTC (permalink / raw)
To: Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Greg Kroah-Hartman, kexec,
linux-mm, linux-kernel
The restored FDT contains the physical address of the LUO session header,
which LUO maps before reading session metadata. Check that physical range
against KHO-preserved memory before calling phys_to_virt(), and abort the
restore if the physical range is not covered.
The session header has a count that controls how far LUO walks the
serialized session array. Check that count against LUO_SESSION_MAX before
deserialization, and abort the restore if the value is out of range.
Signed-off-by: Cris Jacob Maamor <crisjacobmaamor@gmail.com>
---
kernel/liveupdate/luo_session.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/kernel/liveupdate/luo_session.c b/kernel/liveupdate/luo_session.c
index a3327a28fc1f..0244c071936d 100644
--- a/kernel/liveupdate/luo_session.c
+++ b/kernel/liveupdate/luo_session.c
@@ -501,7 +501,18 @@ int __init luo_session_setup_incoming(void *fdt_in)
}
header_ser_pa = get_unaligned((u64 *)ptr);
+ if (!kho_is_preserved(header_ser_pa, LUO_SESSION_PGCNT)) {
+ pr_err("Session header is not KHO preserved: %#llx\n",
+ (unsigned long long)header_ser_pa);
+ return -EINVAL;
+ }
+
header_ser = phys_to_virt(header_ser_pa);
+ if (header_ser->count > LUO_SESSION_MAX) {
+ pr_err("Invalid session count: %llu\n",
+ (unsigned long long)header_ser->count);
+ return -EINVAL;
+ }
luo_session_global.incoming.header_ser = header_ser;
luo_session_global.incoming.ser = (void *)(header_ser + 1);
@@ -515,6 +526,7 @@ int luo_session_deserialize(void)
struct luo_session_header *sh = &luo_session_global.incoming;
static bool is_deserialized;
static int err;
+ u64 count;
/* If has been deserialized, always return the same error code */
if (is_deserialized)
@@ -524,6 +536,13 @@ int luo_session_deserialize(void)
if (!sh->active)
return 0;
+ count = sh->header_ser->count;
+ if (count > LUO_SESSION_MAX) {
+ pr_err("Invalid session count: %llu\n",
+ (unsigned long long)count);
+ return -EINVAL;
+ }
+
/*
* Note on error handling:
*
@@ -539,7 +558,7 @@ int luo_session_deserialize(void)
* userspace to detect the failure and trigger a reboot, which will
* reliably reset devices and reclaim memory.
*/
- for (int i = 0; i < sh->header_ser->count; i++) {
+ for (u64 i = 0; i < count; i++) {
struct luo_session *session;
session = luo_session_alloc(sh->ser[i].name);
@@ -606,4 +625,3 @@ int luo_session_serialize(void)
return err;
}
-
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 4/5] liveupdate: validate restored LUO file set metadata
2026-05-01 17:30 ` [PATCH v2 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
` (2 preceding siblings ...)
2026-05-01 17:30 ` [PATCH v2 3/5] liveupdate: validate restored LUO session metadata Cris Jacob Maamor
@ 2026-05-01 17:30 ` Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 5/5] liveupdate: validate restored LUO FLB metadata Cris Jacob Maamor
2026-05-01 19:34 ` [PATCH v2 0/5] liveupdate: validate restored LUO metadata Pasha Tatashin
5 siblings, 0 replies; 14+ messages in thread
From: Cris Jacob Maamor @ 2026-05-01 17:30 UTC (permalink / raw)
To: Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Greg Kroah-Hartman, kexec,
linux-mm, linux-kernel
The restored session metadata provides the LUO file set address and count.
LUO maps that address with phys_to_virt() and uses the restored count to
walk the serialized file array.
Reject invalid empty file set metadata, reject counts above LUO_FILE_MAX,
and check that the physical range is KHO-preserved before mapping it.
Signed-off-by: Cris Jacob Maamor <crisjacobmaamor@gmail.com>
---
kernel/liveupdate/luo_file.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/kernel/liveupdate/luo_file.c b/kernel/liveupdate/luo_file.c
index a0a419085e28..cde43d822f8f 100644
--- a/kernel/liveupdate/luo_file.c
+++ b/kernel/liveupdate/luo_file.c
@@ -783,11 +783,21 @@ int luo_file_deserialize(struct luo_file_set *file_set,
struct luo_file_ser *file_ser;
u64 i;
- if (!file_set_ser->files) {
- WARN_ON(file_set_ser->count);
+ if (!file_set_ser->count) {
+ if (file_set_ser->files)
+ return -EINVAL;
return 0;
}
+ if (file_set_ser->count > LUO_FILE_MAX)
+ return -EINVAL;
+
+ if (!file_set_ser->files)
+ return -EINVAL;
+
+ if (!kho_is_preserved(file_set_ser->files, LUO_FILE_PGCNT))
+ return -EINVAL;
+
file_set->count = file_set_ser->count;
file_set->files = phys_to_virt(file_set_ser->files);
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v2 5/5] liveupdate: validate restored LUO FLB metadata
2026-05-01 17:30 ` [PATCH v2 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
` (3 preceding siblings ...)
2026-05-01 17:30 ` [PATCH v2 4/5] liveupdate: validate restored LUO file set metadata Cris Jacob Maamor
@ 2026-05-01 17:30 ` Cris Jacob Maamor
2026-05-01 19:34 ` [PATCH v2 0/5] liveupdate: validate restored LUO metadata Pasha Tatashin
5 siblings, 0 replies; 14+ messages in thread
From: Cris Jacob Maamor @ 2026-05-01 17:30 UTC (permalink / raw)
To: Mike Rapoport, Pasha Tatashin, Pratyush Yadav
Cc: Alexander Graf, Andrew Morton, Greg Kroah-Hartman, kexec,
linux-mm, linux-kernel
The restored FDT contains the physical address of the LUO FLB header, which
LUO maps before using the restored FLB metadata.
Check that the FLB header range is KHO-preserved before calling
phys_to_virt(). Reject invalid page counts and counts above LUO_FLB_MAX
before walking the restored FLB array.
Signed-off-by: Cris Jacob Maamor <crisjacobmaamor@gmail.com>
---
kernel/liveupdate/luo_flb.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/kernel/liveupdate/luo_flb.c b/kernel/liveupdate/luo_flb.c
index 00f5494812c4..e80032669cea 100644
--- a/kernel/liveupdate/luo_flb.c
+++ b/kernel/liveupdate/luo_flb.c
@@ -162,6 +162,7 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb)
struct luo_flb_header *fh = &luo_flb_global.incoming;
struct liveupdate_flb_op_args args = {0};
bool found = false;
+ u64 count;
int err;
guard(mutex)(&private->incoming.lock);
@@ -175,7 +176,14 @@ static int luo_flb_retrieve_one(struct liveupdate_flb *flb)
if (!fh->active)
return -ENODATA;
- for (int i = 0; i < fh->header_ser->count; i++) {
+ count = fh->header_ser->count;
+ if (count > LUO_FLB_MAX) {
+ pr_err("Invalid FLB count: %llu\n",
+ (unsigned long long)count);
+ return -EINVAL;
+ }
+
+ for (u64 i = 0; i < count; i++) {
if (!strcmp(fh->ser[i].name, flb->compatible)) {
private->incoming.data = fh->ser[i].data;
private->incoming.count = fh->ser[i].count;
@@ -620,7 +628,20 @@ int __init luo_flb_setup_incoming(void *fdt_in)
}
header_ser_pa = get_unaligned((u64 *)ptr);
+ if (!kho_is_preserved(header_ser_pa, LUO_FLB_PGCNT)) {
+ pr_err("FLB header is not KHO preserved: %#llx\n",
+ (unsigned long long)header_ser_pa);
+ return -EINVAL;
+ }
+
header_ser = phys_to_virt(header_ser_pa);
+ if (header_ser->pgcnt != LUO_FLB_PGCNT ||
+ header_ser->count > LUO_FLB_MAX) {
+ pr_err("Invalid FLB header: pgcnt %llu count %llu\n",
+ (unsigned long long)header_ser->pgcnt,
+ (unsigned long long)header_ser->count);
+ return -EINVAL;
+ }
luo_flb_global.incoming.header_ser = header_ser;
luo_flb_global.incoming.ser = (void *)(header_ser + 1);
--
2.53.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v2 0/5] liveupdate: validate restored LUO metadata
2026-05-01 17:30 ` [PATCH v2 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
` (4 preceding siblings ...)
2026-05-01 17:30 ` [PATCH v2 5/5] liveupdate: validate restored LUO FLB metadata Cris Jacob Maamor
@ 2026-05-01 19:34 ` Pasha Tatashin
5 siblings, 0 replies; 14+ messages in thread
From: Pasha Tatashin @ 2026-05-01 19:34 UTC (permalink / raw)
To: Cris Jacob Maamor
Cc: Mike Rapoport, Pasha Tatashin, Pratyush Yadav, Alexander Graf,
Andrew Morton, Greg Kroah-Hartman, kexec, linux-mm, linux-kernel
On 05-02 01:30, Cris Jacob Maamor wrote:
> LUO restores metadata from KHO/FDT during liveupdate. The restored
> metadata contains physical addresses and count fields used to access and
> walk preserved session, file set, and FLB arrays.
>
> This series adds a non-consuming KHO preserved-range check and uses it
> before phys_to_virt() on restored metadata addresses. It also rejects
> restored counts above LUO_SESSION_MAX, LUO_FILE_MAX, and LUO_FLB_MAX
> before traversal.
>
> As far as I can tell, this is root/admin-only; I do not have evidence
> that a normal unprivileged user can trigger it directly.
>
> Changes since v1:
> - Dropped RFC marking.
> - Added changelog text to each patch.
> - No code changes.
>
> Cris Jacob Maamor (5):
> kexec: handover: add helper to check preserved page ranges
> liveupdate: validate LUO FDT physical address before mapping
> liveupdate: validate restored LUO session metadata
> liveupdate: validate restored LUO file set metadata
> liveupdate: validate restored LUO FLB metadata
I have replied separately in the security report to clarify that this is
not a bug. The behavior follows the ABI specification exactly: we use
the PA addresses and ranges provided by the KHO FDT tree.
NAK
>
> include/linux/kexec_handover.h | 6 +++++
> kernel/liveupdate/kexec_handover.c | 35 ++++++++++++++++++++++++++++++
> kernel/liveupdate/luo_core.c | 10 ++++++++-
> kernel/liveupdate/luo_file.c | 14 ++++++++++--
> kernel/liveupdate/luo_flb.c | 23 +++++++++++++++++++-
> kernel/liveupdate/luo_session.c | 22 +++++++++++++++++--
> 6 files changed, 104 insertions(+), 6 deletions(-)
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-05-01 19:34 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-01 9:46 [PATCH RFC 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
2026-05-01 9:46 ` [PATCH RFC 1/5] kexec: handover: add helper to check preserved page ranges Cris Jacob Maamor
2026-05-01 10:11 ` Greg Kroah-Hartman
2026-05-01 9:46 ` [PATCH RFC 2/5] liveupdate: validate restored LUO FDT before use Cris Jacob Maamor
2026-05-01 9:46 ` [PATCH RFC 3/5] liveupdate: validate restored LUO session metadata Cris Jacob Maamor
2026-05-01 9:46 ` [PATCH RFC 4/5] liveupdate: validate restored LUO file-set metadata Cris Jacob Maamor
2026-05-01 9:46 ` [PATCH RFC 5/5] liveupdate: validate restored LUO FLB metadata Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 0/5] liveupdate: validate restored LUO metadata Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 1/5] kexec: handover: add helper to check preserved page ranges Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 2/5] liveupdate: validate LUO FDT physical address before mapping Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 3/5] liveupdate: validate restored LUO session metadata Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 4/5] liveupdate: validate restored LUO file set metadata Cris Jacob Maamor
2026-05-01 17:30 ` [PATCH v2 5/5] liveupdate: validate restored LUO FLB metadata Cris Jacob Maamor
2026-05-01 19:34 ` [PATCH v2 0/5] liveupdate: validate restored LUO metadata Pasha Tatashin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox