x86/boot/64: Fix crash if kernel image crosses page table boundary
[ Upstream commit81c7ed296d
] A kernel which boots in 5-level paging mode crashes in a small percentage of cases if KASLR is enabled. This issue was tracked down to the case when the kernel image unpacks in a way that it crosses an 1G boundary. The crash is caused by an overrun of the PMD page table in __startup_64() and corruption of P4D page table allocated next to it. This particular issue is not visible with 4-level paging as P4D page tables are not used. But the P4D and the PUD calculation have similar problems. The PMD index calculation is wrong due to operator precedence, which fails to confine the PMDs in the PMD array on wrap around. The P4D calculation for 5-level paging and the PUD calculation calculate the first index correctly, but then blindly increment it which causes the same issue when a kernel image is located across a 512G and for 5-level paging across a 46T boundary. This wrap around mishandling was introduced when these parts moved from assembly to C. Restore it to the correct behaviour. Fixes:c88d71508e
("x86/boot/64: Rewrite startup_64() in C") Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Borislav Petkov <bp@alien8.de> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Link: https://lkml.kernel.org/r/20190620112345.28833-1-kirill.shutemov@linux.intel.com Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Родитель
e5516f59de
Коммит
1642b93f11
|
@ -117,26 +117,27 @@ unsigned long __head __startup_64(unsigned long physaddr,
|
|||
pgd[i + 0] = (pgdval_t)p4d + pgtable_flags;
|
||||
pgd[i + 1] = (pgdval_t)p4d + pgtable_flags;
|
||||
|
||||
i = (physaddr >> P4D_SHIFT) % PTRS_PER_P4D;
|
||||
p4d[i + 0] = (pgdval_t)pud + pgtable_flags;
|
||||
p4d[i + 1] = (pgdval_t)pud + pgtable_flags;
|
||||
i = physaddr >> P4D_SHIFT;
|
||||
p4d[(i + 0) % PTRS_PER_P4D] = (pgdval_t)pud + pgtable_flags;
|
||||
p4d[(i + 1) % PTRS_PER_P4D] = (pgdval_t)pud + pgtable_flags;
|
||||
} else {
|
||||
i = (physaddr >> PGDIR_SHIFT) % PTRS_PER_PGD;
|
||||
pgd[i + 0] = (pgdval_t)pud + pgtable_flags;
|
||||
pgd[i + 1] = (pgdval_t)pud + pgtable_flags;
|
||||
}
|
||||
|
||||
i = (physaddr >> PUD_SHIFT) % PTRS_PER_PUD;
|
||||
pud[i + 0] = (pudval_t)pmd + pgtable_flags;
|
||||
pud[i + 1] = (pudval_t)pmd + pgtable_flags;
|
||||
i = physaddr >> PUD_SHIFT;
|
||||
pud[(i + 0) % PTRS_PER_PUD] = (pudval_t)pmd + pgtable_flags;
|
||||
pud[(i + 1) % PTRS_PER_PUD] = (pudval_t)pmd + pgtable_flags;
|
||||
|
||||
pmd_entry = __PAGE_KERNEL_LARGE_EXEC & ~_PAGE_GLOBAL;
|
||||
pmd_entry += sme_get_me_mask();
|
||||
pmd_entry += physaddr;
|
||||
|
||||
for (i = 0; i < DIV_ROUND_UP(_end - _text, PMD_SIZE); i++) {
|
||||
int idx = i + (physaddr >> PMD_SHIFT) % PTRS_PER_PMD;
|
||||
pmd[idx] = pmd_entry + i * PMD_SIZE;
|
||||
int idx = i + (physaddr >> PMD_SHIFT);
|
||||
|
||||
pmd[idx % PTRS_PER_PMD] = pmd_entry + i * PMD_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Загрузка…
Ссылка в новой задаче