• Michael Ellerman's avatar
    powerpc/mm/radix: Make Radix require HUGETLB_PAGE · 8adddf34
    Michael Ellerman authored
    Joel reported weird crashes using skiroot_defconfig, in his case we
    jumped into an NX page:
    
      kernel tried to execute exec-protected page (c000000002bff4f0) - exploit attempt? (uid: 0)
      BUG: Unable to handle kernel instruction fetch
      Faulting instruction address: 0xc000000002bff4f0
    
    Looking at the disassembly, we had simply branched to that address:
    
      c000000000c001bc  49fff335    bl     c000000002bff4f0
    
    But that didn't match the original kernel image:
    
      c000000000c001bc  4bfff335    bl     c000000000bff4f0 <kobject_get+0x8>
    
    When STRICT_KERNEL_RWX is enabled, and we're using the radix MMU, we
    call radix__change_memory_range() late in boot to change page
    protections. We do that both to mark rodata read only and also to mark
    init text no-execute. That involves walking the kernel page tables,
    and clearing _PAGE_WRITE or _PAGE_EXEC respectively.
    
    With radix we may use hugepages for the linear mapping, so the code in
    radix__change_memory_range() uses eg. pmd_huge() to test if it has
    found a huge mapping, and if so it stops the page table walk and
    changes the PMD permissions.
    
    However if the kernel is built without HUGETLBFS support, pmd_huge()
    is just a #define that always returns 0. That causes the code in
    radix__change_memory_range() to incorrectly interpret the PMD value as
    a pointer to a PTE page rather than as a PTE at the PMD level.
    
    We can see this using `dv` in xmon which also uses pmd_huge():
    
      0:mon> dv c000000000000000
      pgd  @ 0xc000000001740000
      pgdp @ 0xc000000001740000 = 0x80000000ffffb009
      pudp @ 0xc0000000ffffb000 = 0x80000000ffffa009
      pmdp @ 0xc0000000ffffa000 = 0xc00000000000018f   <- this is a PTE
      ptep @ 0xc000000000000100 = 0xa64bb17da64ab07d   <- kernel text
    
    The end result is we treat the value at 0xc000000000000100 as a PTE
    and clear _PAGE_WRITE or _PAGE_EXEC, potentially corrupting the code
    at that address.
    
    In Joel's specific case we cleared the sign bit in the offset of the
    branch, causing a backward branch to turn into a forward branch which
    caused us to branch into a non-executable page. However the exact
    nature of the crash depends on kernel version, compiler version, and
    other factors.
    
    We need to fix radix__change_memory_range() to not use accessors that
    depend on HUGETLBFS, but we also have radix memory hotplug code that
    uses pmd_huge() etc that will also need fixing. So for now just
    disallow the broken combination of Radix with HUGETLBFS disabled.
    
    The only defconfig we have that is affected is skiroot_defconfig, so
    turn on HUGETLBFS there so that it still gets Radix.
    
    Fixes: 566ca99a ("powerpc/mm/radix: Add dummy radix_enabled()")
    Cc: stable@vger.kernel.org # v4.7+
    Reported-by: default avatarJoel Stanley <joel@jms.id.au>
    Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
    8adddf34
skiroot_defconfig 7.58 KB