2015-06-09 09:41:38

by Alexander Popov

[permalink] [raw]
Subject: [PATCH v5 0/2] x86_64: fix KASan shadow region page tables

This patch series:
- fixes kernel halt caused by incorrect physical addresses
in KASan shadow region page tables in case of non-zero phys_base;
- consolidates early KASan initialization.

Changes from v2:
- move KASan shadow region page tables to BSS;
- use __PAGE_KERNEL flags for describing kasan_zero_page in kasan_zero_pte.

Changes from v3:
- improve commit message.

Changes from v4:
- add Andrey's patch which removes faulty clear_page(init_level4_pgt);
- call kasan_map_early_shadow() for early_level4_pgt and init_level4_pgt
in kasan_early_init().

Alexander Popov (1):
x86_64: fix KASan shadow region page tables

Andrey Ryabinin (1):
x86_64: remove not needed clear_page for init_level4_page

arch/x86/include/asm/kasan.h | 8 ++------
arch/x86/kernel/head64.c | 12 ++++++------
arch/x86/kernel/head_64.S | 29 -----------------------------
arch/x86/mm/kasan_init_64.c | 36 ++++++++++++++++++++++++++++++++++--
4 files changed, 42 insertions(+), 43 deletions(-)

--
1.9.1


2015-06-09 09:41:50

by Alexander Popov

[permalink] [raw]
Subject: [PATCH v5 1/2] x86_64: remove not needed clear_page for init_level4_page

From: Andrey Ryabinin <[email protected]>

Commit 8170e6bed465 ("x86, 64bit: Use a #PF handler to materialize
early mappings on demand") introduced clear_page(init_level4_pgt);
call in x86_64_start_kernel(). However, this clear_page is useless
because init_level4_page already filled with zeroes in head_64.S

Commit message in 8170e6bed465 says that this clear_page() was
dropped in v7, but it accidentally reappeared in later versions
of that patchset.

Signed-off-by: Andrey Ryabinin <[email protected]>
---
arch/x86/kernel/head64.c | 1 -
1 file changed, 1 deletion(-)

diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c
index 5a46681..6a6eefd 100644
--- a/arch/x86/kernel/head64.c
+++ b/arch/x86/kernel/head64.c
@@ -177,7 +177,6 @@ asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
*/
load_ucode_bsp();

- clear_page(init_level4_pgt);
/* set init_level4_pgt kernel high mapping*/
init_level4_pgt[511] = early_level4_pgt[511];

--
1.9.1

2015-06-09 09:42:00

by Alexander Popov

[permalink] [raw]
Subject: [PATCH v5 2/2] x86_64: fix KASan shadow region page tables

Physical addresses in KASan shadow region page tables need fixup similarly
to the other page tables. Current code doesn't do it which causes
kernel halt if phys_base is not zero.
So let's initialize KASan shadow region page tables in kasan_early_init()
using __pa_nodebug() which considers phys_base.

Signed-off-by: Alexander Popov <[email protected]>
---
arch/x86/include/asm/kasan.h | 8 ++------
arch/x86/kernel/head64.c | 11 ++++++-----
arch/x86/kernel/head_64.S | 29 -----------------------------
arch/x86/mm/kasan_init_64.c | 36 ++++++++++++++++++++++++++++++++++--
4 files changed, 42 insertions(+), 42 deletions(-)

diff --git a/arch/x86/include/asm/kasan.h b/arch/x86/include/asm/kasan.h
index 8b22422..74a2a8d 100644
--- a/arch/x86/include/asm/kasan.h
+++ b/arch/x86/include/asm/kasan.h
@@ -14,15 +14,11 @@

#ifndef __ASSEMBLY__

-extern pte_t kasan_zero_pte[];
-extern pte_t kasan_zero_pmd[];
-extern pte_t kasan_zero_pud[];
-
#ifdef CONFIG_KASAN
-void __init kasan_map_early_shadow(pgd_t *pgd);
+void __init kasan_early_init(void);
void __init kasan_init(void);
#else
-static inline void kasan_map_early_shadow(pgd_t *pgd) { }
+static inline void kasan_early_init(void) { }
static inline void kasan_init(void) { }
#endif

diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c
index 6a6eefd..74da193 100644
--- a/arch/x86/kernel/head64.c
+++ b/arch/x86/kernel/head64.c
@@ -161,11 +161,14 @@ asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
/* Kill off the identity-map trampoline */
reset_early_page_tables();

- kasan_map_early_shadow(early_level4_pgt);
-
- /* clear bss before set_intr_gate with early_idt_handler */
+ /*
+ * Clear bss before kasan_early_init and set_intr_gate
+ * with early_idt_handler
+ */
clear_bss();

+ kasan_early_init();
+
for (i = 0; i < NUM_EXCEPTION_VECTORS; i++)
set_intr_gate(i, early_idt_handler_array[i]);
load_idt((const struct desc_ptr *)&idt_descr);
@@ -180,8 +183,6 @@ asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
/* set init_level4_pgt kernel high mapping*/
init_level4_pgt[511] = early_level4_pgt[511];

- kasan_map_early_shadow(init_level4_pgt);
-
x86_64_start_reservations(real_mode_data);
}

diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S
index df7e780..7e5da2c 100644
--- a/arch/x86/kernel/head_64.S
+++ b/arch/x86/kernel/head_64.S
@@ -516,38 +516,9 @@ ENTRY(phys_base)
/* This must match the first entry in level2_kernel_pgt */
.quad 0x0000000000000000

-#ifdef CONFIG_KASAN
-#define FILL(VAL, COUNT) \
- .rept (COUNT) ; \
- .quad (VAL) ; \
- .endr
-
-NEXT_PAGE(kasan_zero_pte)
- FILL(kasan_zero_page - __START_KERNEL_map + _KERNPG_TABLE, 512)
-NEXT_PAGE(kasan_zero_pmd)
- FILL(kasan_zero_pte - __START_KERNEL_map + _KERNPG_TABLE, 512)
-NEXT_PAGE(kasan_zero_pud)
- FILL(kasan_zero_pmd - __START_KERNEL_map + _KERNPG_TABLE, 512)
-
-#undef FILL
-#endif
-
-
#include "../../x86/xen/xen-head.S"

__PAGE_ALIGNED_BSS
NEXT_PAGE(empty_zero_page)
.skip PAGE_SIZE

-#ifdef CONFIG_KASAN
-/*
- * This page used as early shadow. We don't use empty_zero_page
- * at early stages, stack instrumentation could write some garbage
- * to this page.
- * Latter we reuse it as zero shadow for large ranges of memory
- * that allowed to access, but not instrumented by kasan
- * (vmalloc/vmemmap ...).
- */
-NEXT_PAGE(kasan_zero_page)
- .skip PAGE_SIZE
-#endif
diff --git a/arch/x86/mm/kasan_init_64.c b/arch/x86/mm/kasan_init_64.c
index 4860906..0e4a05f 100644
--- a/arch/x86/mm/kasan_init_64.c
+++ b/arch/x86/mm/kasan_init_64.c
@@ -11,7 +11,19 @@
extern pgd_t early_level4_pgt[PTRS_PER_PGD];
extern struct range pfn_mapped[E820_X_MAX];

-extern unsigned char kasan_zero_page[PAGE_SIZE];
+static pud_t kasan_zero_pud[PTRS_PER_PUD] __page_aligned_bss;
+static pmd_t kasan_zero_pmd[PTRS_PER_PMD] __page_aligned_bss;
+static pte_t kasan_zero_pte[PTRS_PER_PTE] __page_aligned_bss;
+
+/*
+ * This page used as early shadow. We don't use empty_zero_page
+ * at early stages, stack instrumentation could write some garbage
+ * to this page.
+ * Latter we reuse it as zero shadow for large ranges of memory
+ * that allowed to access, but not instrumented by kasan
+ * (vmalloc/vmemmap ...).
+ */
+static unsigned char kasan_zero_page[PAGE_SIZE] __page_aligned_bss;

static int __init map_range(struct range *range)
{
@@ -36,7 +48,7 @@ static void __init clear_pgds(unsigned long start,
pgd_clear(pgd_offset_k(start));
}

-void __init kasan_map_early_shadow(pgd_t *pgd)
+static void __init kasan_map_early_shadow(pgd_t *pgd)
{
int i;
unsigned long start = KASAN_SHADOW_START;
@@ -166,6 +178,26 @@ static struct notifier_block kasan_die_notifier = {
};
#endif

+void __init kasan_early_init(void)
+{
+ int i;
+ pteval_t pte_val = __pa_nodebug(kasan_zero_page) | __PAGE_KERNEL;
+ pmdval_t pmd_val = __pa_nodebug(kasan_zero_pte) | _KERNPG_TABLE;
+ pudval_t pud_val = __pa_nodebug(kasan_zero_pmd) | _KERNPG_TABLE;
+
+ for (i = 0; i < PTRS_PER_PTE; i++)
+ kasan_zero_pte[i] = __pte(pte_val);
+
+ for (i = 0; i < PTRS_PER_PMD; i++)
+ kasan_zero_pmd[i] = __pmd(pmd_val);
+
+ for (i = 0; i < PTRS_PER_PUD; i++)
+ kasan_zero_pud[i] = __pud(pud_val);
+
+ kasan_map_early_shadow(early_level4_pgt);
+ kasan_map_early_shadow(init_level4_pgt);
+}
+
void __init kasan_init(void)
{
int i;
--
1.9.1

2015-06-12 17:13:58

by Andrey Ryabinin

[permalink] [raw]
Subject: Re: [PATCH v5 2/2] x86_64: fix KASan shadow region page tables

2015-06-09 12:42 GMT+03:00 Alexander Popov <[email protected]>:
> Physical addresses in KASan shadow region page tables need fixup similarly
> to the other page tables. Current code doesn't do it which causes
> kernel halt if phys_base is not zero.
> So let's initialize KASan shadow region page tables in kasan_early_init()
> using __pa_nodebug() which considers phys_base.
>
> Signed-off-by: Alexander Popov <[email protected]>

Acked-by: Andrey Ryabinin <[email protected]>

2015-06-16 11:16:50

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v5 1/2] x86_64: remove not needed clear_page for init_level4_page

On Tue, Jun 09, 2015 at 12:42:00PM +0300, Alexander Popov wrote:
> From: Andrey Ryabinin <[email protected]>
>
> Commit 8170e6bed465 ("x86, 64bit: Use a #PF handler to materialize
> early mappings on demand") introduced clear_page(init_level4_pgt);
> call in x86_64_start_kernel(). However, this clear_page is useless
> because init_level4_page already filled with zeroes in head_64.S

I really doubt that, see below:

> Commit message in 8170e6bed465 says that this clear_page() was
> dropped in v7, but it accidentally reappeared in later versions
> of that patchset.
>
> Signed-off-by: Andrey Ryabinin <[email protected]>
> ---
> arch/x86/kernel/head64.c | 1 -
> 1 file changed, 1 deletion(-)
>
> diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c
> index 5a46681..6a6eefd 100644
> --- a/arch/x86/kernel/head64.c
> +++ b/arch/x86/kernel/head64.c
> @@ -177,7 +177,6 @@ asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
> */
> load_ucode_bsp();
>
> - clear_page(init_level4_pgt);
> /* set init_level4_pgt kernel high mapping*/
> init_level4_pgt[511] = early_level4_pgt[511];

vmlinux has:

ffffffff81a00000 <init_level4_pgt>:
ffffffff81a00000: 63 10 movslq (%rax),%edx
ffffffff81a00002: a0 01 00 00 00 00 00 movabs 0x1,%al
ffffffff81a00009: 00 00
...
ffffffff81a0087f: 00 63 10 add %ah,0x10(%rbx)
ffffffff81a00882: a0 01 00 00 00 00 00 movabs 0x1,%al
ffffffff81a00889: 00 00
...
ffffffff81a00ff7: 00 67 30 add %ah,0x30(%rdi)
ffffffff81a00ffa: a0 01 00 00 00 00 63 movabs 0xa020630000000001,%al
ffffffff81a01001: 20 a0

ffffffff81a01000 <level3_ident_pgt>:
ffffffff81a01000: 63 20 movslq (%rax),%esp
ffffffff81a01002: a0 01 00 00 00 00 00 movabs 0x1,%al
ffffffff81a01009: 00 00
...

Booting a kvm quest and halting it before the clear_page:

---
/*
* Load microcode early on BSP.
*/
load_ucode_bsp();

// clear_page(init_level4_pgt);

while(1)
cpu_relax();

/* set init_level4_pgt kernel high mapping*/
init_level4_pgt[511] = early_level4_pgt[511];
---

and dumping the memory at ffffffff81a00000 gives:

---
00000000 63 10 a0 01 00 00 00 00 00 00 00 00 00 00 00 00 |c...............|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000880 63 10 a0 01 00 00 00 00 00 00 00 00 00 00 00 00 |c...............|
00000890 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000ff0 00 00 00 00 00 00 00 00 67 30 a0 01 00 00 00 00 |........g0......|
00001000
---

These are basically the PTE entries it gets for the CONFIG_XEN case
which we clear because we're on baremetal.

Now my hunch is that those entries get overwritten later but I wouldn't
want to debug any strange bugs from leftovers in init_level4_pgt so
having the clear_page() is actually a good thing.

--
Regards/Gruss,
Boris.

ECO tip #101: Trim your mails when you reply.
--

2015-06-16 11:35:06

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v5 1/2] x86_64: remove not needed clear_page for init_level4_page

On Tue, Jun 16, 2015 at 01:16:32PM +0200, Borislav Petkov wrote:
> Now my hunch is that those entries get overwritten later but I wouldn't
> want to debug any strange bugs from leftovers in init_level4_pgt so
> having the clear_page() is actually a good thing.

So I guess we can do that:

#ifndef CONFIG_KASAN
clear_page(init_level4_pgt);
#endif

Hmm...

--
Regards/Gruss,
Boris.

ECO tip #101: Trim your mails when you reply.
--

2015-06-16 11:45:18

by Andrey Ryabinin

[permalink] [raw]
Subject: Re: [PATCH v5 1/2] x86_64: remove not needed clear_page for init_level4_page

On 06/16/2015 02:34 PM, Borislav Petkov wrote:
> On Tue, Jun 16, 2015 at 01:16:32PM +0200, Borislav Petkov wrote:
>> Now my hunch is that those entries get overwritten later but I wouldn't
>> want to debug any strange bugs from leftovers in init_level4_pgt so
>> having the clear_page() is actually a good thing.
>
> So I guess we can do that:
>
> #ifndef CONFIG_KASAN
> clear_page(init_level4_pgt);
> #endif
>

Ugh..

Can't we just move clear_page(init_level4_pgt) higher? Before or right after clear_bss() (iow before kasan_early_init()).


2015-06-16 15:46:32

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v5 1/2] x86_64: remove not needed clear_page for init_level4_page

On Tue, Jun 16, 2015 at 02:45:06PM +0300, Andrey Ryabinin wrote:
> Can't we just move clear_page(init_level4_pgt) higher? Before or right
> after clear_bss() (iow before kasan_early_init()).

That sounds much better. And I don't see anything depending on
init_level4_pgt before we clear it. And it boots here if I move it
before

kasan_map_early_shadow(early_level4_pgt);

--
Regards/Gruss,
Boris.

ECO tip #101: Trim your mails when you reply.
--

2015-06-17 11:42:18

by Alexander Popov

[permalink] [raw]
Subject: Re: [PATCH v5 1/2] x86_64: remove not needed clear_page for init_level4_page

On 16.06.2015 18:46, Borislav Petkov wrote:
> On Tue, Jun 16, 2015 at 02:45:06PM +0300, Andrey Ryabinin wrote:
>> Can't we just move clear_page(init_level4_pgt) higher? Before or right
>> after clear_bss() (iow before kasan_early_init()).
>
> That sounds much better. And I don't see anything depending on
> init_level4_pgt before we clear it. And it boots here if I move it
> before
>
> kasan_map_early_shadow(early_level4_pgt);

Thanks, Borislav and Andrey.
I'll return with version 6.

Best regards,
Alexander