mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/
synced 2026-10-14 05:55:17 +00:00
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:
|
History
|
|
|---|---|---|
| .. | ||
| 40x | ||
| 44x | ||
| 52xx | ||
| 83xx | ||
| 85xx | ||
| 85xx-32bit.config | ||
| 85xx-64bit.config | ||
| 85xx-hw.config | ||
| 85xx-smp.config | ||
| 86xx-hw.config | ||
| 86xx-smp.config | ||
| adder875_defconfig | ||
| altivec.config | ||
| amigaone_defconfig | ||
| be.config | ||
| book3s_32.config | ||
| cell_defconfig | ||
| chrp32_defconfig | ||
| corenet_basic_defconfig | ||
| dpaa.config | ||
| ep88xc_defconfig | ||
| ep8248e_defconfig | ||
| fsl-emb-nonhw.config | ||
| g5_defconfig | ||
| gamecube_defconfig | ||
| guest.config | ||
| holly_defconfig | ||
| le.config | ||
| linkstation_defconfig | ||
| maple_defconfig | ||
| mgcoge_defconfig | ||
| mpc83xx_defconfig | ||
| mpc85xx_basic_defconfig | ||
| mpc86xx_basic_defconfig | ||
| mpc512x_defconfig | ||
| mpc866_ads_defconfig | ||
| mpc885_ads_defconfig | ||
| mpc5200_defconfig | ||
| mpc7448_hpc2_defconfig | ||
| mpc8272_ads_defconfig | ||
| mvme5100_defconfig | ||
| pasemi_defconfig | ||
| pmac32_defconfig | ||
| powernv_defconfig | ||
| ppc6xx_defconfig | ||
| ppc40x_defconfig | ||
| ppc44x_defconfig | ||
| ppc64_defconfig | ||
| ppc64e_defconfig | ||
| pq2fads_defconfig | ||
| ps3_defconfig | ||
| pseries_defconfig | ||
| skiroot_defconfig | ||
| storcenter_defconfig | ||
| tqm8xx_defconfig | ||
| wii_defconfig | ||