diff mbox series

[V2] mips: kernel: fix detect_memory_region() function

Message ID TYCP286MB089598ABD1E2F66003D71EB8BCD52@TYCP286MB0895.JPNP286.PROD.OUTLOOK.COM (mailing list archive)
State Superseded
Headers show
Series [V2] mips: kernel: fix detect_memory_region() function | expand

Commit Message

Shiji Yang June 25, 2024, 1:44 a.m. UTC
The detect_memory_region() has been broken on 6.6 kernel[1]. This
patch fixes it by:
1. Do not use memcmp() on unallocated memory, as the new introduced
   fortify dynamic object size check[2] will return unexpected result.
2. Use a fixed pattern instead of a random function pointer as the
   magic value.
3. Flip magic value and double check it.
4. Enable this feature only for 32-bit CPUs. Currently, only ath79 and
   ralink CPUs are using it. And 64-bit CPU doesn't have the KSEG1ADDR
   definition.

[1] https://github.com/openwrt/openwrt/pull/14774#issuecomment-1989356746
[2] commit 439a1bcac648 ("fortify: Use __builtin_dynamic_object_size() when available")
Signed-off-by: Shiji Yang <yangshiji66@outlook.com>
Tested-by: Mieczyslaw Nalewaj <namiltd@yahoo.com>
---
 arch/mips/include/asm/bootinfo.h |  2 ++
 arch/mips/kernel/setup.c         | 17 ++++++++++++-----
 2 files changed, 14 insertions(+), 5 deletions(-)

Comments

Jiaxun Yang June 25, 2024, 1:58 a.m. UTC | #1
在2024年6月25日六月 上午2:44,Shiji Yang写道:
> The detect_memory_region() has been broken on 6.6 kernel[1]. This
> patch fixes it by:
> 1. Do not use memcmp() on unallocated memory, as the new introduced
>    fortify dynamic object size check[2] will return unexpected result.
> 2. Use a fixed pattern instead of a random function pointer as the
>    magic value.
> 3. Flip magic value and double check it.
> 4. Enable this feature only for 32-bit CPUs. Currently, only ath79 and
>    ralink CPUs are using it. And 64-bit CPU doesn't have the KSEG1ADDR
>    definition.

Hi Shiji,

Thanks for your patch.

Please don't break 64bit system.
Use CKSEG1ADDR_OR_64BIT instead.

Thanks
- Jiaxun

>
> [1] 
> https://github.com/openwrt/openwrt/pull/14774#issuecomment-1989356746
> [2] commit 439a1bcac648 ("fortify: Use __builtin_dynamic_object_size() 
> when available")
> Signed-off-by: Shiji Yang <yangshiji66@outlook.com>
> Tested-by: Mieczyslaw Nalewaj <namiltd@yahoo.com>
> ---
>  arch/mips/include/asm/bootinfo.h |  2 ++
>  arch/mips/kernel/setup.c         | 17 ++++++++++++-----
>  2 files changed, 14 insertions(+), 5 deletions(-)
>
> diff --git a/arch/mips/include/asm/bootinfo.h b/arch/mips/include/asm/bootinfo.h
> index 2128ba903391..8516c11916a4 100644
> --- a/arch/mips/include/asm/bootinfo.h
> +++ b/arch/mips/include/asm/bootinfo.h
> @@ -93,7 +93,9 @@ const char *get_system_type(void);
> 
>  extern unsigned long mips_machtype;
> 
> +#ifndef CONFIG_64BIT
>  extern void detect_memory_region(phys_addr_t start, phys_addr_t 
> sz_min,  phys_addr_t sz_max);
> +#endif
> 
>  extern void prom_init(void);
>  extern void prom_free_prom_memory(void);
> diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
> index 12a1a4ffb602..3a3bc1b7e62e 100644
> --- a/arch/mips/kernel/setup.c
> +++ b/arch/mips/kernel/setup.c
> @@ -86,21 +86,27 @@ static struct resource bss_resource = { .name = 
> "Kernel bss", };
>  unsigned long __kaslr_offset __ro_after_init;
>  EXPORT_SYMBOL(__kaslr_offset);
> 
> -static void *detect_magic __initdata = detect_memory_region;
> -
>  #ifdef CONFIG_MIPS_AUTO_PFN_OFFSET
>  unsigned long ARCH_PFN_OFFSET;
>  EXPORT_SYMBOL(ARCH_PFN_OFFSET);
>  #endif
> 
> +#ifndef CONFIG_64BIT
> +static u32 detect_magic __initdata;
> +#define MIPS_MEM_TEST_PATTERN		0xaa5555aa
> +
>  void __init detect_memory_region(phys_addr_t start, phys_addr_t 
> sz_min, phys_addr_t sz_max)
>  {
> -	void *dm = &detect_magic;
> +	void *dm = (void *)KSEG1ADDR(&detect_magic);
>  	phys_addr_t size;
> 
>  	for (size = sz_min; size < sz_max; size <<= 1) {
> -		if (!memcmp(dm, dm + size, sizeof(detect_magic)))
> -			break;
> +		__raw_writel(MIPS_MEM_TEST_PATTERN, dm);
> +		if (__raw_readl(dm) == __raw_readl(dm + size)) {
> +			__raw_writel(~MIPS_MEM_TEST_PATTERN, dm);
> +			if (__raw_readl(dm) == __raw_readl(dm + size))
> +				break;
> +		}
>  	}
> 
>  	pr_debug("Memory: %lluMB of RAM detected at 0x%llx (min: %lluMB, max: 
> %lluMB)\n",
> @@ -111,6 +117,7 @@ void __init detect_memory_region(phys_addr_t start, 
> phys_addr_t sz_min, phys_add
> 
>  	memblock_add(start, size);
>  }
> +#endif /* CONFIG_64BIT */
> 
>  /*
>   * Manage initrd
> -- 
> 2.45.1
Thomas Bogendoerfer June 25, 2024, 7:46 a.m. UTC | #2
On Tue, Jun 25, 2024 at 09:44:44AM +0800, Shiji Yang wrote:
>  	for (size = sz_min; size < sz_max; size <<= 1) {
> -		if (!memcmp(dm, dm + size, sizeof(detect_magic)))
> -			break;
> +		__raw_writel(MIPS_MEM_TEST_PATTERN, dm);
> +		if (__raw_readl(dm) == __raw_readl(dm + size)) {
> +			__raw_writel(~MIPS_MEM_TEST_PATTERN, dm);
> +			if (__raw_readl(dm) == __raw_readl(dm + size))
> +				break;
> +		}

you are testing memory, so just use pointers. __raw_readl and __raw_writel
are for io regions (which should be ioremppaed before usage).

Thomas.
Thomas Bogendoerfer June 25, 2024, 7:52 a.m. UTC | #3
On Tue, Jun 25, 2024 at 09:44:44AM +0800, Shiji Yang wrote:
> The detect_memory_region() has been broken on 6.6 kernel[1]. This
> patch fixes it by:
> 1. Do not use memcmp() on unallocated memory, as the new introduced
>    fortify dynamic object size check[2] will return unexpected result.

hmm, so there should a new way for doing memory probing without
triggering this fortify check. How do other platforms deal with this ?

> 2. Use a fixed pattern instead of a random function pointer as the
>    magic value.

why is this better ?

Thomas.
kernel test robot June 28, 2024, 4:07 p.m. UTC | #4
Hi Shiji,

kernel test robot noticed the following build warnings:

[auto build test WARNING on soc/for-next]
[also build test WARNING on linus/master v6.10-rc5 next-20240627]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Shiji-Yang/mips-kernel-fix-detect_memory_region-function/20240626-070033
base:   https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git for-next
patch link:    https://lore.kernel.org/r/TYCP286MB089598ABD1E2F66003D71EB8BCD52%40TYCP286MB0895.JPNP286.PROD.OUTLOOK.COM
patch subject: [PATCH V2] mips: kernel: fix detect_memory_region() function
config: mips-randconfig-r113-20240628 (https://download.01.org/0day-ci/archive/20240628/202406282354.0hGuOKo0-lkp@intel.com/config)
compiler: clang version 15.0.7 (https://github.com/llvm/llvm-project 8dfdcc7b7bf66834a761bd8de445840ef68e4d1a)
reproduce: (https://download.01.org/0day-ci/archive/20240628/202406282354.0hGuOKo0-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202406282354.0hGuOKo0-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> arch/mips/kernel/setup.c:104:53: sparse: sparse: incorrect type in argument 2 (different address spaces) @@     expected void volatile [noderef] __iomem *mem @@     got void *dm @@
   arch/mips/kernel/setup.c:104:53: sparse:     expected void volatile [noderef] __iomem *mem
   arch/mips/kernel/setup.c:104:53: sparse:     got void *dm
>> arch/mips/kernel/setup.c:105:33: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected void const volatile [noderef] __iomem *mem @@     got void *dm @@
   arch/mips/kernel/setup.c:105:33: sparse:     expected void const volatile [noderef] __iomem *mem
   arch/mips/kernel/setup.c:105:33: sparse:     got void *dm
>> arch/mips/kernel/setup.c:105:55: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected void const volatile [noderef] __iomem *mem @@     got void * @@
   arch/mips/kernel/setup.c:105:55: sparse:     expected void const volatile [noderef] __iomem *mem
   arch/mips/kernel/setup.c:105:55: sparse:     got void *
   arch/mips/kernel/setup.c:106:62: sparse: sparse: incorrect type in argument 2 (different address spaces) @@     expected void volatile [noderef] __iomem *mem @@     got void *dm @@
   arch/mips/kernel/setup.c:106:62: sparse:     expected void volatile [noderef] __iomem *mem
   arch/mips/kernel/setup.c:106:62: sparse:     got void *dm
   arch/mips/kernel/setup.c:107:41: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected void const volatile [noderef] __iomem *mem @@     got void *dm @@
   arch/mips/kernel/setup.c:107:41: sparse:     expected void const volatile [noderef] __iomem *mem
   arch/mips/kernel/setup.c:107:41: sparse:     got void *dm
   arch/mips/kernel/setup.c:107:63: sparse: sparse: incorrect type in argument 1 (different address spaces) @@     expected void const volatile [noderef] __iomem *mem @@     got void * @@
   arch/mips/kernel/setup.c:107:63: sparse:     expected void const volatile [noderef] __iomem *mem
   arch/mips/kernel/setup.c:107:63: sparse:     got void *
   arch/mips/kernel/setup.c: note: in included file (through include/linux/mmzone.h, include/linux/gfp.h, include/linux/xarray.h, ...):
   include/linux/page-flags.h:240:46: sparse: sparse: self-comparison always evaluates to false
   include/linux/page-flags.h:240:46: sparse: sparse: self-comparison always evaluates to false

vim +104 arch/mips/kernel/setup.c

    97	
    98	void __init detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max)
    99	{
   100		void *dm = (void *)KSEG1ADDR(&detect_magic);
   101		phys_addr_t size;
   102	
   103		for (size = sz_min; size < sz_max; size <<= 1) {
 > 104			__raw_writel(MIPS_MEM_TEST_PATTERN, dm);
 > 105			if (__raw_readl(dm) == __raw_readl(dm + size)) {
   106				__raw_writel(~MIPS_MEM_TEST_PATTERN, dm);
   107				if (__raw_readl(dm) == __raw_readl(dm + size))
   108					break;
   109			}
   110		}
   111	
   112		pr_debug("Memory: %lluMB of RAM detected at 0x%llx (min: %lluMB, max: %lluMB)\n",
   113			((unsigned long long) size) / SZ_1M,
   114			(unsigned long long) start,
   115			((unsigned long long) sz_min) / SZ_1M,
   116			((unsigned long long) sz_max) / SZ_1M);
   117	
   118		memblock_add(start, size);
   119	}
   120	#endif /* CONFIG_64BIT */
   121
Shiji Yang June 29, 2024, 4:56 a.m. UTC | #5
On Tue, 25 Jun 2024 02:58:54 +0100, Jiaxun Yang wrote:
>> The detect_memory_region() has been broken on 6.6 kernel[1]. This
>> patch fixes it by:
>> 1. Do not use memcmp() on unallocated memory, as the new introduced
>>    fortify dynamic object size check[2] will return unexpected result.
>> 2. Use a fixed pattern instead of a random function pointer as the
>>    magic value.
>> 3. Flip magic value and double check it.
>> 4. Enable this feature only for 32-bit CPUs. Currently, only ath79 and
>>    ralink CPUs are using it. And 64-bit CPU doesn't have the KSEG1ADDR
>>    definition.
>
>Hi Shiji,
>
>Thanks for your patch.
>
>Please don't break 64bit system.
>Use CKSEG1ADDR_OR_64BIT instead.
>
>Thanks
>- Jiaxun

Thanks. I've updated and tested it in v2 patch.
https://lore.kernel.org/linux-mips/TYCP286MB0895F65439037ED134FEA7DDBCD12@TYCP286MB0895.JPNP286.PROD.OUTLOOK.COM/

Regards,
Shiji Yang
Shiji Yang June 29, 2024, 5:19 a.m. UTC | #6
On Tue, 25 Jun 2024 09:46:34 +0200, Thomas Bogendoerfer wrote:
>>  	for (size = sz_min; size < sz_max; size <<= 1) {
>> -		if (!memcmp(dm, dm + size, sizeof(detect_magic)))
>> -			break;
>> +		__raw_writel(MIPS_MEM_TEST_PATTERN, dm);
>> +		if (__raw_readl(dm) == __raw_readl(dm + size)) {
>> +			__raw_writel(~MIPS_MEM_TEST_PATTERN, dm);
>> +			if (__raw_readl(dm) == __raw_readl(dm + size))
>> +				break;
>> +		}
>
>you are testing memory, so just use pointers. __raw_readl and __raw_writel
>are for io regions (which should be ioremppaed before usage).

Fixed in v3:
https://lore.kernel.org/linux-mips/TYCP286MB0895F65439037ED134FEA7DDBCD12@TYCP286MB0895.JPNP286.PROD.OUTLOOK.COM/


>
>> The detect_memory_region() has been broken on 6.6 kernel[1]. This
>> patch fixes it by:
>> 1. Do not use memcmp() on unallocated memory, as the new introduced
>>    fortify dynamic object size check[2] will return unexpected result.
>
>hmm, so there should a new way for doing memory probing without
>triggering this fortify check. How do other platforms deal with this ?

I guess __builtin_memcmp() should work. But this patch also fixes a
potential wrong memory size issue[1] by fliping magic number and
double checking it. And there is no need to ues memcmp() to check
an u32 variable.

It seems that other ARCHs directly read some registers to judge
the memory size. However, the theory in mips ARCH is checking the
overlapping memory address.

>
>> 2. Use a fixed pattern instead of a random function pointer as the
>>    magic value.
>
>why is this better ?

Just engineering experience. Byte 0xaa/0x55 has the largest information
entropy and is widely used in memory testing. Most codes in v2 patch
are copied from 'arch/mips/ralink/mt7621.c'.

>
>Thomas.
>

[1] https://github.com/openwrt/openwrt/pull/14282


Regards,
Shiji Yang
diff mbox series

Patch

diff --git a/arch/mips/include/asm/bootinfo.h b/arch/mips/include/asm/bootinfo.h
index 2128ba903391..8516c11916a4 100644
--- a/arch/mips/include/asm/bootinfo.h
+++ b/arch/mips/include/asm/bootinfo.h
@@ -93,7 +93,9 @@  const char *get_system_type(void);
 
 extern unsigned long mips_machtype;
 
+#ifndef CONFIG_64BIT
 extern void detect_memory_region(phys_addr_t start, phys_addr_t sz_min,  phys_addr_t sz_max);
+#endif
 
 extern void prom_init(void);
 extern void prom_free_prom_memory(void);
diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
index 12a1a4ffb602..3a3bc1b7e62e 100644
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -86,21 +86,27 @@  static struct resource bss_resource = { .name = "Kernel bss", };
 unsigned long __kaslr_offset __ro_after_init;
 EXPORT_SYMBOL(__kaslr_offset);
 
-static void *detect_magic __initdata = detect_memory_region;
-
 #ifdef CONFIG_MIPS_AUTO_PFN_OFFSET
 unsigned long ARCH_PFN_OFFSET;
 EXPORT_SYMBOL(ARCH_PFN_OFFSET);
 #endif
 
+#ifndef CONFIG_64BIT
+static u32 detect_magic __initdata;
+#define MIPS_MEM_TEST_PATTERN		0xaa5555aa
+
 void __init detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max)
 {
-	void *dm = &detect_magic;
+	void *dm = (void *)KSEG1ADDR(&detect_magic);
 	phys_addr_t size;
 
 	for (size = sz_min; size < sz_max; size <<= 1) {
-		if (!memcmp(dm, dm + size, sizeof(detect_magic)))
-			break;
+		__raw_writel(MIPS_MEM_TEST_PATTERN, dm);
+		if (__raw_readl(dm) == __raw_readl(dm + size)) {
+			__raw_writel(~MIPS_MEM_TEST_PATTERN, dm);
+			if (__raw_readl(dm) == __raw_readl(dm + size))
+				break;
+		}
 	}
 
 	pr_debug("Memory: %lluMB of RAM detected at 0x%llx (min: %lluMB, max: %lluMB)\n",
@@ -111,6 +117,7 @@  void __init detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_add
 
 	memblock_add(start, size);
 }
+#endif /* CONFIG_64BIT */
 
 /*
  * Manage initrd