linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 1/2] kho: move sanity checks to kho_restore_page()
@ 2025-09-17 12:56 Pratyush Yadav
  2025-09-17 12:56 ` [PATCH v2 2/2] kho: make sure page being restored is actually from KHO Pratyush Yadav
  2025-09-17 14:31 ` [PATCH v2 1/2] kho: move sanity checks to kho_restore_page() Mike Rapoport
  0 siblings, 2 replies; 4+ messages in thread
From: Pratyush Yadav @ 2025-09-17 12:56 UTC (permalink / raw)
  To: Alexander Graf, Mike Rapoport, Changyuan Lyu, Andrew Morton,
	Baoquan He, Pratyush Yadav, Pasha Tatashin, Jason Gunthorpe,
	Chris Li, Jason Miu
  Cc: linux-kernel, kexec, linux-mm

While KHO exposes folio as the primitive externally, internally its
restoration machinery operates on pages. This can be seen with
kho_restore_folio() for example. It performs some sanity checks and
hands it over to kho_restore_page() to do the heavy lifting of page
restoration. After the work done by kho_restore_page(),
kho_restore_folio() only converts the head page to folio and returns it.
Similarly, deserialize_bitmap() operates on the head page directly to
store the order.

Move the sanity checks for valid phys and order from the public-facing
kho_restore_folio() to the private-facing kho_restore_page(). This makes
the boundary between page and folio clearer from KHO's perspective.

While at it, drop the comment above kho_restore_page(). The comment is
misleading now. The function stopped looking like free_reserved_page()
since 12b9a2c05d1b4 ("kho: initialize tail pages for higher order folios
properly"), and now looks even more different.

Signed-off-by: Pratyush Yadav <pratyush@kernel.org>
---

Notes:
    Changes in v2:
    
    - New in v2.

 kernel/kexec_handover.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/kernel/kexec_handover.c b/kernel/kexec_handover.c
index ecd1ac210dbd7..69cab82abaaef 100644
--- a/kernel/kexec_handover.c
+++ b/kernel/kexec_handover.c
@@ -183,10 +183,18 @@ static int __kho_preserve_order(struct kho_mem_track *track, unsigned long pfn,
 	return 0;
 }
 
-/* almost as free_reserved_page(), just don't free the page */
-static void kho_restore_page(struct page *page, unsigned int order)
+static struct page *kho_restore_page(phys_addr_t phys)
 {
-	unsigned int nr_pages = (1 << order);
+	struct page *page = pfn_to_online_page(PHYS_PFN(phys));
+	unsigned int nr_pages, order;
+
+	if (!page)
+		return NULL;
+
+	order = page->private;
+	if (order > MAX_PAGE_ORDER)
+		return NULL;
+	nr_pages = (1 << order);
 
 	/* Head page gets refcount of 1. */
 	set_page_count(page, 1);
@@ -199,6 +207,7 @@ static void kho_restore_page(struct page *page, unsigned int order)
 		prep_compound_page(page, order);
 
 	adjust_managed_page_count(page, nr_pages);
+	return page;
 }
 
 /**
@@ -209,18 +218,9 @@ static void kho_restore_page(struct page *page, unsigned int order)
  */
 struct folio *kho_restore_folio(phys_addr_t phys)
 {
-	struct page *page = pfn_to_online_page(PHYS_PFN(phys));
-	unsigned long order;
-
-	if (!page)
-		return NULL;
-
-	order = page->private;
-	if (order > MAX_PAGE_ORDER)
-		return NULL;
+	struct page *page = kho_restore_page(phys);
 
-	kho_restore_page(page, order);
-	return page_folio(page);
+	return page ? page_folio(page) : NULL;
 }
 EXPORT_SYMBOL_GPL(kho_restore_folio);
 
-- 
2.47.3



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v2 2/2] kho: make sure page being restored is actually from KHO
  2025-09-17 12:56 [PATCH v2 1/2] kho: move sanity checks to kho_restore_page() Pratyush Yadav
@ 2025-09-17 12:56 ` Pratyush Yadav
  2025-09-17 14:38   ` Mike Rapoport
  2025-09-17 14:31 ` [PATCH v2 1/2] kho: move sanity checks to kho_restore_page() Mike Rapoport
  1 sibling, 1 reply; 4+ messages in thread
From: Pratyush Yadav @ 2025-09-17 12:56 UTC (permalink / raw)
  To: Alexander Graf, Mike Rapoport, Changyuan Lyu, Andrew Morton,
	Baoquan He, Pratyush Yadav, Pasha Tatashin, Jason Gunthorpe,
	Chris Li, Jason Miu
  Cc: linux-kernel, kexec, linux-mm

When restoring a page, no sanity checks are done to make sure the page
actually came from a kexec handover. The caller is trusted to pass in
the right address. If the caller has a bug and passes in a wrong
address, an in-use page might be "restored" and returned, causing all
sorts of memory corruption.

Harden the page restore logic by stashing in a magic number in
page->private along with the order. If the magic number does not match,
the page won't be touched. page->private is an unsigned long. The union
kho_page_info splits it into two parts, with one holding the order and
the other holding the magic number.

Signed-off-by: Pratyush Yadav <pratyush@kernel.org>
---

Notes:
    Changes in v2:
    
    - Add a WARN_ON_ONCE() if order or magic is invalid.
    - Add a comment explaining why the magic check also implicitly makes
      sure phys is order-aligned.
    - Clear page private to make sure later restores of the same page error
      out.
    - Move the checks to kho_restore_page() since patch 1 now moves sanity
      checking to it.

 kernel/kexec_handover.c | 41 ++++++++++++++++++++++++++++++++++-------
 1 file changed, 34 insertions(+), 7 deletions(-)

diff --git a/kernel/kexec_handover.c b/kernel/kexec_handover.c
index 69cab82abaaef..911fda8532b2e 100644
--- a/kernel/kexec_handover.c
+++ b/kernel/kexec_handover.c
@@ -32,6 +32,22 @@
 #define PROP_PRESERVED_MEMORY_MAP "preserved-memory-map"
 #define PROP_SUB_FDT "fdt"
 
+#define KHO_PAGE_MAGIC 0x4b484f50U /* ASCII for 'KHOP' */
+
+/*
+ * KHO uses page->private, which is an unsigned long, to store page metadata.
+ * Use it to store both the magic and the order.
+ */
+union kho_page_info {
+	unsigned long page_private;
+	struct {
+		unsigned int order;
+		unsigned int magic;
+	};
+};
+
+static_assert(sizeof(union kho_page_info) == sizeof(((struct page *)0)->private));
+
 static bool kho_enable __ro_after_init;
 
 bool kho_is_enabled(void)
@@ -186,16 +202,24 @@ static int __kho_preserve_order(struct kho_mem_track *track, unsigned long pfn,
 static struct page *kho_restore_page(phys_addr_t phys)
 {
 	struct page *page = pfn_to_online_page(PHYS_PFN(phys));
-	unsigned int nr_pages, order;
+	union kho_page_info info;
+	unsigned int nr_pages;
 
 	if (!page)
 		return NULL;
 
-	order = page->private;
-	if (order > MAX_PAGE_ORDER)
+	info.page_private = page->private;
+	/*
+	 * deserialize_bitmap() only sets the magic on the head page. This magic
+	 * check also implicitly makes sure phys is order-aligned since for
+	 * non-order-aligned phys addresses, magic will never be set.
+	 */
+	if (WARN_ON_ONCE(info.magic != KHO_PAGE_MAGIC || info.order > MAX_PAGE_ORDER))
 		return NULL;
-	nr_pages = (1 << order);
+	nr_pages = (1 << info.order);
 
+	/* Clear private to make sure later restores on this page error out. */
+	page->private = 0;
 	/* Head page gets refcount of 1. */
 	set_page_count(page, 1);
 
@@ -203,8 +227,8 @@ static struct page *kho_restore_page(phys_addr_t phys)
 	for (unsigned int i = 1; i < nr_pages; i++)
 		set_page_count(page + i, 0);
 
-	if (order > 0)
-		prep_compound_page(page, order);
+	if (info.order > 0)
+		prep_compound_page(page, info.order);
 
 	adjust_managed_page_count(page, nr_pages);
 	return page;
@@ -341,10 +365,13 @@ static void __init deserialize_bitmap(unsigned int order,
 		phys_addr_t phys =
 			elm->phys_start + (bit << (order + PAGE_SHIFT));
 		struct page *page = phys_to_page(phys);
+		union kho_page_info info;
 
 		memblock_reserve(phys, sz);
 		memblock_reserved_mark_noinit(phys, sz);
-		page->private = order;
+		info.magic = KHO_PAGE_MAGIC;
+		info.order = order;
+		page->private = info.page_private;
 	}
 }
 
-- 
2.47.3



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 1/2] kho: move sanity checks to kho_restore_page()
  2025-09-17 12:56 [PATCH v2 1/2] kho: move sanity checks to kho_restore_page() Pratyush Yadav
  2025-09-17 12:56 ` [PATCH v2 2/2] kho: make sure page being restored is actually from KHO Pratyush Yadav
@ 2025-09-17 14:31 ` Mike Rapoport
  1 sibling, 0 replies; 4+ messages in thread
From: Mike Rapoport @ 2025-09-17 14:31 UTC (permalink / raw)
  To: Pratyush Yadav
  Cc: Alexander Graf, Changyuan Lyu, Andrew Morton, Baoquan He,
	Pasha Tatashin, Jason Gunthorpe, Chris Li, Jason Miu,
	linux-kernel, kexec, linux-mm

On Wed, Sep 17, 2025 at 02:56:53PM +0200, Pratyush Yadav wrote:
> While KHO exposes folio as the primitive externally, internally its
> restoration machinery operates on pages. This can be seen with
> kho_restore_folio() for example. It performs some sanity checks and
> hands it over to kho_restore_page() to do the heavy lifting of page
> restoration. After the work done by kho_restore_page(),
> kho_restore_folio() only converts the head page to folio and returns it.
> Similarly, deserialize_bitmap() operates on the head page directly to
> store the order.
> 
> Move the sanity checks for valid phys and order from the public-facing
> kho_restore_folio() to the private-facing kho_restore_page(). This makes
> the boundary between page and folio clearer from KHO's perspective.
> 
> While at it, drop the comment above kho_restore_page(). The comment is
> misleading now. The function stopped looking like free_reserved_page()
> since 12b9a2c05d1b4 ("kho: initialize tail pages for higher order folios
> properly"), and now looks even more different.
> 
> Signed-off-by: Pratyush Yadav <pratyush@kernel.org>

Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

> ---
> 
> Notes:
>     Changes in v2:
>     
>     - New in v2.
> 
>  kernel/kexec_handover.c | 28 ++++++++++++++--------------
>  1 file changed, 14 insertions(+), 14 deletions(-)
> 
> diff --git a/kernel/kexec_handover.c b/kernel/kexec_handover.c
> index ecd1ac210dbd7..69cab82abaaef 100644
> --- a/kernel/kexec_handover.c
> +++ b/kernel/kexec_handover.c
> @@ -183,10 +183,18 @@ static int __kho_preserve_order(struct kho_mem_track *track, unsigned long pfn,
>  	return 0;
>  }
>  
> -/* almost as free_reserved_page(), just don't free the page */
> -static void kho_restore_page(struct page *page, unsigned int order)
> +static struct page *kho_restore_page(phys_addr_t phys)
>  {
> -	unsigned int nr_pages = (1 << order);
> +	struct page *page = pfn_to_online_page(PHYS_PFN(phys));
> +	unsigned int nr_pages, order;
> +
> +	if (!page)
> +		return NULL;
> +
> +	order = page->private;
> +	if (order > MAX_PAGE_ORDER)
> +		return NULL;
> +	nr_pages = (1 << order);
>  
>  	/* Head page gets refcount of 1. */
>  	set_page_count(page, 1);
> @@ -199,6 +207,7 @@ static void kho_restore_page(struct page *page, unsigned int order)
>  		prep_compound_page(page, order);
>  
>  	adjust_managed_page_count(page, nr_pages);
> +	return page;
>  }
>  
>  /**
> @@ -209,18 +218,9 @@ static void kho_restore_page(struct page *page, unsigned int order)
>   */
>  struct folio *kho_restore_folio(phys_addr_t phys)
>  {
> -	struct page *page = pfn_to_online_page(PHYS_PFN(phys));
> -	unsigned long order;
> -
> -	if (!page)
> -		return NULL;
> -
> -	order = page->private;
> -	if (order > MAX_PAGE_ORDER)
> -		return NULL;
> +	struct page *page = kho_restore_page(phys);
>  
> -	kho_restore_page(page, order);
> -	return page_folio(page);
> +	return page ? page_folio(page) : NULL;
>  }
>  EXPORT_SYMBOL_GPL(kho_restore_folio);
>  
> -- 
> 2.47.3
> 

-- 
Sincerely yours,
Mike.


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 2/2] kho: make sure page being restored is actually from KHO
  2025-09-17 12:56 ` [PATCH v2 2/2] kho: make sure page being restored is actually from KHO Pratyush Yadav
@ 2025-09-17 14:38   ` Mike Rapoport
  0 siblings, 0 replies; 4+ messages in thread
From: Mike Rapoport @ 2025-09-17 14:38 UTC (permalink / raw)
  To: Pratyush Yadav
  Cc: Alexander Graf, Changyuan Lyu, Andrew Morton, Baoquan He,
	Pasha Tatashin, Jason Gunthorpe, Chris Li, Jason Miu,
	linux-kernel, kexec, linux-mm

On Wed, Sep 17, 2025 at 02:56:54PM +0200, Pratyush Yadav wrote:
> When restoring a page, no sanity checks are done to make sure the page
> actually came from a kexec handover. The caller is trusted to pass in
> the right address. If the caller has a bug and passes in a wrong
> address, an in-use page might be "restored" and returned, causing all
> sorts of memory corruption.
> 
> Harden the page restore logic by stashing in a magic number in
> page->private along with the order. If the magic number does not match,
> the page won't be touched. page->private is an unsigned long. The union
> kho_page_info splits it into two parts, with one holding the order and
> the other holding the magic number.
> 
> Signed-off-by: Pratyush Yadav <pratyush@kernel.org>

Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

> ---
> 
> Notes:
>     Changes in v2:
>     
>     - Add a WARN_ON_ONCE() if order or magic is invalid.
>     - Add a comment explaining why the magic check also implicitly makes
>       sure phys is order-aligned.
>     - Clear page private to make sure later restores of the same page error
>       out.
>     - Move the checks to kho_restore_page() since patch 1 now moves sanity
>       checking to it.
> 
>  kernel/kexec_handover.c | 41 ++++++++++++++++++++++++++++++++++-------
>  1 file changed, 34 insertions(+), 7 deletions(-)
> 
> diff --git a/kernel/kexec_handover.c b/kernel/kexec_handover.c
> index 69cab82abaaef..911fda8532b2e 100644
> --- a/kernel/kexec_handover.c
> +++ b/kernel/kexec_handover.c
> @@ -32,6 +32,22 @@
>  #define PROP_PRESERVED_MEMORY_MAP "preserved-memory-map"
>  #define PROP_SUB_FDT "fdt"
>  
> +#define KHO_PAGE_MAGIC 0x4b484f50U /* ASCII for 'KHOP' */
> +
> +/*
> + * KHO uses page->private, which is an unsigned long, to store page metadata.
> + * Use it to store both the magic and the order.
> + */
> +union kho_page_info {
> +	unsigned long page_private;
> +	struct {
> +		unsigned int order;
> +		unsigned int magic;
> +	};
> +};
> +
> +static_assert(sizeof(union kho_page_info) == sizeof(((struct page *)0)->private));
> +
>  static bool kho_enable __ro_after_init;
>  
>  bool kho_is_enabled(void)
> @@ -186,16 +202,24 @@ static int __kho_preserve_order(struct kho_mem_track *track, unsigned long pfn,
>  static struct page *kho_restore_page(phys_addr_t phys)
>  {
>  	struct page *page = pfn_to_online_page(PHYS_PFN(phys));
> -	unsigned int nr_pages, order;
> +	union kho_page_info info;
> +	unsigned int nr_pages;
>  
>  	if (!page)
>  		return NULL;
>  
> -	order = page->private;
> -	if (order > MAX_PAGE_ORDER)
> +	info.page_private = page->private;
> +	/*
> +	 * deserialize_bitmap() only sets the magic on the head page. This magic
> +	 * check also implicitly makes sure phys is order-aligned since for
> +	 * non-order-aligned phys addresses, magic will never be set.
> +	 */
> +	if (WARN_ON_ONCE(info.magic != KHO_PAGE_MAGIC || info.order > MAX_PAGE_ORDER))
>  		return NULL;
> -	nr_pages = (1 << order);
> +	nr_pages = (1 << info.order);
>  
> +	/* Clear private to make sure later restores on this page error out. */
> +	page->private = 0;
>  	/* Head page gets refcount of 1. */
>  	set_page_count(page, 1);
>  
> @@ -203,8 +227,8 @@ static struct page *kho_restore_page(phys_addr_t phys)
>  	for (unsigned int i = 1; i < nr_pages; i++)
>  		set_page_count(page + i, 0);
>  
> -	if (order > 0)
> -		prep_compound_page(page, order);
> +	if (info.order > 0)
> +		prep_compound_page(page, info.order);
>  
>  	adjust_managed_page_count(page, nr_pages);
>  	return page;
> @@ -341,10 +365,13 @@ static void __init deserialize_bitmap(unsigned int order,
>  		phys_addr_t phys =
>  			elm->phys_start + (bit << (order + PAGE_SHIFT));
>  		struct page *page = phys_to_page(phys);
> +		union kho_page_info info;
>  
>  		memblock_reserve(phys, sz);
>  		memblock_reserved_mark_noinit(phys, sz);
> -		page->private = order;
> +		info.magic = KHO_PAGE_MAGIC;
> +		info.order = order;
> +		page->private = info.page_private;
>  	}
>  }
>  
> -- 
> 2.47.3
> 

-- 
Sincerely yours,
Mike.


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2025-09-17 14:38 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-09-17 12:56 [PATCH v2 1/2] kho: move sanity checks to kho_restore_page() Pratyush Yadav
2025-09-17 12:56 ` [PATCH v2 2/2] kho: make sure page being restored is actually from KHO Pratyush Yadav
2025-09-17 14:38   ` Mike Rapoport
2025-09-17 14:31 ` [PATCH v2 1/2] kho: move sanity checks to kho_restore_page() Mike Rapoport

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox