mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: haoran.jiang@linux.dev
To: chenhuacai@kernel.org
Cc: loongarch@lists.linux.dev, kasan-dev@googlegroups.com,
	linux-kernel@vger.kernel.org, kernel@xen0n.name,
	"Haoran Jiang" <jianghaoran@kylinos.cn>
Subject: Re: [PATCH v2] LoongArch: add KASAN_VMALLOC support
Date: Mon, 09 Nov 2026 06:27:05 +0000	[thread overview]
Message-ID: <6c46aae6dac0d2fbcf27cad76a8cc34256998a26@linux.dev> (raw)
In-Reply-To: <20261027055545.381445-1-haoran.jiang@linux.dev>

Gentle ping :)

Any review or feedback would be appreciated, thanks.




2026年10月27日 13:55, haoran.jiang@linux.dev mailto:haoran.jiang@linux.dev  写到:


> 
> From: Haoran Jiang <jianghaoran@kylinos.cn>
> 
> For the page table of the VMALLOC and MODULES
> shadow regions, only partial page table needs
> to be established, allowing the complete page
> table entries to be populated on demand during use.
> 
> ---
> 1,Add shallow parameter to kasan_pgd/p4d/pud/pmd/pte_populate,
> page tables are established for the modules and vmalloc regions,
> but the page tables are not fully populated; the complete page
> tables are established automatically when vmalloc is used.
> If the address is pgd-aligned, the page table is established
> down to the pgd level; if it is pud-aligned, it is established
> down to the pud level, and so on. While establishing the
> /pgd/pud/pmd/pte levels, the page table entries must be initialized
> to none. For example, a pmd entry of none is an invalid_pte_table.
> Only in this way can the correct page tables be established at
> use time. At the same time, boundary issues must be considered.
> For example, mem_to_shadow(MALLOC_END) and mem_to_shadow(vmemmap)
> are in the same pte, and the region [mem_to_shadow(vmemmap), mem_to_shadow(VMEMMAP_END)]
> needs to be mapped to page 0. To avoid affecting the mem_to_shadow(vmemmap) region,
> only the pte entries for [mem_to_shadow(MALLOC_END) & PMD_MASK, mem_to_shadow(MALLOC_END)]
> need to be initialized to none. The remaining pte entries are left as they are.
> 
> 2,The removal of kasan_populate_early_shadow(mem_to_shadow((void *)VMALLOC_START),
> mem_to_shadow((void *)KFENCE_AREA_END)) achieves the same effect as
> kasan_pgd_populate(KASAN_SHADOW_START, KASAN_SHADOW_END, NUMA_NO_NODE, true);
>  both map this region to a shared zero page. (KASAN_SHADOW_START, KASAN_SHADOW_END)
> already covers (mem_to_shadow((void *)VMALLOC_START), mem_to_shadow((void *)KFENCE_AREA_END)).
> Other architectures such as arm64/riscv need kasan_populate_early_shadow
> because clear_shadow(KASAN_SHADOW_START, KASAN_SHADOW_END) is performed before
>  kasan_populate_early_shadow, so the reserved region must be mapped to the zero
> page again. On LoongArch, however, the entire shadow space is first mapped to
> page 0 via kasan_pgd_populate, and then page tables are established for the
> required regions, which is simpler and clearer.
> 
> 3,pgd_none/p4d_none/pmd_none/pte_none were added to the definitions of
> __pgd_none/__p4d_none/__pud_none/__pmd_none/__pte_none respectively
> because kasan_populate_early_vm_area_shadow requires them.
> kasan_populate_early_vm_area_shadow establishes complete KASAN shadow
> page table mappings for the early vmalloc regions before vmalloc_init.
> At this point, complete page tables need to be established, but
> kasan_populate_early_vm_area_shadow is called after kasan_shallow_populate,
> at which time only incomplete page tables have been established for the
> vmalloc region.The condition for a page table entry to be none requires the
> pgd_none/p4d_none/pmd_none/pte_none checks. Only by correctly determining
> whether an entry is none can the complete page tables be established properly.
> 
> Test:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson.git
> 
> CONFIG_KUNIT=y
> 
> CONFIG_KASAN_KUNIT_TEST=m
> 
> Add `kunit.enable=1 kasan_multi_shot` to the cmdline.
> 
> Test with 3-level 16K pages and 4-level 4K pages respectively.
> 
> before this patch:
> 
> ...
> 
> [ 502.760009] ok 68 vmalloc_oob # SKIP Test requires CONFIG_KASAN_VMALLOC=y
> 
> ...
> 
> [ 504.016000] # kasan: pass:49 fail:0 skip:26 total:75
> [ 504.016092] # Totals: pass:49 fail:0 skip:26 total:75
> [ 504.016171] ok 1 kasan
> 
> after this patch:
> 
> ...
> 
> [ 531.630760] ok 68 vmalloc_oob
> 
> ...
> 
> [ 532.891057] # kasan: pass:50 fail:0 skip:25 total:75
> [ 532.891142] # Totals: pass:50 fail:0 skip:25 total:75
> [ 532.891219] ok 1 kasan
> 
> V2:
> 
> 1, Add a shallow parameter to kasan_pgd/p4d/pud/pmd/pte_populate to create incomplete page tables.
> 
> 2,select HAVE_ARCH_KASAN_VMALLOC -> select HAVE_ARCH_KASAN_VMALLOC if 64BIT.
> 
> Signed-off-by: Haoran Jiang <jianghaoran@kylinos.cn>
> 
> ---
>  arch/loongarch/Kconfig | 2 +
>  arch/loongarch/mm/kasan_init.c | 132 ++++++++++++++++++++++++---------
>  2 files changed, 101 insertions(+), 33 deletions(-)
> 
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 2067d1f2ad7a..9cdb80ee58a1 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -119,6 +119,7 @@ config LOONGARCH
>  select HAVE_ARCH_JUMP_LABEL
>  select HAVE_ARCH_JUMP_LABEL_RELATIVE
>  select HAVE_ARCH_KASAN if 64BIT
> + select HAVE_ARCH_KASAN_VMALLOC if 64BIT
>  select HAVE_ARCH_KFENCE if 64BIT
>  select HAVE_ARCH_KGDB if PERF_EVENTS
>  select HAVE_ARCH_KSTACK_ERASE
> @@ -187,6 +188,7 @@ config LOONGARCH
>  select HOTPLUG_SMT if HOTPLUG_CPU
>  select IRQ_FORCED_THREADING
>  select IRQ_LOONGARCH_CPU
> + select KASAN_VMALLOC if KASAN
>  select LOCK_MM_AND_FIND_VMA
>  select MMU_GATHER_MERGE_VMAS if MMU
>  select MMU_GATHER_RCU_TABLE_FREE
> diff --git a/arch/loongarch/mm/kasan_init.c b/arch/loongarch/mm/kasan_init.c
> index 92ca0ba86406..08fbdc9aafb4 100644
> --- a/arch/loongarch/mm/kasan_init.c
> +++ b/arch/loongarch/mm/kasan_init.c
> @@ -17,28 +17,28 @@ static pgd_t kasan_pg_dir[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE);
>  #define __pgd_none(early, pgd) (0)
>  #else
>  #define __pgd_none(early, pgd) (early ? (pgd_val(pgd) == 0) : \
> -(__pa(pgd_val(pgd)) == (unsigned long)__pa(kasan_early_shadow_p4d)))
> +((__pa(pgd_val(pgd)) == (unsigned long)__pa(kasan_early_shadow_p4d)) || pgd_none(pgd)))
>  #endif
>  
>  #ifdef __PAGETABLE_PUD_FOLDED
>  #define __p4d_none(early, p4d) (0)
>  #else
>  #define __p4d_none(early, p4d) (early ? (p4d_val(p4d) == 0) : \
> -(__pa(p4d_val(p4d)) == (unsigned long)__pa(kasan_early_shadow_pud)))
> +((__pa(p4d_val(p4d)) == (unsigned long)__pa(kasan_early_shadow_pud)) || p4d_none(p4d)))
>  #endif
>  
>  #ifdef __PAGETABLE_PMD_FOLDED
>  #define __pud_none(early, pud) (0)
>  #else
>  #define __pud_none(early, pud) (early ? (pud_val(pud) == 0) : \
> -(__pa(pud_val(pud)) == (unsigned long)__pa(kasan_early_shadow_pmd)))
> +((__pa(pud_val(pud)) == (unsigned long)__pa(kasan_early_shadow_pmd)) || pud_none(pud)))
>  #endif
>  
>  #define __pmd_none(early, pmd) (early ? (pmd_val(pmd) == 0) : \
> -(__pa(pmd_val(pmd)) == (unsigned long)__pa(kasan_early_shadow_pte)))
> +((__pa(pmd_val(pmd)) == (unsigned long)__pa(kasan_early_shadow_pte)) || pmd_none(pmd)))
>  
>  #define __pte_none(early, pte) (early ? pte_none(pte) : \
> -((pte_val(pte) & _PFN_MASK) == (unsigned long)__pa(kasan_early_shadow_page)))
> +(((pte_val(pte) & _PFN_MASK) == (unsigned long)__pa(kasan_early_shadow_page)) || pte_none(pte)))
>  
>  static void *mem_to_shadow(const void *addr)
>  {
> @@ -117,52 +117,78 @@ static phys_addr_t __init kasan_alloc_zeroed_page(int node)
>  return __pa(p);
>  }
>  
> -static pte_t *__init kasan_pte_offset(pmd_t *pmdp, unsigned long addr, int node, bool early)
> +static pte_t *__init kasan_pte_offset(pmd_t *pmdp, unsigned long addr,
> + unsigned long end, int node, bool early, bool shallow)
>  {
>  if (__pmd_none(early, pmdp_get(pmdp))) {
>  phys_addr_t pte_phys = early ?
>  __pa_symbol(kasan_early_shadow_pte) : kasan_alloc_zeroed_page(node);
>  if (!early)
>  memcpy(__va(pte_phys), kasan_early_shadow_pte, sizeof(kasan_early_shadow_pte));
> +
> + if (IS_ALIGNED(addr, PMD_SIZE) && end - addr >= PMD_SIZE && shallow)
> + kernel_pte_init(__va(pte_phys));
> +
>  pmd_populate_kernel(NULL, pmdp, (pte_t *)__va(pte_phys));
>  }
>  
>  return pte_offset_kernel(pmdp, addr);
>  }
>  
> -static pmd_t *__init kasan_pmd_offset(pud_t *pudp, unsigned long addr, int node, bool early)
> +static pmd_t *__init kasan_pmd_offset(pud_t *pudp, unsigned long addr,
> + unsigned long end, int node, bool early, bool shallow)
>  {
>  if (__pud_none(early, pudp_get(pudp))) {
>  phys_addr_t pmd_phys = early ?
>  __pa_symbol(kasan_early_shadow_pmd) : kasan_alloc_zeroed_page(node);
>  if (!early)
>  memcpy(__va(pmd_phys), kasan_early_shadow_pmd, sizeof(kasan_early_shadow_pmd));
> +
> + if (IS_ALIGNED(addr, PUD_SIZE) && end - addr >= PUD_SIZE && shallow) {
> +#ifndef __PAGETABLE_PMD_FOLDED
> + pmd_init(__va(pmd_phys));
> +#endif
> + }
> +
>  pud_populate(&init_mm, pudp, (pmd_t *)__va(pmd_phys));
>  }
>  
>  return pmd_offset(pudp, addr);
>  }
>  
> -static pud_t *__init kasan_pud_offset(p4d_t *p4dp, unsigned long addr, int node, bool early)
> +static pud_t *__init kasan_pud_offset(p4d_t *p4dp, unsigned long addr,
> + unsigned long end, int node, bool early, bool shallow)
>  {
>  if (__p4d_none(early, p4dp_get(p4dp))) {
>  phys_addr_t pud_phys = early ?
>  __pa_symbol(kasan_early_shadow_pud) : kasan_alloc_zeroed_page(node);
>  if (!early)
>  memcpy(__va(pud_phys), kasan_early_shadow_pud, sizeof(kasan_early_shadow_pud));
> +
> + if (IS_ALIGNED(addr, P4D_SIZE) && end - addr >= P4D_SIZE && shallow) {
> +#ifndef __PAGETABLE_PUD_FOLDED
> + pud_init(__va(pud_phys));
> +#endif
> + }
> +
>  p4d_populate(&init_mm, p4dp, (pud_t *)__va(pud_phys));
>  }
>  
>  return pud_offset(p4dp, addr);
>  }
>  
> -static p4d_t *__init kasan_p4d_offset(pgd_t *pgdp, unsigned long addr, int node, bool early)
> +static p4d_t *__init kasan_p4d_offset(pgd_t *pgdp, unsigned long addr,
> + unsigned long end, int node, bool early, bool shallow)
>  {
>  if (__pgd_none(early, pgdp_get(pgdp))) {
>  phys_addr_t p4d_phys = early ?
>  __pa_symbol(kasan_early_shadow_p4d) : kasan_alloc_zeroed_page(node);
> +
>  if (!early)
>  memcpy(__va(p4d_phys), kasan_early_shadow_p4d, sizeof(kasan_early_shadow_p4d));
> +
> + if (IS_ALIGNED(addr, PGDIR_SIZE) && end - addr >= PGDIR_SIZE && shallow)
> + pgd_init(__va(p4d_phys));
>  pgd_populate(&init_mm, pgdp, (p4d_t *)__va(p4d_phys));
>  }
>  
> @@ -170,58 +196,77 @@ static p4d_t *__init kasan_p4d_offset(pgd_t *pgdp, unsigned long addr, int node,
>  }
>  
>  static void __init kasan_pte_populate(pmd_t *pmdp, unsigned long addr,
> - unsigned long end, int node, bool early)
> + unsigned long end, int node, bool early, bool shallow)
>  {
>  unsigned long next;
> - pte_t *ptep = kasan_pte_offset(pmdp, addr, node, early);
> + pte_t *ptep = kasan_pte_offset(pmdp, addr, end, node, early, shallow);
> +
> + if (pte_none(ptep_get(ptep)) && shallow)
> + return;
> +
>  
>  do {
> - phys_addr_t page_phys = early ?
> + if (shallow) {
> + next = addr + PAGE_SIZE;
> + if (__pte_none(early, ptep_get(ptep)))
> + pte_clear(&init_mm, addr, ptep);
> + } else {
> + phys_addr_t page_phys = early ?
>  __pa_symbol(kasan_early_shadow_page)
>  : kasan_alloc_zeroed_page(node);
> - next = addr + PAGE_SIZE;
> - set_pte(ptep, pfn_pte(__phys_to_pfn(page_phys), PAGE_KERNEL));
> + next = addr + PAGE_SIZE;
> + set_pte(ptep, pfn_pte(__phys_to_pfn(page_phys), PAGE_KERNEL));
> + }
>  } while (ptep++, addr = next, addr != end && __pte_none(early, ptep_get(ptep)));
>  }
>  
>  static void __init kasan_pmd_populate(pud_t *pudp, unsigned long addr,
> - unsigned long end, int node, bool early)
> + unsigned long end, int node, bool early, bool shallow)
>  {
>  unsigned long next;
> - pmd_t *pmdp = kasan_pmd_offset(pudp, addr, node, early);
> + pmd_t *pmdp = kasan_pmd_offset(pudp, addr, end, node, early, shallow);
> +
> + if (pmd_none(pmdp_get(pmdp)) && shallow)
> + return;
>  
>  do {
>  next = pmd_addr_end(addr, end);
> - kasan_pte_populate(pmdp, addr, next, node, early);
> + kasan_pte_populate(pmdp, addr, next, node, early, shallow);
>  } while (pmdp++, addr = next, addr != end && __pmd_none(early, pmdp_get(pmdp)));
>  }
>  
>  static void __init kasan_pud_populate(p4d_t *p4dp, unsigned long addr,
> - unsigned long end, int node, bool early)
> + unsigned long end, int node, bool early, bool shallow)
>  {
>  unsigned long next;
> - pud_t *pudp = kasan_pud_offset(p4dp, addr, node, early);
> + pud_t *pudp = kasan_pud_offset(p4dp, addr, end, node, early, shallow);
> +
> + if (pud_none(pudp_get(pudp)) && shallow)
> + return;
>  
>  do {
>  next = pud_addr_end(addr, end);
> - kasan_pmd_populate(pudp, addr, next, node, early);
> + kasan_pmd_populate(pudp, addr, next, node, early, shallow);
>  } while (pudp++, addr = next, addr != end && __pud_none(early, READ_ONCE(*pudp)));
>  }
>  
>  static void __init kasan_p4d_populate(pgd_t *pgdp, unsigned long addr,
> - unsigned long end, int node, bool early)
> + unsigned long end, int node, bool early, bool shallow)
>  {
>  unsigned long next;
> - p4d_t *p4dp = kasan_p4d_offset(pgdp, addr, node, early);
> + p4d_t *p4dp = kasan_p4d_offset(pgdp, addr, end, node, early, shallow);
> +
> + if (p4d_none(p4dp_get(p4dp)) && shallow)
> + return;
>  
>  do {
>  next = p4d_addr_end(addr, end);
> - kasan_pud_populate(p4dp, addr, next, node, early);
> + kasan_pud_populate(p4dp, addr, next, node, early, shallow);
>  } while (p4dp++, addr = next, addr != end && __p4d_none(early, READ_ONCE(*p4dp)));
>  }
>  
>  static void __init kasan_pgd_populate(unsigned long addr, unsigned long end,
> - int node, bool early)
> + int node, bool early, bool shallow)
>  {
>  unsigned long next;
>  pgd_t *pgdp;
> @@ -230,7 +275,7 @@ static void __init kasan_pgd_populate(unsigned long addr, unsigned long end,
>  
>  do {
>  next = pgd_addr_end(addr, end);
> - kasan_p4d_populate(pgdp, addr, next, node, early);
> + kasan_p4d_populate(pgdp, addr, next, node, early, shallow);
>  } while (pgdp++, addr = next, addr != end);
>  
>  }
> @@ -239,9 +284,34 @@ static void __init kasan_pgd_populate(unsigned long addr, unsigned long end,
>  static void __init kasan_map_populate(unsigned long start, unsigned long end,
>  int node)
>  {
> - kasan_pgd_populate(start & PAGE_MASK, PAGE_ALIGN(end), node, false);
> + kasan_pgd_populate(start & PAGE_MASK, PAGE_ALIGN(end), node, false, false);
>  }
>  
> +
> +static void __init kasan_shallow_populate(unsigned long start, unsigned long end, int node)
> +{
> +
> + kasan_pgd_populate(start & PAGE_MASK, PAGE_ALIGN(end), node, false, true);
> +
> +}
> +
> +#ifdef CONFIG_KASAN_VMALLOC
> +void __init kasan_populate_early_vm_area_shadow(void *start, unsigned long size)
> +{
> + unsigned long shadow_start, shadow_end;
> +
> + if (!is_vmalloc_or_module_addr(start))
> + return;
> +
> + shadow_start = (unsigned long)mem_to_shadow(start);
> + shadow_start = ALIGN_DOWN(shadow_start, PAGE_SIZE);
> + shadow_end = (unsigned long)mem_to_shadow(start + size);
> + shadow_end = ALIGN(shadow_end, PAGE_SIZE);
> +
> + kasan_map_populate(shadow_start, shadow_end, NUMA_NO_NODE);
> +}
> +#endif
> +
>  asmlinkage void __init kasan_early_init(void)
>  {
>  BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_START, PGDIR_SIZE));
> @@ -295,10 +365,7 @@ void __init kasan_init(void)
>  clear_pgds(KASAN_SHADOW_START, KASAN_SHADOW_END);
>  
>  /* Maps everything to a single page of zeroes */
> - kasan_pgd_populate(KASAN_SHADOW_START, KASAN_SHADOW_END, NUMA_NO_NODE, true);
> -
> - kasan_populate_early_shadow(mem_to_shadow((void *)VMALLOC_START),
> - mem_to_shadow((void *)KFENCE_AREA_END));
> + kasan_pgd_populate(KASAN_SHADOW_START, KASAN_SHADOW_END, NUMA_NO_NODE, true, false);
>  
>  /* Populate the linear mapping */
>  for_each_mem_range(i, &pa_start, &pa_end) {
> @@ -309,9 +376,8 @@ void __init kasan_init(void)
>  (unsigned long)mem_to_shadow(end), NUMA_NO_NODE);
>  }
>  
> - /* Populate modules mapping */
> - kasan_map_populate((unsigned long)mem_to_shadow((void *)MODULES_VADDR),
> - (unsigned long)mem_to_shadow((void *)MODULES_END), NUMA_NO_NODE);
> + kasan_shallow_populate((unsigned long)mem_to_shadow((void *)MODULES_VADDR),
> + (unsigned long)mem_to_shadow((void *)VMALLOC_END), NUMA_NO_NODE);
>  /*
>  * KAsan may reuse the contents of kasan_early_shadow_pte directly, so we
>  * should make sure that it maps the zero page read-only.
> -- 
> 2.43.0
>

      reply	other threads:[~2026-11-09  6:27 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-27  5:55 haoran.jiang
2026-11-09  6:27 ` haoran.jiang [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6c46aae6dac0d2fbcf27cad76a8cc34256998a26@linux.dev \
    --to=haoran.jiang@linux.dev \
    --cc=chenhuacai@kernel.org \
    --cc=jianghaoran@kylinos.cn \
    --cc=kasan-dev@googlegroups.com \
    --cc=kernel@xen0n.name \
    --cc=linux-kernel@vger.kernel.org \
    --cc=loongarch@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®