污点内核

当发生一些可能与以后调查问题相关的事情时,内核会将自己标记为“污点”。不要太担心这一点,大多数时候运行污点内核没有问题;一旦有人想要调查某些问题,这些信息主要是有意义的,因为它的真正原因可能是导致内核污点的事件。这就是为什么来自污点内核的错误报告通常会被开发人员忽略,因此请尝试用未污点的内核重现问题。

请注意,即使在您撤消导致污点的原因(即卸载专有内核模块)后,内核仍将保持污点状态,以表明内核仍然不可信。这也是为什么当内核注意到内部问题(“内核错误”)、可恢复错误(“内核 oops”)或不可恢复错误(“内核 panic”)时,会打印污点状态并将有关此问题的调试信息写入日志 dmesg 输出。也可以通过/proc/中的文件在运行时检查污点状态。

错误、oops或panic消息中的污点标志

您可以在以“CPU:”开头的一行的顶部附近找到污点状态;如果在进程ID (“PID:”) 和触发该事件的命令的缩写名称 (“Comm:”) 之后显示内核被污点的原因

BUG: unable to handle kernel NULL pointer dereference at 0000000000000000
Oops: 0002 [#1] SMP PTI
CPU: 0 PID: 4424 Comm: insmod Tainted: P        W  O      4.20.0-0.rc6.fc30 #1
Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011
RIP: 0010:my_oops_init+0x13/0x1000 [kpanic]
[...]

如果内核在事件发生时未被污点,您会在此处找到“Not tainted: ”;如果被污点,则将打印“Tainted: ”以及字母或空格。在上面的例子中,它看起来像这样

Tainted: P        W  O

这些字符的含义在下表中进行了解释。在这种情况下,内核早期被污染是因为加载了专有模块 (P),发生了警告 (W),并且加载了外部构建的模块 (O)。要解码其他字母,请使用下表。

在运行时解码污点状态

在运行时,您可以通过读取cat /proc/sys/kernel/tainted来查询污点状态。如果返回0,则内核未被污点;任何其他数字都表示被污点的原因。解码该数字的最简单方法是脚本tools/debugging/kernel-chktaint,您的发行版可能会将其作为名为linux-toolskernel-tools的软件包的一部分;如果没有,您可以从git.kernel.org下载该脚本,并使用sh kernel-chktaint执行它,这将在先前引用的日志中包含语句的机器上打印如下内容

Kernel is Tainted for following reasons:
 * Proprietary module was loaded (#0)
 * Kernel issued warning (#9)
 * Externally-built ('out-of-tree') module was loaded  (#12)
See Documentation/admin-guide/tainted-kernels.rst in the Linux kernel or
 https://linuxkernel.org.cn/doc/html/latest/admin-guide/tainted-kernels.html for
 a more details explanation of the various taint flags.
Raw taint value as int/string: 4609/'P        W  O     '

您可以尝试自己解码该数字。如果只有一种原因导致您的内核被污点,那么这很容易,在这种情况下,您可以在下表中找到该数字。如果有多个原因,您需要解码该数字,因为它是一个位域,其中每个位表示是否存在特定类型的污点。最好将此留给前面提到的脚本,但如果您需要快速的东西,可以使用此shell命令来检查设置了哪些位

$ for i in $(seq 18); do echo $(($i-1)) $(($(cat /proc/sys/kernel/tainted)>>($i-1)&1));done

解码污点状态的表

日志

编号

导致内核被污染的原因

0

G/P

1

已加载专有模块

1

_/F

2

强制加载模块

2

_/S

4

内核在不符合规范的系统上运行

3

_/R

8

强制卸载模块

4

_/M

16

处理器报告了机器检查异常 (MCE)

5

_/B

32

引用了错误的页面或一些意外的页面标志

6

_/U

64

用户空间应用程序请求污点

7

_/D

128

内核最近已死亡,即存在 OOPS 或 BUG

8

_/A

256

ACPI表被用户覆盖

9

_/W

512

内核发出警告

10

_/C

1024

已加载暂存驱动程序

11

_/I

2048

应用了针对平台固件中错误的解决方法

12

_/O

4096

已加载外部构建(“树外”)模块

13

_/E

8192

已加载未签名的模块

14

_/L

16384

发生软锁死

15

_/K

32768

内核已进行实时修补

16

_/X

65536

辅助污点,为发行版定义和使用

17

_/T

131072

内核使用结构随机化插件构建

18

_/N

262144

已运行内核测试

19

_/J

524288

用户空间在fwctl中使用了改变调试操作

注意:字符_在此表中表示空格,以方便阅读。

污点的更详细说明

  1. 如果加载的所有模块都具有GPL或兼容许可证,则为G;如果已加载任何专有模块,则为P。没有MODULE_LICENSE或MODULE_LICENSE未被insmod识别为GPL兼容的模块被假定为专有模块。

  2. 如果任何模块被insmod -f强制加载,则为F;如果所有模块都正常加载,则为' '

  3. 如果内核在不符合规范的处理器或系统上运行,则为S:硬件已置于不受支持的配置中,因此无法保证正确的执行。例如,如果内核被污染

    • 在x86上:在不报告PAE但可能具有功能实现的intel CPU(例如Pentium M)上,通过forcepae强制使用PAE,SMP内核在非官方支持SMP的Athlon CPU上运行,MSR正在从用户空间被探测。

    • 在arm上:内核在某些CPU(例如Keystone 2)上运行,而没有启用某些内核功能。

    • 在arm64上:CPU之间的硬件功能不匹配,引导加载程序以不同的模式启动CPU。

    • 某些驱动程序在不受支持的体系结构上使用(例如,x86_64以外的scsi/snic,非x86/x86_64/itanium上的scsi/ips,为arm64上的irqchip/irq-gic具有损坏的固件设置......)。

    • x86/x86_64:微代码延迟加载是危险的,会导致内核污染。它要求所有CPU汇合以确保在系统尽可能静止时进行更新。但是,更高优先级的MCE/SMI/NMI可以使控制流远离该汇合并中断更新,这对机器可能有害。

  4. 如果模块被rmmod -f强制卸载,则为R;如果所有模块都正常卸载,则为' '

  5. 如果任何处理器报告了机器检查异常,则为M;如果未发生机器检查异常,则为' '

  6. 如果页面释放函数找到错误的页面引用或一些意外的页面标志。这表示硬件问题或内核错误;日志中应该有其他信息指示发生此污点的原因。

  7. 如果用户或用户应用程序专门请求设置Tainted标志,则为U;否则为' '

  8. 如果内核最近已死亡,即存在OOPS或BUG,则为D

  9. 如果ACPI表已被覆盖,则为A

  10. 如果内核先前已发出警告,则为W。(尽管某些警告可能会设置更具体的污点标志。)

  11. 如果已加载暂存驱动程序,则为C

  12. 如果内核正在解决平台固件(BIOS或类似固件)中的严重错误,则为I

  13. 如果已加载外部构建(“树外”)模块,则为O

  14. 如果在支持模块签名的内核中加载了未签名的模块,则为E

  15. 如果系统先前发生了软锁死,则为L

  16. 如果内核已进行实时修补,则为K

  17. X 辅助污点,为Linux发行商定义和使用。

  18. T 内核使用 randstruct 插件构建,该插件可能会有意产生极其不寻常的内核结构布局(甚至是性能病态的布局),这在调试时非常重要。 在构建时设置。

  19. N 如果已运行内核测试,例如 KUnit 测试。

  20. J 如果用户空间打开了 /dev/fwctl/* 并执行了 FWTCL_RPC_DEBUG_WRITE 来使用该设备的调试功能。 设备调试功能可能会导致设备以未定义的方式发生故障。