public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [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