Kernel之模块版本检查
Kernel之模块版本检查
背景
通过insmod
或modprobe
加载核外驱动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
,模块就会带有CRC值,对比时会忽略version magic的第一部分:内核版本(kernel version),但会去校验模块每个符号的CRC,MODVERSIONS
具体可参见另一篇博文:https://notes.z-dd.online/2022/03/27/Kernel%E4%B9%8BMODVERSION/ - 未打开
CONFIG_MODVERSIONS
,对比整个version magic字符串,vermagic
具体可参见另一篇博文:https://notes.z-dd.online/2022/03/27/Kernel%E4%B9%8Bvermagic/
所以,在没有打开CONFIG_MODVERSIONS
时,如果模块的modmagic
和内核的vermagic
不匹配,就会报最开始的那个错误,导致加载模块失败,这也保证了驱动模块与内核匹配,避免随意模块都可以加载使用,从而导致内核出现各种问题,甚至是崩溃
忽略版本检查
使用modprobe
命令加载模块时, 提供了两个选项, 用于在加载模块时忽略vermagic或者CRC检查, 强制加载一个模块
–force-vermagic : 忽略vermagic检查
–force-modversion : 忽略CRC检查
-f : 相当于同时使用 –force-vermagic 和 –force-modversion
如果存在兼容性问题的情况下使用, 会导致内核异常,所以使用需谨慎!
参考
- 内核源码及文档
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 DD'Notes!
评论