[PATCH 2/2] efi: Align unaccepted memory range to page boundary

Kiryl Shutsemau (Meta) posted 2 patches 1 month, 2 weeks ago
There is a newer version of this series
[PATCH 2/2] efi: Align unaccepted memory range to page boundary
Posted by Kiryl Shutsemau (Meta) 1 month, 2 weeks ago
The accept_memory() and range_contains_unaccepted_memory() functions
employ a "guard page" logic to prevent crashes with load_unaligned_zeropad().
This logic extends the range to be accepted (or checked) by one unit_size
if the end of the range is aligned to a unit_size boundary.

However, if the caller passes a range that is not page-aligned, the
'end' of the range might not be numerically aligned to unit_size, even
if it covers the last page of a unit. This causes the "if (!(end % unit_size))"
check to fail, skipping the necessary extension and leaving the next
unit unaccepted, which can lead to a kernel panic when accessed by
load_unaligned_zeropad().

Align the start address down and the size up to the nearest page
boundary before performing the unit_size alignment check. This ensures
that the guard unit is correctly added when the range effectively ends
on a unit boundary.

Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
 drivers/firmware/efi/unaccepted_memory.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/efi/unaccepted_memory.c b/drivers/firmware/efi/unaccepted_memory.c
index c2c067eff634..9ddf3dedd514 100644
--- a/drivers/firmware/efi/unaccepted_memory.c
+++ b/drivers/firmware/efi/unaccepted_memory.c
@@ -35,14 +35,18 @@ void accept_memory(phys_addr_t start, unsigned long size)
 	struct efi_unaccepted_memory *unaccepted;
 	unsigned long range_start, range_end;
 	struct accept_range range, *entry;
-	phys_addr_t end = start + size;
 	unsigned long flags;
+	phys_addr_t end;
 	u64 unit_size;
 
 	unaccepted = efi_get_unaccepted_table();
 	if (!unaccepted)
 		return;
 
+	start = PAGE_ALIGN_DOWN(start);
+	size = PAGE_ALIGN(size);
+	end = start + size;
+
 	unit_size = unaccepted->unit_size;
 
 	/*
@@ -160,15 +164,19 @@ void accept_memory(phys_addr_t start, unsigned long size)
 bool range_contains_unaccepted_memory(phys_addr_t start, unsigned long size)
 {
 	struct efi_unaccepted_memory *unaccepted;
-	phys_addr_t end = start + size;
 	unsigned long flags;
 	bool ret = false;
+	phys_addr_t end;
 	u64 unit_size;
 
 	unaccepted = efi_get_unaccepted_table();
 	if (!unaccepted)
 		return false;
 
+	start = PAGE_ALIGN_DOWN(start);
+	size = PAGE_ALIGN(size);
+	end = start + size;
+
 	unit_size = unaccepted->unit_size;
 
 	/*
-- 
2.51.2
Re: [PATCH 2/2] efi: Align unaccepted memory range to page boundary
Posted by Tom Lendacky 1 month, 2 weeks ago
On 2/13/26 09:48, Kiryl Shutsemau (Meta) wrote:
> The accept_memory() and range_contains_unaccepted_memory() functions
> employ a "guard page" logic to prevent crashes with load_unaligned_zeropad().
> This logic extends the range to be accepted (or checked) by one unit_size
> if the end of the range is aligned to a unit_size boundary.
> 
> However, if the caller passes a range that is not page-aligned, the
> 'end' of the range might not be numerically aligned to unit_size, even
> if it covers the last page of a unit. This causes the "if (!(end % unit_size))"
> check to fail, skipping the necessary extension and leaving the next
> unit unaccepted, which can lead to a kernel panic when accessed by
> load_unaligned_zeropad().
> 
> Align the start address down and the size up to the nearest page
> boundary before performing the unit_size alignment check. This ensures
> that the guard unit is correctly added when the range effectively ends
> on a unit boundary.
> 
> Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
> ---
>  drivers/firmware/efi/unaccepted_memory.c | 12 ++++++++++--
>  1 file changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/firmware/efi/unaccepted_memory.c b/drivers/firmware/efi/unaccepted_memory.c
> index c2c067eff634..9ddf3dedd514 100644
> --- a/drivers/firmware/efi/unaccepted_memory.c
> +++ b/drivers/firmware/efi/unaccepted_memory.c
> @@ -35,14 +35,18 @@ void accept_memory(phys_addr_t start, unsigned long size)
>  	struct efi_unaccepted_memory *unaccepted;
>  	unsigned long range_start, range_end;
>  	struct accept_range range, *entry;
> -	phys_addr_t end = start + size;
>  	unsigned long flags;
> +	phys_addr_t end;
>  	u64 unit_size;
>  
>  	unaccepted = efi_get_unaccepted_table();
>  	if (!unaccepted)
>  		return;
>  
> +	start = PAGE_ALIGN_DOWN(start);
> +	size = PAGE_ALIGN(size);
> +	end = start + size;

Should this really be:

	end = PAGE_ALIGN(start + size);
	start = PAGE_ALIGN_DOWN(start);

?

Thanks,
Tom

> +
>  	unit_size = unaccepted->unit_size;
>  
>  	/*
> @@ -160,15 +164,19 @@ void accept_memory(phys_addr_t start, unsigned long size)
>  bool range_contains_unaccepted_memory(phys_addr_t start, unsigned long size)
>  {
>  	struct efi_unaccepted_memory *unaccepted;
> -	phys_addr_t end = start + size;
>  	unsigned long flags;
>  	bool ret = false;
> +	phys_addr_t end;
>  	u64 unit_size;
>  
>  	unaccepted = efi_get_unaccepted_table();
>  	if (!unaccepted)
>  		return false;
>  
> +	start = PAGE_ALIGN_DOWN(start);
> +	size = PAGE_ALIGN(size);
> +	end = start + size;
> +
>  	unit_size = unaccepted->unit_size;
>  
>  	/*
Re: [PATCH 2/2] efi: Align unaccepted memory range to page boundary
Posted by Kiryl Shutsemau 1 month, 2 weeks ago
On Mon, Feb 16, 2026 at 08:51:17AM -0600, Tom Lendacky wrote:
> On 2/13/26 09:48, Kiryl Shutsemau (Meta) wrote:
> > The accept_memory() and range_contains_unaccepted_memory() functions
> > employ a "guard page" logic to prevent crashes with load_unaligned_zeropad().
> > This logic extends the range to be accepted (or checked) by one unit_size
> > if the end of the range is aligned to a unit_size boundary.
> > 
> > However, if the caller passes a range that is not page-aligned, the
> > 'end' of the range might not be numerically aligned to unit_size, even
> > if it covers the last page of a unit. This causes the "if (!(end % unit_size))"
> > check to fail, skipping the necessary extension and leaving the next
> > unit unaccepted, which can lead to a kernel panic when accessed by
> > load_unaligned_zeropad().
> > 
> > Align the start address down and the size up to the nearest page
> > boundary before performing the unit_size alignment check. This ensures
> > that the guard unit is correctly added when the range effectively ends
> > on a unit boundary.
> > 
> > Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
> > ---
> >  drivers/firmware/efi/unaccepted_memory.c | 12 ++++++++++--
> >  1 file changed, 10 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/firmware/efi/unaccepted_memory.c b/drivers/firmware/efi/unaccepted_memory.c
> > index c2c067eff634..9ddf3dedd514 100644
> > --- a/drivers/firmware/efi/unaccepted_memory.c
> > +++ b/drivers/firmware/efi/unaccepted_memory.c
> > @@ -35,14 +35,18 @@ void accept_memory(phys_addr_t start, unsigned long size)
> >  	struct efi_unaccepted_memory *unaccepted;
> >  	unsigned long range_start, range_end;
> >  	struct accept_range range, *entry;
> > -	phys_addr_t end = start + size;
> >  	unsigned long flags;
> > +	phys_addr_t end;
> >  	u64 unit_size;
> >  
> >  	unaccepted = efi_get_unaccepted_table();
> >  	if (!unaccepted)
> >  		return;
> >  
> > +	start = PAGE_ALIGN_DOWN(start);
> > +	size = PAGE_ALIGN(size);
> > +	end = start + size;
> 
> Should this really be:
> 
> 	end = PAGE_ALIGN(start + size);
> 	start = PAGE_ALIGN_DOWN(start);
> 
> ?

Doh! Yes, you are right.

-- 
  Kiryl Shutsemau / Kirill A. Shutemov