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
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;
>
> /*
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
© 2016 - 2026 Red Hat, Inc.