Thread (54 messages) 54 messages, 12 authors, 2017-07-24

Re: KASAN vs. boot-time switching between 4- and 5-level paging

From: Andrey Ryabinin <hidden>
Date: 2017-06-01 14:54:47
Also in: linux-mm, lkml
Subsystem: kasan, the rest, x86 architecture (32-bit and 64-bit), x86 mm · Maintainers: Andrey Ryabinin, Linus Torvalds, Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, Andy Lutomirski, Peter Zijlstra

On 05/29/2017 03:46 PM, Andrey Ryabinin wrote:
On 05/29/2017 02:45 PM, Andrey Ryabinin wrote:
quoted
quoted
quoted
quoted
quoted
Looks like KASAN will be a problem for boot-time paging mode switching.
It wants to know CONFIG_KASAN_SHADOW_OFFSET at compile-time to pass to
gcc -fasan-shadow-offset=. But this value varies between paging modes...

I don't see how to solve it. Folks, any ideas?
+kasan-dev

I wonder if we can use the same offset for both modes. If we use
0xFFDFFC0000000000 as start of shadow for 5 levels, then the same
offset that we use for 4 levels (0xdffffc0000000000) will also work
for 5 levels. Namely, ending of 5 level shadow will overlap with 4
level mapping (both end at 0xfffffbffffffffff), but 5 level mapping
extends towards lower addresses. The current 5 level start of shadow
is actually close -- 0xffd8000000000000 and it seems that the required
space after it is unused at the moment (at least looking at mm.txt).
So just try to move it to 0xFFDFFC0000000000?
Yeah, this should work, but note that 0xFFDFFC0000000000 is not PGDIR aligned address. Our init code
assumes that kasan shadow stars and ends on the PGDIR aligned address.
Fortunately this is fixable, we'd need two more pages for page tables to map unaligned start/end
of the shadow.
I think we can extend the shadow backwards (to the current address),
provided that it does not affect shadow offset that we pass to
compiler.
I thought about this. We can round down shadow start to 0xffdf000000000000, but we can't
round up shadow end, because in that case shadow would end at 0xffffffffffffffff.
So we still need at least one more page to cover unaligned end.
Actually, I'm wrong here. I assumed that we would need an additional page to store p4d entries,
but in fact we don't need it, as such page should already exist. It's the same last pgd where kernel image
is mapped.

Something like bellow might work. It's just a proposal to demonstrate the idea, so some code might look ugly.
And it's only build-tested.

Based on top of: git://git.kernel.org/pub/scm/linux/kernel/git/kas/linux.git la57/integration


---
 arch/x86/Kconfig            |  1 -
 arch/x86/mm/kasan_init_64.c | 74 ++++++++++++++++++++++++++++++++-------------
 2 files changed, 53 insertions(+), 22 deletions(-)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 11bd0498f64c..3456f2fdda52 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -291,7 +291,6 @@ config ARCH_SUPPORTS_DEBUG_PAGEALLOC
 config KASAN_SHADOW_OFFSET
 	hex
 	depends on KASAN
-	default 0xdff8000000000000 if X86_5LEVEL
 	default 0xdffffc0000000000
 
 config HAVE_INTEL_TXT
diff --git a/arch/x86/mm/kasan_init_64.c b/arch/x86/mm/kasan_init_64.c
index 88215ac16b24..d79a7ea83d05 100644
--- a/arch/x86/mm/kasan_init_64.c
+++ b/arch/x86/mm/kasan_init_64.c
@@ -15,6 +15,10 @@
 extern pgd_t early_top_pgt[PTRS_PER_PGD];
 extern struct range pfn_mapped[E820_MAX_ENTRIES];
 
+#if CONFIG_PGTABLE_LEVELS == 5
+p4d_t tmp_p4d_table[PTRS_PER_P4D] __initdata __aligned(PAGE_SIZE);
+#endif
+
 static int __init map_range(struct range *range)
 {
 	unsigned long start;
@@ -35,8 +39,9 @@ static void __init clear_pgds(unsigned long start,
 			unsigned long end)
 {
 	pgd_t *pgd;
+	unsigned long pgd_end = end & PGDIR_MASK;
 
-	for (; start < end; start += PGDIR_SIZE) {
+	for (; start < pgd_end; start += PGDIR_SIZE) {
 		pgd = pgd_offset_k(start);
 		/*
 		 * With folded p4d, pgd_clear() is nop, use p4d_clear()
@@ -47,29 +52,50 @@ static void __init clear_pgds(unsigned long start,
 		else
 			pgd_clear(pgd);
 	}
+
+	pgd = pgd_offset_k(start);
+	for (; start < end; start += P4D_SIZE)
+		p4d_clear(p4d_offset(pgd, start));
+}
+
+static void __init kasan_early_p4d_populate(pgd_t *pgd,
+					unsigned long addr,
+					unsigned long end)
+{
+	p4d_t *p4d;
+	unsigned long next;
+
+	if (pgd_none(*pgd))
+		set_pgd(pgd, __pgd(_KERNPG_TABLE | __pa_nodebug(kasan_zero_p4d)));
+
+	/* early p4d_offset()
+	 * TODO: we need helpers for this shit
+	 */
+	if (CONFIG_PGTABLE_LEVELS == 5)
+		p4d = ((p4d_t*)((__pa_nodebug(pgd->pgd) & PTE_PFN_MASK) + __START_KERNEL_map))
+			+ p4d_index(addr);
+	else
+		p4d = (p4d_t*)pgd;
+	do {
+		next = p4d_addr_end(addr, end);
+
+		if (p4d_none(*p4d))
+			set_p4d(p4d, __p4d(_KERNPG_TABLE |
+					__pa_nodebug(kasan_zero_pud)));
+	} while (p4d++, addr = next, addr != end && p4d_none(*p4d));
 }
 
 static void __init kasan_map_early_shadow(pgd_t *pgd)
 {
-	int i;
-	unsigned long start = KASAN_SHADOW_START;
+	unsigned long addr = KASAN_SHADOW_START & PGDIR_MASK;
 	unsigned long end = KASAN_SHADOW_END;
+	unsigned long next;
 
-	for (i = pgd_index(start); start < end; i++) {
-		switch (CONFIG_PGTABLE_LEVELS) {
-		case 4:
-			pgd[i] = __pgd(__pa_nodebug(kasan_zero_pud) |
-					_KERNPG_TABLE);
-			break;
-		case 5:
-			pgd[i] = __pgd(__pa_nodebug(kasan_zero_p4d) |
-					_KERNPG_TABLE);
-			break;
-		default:
-			BUILD_BUG();
-		}
-		start += PGDIR_SIZE;
-	}
+	pgd = pgd + pgd_index(addr);
+	do {
+		next = pgd_addr_end(addr, end);
+		kasan_early_p4d_populate(pgd, addr, next);
+	} while (pgd++, addr = next, addr != end);
 }
 
 #ifdef CONFIG_KASAN_INLINE
@@ -120,14 +146,20 @@ void __init kasan_init(void)
 #ifdef CONFIG_KASAN_INLINE
 	register_die_notifier(&kasan_die_notifier);
 #endif
-
 	memcpy(early_top_pgt, init_top_pgt, sizeof(early_top_pgt));
+#if CONFIG_PGTABLE_LEVELS == 5
+	memcpy(tmp_p4d_table, (void*)pgd_page_vaddr(*pgd_offset_k(KASAN_SHADOW_END)),
+		sizeof(tmp_p4d_table));
+	set_pgd(&early_top_pgt[pgd_index(KASAN_SHADOW_END)],
+		__pgd(__pa(tmp_p4d_table) | _KERNPG_TABLE));
+#endif
+
 	load_cr3(early_top_pgt);
 	__flush_tlb_all();
 
-	clear_pgds(KASAN_SHADOW_START, KASAN_SHADOW_END);
+	clear_pgds(KASAN_SHADOW_START & PGDIR_MASK, KASAN_SHADOW_END);
 
-	kasan_populate_zero_shadow((void *)KASAN_SHADOW_START,
+	kasan_populate_zero_shadow((void *)(KASAN_SHADOW_START & PGDIR_MASK),
 			kasan_mem_to_shadow((void *)PAGE_OFFSET));
 
 	for (i = 0; i < E820_MAX_ENTRIES; i++) {
-- 
2.13.0

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help