Tainted kernels


前言

(1)
在加载自己编写得驱动模块是,加载时看到:

module verification failed: signature and/or required key missing - tainting kernel

signature 或者 required key missing是导致内核受到污染的事件,不过这对于模块的运行没啥影响。因此我们一般都忽略掉这个问题。

(2)系统宕机时看到:

dmesg --follow
	-w, --follow
  	 Wait for new messages. This feature is supported on systems with readable /dev/kmsg only (since kernel 3.5.0).

CPU: 0 PID: 31024 Comm: ... Kdump: loaded Tainted: G        W  OE  ----V-------   3.10.0-&version.x86_64 #1

于是便查阅 tainting kernel 相关资料。

一、简介

大多数情况下,运行受污染的内核没有什么问题,来自受污染内核的错误报告通常会被开发人员忽略。

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

在以“CPU:”开头的行中的顶部附近找到受污染的状态; 内核是否或为什么被污染显示在进程 ID (‘PID:’) 和触发事件的command的缩写名称 (‘Comm:’) 之后:

CPU: 0 PID: 31024 Comm: ... Kdump: loaded Tainted: G        W  OE  ----V-------   3.10.0-&version.x86_64 #1

如果内核在事件发生时没有被污染,将会显示 Not tainted: 如果污染了,那么将打印“Tainted:和字符,无论是字母还是空白。 在上面的示例中:

Tainted: G        W  OE

G :表示所有加载的模块都有一个GPL或兼容的许可,P表示加载了任何专有模块。
没有MODULE_LICENSE或具有MODULE_LICENSE但不被insmod认可为兼容GPL的模块被认为是专有的。

O :表示如果已加载外部构建(out-of-tree)模块。

E :表示内核支持模块签名,加载了未签名的模块。

二、Decoding tainted state at runtime

在运行时,您可以通过读取 cat /proc/sys/kernel/tainted 来查询污染状态。
在这里插入图片描述
如果返回 0,则内核没有被污染; 任何其他数字都表明了它的原因。 解码该数字的最简单方法是脚本 tools/debugging/kernel-chktaint。

#! /bin/sh
# SPDX-License-Identifier: GPL-2.0
#
# Randy Dunlap <rdunlap@infradead.org>, 2018
# Thorsten Leemhuis <linux@leemhuis.info>, 2018

usage()
{
    
    
	cat <<EOF
usage: ${
    
    0##*/}
       ${
    
    0##*/} <int>

Call without parameters to decode /proc/sys/kernel/tainted.

Call with a positive integer as parameter to decode a value you
retrieved from /proc/sys/kernel/tainted on another system.

EOF
}

if [ "$1"x != "x" ]; then
	if  [ "$1"x == "--helpx" ] || [ "$1"x == "-hx" ] ; then
		usage
		exit 1
	elif  [ $1 -ge 0 ] 2>/dev/null ; then
		taint=$1
	else
		echo "Error: Parameter '$1' not a positive integer. Aborting." >&2
		exit 1
	fi
else
	TAINTFILE="/proc/sys/kernel/tainted"
	if [ ! -r $TAINTFILE ]; then
		echo "No file: $TAINTFILE"
		exit
	fi

	taint=`cat $TAINTFILE`
fi

if [ $taint -eq 0 ]; then
	echo "Kernel not Tainted"
	exit
else
	echo "Kernel is \"tainted\" for the following reasons:"
fi

T=$taint
out=

addout() {
    
    
	out=$out$1
}

if [ `expr $T % 2` -eq 0 ]; then
	addout "G"
else
	addout "P"
	echo " * proprietary module was loaded (#0)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "F"
	echo " * module was force loaded (#1)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "S"
	echo " * kernel running on an out of specification system (#2)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "R"
	echo " * module was force unloaded (#3)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "M"
	echo " * processor reported a Machine Check Exception (MCE) (#4)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "B"
	echo " * bad page referenced or some unexpected page flags (#5)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "U"
	echo " * taint requested by userspace application (#6)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "D"
	echo " * kernel died recently, i.e. there was an OOPS or BUG (#7)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "A"
	echo " * an ACPI table was overridden by user (#8)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "W"
	echo " * kernel issued warning (#9)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "C"
	echo " * staging driver was loaded (#10)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "I"
	echo " * workaround for bug in platform firmware applied (#11)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "O"
	echo " * externally-built ('out-of-tree') module was loaded  (#12)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "E"
	echo " * unsigned module was loaded (#13)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "L"
	echo " * soft lockup occurred (#14)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "K"
	echo " * kernel has been live patched (#15)"
fi

T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "X"
	echo " * auxiliary taint, defined for and used by distros (#16)"

fi
T=`expr $T / 2`
if [ `expr $T % 2` -eq 0 ]; then
	addout " "
else
	addout "T"
	echo " * kernel was built with the struct randomization plugin (#17)"
fi

echo "Raw taint value as int/string: $taint/'$out'"
#EOF#

可以通过上述数字解码该号码。 如果只有一个原因导致内核受到污染,这很容易,因为在这种情况下,可以在下表中找到数字。 如果有多种原因,您需要解码该数字,因为它是一个位域,其中每个位表示特定类型的污点的缺失或存在。 最好用之前提到的脚本进行解码 tools/debugging/kernel-chktaint。

但也可以使用下面 shell 命令来检查哪些位被设置:

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

在这里插入图片描述
在这里插入图片描述
注:为方便阅读,本表中字符_代表空白。
上面显示:
bit9=1,kernel issued warning。
bit12=1,externally-built (“out-of-tree”) module was loaded。

执行 tools/debugging/kernel-chktaint脚本:
在这里插入图片描述

加载一个自定义模块后:
在这里插入图片描述上面显示
bit9=1,kernel issued warning。
bit12=1,externally-built (“out-of-tree”) module was loaded。
bit13=1,unsigned module was loaded。

执行 tools/debugging/kernel-chktaint脚本:
在这里插入图片描述
卸载掉加载的模块后:
在这里插入图片描述
污染event仍然存在:即使撤消导致污染的原因(即卸载专有内核模块),内核仍将保持污染,这表明内核仍然不可信。

三、源码分析

// include/linux/kernel.h

/* This cannot be an enum because some may be used in assembly source. */
#define TAINT_PROPRIETARY_MODULE	0
#define TAINT_FORCED_MODULE		1
#define TAINT_CPU_OUT_OF_SPEC		2
#define TAINT_FORCED_RMMOD		3
#define TAINT_MACHINE_CHECK		4
#define TAINT_BAD_PAGE			5
#define TAINT_USER			6
#define TAINT_DIE			7
#define TAINT_OVERRIDDEN_ACPI_TABLE	8
#define TAINT_WARN			9
#define TAINT_CRAP			10
#define TAINT_FIRMWARE_WORKAROUND	11
#define TAINT_OOT_MODULE		12
#define TAINT_UNSIGNED_MODULE		13
#define TAINT_SOFTLOCKUP		14
#define TAINT_LIVEPATCH			15
#define TAINT_AUX			16
#define TAINT_RANDSTRUCT		17
#define TAINT_FLAGS_COUNT		18
#define TAINT_FLAGS_MAX			((1UL << TAINT_FLAGS_COUNT) - 1)

struct taint_flag {
    
    
	char c_true;	/* character printed when tainted */
	char c_false;	/* character printed when not tainted */
	bool module;	/* also show as a per-module taint flag */

extern const struct taint_flag taint_flags[TAINT_FLAGS_COUNT];
// /kernel/panic.c

static unsigned long tainted_mask =
	IS_ENABLED(CONFIG_GCC_PLUGIN_RANDSTRUCT) ? (1 << TAINT_RANDSTRUCT) : 0;

/*
 * TAINT_FORCED_RMMOD could be a per-module flag but the module
 * is being removed anyway.
 */
const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = {
    
    
	[ TAINT_PROPRIETARY_MODULE ]	= {
    
     'P', 'G', true },
	[ TAINT_FORCED_MODULE ]		= {
    
     'F', ' ', true },
	[ TAINT_CPU_OUT_OF_SPEC ]	= {
    
     'S', ' ', false },
	[ TAINT_FORCED_RMMOD ]		= {
    
     'R', ' ', false },
	[ TAINT_MACHINE_CHECK ]		= {
    
     'M', ' ', false },
	[ TAINT_BAD_PAGE ]		= {
    
     'B', ' ', false },
	[ TAINT_USER ]			= {
    
     'U', ' ', false },
	[ TAINT_DIE ]			= {
    
     'D', ' ', false },
	[ TAINT_OVERRIDDEN_ACPI_TABLE ]	= {
    
     'A', ' ', false },
	[ TAINT_WARN ]			= {
    
     'W', ' ', false },
	[ TAINT_CRAP ]			= {
    
     'C', ' ', true },
	[ TAINT_FIRMWARE_WORKAROUND ]	= {
    
     'I', ' ', false },
	[ TAINT_OOT_MODULE ]		= {
    
     'O', ' ', true },
	[ TAINT_UNSIGNED_MODULE ]	= {
    
     'E', ' ', true },
	[ TAINT_SOFTLOCKUP ]		= {
    
     'L', ' ', false },
	[ TAINT_LIVEPATCH ]		= {
    
     'K', ' ', true },
	[ TAINT_AUX ]			= {
    
     'X', ' ', true },
	[ TAINT_RANDSTRUCT ]		= {
    
     'T', ' ', true },
};

/**
 * print_tainted - return a string to represent the kernel taint state.
 *
 * For individual taint flag meanings, see Documentation/admin-guide/sysctl/kernel.rst
 *
 * The string is overwritten by the next call to print_tainted(),
 * but is always NULL terminated.
 */
const char *print_tainted(void)
{
    
    
	static char buf[TAINT_FLAGS_COUNT + sizeof("Tainted: ")];

	BUILD_BUG_ON(ARRAY_SIZE(taint_flags) != TAINT_FLAGS_COUNT);

	if (tainted_mask) {
    
    
		char *s;
		int i;

		s = buf + sprintf(buf, "Tainted: ");
		for (i = 0; i < TAINT_FLAGS_COUNT; i++) {
    
    
			const struct taint_flag *t = &taint_flags[i];
			*s++ = test_bit(i, &tainted_mask) ?
					t->c_true : t->c_false;
		}
		*s = 0;
	} else
		snprintf(buf, sizeof(buf), "Not tainted");

	return buf;
}

int test_taint(unsigned flag)
{
    
    
	return test_bit(flag, &tainted_mask);
}
EXPORT_SYMBOL(test_taint);

unsigned long get_taint(void)
{
    
    
	return tainted_mask;
}

/**
 * add_taint: add a taint flag if not already set.
 * @flag: one of the TAINT_* constants.
 * @lockdep_ok: whether lock debugging is still OK.
 *
 * If something bad has gone wrong, you'll want @lockdebug_ok = false, but for
 * some notewortht-but-not-corrupting cases, it can be set to true.
 */
void add_taint(unsigned flag, enum lockdep_ok lockdep_ok)
{
    
    
	if (lockdep_ok == LOCKDEP_NOW_UNRELIABLE && __debug_locks_off())
		pr_warn("Disabling lock debugging due to kernel taint\n");

	set_bit(flag, &tainted_mask);

	if (tainted_mask & panic_on_taint) {
    
    
		panic_on_taint = 0;
		panic("panic_on_taint set ...");
	}
}
EXPORT_SYMBOL(add_taint);

看了下源码,感觉挺清晰的,就不分析了。

总结

以上就是Tainted kernels的相关知识。

参考资料

Linux 5.13.0

https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html

猜你喜欢

转载自blog.csdn.net/weixin_45030965/article/details/126261043