宋宝华:ARM64 Linux meltdown修复补丁KPTI的最重要3个patch

看完这篇文章,可以知道AARCH64平台修复meltdown漏洞的KPTI补丁的基本原理。此文很难很分裂,需要具备大量背景知识,慎重阅读。

看不懂也没有关系,记住最后三张页表的结论即可。

一个patch是 "arm64: Kconfig: Add CONFIG_UNMAP_KERNEL_AT_EL0",此patch前后2次修正,地址如下:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=084eb77cd3a81134d02500977dc0ecc9277dc97d

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0617052ddde355ee663b2f048e67dd381e5ebd6a

它其实是,使能了一个从EL1的kernel返回EL0的时候,把kernel unmap掉的功能,这样让user不可见内核:

+config UNMAP_KERNEL_AT_EL0

+bool "Unmap kernel when running in userspace (aka \"KAISER\")"

+default y

+help

+ Some attacks against KASLR make use of the timing difference between

+ a permission fault which could arise from a page table entry that is

+ present in the TLB, and a translation fault which always requires a

+ page table walk. This option defends against these attacks by unmapping

+ the kernel whilst running in userspace, therefore forcing translation

+ faults for all of kernel space.

+

+ If unsure, say Y.

+

第二个patch是"arm64: mm: Map entry trampoline into trampoline and kernel page tables",地址:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=51a0048beb449682d632d0af52a515adb9f9882e

此处建立了一个只包含EL0陷入EL1和退出的必要的代码的页被映射的页表(trampoline page table),

+#ifdef CONFIG_UNMAP_KERNEL_AT_EL0

+static int __init map_entry_trampoline(void)

+{

+extern char __entry_tramp_text_start[];

+

+pgprot_t prot = rodata_enabled ? PAGE_KERNEL_ROX : PAGE_KERNEL_EXEC;

+phys_addr_t pa_start = __pa_symbol(__entry_tramp_text_start);

+

+/* The trampoline is always mapped and can therefore be global */

+pgprot_val(prot) &= ~PTE_NG;

+

+/* Map only the text into the trampoline page table */

+memset(tramp_pg_dir, 0, PGD_SIZE);

+__create_pgd_mapping(tramp_pg_dir, pa_start, TRAMP_VALIAS, PAGE_SIZE,

+ prot, pgd_pgtable_alloc, 0);

+

+/* ...as well as the kernel page table */

+__set_fixmap(FIX_ENTRY_TRAMP_TEXT, pa_start, prot);

+return 0;

+}

+core_initcall(map_entry_trampoline);

+#endif

第三个patch是“arm64: entry: Add exception trampoline page for exceptions from EL0”,https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c7b9adaf85f818d747eeff5145eb4095ccd587fb

这个patch里面提供了内核完整页表与trampoline页表切换的接口:

+.macro tramp_map_kernel, tmp

+mrs\tmp, ttbr1_el1

+sub\tmp, \tmp, #(SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE)

+bic\tmp, \tmp, #USER_ASID_FLAG

+msrttbr1_el1, \tmp

+.endm

+

+.macro tramp_unmap_kernel, tmp

+mrs\tmp, ttbr1_el1

+add\tmp, \tmp, #(SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE)

+orr\tmp, \tmp, #USER_ASID_FLAG

+msrttbr1_el1, \tmp

tramp_map_kernel可以让EL0陷入EL1的时候,载入FULL的kernel page table,而 tramp_unmap_kernel可以帮助kernel从EL1退回到EL0的时候,TTBR1里面切换到一个只包含最基本EL0到EL1入口和出口代码的向量表等信息映射的页表(trampoline page table),这样kernel不至于暴露给user。

由此,我们发现AARCH64对meltdown的修复看起来比X86还要复杂,X86好歹只有2个页表,见维基百科:https://en.wikipedia.org/wiki/Kernel_page-table_isolation :

但是ARM64 Linux的需要3个页表:

kernel完整的页表 TTBR1

kernel的只包含trampoline的trampoline页表(这部分完全是为了让EL0陷入和退出EL1用的,并且帮助完成trampoline页表和kernel完整页表的切换)

user的页表 TTBR0

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180323B1XGJV00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券