Kernel之模块版本检查

背景

通过insmodmodprobe加载核外驱动ko时,特别是第三方驱动,经常会遇到模块版本检查报错,类似:

version magic '5.4.96-xx SMP preempt mod_unload aarch64' should be '5.4.96-yy SMP preempt mod_unload aarch64'

那我们就来看看模块版本检查的大致过程

Kernel: v5.4.96
vermagic: version magic

过程分析

内核相关源码:kernel/module.c
逻辑过程比较简单
check_modinfo() -> same_magic()
check_modinfo() 函数首先获取模块相关的modinfo,即modmagic,然后会和内核的vermagic做对比,即same_magic()函数

可以通过cat /proc/version 来查看linux 系统的 vermagic ,对于module, 可以通过modinfo xxx 的方式来查看module的 vermagic

check_modinfo()

check_modinfo()函数:

static int check_modinfo(struct module *mod, struct load_info *info, int flags)
{
	const char *modmagic = get_modinfo(info, "vermagic");
	int err;

	if (flags & MODULE_INIT_IGNORE_VERMAGIC)
		modmagic = NULL;

	/* This is allowed: modprobe --force will invalidate it. */
	if (!modmagic) {
		err = try_to_force_load(mod, "bad vermagic");
		if (err)
			return err;
	} else if (!same_magic(modmagic, vermagic, info->index.vers)) {
		pr_err("%s: version magic '%s' should be '%s'\n",
		       info->name, modmagic, vermagic);
		return -ENOEXEC;
	}

	if (!get_modinfo(info, "intree")) {
		if (!test_taint(TAINT_OOT_MODULE))
			pr_warn("%s: loading out-of-tree module taints kernel.\n",
				mod->name);
		add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK);
	}

	check_modinfo_retpoline(mod, info);

	if (get_modinfo(info, "staging")) {
		add_taint_module(mod, TAINT_CRAP, LOCKDEP_STILL_OK);
		pr_warn("%s: module is from the staging directory, the quality "
			"is unknown, you have been warned.\n", mod->name);
	}

	err = check_modinfo_livepatch(mod, info);
	if (err)
		return err;

	/* Set up license info based on the info section */
	set_license(mod, get_modinfo(info, "license"));

	return 0;
}

same_magic()

same_magic()函数

#ifdef CONFIG_MODVERSIONS
/* First part is kernel version, which we ignore if module has crcs. */
static inline int same_magic(const char *amagic, const char *bmagic,
			     bool has_crcs)
{
	if (has_crcs) {
		amagic += strcspn(amagic, " ");
		bmagic += strcspn(bmagic, " ");
	}
	return strcmp(amagic, bmagic) == 0;
}
#else
static inline int same_magic(const char *amagic, const char *bmagic,
			     bool has_crcs)
{
	return strcmp(amagic, bmagic) == 0;
}
#endif /* CONFIG_MODVERSIONS */

对比magic时,就要区分是否打开了CONFIG_MODVERSIONS

所以,在没有打开CONFIG_MODVERSIONS时,如果模块的modmagic和内核的vermagic不匹配,就会报最开始的那个错误,导致加载模块失败,这也保证了驱动模块与内核匹配,避免随意模块都可以加载使用,从而导致内核出现各种问题,甚至是崩溃

忽略版本检查

使用modprobe命令加载模块时, 提供了两个选项, 用于在加载模块时忽略vermagic或者CRC检查, 强制加载一个模块

–force-vermagic : 忽略vermagic检查
–force-modversion : 忽略CRC检查
-f : 相当于同时使用 –force-vermagic 和 –force-modversion

如果存在兼容性问题的情况下使用, 会导致内核异常,所以使用需谨慎!

参考

  • 内核源码及文档