• Kirill A. Shutemov's avatar
    x86/mm: Handle physical-virtual alignment mismatch in phys_p4d_init() · 432c8332
    Kirill A. Shutemov authored
    Kyle has reported occasional crashes when booting a kernel in 5-level
    paging mode with KASLR enabled:
    
      WARNING: CPU: 0 PID: 0 at arch/x86/mm/init_64.c:87 phys_p4d_init+0x1d4/0x1ea
      RIP: 0010:phys_p4d_init+0x1d4/0x1ea
      Call Trace:
       __kernel_physical_mapping_init+0x10a/0x35c
       kernel_physical_mapping_init+0xe/0x10
       init_memory_mapping+0x1aa/0x3b0
       init_range_memory_mapping+0xc8/0x116
       init_mem_mapping+0x225/0x2eb
       setup_arch+0x6ff/0xcf5
       start_kernel+0x64/0x53b
       ? copy_bootdata+0x1f/0xce
       x86_64_start_reservations+0x24/0x26
       x86_64_start_kernel+0x8a/0x8d
       secondary_startup_64+0xb6/0xc0
    
    which causes later:
    
      BUG: unable to handle page fault for address: ff484d019580eff8
      #PF: supervisor read access in kernel mode
      #PF: error_code(0x0000) - not-present page
      BAD
      Oops: 0000 [#1] SMP NOPTI
      RIP: 0010:fill_pud+0x13/0x130
      Call Trace:
       set_pte_vaddr_p4d+0x2e/0x50
       set_pte_vaddr+0x6f/0xb0
       __native_set_fixmap+0x28/0x40
       native_set_fixmap+0x39/0x70
       register_lapic_address+0x49/0xb6
       early_acpi_boot_init+0xa5/0xde
       setup_arch+0x944/0xcf5
       start_kernel+0x64/0x53b
    
    Kyle bisected the issue to commit b569c184 ("x86/mm/KASLR: Reduce
    randomization granularity for 5-level paging to 1GB")
    
    Before this commit PAGE_OFFSET was always aligned to P4D_SIZE when booting
    5-level paging mode. But now only PUD_SIZE alignment is guaranteed.
    
    In the case I was able to reproduce the following vaddr/paddr values were
    observed in phys_p4d_init():
    
    Iteration     vaddr			paddr
       1 	      0xff4228027fe00000 	0x033fe00000
       2	      0xff42287f40000000	0x8000000000
    
    'vaddr' in both cases belongs to the same p4d entry.
    
    But due to the original assumption that PAGE_OFFSET is aligned to P4D_SIZE
    this overlap cannot be handled correctly. The code assumes strictly aligned
    entries and unconditionally increments the index into the P4D table, which
    creates false duplicate entries. Once the index reaches the end, the last
    entry in the page table is missing.
    
    Aside of that the 'paddr >= paddr_end' condition can evaluate wrong which
    causes an P4D entry to be cleared incorrectly.
    
    Change the loop in phys_p4d_init() to walk purely based on virtual
    addresses like __kernel_physical_mapping_init() does. This makes it work
    correctly with unaligned virtual addresses.
    
    Fixes: b569c184 ("x86/mm/KASLR: Reduce randomization granularity for 5-level paging to 1GB")
    Reported-by: default avatarKyle Pelton <kyle.d.pelton@intel.com>
    Signed-off-by: default avatarKirill A. Shutemov <kirill.shutemov@linux.intel.com>
    Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
    Tested-by: default avatarKyle Pelton <kyle.d.pelton@intel.com>
    Acked-by: default avatarBaoquan He <bhe@redhat.com>
    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/20190624123150.920-1-kirill.shutemov@linux.intel.com
    432c8332
init_64.c 40.3 KB