前言

(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 :表示如果已加载外部构建outof-tree)模块。

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

二、Decoding tainted state at runtime

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

#! /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." >&amp;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)&amp;1));
done

在这里插入图片描述
在这里插入图片描述
注:为方便阅读,本表中字符_代表空白
上面显示
bit9=1,kernel issued warning
bit12=1,externallybuilt (“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仍然存在:即使撤消导致污染的原因(即卸载专有内核模块),内核仍将保持污染,这表明内核仍然不可信。

三、源码分析

struct module结构中的成员taints表示内核模块是否会污染内核,如果模块会污染内核,便调用add_taint_module函数 设置 struct module的taints成员

// linux-5.13/include/linux/module.h

struct module {
	......
	unsigned long taints;	/* same bits as kernel:taint_flags */
	......
}
// linux-5.13/kernel/module.c

static inline void add_taint_module(struct module *mod, unsigned flag,
				    enum lockdep_ok lockdep_ok)
{
	add_taint(flag, lockdep_ok);
	set_bit(flag, &amp;mod->taints);
}
// linux-5.13/kernel/panic.c

/**
 * 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 &amp;&amp; __debug_locks_off())
		pr_warn("Disabling lock debugging due to kernel taintn");

	set_bit(flag, &amp;tainted_mask);

	if (tainted_mask &amp; panic_on_taint) {
		panic_on_taint = 0;
		panic("panic_on_taint set ...");
	}
}
EXPORT_SYMBOL(add_taint);
// 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;
}

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

总结

以上就是Tainted kernels相关知识

参考资料

Linux 5.13.0

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

原文地址:https://blog.csdn.net/weixin_45030965/article/details/126261043

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_47048.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注