aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKirill A. Shutemov <kirill@shutemov.name>2019-06-20 07:23:45 -0400
committerThomas Gleixner <tglx@linutronix.de>2019-06-26 01:25:09 -0400
commit81c7ed296dcd02bc0b4488246d040e03e633737a (patch)
tree2f7bd9718f21ae74b117ed8a5bca3da1ac255b24
parentea136a112d89bade596314a1ae49f748902f4727 (diff)
x86/boot/64: Fix crash if kernel image crosses page table boundary
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: c88d71508e36 ("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
-rw-r--r--arch/x86/kernel/head64.c17
1 files changed, 9 insertions, 8 deletions
diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c
index 16b1cbd3a61e..7df5bce4e1be 100644
--- a/arch/x86/kernel/head64.c
+++ b/arch/x86/kernel/head64.c
@@ -190,18 +190,18 @@ unsigned long __head __startup_64(unsigned long physaddr,
190 pgd[i + 0] = (pgdval_t)p4d + pgtable_flags; 190 pgd[i + 0] = (pgdval_t)p4d + pgtable_flags;
191 pgd[i + 1] = (pgdval_t)p4d + pgtable_flags; 191 pgd[i + 1] = (pgdval_t)p4d + pgtable_flags;
192 192
193 i = (physaddr >> P4D_SHIFT) % PTRS_PER_P4D; 193 i = physaddr >> P4D_SHIFT;
194 p4d[i + 0] = (pgdval_t)pud + pgtable_flags; 194 p4d[(i + 0) % PTRS_PER_P4D] = (pgdval_t)pud + pgtable_flags;
195 p4d[i + 1] = (pgdval_t)pud + pgtable_flags; 195 p4d[(i + 1) % PTRS_PER_P4D] = (pgdval_t)pud + pgtable_flags;
196 } else { 196 } else {
197 i = (physaddr >> PGDIR_SHIFT) % PTRS_PER_PGD; 197 i = (physaddr >> PGDIR_SHIFT) % PTRS_PER_PGD;
198 pgd[i + 0] = (pgdval_t)pud + pgtable_flags; 198 pgd[i + 0] = (pgdval_t)pud + pgtable_flags;
199 pgd[i + 1] = (pgdval_t)pud + pgtable_flags; 199 pgd[i + 1] = (pgdval_t)pud + pgtable_flags;
200 } 200 }
201 201
202 i = (physaddr >> PUD_SHIFT) % PTRS_PER_PUD; 202 i = physaddr >> PUD_SHIFT;
203 pud[i + 0] = (pudval_t)pmd + pgtable_flags; 203 pud[(i + 0) % PTRS_PER_PUD] = (pudval_t)pmd + pgtable_flags;
204 pud[i + 1] = (pudval_t)pmd + pgtable_flags; 204 pud[(i + 1) % PTRS_PER_PUD] = (pudval_t)pmd + pgtable_flags;
205 205
206 pmd_entry = __PAGE_KERNEL_LARGE_EXEC & ~_PAGE_GLOBAL; 206 pmd_entry = __PAGE_KERNEL_LARGE_EXEC & ~_PAGE_GLOBAL;
207 /* Filter out unsupported __PAGE_KERNEL_* bits: */ 207 /* Filter out unsupported __PAGE_KERNEL_* bits: */
@@ -211,8 +211,9 @@ unsigned long __head __startup_64(unsigned long physaddr,
211 pmd_entry += physaddr; 211 pmd_entry += physaddr;
212 212
213 for (i = 0; i < DIV_ROUND_UP(_end - _text, PMD_SIZE); i++) { 213 for (i = 0; i < DIV_ROUND_UP(_end - _text, PMD_SIZE); i++) {
214 int idx = i + (physaddr >> PMD_SHIFT) % PTRS_PER_PMD; 214 int idx = i + (physaddr >> PMD_SHIFT);
215 pmd[idx] = pmd_entry + i * PMD_SIZE; 215
216 pmd[idx % PTRS_PER_PMD] = pmd_entry + i * PMD_SIZE;
216 } 217 }
217 218
218 /* 219 /*