2022-08-26 22:08:13

by Marek Bykowski

[permalink] [raw]
Subject: [PATCH] of/fdt: Don't calculate initrd_start from the DT if 'linux,initrd-end' is 0

If the 'linux,initrd-end' property is 0 and 'linux,initrd-start' property
is other than 0, then phys_initrd_size calculated from 'linux,initrd-end'
- 'linux,initrd-start' is negative, that subsequently gets converted to
a high positive value as being u64.

For example if 'linux,initrd-start' is 8800_0000, 'linux,initrd-end' is 0,
then the phys_initrd_size calculated is ffff_ffff_7800_0000 (= 0 -
8800_0000 = -8800_0000 + ULLONG_MAX + 1). On my system, FVP ARM64,
the intird memory region with the (wrong) size is added to the bootmem and
then attempted to being paged in paging_init() that results in the kernel
oops as shown below.

Generally, it is a fault of the bootloader as the kernel is relying on it
but we should not allow the bootloader's misconfiguration to lead to
the kernel oops. Not only the kernel should get bullet proof against it
but also finding the root cause may get lengthy on occasion as involves
checking the bootloader, the chosen node it makes the changes in and
the kernel.

Unable to handle kernel paging request at virtual address fffffffefe43c000
Mem abort info:                                                    
  ESR = 0x96000007                                                                                  
  EC = 0x25: DABT (current EL), IL = 32 bits
  SET = 0, FnV = 0                                                
  EA = 0, S1PTW = 0                                                                                
Data abort info:                                                                                    
  ISV = 0, ISS = 0x00000007
  CM = 0, WnR = 0
swapper pgtable: 4k pages, 39-bit VAs, pgdp=0000000080e3d000
[fffffffefe43c000] pgd=0000000080de9003, pud=0000000080de9003
Unable to handle kernel paging request at virtual address ffffff8000de9f90
Mem abort info:
  ESR = 0x96000005
  EC = 0x25: DABT (current EL), IL = 32 bits
  SET = 0, FnV = 0
  EA = 0, S1PTW = 0
Data abort info:
  ISV = 0, ISS = 0x00000005
  CM = 0, WnR = 0
swapper pgtable: 4k pages, 39-bit VAs, pgdp=0000000080e3d000
[ffffff8000de9f90] pgd=0000000000000000, pud=0000000000000000
Internal error: Oops: 96000005 [#1] PREEMPT SMP
Modules linked in:
CPU: 0 PID: 0 Comm: swapper Not tainted 5.4.51-yocto-standard #1
Hardware name: FVP Base (DT)
pstate: 60000085 (nZCv daIf -PAN -UAO)
pc : show_pte+0x12c/0x1b4
lr : show_pte+0x100/0x1b4
sp : ffffffc010ce3b30
x29: ffffffc010ce3b30 x28: ffffffc010ceed80
x27: fffffffefe43c000 x26: fffffffefe43a028
x25: 0000000080bf0000 x24: 0000000000000025
x23: ffffffc010b8d000 x22: ffffffc010e3d000
x21: 0000000080de9000 x20: ffffff7f80000f90
x19: fffffffefe43c000 x18: 0000000000000030
x17: 0000000000001400 x16: 0000000000001c00
x15: ffffffc010cef1b8 x14: ffffffffffffffff
x13: ffffffc010df1f40 x12: ffffffc010df1b70
x11: ffffffc010ce3b30 x10: ffffffc010ce3b30
x9 : 00000000ffffffc8 x8 : 0000000000000000
x7 : 000000000000000f x6 : ffffffc010df16e8
x5 : 0000000000000000 x4 : 0000000000000000
x3 : 00000000ffffffff x2 : 0000000000000000
x1 : 0000008080000000 x0 : ffffffc010af1d68
Call trace:
 show_pte+0x12c/0x1b4
 die_kernel_fault+0x54/0x78
 __do_kernel_fault+0x11c/0x128
 do_translation_fault+0x58/0xac
 do_mem_abort+0x50/0xb0
 el1_da+0x1c/0x90
 __create_pgd_mapping+0x348/0x598
 paging_init+0x3f0/0x70d0
 setup_arch+0x2c0/0x5d4
 start_kernel+0x94/0x49c
Code: 92748eb5 900052a0 9135a000 cb010294 (f8756a96) 

Signed-off-by: Marek Bykowski <[email protected]>
---
drivers/of/fdt.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 7bc92923104c..5f18c175dd78 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -936,6 +936,8 @@ static void __init early_init_dt_check_for_initrd(unsigned long node)
if (!prop)
return;
end = of_read_number(prop, len/4);
+ if (!end)
+ return;

__early_init_dt_declare_initrd(start, end);
phys_initrd_start = start;
--
2.25.1


2022-08-29 01:58:36

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH] of/fdt: Don't calculate initrd_start from the DT if 'linux,initrd-end' is 0

On Fri, Aug 26, 2022 at 5:00 PM Marek Bykowski <[email protected]> wrote:
>
> If the 'linux,initrd-end' property is 0 and 'linux,initrd-start' property
> is other than 0, then phys_initrd_size calculated from 'linux,initrd-end'
> - 'linux,initrd-start' is negative, that subsequently gets converted to
> a high positive value as being u64.
>
> For example if 'linux,initrd-start' is 8800_0000, 'linux,initrd-end' is 0,
> then the phys_initrd_size calculated is ffff_ffff_7800_0000 (= 0 -
> 8800_0000 = -8800_0000 + ULLONG_MAX + 1). On my system, FVP ARM64,
> the intird memory region with the (wrong) size is added to the bootmem and
> then attempted to being paged in paging_init() that results in the kernel
> oops as shown below.

Shouldn't we just check that start < end?

Can we check this somewhere not DT specific (and also not arch
specific)? Then we don't have to worry if any other method of setting
initrd could have the same error.

Rob

2022-08-30 16:37:07

by Marek Bykowski

[permalink] [raw]
Subject: Re: [PATCH] of/fdt: Don't calculate initrd_start from the DT if 'linux,initrd-end' is 0

On Sun, 28 Aug 2022 20:12:41 -0500
Rob Herring <[email protected]> wrote:

>
> Shouldn't we just check that start < end?
>
> Can we check this somewhere not DT specific (and also not arch
> specific)? Then we don't have to worry if any other method of setting
> initrd could have the same error.

Yes, we can switch from checking on the end being 0 to that proposed:
- if (!end)
- return;
+ if (start >= end)
+ return;

Then the check would even go further as would also catch cases where
end < start.

My taking is early_init_dt_scan_chosen() that sets initrd size
incorrectly is DT specific but generic/arch agnostic. So that if
the error got introduced by a bootloader/U-Boot through the DT
chosen node, we should catch it in DT and react.

ARM64, for example, before going down for mapping for the incorrect
address (some extra large address resulting from the negative to
positive value conversion), has a check after DT parsing if
phys_initrd_size is other than 0 to proceed, and it is so that it
passes or in other words it doesn't catch the error.

Marek

2022-09-06 10:12:44

by Marek Bykowski

[permalink] [raw]
Subject: Re: [PATCH] of/fdt: Don't calculate initrd_start from the DT if 'linux,initrd-end' is 0

On Tue, 30 Aug 2022 15:35:00 +0000
Marek Bykowski <[email protected]> wrote:

> On Sun, 28 Aug 2022 20:12:41 -0500
> Rob Herring <[email protected]> wrote:
>
> >
> > Shouldn't we just check that start < end?
> >
> > Can we check this somewhere not DT specific (and also not arch
> > specific)? Then we don't have to worry if any other method of
> > setting initrd could have the same error.
>
> Yes, we can switch from checking on the end being 0 to that proposed:
> - if (!end)
> - return;
> + if (start >= end)
> + return;
>
> Then the check would even go further as would also catch cases where
> end < start.
>
> My taking is early_init_dt_scan_chosen() that sets initrd size
> incorrectly is DT specific but generic/arch agnostic. So that if
> the error got introduced by a bootloader/U-Boot through the DT
> chosen node, we should catch it in DT and react.
>
> ARM64, for example, before going down for mapping for the incorrect
> address (some extra large address resulting from the negative to
> positive value conversion), has a check after DT parsing if
> phys_initrd_size is other than 0 to proceed, and it is so that it
> passes or in other words it doesn't catch the error.
>
> Marek

Hello Rob and others,

Any updates on it?

Marek

2022-09-06 15:45:29

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH] of/fdt: Don't calculate initrd_start from the DT if 'linux,initrd-end' is 0

On Tue, Aug 30, 2022 at 10:35 AM Marek Bykowski
<[email protected]> wrote:
>
> On Sun, 28 Aug 2022 20:12:41 -0500
> Rob Herring <[email protected]> wrote:
>
> >
> > Shouldn't we just check that start < end?
> >
> > Can we check this somewhere not DT specific (and also not arch
> > specific)? Then we don't have to worry if any other method of setting
> > initrd could have the same error.
>
> Yes, we can switch from checking on the end being 0 to that proposed:
> - if (!end)
> - return;
> + if (start >= end)
> + return;
>
> Then the check would even go further as would also catch cases where
> end < start.
>
> My taking is early_init_dt_scan_chosen() that sets initrd size
> incorrectly is DT specific but generic/arch agnostic. So that if
> the error got introduced by a bootloader/U-Boot through the DT
> chosen node, we should catch it in DT and react.
>
> ARM64, for example, before going down for mapping for the incorrect
> address (some extra large address resulting from the negative to
> positive value conversion), has a check after DT parsing if
> phys_initrd_size is other than 0 to proceed, and it is so that it
> passes or in other words it doesn't catch the error.

Okay.

Please send an updated patch.

Rob