LWN:不再export kallsyms_lookup_name()!

本文探讨了Linux内核开发中的一条基本原则:模块只能访问明确导出的符号。然而,存在一种规避策略,允许模块访问任何符号。此漏洞将通过移除kallsyms_lookup_name()的导出声明来修复,此举可能影响某些合理用途,如实时补丁系统。但鉴于其滥用可能性,社区广泛支持这一变动。

关注了就能看到更多这么棒的文章哦~

Unexporting kallsyms_lookup_name()

By Jonathan Corbet
February 28, 2020

原文来自:https://lwn.net/Articles/813350/

kernel module开发中有一条基本原则,module只能访问那些明确export出来的符号(symbols,指函数和数据结构)。甚至,有许多symbol明确指明了只有哪些兼容GPL license的module才可以访问。不过,现在看来其实有一些workaround方式,可以让module来访问任意的symbol。这个workaround方式很快会被堵上,尽管这会导致一些使用者碰到问题。这个事情的来龙去脉值得给大家详细讲讲。

这个后门就是kallsyms_lookup_name(),它会利用kernel的symbol table来查找,返回指定symbol的地址。module的代码直接访问symbol的话都会被拒绝,但是可以利用kallsyms_lookup_name()来获取地址,然后直接使用这个地址。这个函数本身被export为GPL-only的,理论上来说只有free software(自由软件)才能使用它。不过如果某个proprietary(专有的、私有的) module先通过虚假声明一个自由软件的license,那么就能访问到这些GPL-only的symbol。这种事情发生了不止一次。

Will Deacon提交了一组patch set,移除了对kallsyms_lookup_name()和kallsyms_on_each_symbol()的export声明。马上就有许多人对此表示赞同。毕竟很少有kernel开发者愿意看到module作者能有办法绕过export的限制。不过,还是有些人表达了一些担心。

其中一个问题在于,有一些out-of-tree用户在利用kallsyms_lookup_name(),这种用法其实开发者们都能理解、也认为是合理的,这就是用作kernel的live-patching系统。很讽刺的是,kernel bug通常都有export出来的函数,那么可以使用live patch来确定kernel里某个函数的位置,从而对它进行修补。kallsyms_lookup_name()可以很轻松地实现这个功能。在讨论中,Joe Lawrence提醒大家要多用用kpatch系统,它完全可以替代kallsyms_lookup_name()在live patch中的作用,并且在mainline kernel中已经完全可用了。不过,看起来Ksplice系统还在使用kallsyms_lookup_name。不过Miroslave Benes说了句大实话:“没人关心现在kernel里的ksplice功能”。这样看来,live patch功能应该不会阻止这组patch set的合入了。

Masami Hiramatsu提出了另一个问题,在kernel里面其实还有其他一些方法可以拿到kernel symbol对应的地址。User space可以使用kprobe来提取这些信息,kernel module也可以利用snprintf()的“%pF”格式(会打印出某个函数的对应地址)来遍历获取symbol地址(当然这个比较费时间和CPU资源)。他有点担心做了这个改动之后,普通开发者会碰到一些麻烦,而真正想要滥用module机制的人却不会担心这点麻烦。

Deacon回复的时候,解释了一下他做这个改动的来龙去脉。虽然Kernel开发者们都很喜欢做这些会让滥用Linux系统的人碰到更多麻烦的事情,不过这次来说,他的动力其实是为了帮Google解决一个问题。

在2018年的时候,LWN就报道过,为了能让Android kernel跟mainline尽量接近,有许多工作在进行中。其中之一就是把kernel放到Android generic system image (GSI)里,这是一个通用的Android image版本,可以在任何满足了Android的要求的设备上直接启动并正常运行。把kernel作为GSI的一部分,就意味着硬件厂商不能再修改kernel,所以就只能通过在GSI增加kernel module的方式来实现他们所要做的功能。

通过限制供应商只能使用kernel module,就能限制住他们所做的大多数改动。比如,不会再有Android设备能把CPU scheduler换成vendor自己的特制版本。不过,这就要求module必须只能使用这些export出来的symbol。如果module开始利用kernel里任意的代码,那么这个保护就完全没有意义了。Deacon虽然没有明说,不过看起来很明显有些厂商就在考虑这么做(译者注:哈哈,太鸡贼了吧大家~就不能跟Google好好玩吗?)。所以,要对移除这个接口的export做个冠冕堂皇的解释的话,就是:“如果kallsyms_lookup_name()包含了所有的数据和函数,那么就无法有效地监控和管理ABI接口”。

Hiramatsu接受了这个解释,回复了自己的Reviewed-by tag。所以,他原本担心的问题,也不会组织这组patch set被合入了。

其实,再说一遍,仅仅“给那些绕过export机制的module开发者找些麻烦”这个理由,就足够推动大家合入这个patch set了。不过很有意思的是看到一个大公司也支持这种做法。Google在把Android kernel跟mainline绑定得更加紧密之后,它和kernel社区的长期目标(至少在文中讲到的这一点上)也更加吻合了。希望这种吻合能够在我们那些使用了更像mainline kernel的设备上,为我们带来更好的kernel!

全文完

LWN文章遵循CC BY-SA 4.0许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注LWN深度文章以及开源社区的各种新近言论~

<think> 嗯,用户这次的问题非常具体,是关于kernel_device_modules-6.6构建过程中mgk_64_k66_customer_modules_install.user失败的解决方案。从问题描述看,用户应该是嵌入式Linux开发工程师,正在定制内核模块,遇到构建中断的问题。 上次对话中用户提到了“kernel_device_modules-6.6”这个关键词,说明是在处理内核设备模块的编译问题。这次报错文件扩展名.user比较特殊,像是某种自定义构建脚本。用户能准确提供文件名,说明已经有一定技术基础,可能排查过基础路径问题。 需要特别注意几个关键点:一是“kernel_device_modules-6.6”这个项目名称看起来像商业嵌入式方案,可能是客户定制内核;二是“mgk_64_k66”这个前缀,mgk可能是模块组缩写,64指64位架构,k66疑似指NXP的Kinetis K66微控制器;三是构建脚本后缀.user比较罕见,可能是企业内部自定义的构建系统。 构建失败通常有几类原因:环境配置问题(如交叉编译器缺失)、脚本逻辑错误(路径处理不当)、依赖缺失(头文件或库文件)、权限问题等。考虑到.user脚本的特殊性,应该优先检查:1)执行权限;2)脚本内部路径是否正确指向内核源码目录;3)交叉编译工具链是否配置。 用户没有提供具体错误日志是最棘手的。需要引导用户提供更多信息,比如构建时的终端输出、脚本开头部分的路径配置等。另外要注意这个模块可能是依赖特定内核配置选项的,需要确认.config文件中相关驱动是否启用。 最后要提醒用户注意风险——这类定制内核模块的构建失败往往涉及多方因素,企业内部的构建系统可能还有非公开依赖。建议用户先联系原厂支持渠道,同时提供尽可能详细的错误信息方便进一步排查。 </think> 针对 `kernel_device_modules-6.6` 中 `mgk_64_k66_customer_modules_install.user` 构建失败的解决方案,请按以下步骤排查: --- ### **排查步骤** #### 1. **检查构建环境** - **内核源码完整性** 确认 `kernel_device_modules-6.6` 源码完整且与当前运行内核版本一致: ```bash uname -r # 查看当前内核版本 ls /lib/modules/$(uname -r)/build # 验证内核头文件存在 ``` 若路径缺失,需安装对应内核头文件包[^1]。 - **交叉编译工具链** 若为嵌入式设备(如 `k66` 可能指 NXP Kinetis K66),检查交叉编译器是否配置: ```bash echo $CC # 查看环境变量 arm-none-eabi-gcc --version # 示例(具体工具链名称需确认) ``` #### 2. **分析构建日志** - 查找错误关键字(如 `Error`/`Failed`): ```bash grep -i "error\|failed" build.log # 替换为实际日志文件名 ``` **常见问题方向**: - **依赖缺失**:如 `undefined reference` 提示缺少库或头文件 - **语法错误**:内核 API 变更导致函数签名不兼容 - **权限问题**:`Permission denied` 提示需 `sudo` 或修复路径权限 #### 3. **验证脚本权限与路径** - 添加可执行权限: ```bash chmod +x mgk_64_k66_customer_modules_install.user ``` - 检查脚本内路径是否为**绝对路径**(避免相对路径嵌套错误): ```bash head -n 20 mgk_64_k66_customer_modules_install.user # 查看脚本头部配置 ``` #### 4. **依赖项检查** - 安装基础编译依赖: ```bash sudo apt-get install build-essential libssl-dev flex bison libelf-dev ``` - 确认模块特殊依赖(如厂商 SDK、闭源驱动)是否部署。 #### 5. **兼容性调整** - **内核符号导出问题**: 若日志含 `Unknown symbol`,需在模块代码中添加导出声明: ```c EXPORT_SYMBOL(your_function); // 内核源码中声明 ``` - **版本适配**: 若内核 ≥5.7,需替换弃用的 `get_user_pages()` 为 `get_user_pages_remote()`[^2]。 #### 6. **增量构建测试** - 分步编译定位故障点: ```bash make -C /lib/modules/$(uname -r)/build M=$(pwd) modules # 仅编译模块 sudo insmod your_module.ko # 手动加载测试 ``` --- ### **典型错误场景与修复** | **错误类型** | **解决方案** | |------------------------|------------------------------------------| | **`Makefile: No such file`** | 确认脚本中 `KERNEL_SRC` 路径正确指向内核源码目录 | | **`Invalid module format`** | 强制编译目标版本:`make KERNELRELEASE=$(uname -r)` | | **`undefined reference`** | 检查模块依赖链,添加 `obj-m += dependent_module.o` | | **`Function not implemented`** | 替换新版内核 API(如 `vmalloc_user()` 替代旧接口) | --- ### **补充建议** 1. **联系厂商支持**: `mgk_64_k66` 可能是定制模块,需获取厂商提供的补丁或更新脚本。 2. **版本回退测试**: 尝试旧内核版本(如 5.4 LTS)验证是否兼容性问题。 3. **调试符号分析**: 启用 `CONFIG_DEBUG_INFO=y` 后使用 `dmesg` 查看详细崩溃堆栈。 > 请优先提供 **完整构建日志** 以精准定位问题。若上述步骤未解决,建议提交日志到社区论坛(如 [Stack Overflow](https://stackoverflow.com))或内核邮件列表[^3]。 --- [^1]: 内核头文件安装参考:[Ubuntu Kernel Headers](https://wiki.ubuntu.com/Kernel/BuildYourOwnKernel) [^2]: 内核 API 变更说明:[Linux 5.7 API Changes](https://lwn.net/Articles/817101/) [^3]: 内核社区支持:[LKML (Linux Kernel Mailing List)](https://lkml.org/)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值