*https://lwn.net/Articles/603116/

夷则

kexec 可以用于从第一个内核切换到第二个内核,它用了 kexec_load() 这个系统调用,把新内核加载到内存中,然后用 reboot() 系统调用快速重启;用户态也有一个 kexec 命令,可以加载新内核然后启动之。由于 kexec 跳过了固件加载和bootloader阶段,它可以用于快速启动,当然它最主要还是被用于 kdump 以生成 vmcore . 不过最近 mjg 发现个问题,kexec 可能绕过 UEFI 安全启动的限制,简单来说 UEFI 安全启动机制要求内核必须是经过有效签名的,而 kexec 启动第二个内核的时候可以直接跳过 UEFI 的检查,这样就导致了安全问题,而一些公司比如微软,觉察到这个风险之后可能会把用来签 Linux bootloader 的 key 给禁掉,从而给 Linux 发行版带来灾难(比如说就不能装 Linux 到一些微软合作的厂商的电脑上),所以他们不得不把 kexec 禁掉了。

幸好 kdump 的开发者 Vivek Goyal 最近提交了一系列 patch 可以让 kexec 只启动签过名的内核,这样应该就可以解决 mjg 的问题。不过 mjg 还是建议那些需要支持 Secure Boot 的发行版禁用 kexec,因为还有方法可以绕开 Secure Boot 的检查限制,比如说改一下 sysfs 中的 sig_enforce 参数,然后跳回原内核。不过不管怎么说, Vivek 一直在努力解决这个问题,以尽早消除这个安全风险。

Vivek 现在的 patch 里,引入了一个新的系统调用:

 long kexec_file_load(int kernel_fd, int initrd_fd,
 			 const char *cmdline_ptr, unsigned long cmdline_len,
                      unsigned long flags);

和原来的的 kexec_load 系统调用对比:

 long kexec_load(unsigned long entry, unsigned long nr_segments,
                 struct kexec_segment *segments, unsigned long flags);

新旧系统调用都会把内核分成不同的片段(segments),所不同的是,用了新的系统调用之后,只会先加载签过名的部分的片段,而不会像原来一样什么都不检查直接把所有片段都加载到内存中;然后,kexec-tools 里引入了一个新的工具,叫 “purgatory”,它在旧内核调用 reboot 之后,新内核启动之前检查剩下那些片段的hash,验证通过了才会启动第二个内核。不过如果不是在起第二个内核,而是正常内核启动的时候,也是需要 prgatory 的,这部分代码也被 Vivek 加到了内核中。

这部分代码目前还是 RFC 阶段,还有很多要完成,比如最重要的是怎么利用这个思想去验证签名。Vivek 也解释了一下他的思路,主要是基于 David Howell 验证内核模块签名的想法。大体上是在 kexec_load_file() 调用发生的时候进行签名验证,此时还会要计算每个片段的 SHA-256 hash,然后存到 purgatory 中。

社区对这批补丁的接受度还挺高的,估计过完年就能进主线了。