LWN: 5.14 中的 memfd_secret()!

memfd_secret() 是Linux内核5.14版本引入的新系统调用,用于创建安全的内存区域,防止包括内核在内的其他进程访问。该内存区域可用于存储敏感数据如加密密钥,且在execve()调用后自动关闭。虽然它不支持系统调用参数传递和DMA操作,且可能导致系统无法休眠,但提供了额外的安全保障。经过多次迭代,最终版本默认禁用,需通过启动参数启用,以避免潜在的性能影响。

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

memfd_secret() in 5.14

By Jonathan Corbet
August 6, 2021
DeepL assisted translation
https://lwn.net/Articles/865256/

2020 年 2 月之后,memfd_secret() 系统调用其实已经在 LWN 以其他形式报道过。一开始,它是 memfd_create()的一个 flag,但这部分功能后来被移到单独的系统调用中了。在这个功能发展的过程中有许多变化,但核心目的仍然是保持未变的,即允许用户空间的进程来创建一个内存区域,确保其他人(包括 kernel 本身)都无法访问。该内存区域可以用来存放加密密钥,或其他类似的不能暴露给他人的数据。这个新的系统调用最终被合并到了即将发布的 5.14 版本中。下面我们来看看这个系统调用最终在 mainline kernel 中是什么样子的。

memfd_secret()的原型如下:

int memfd_secret(unsigned int flags);

其中唯一可用的 flag 取值是 O_CLOEXEC,用来设置当此进程后续调用 execve()时,这部分区域会被移除掉。不过,在 fork 调用之后这部分应该保密的区域对子进程仍是可以访问的。memfd_secret() 的返回值是一个文件描述符,用来跟这个新创建的秘密内存区域关联起来。

在这个时刻,进程实际上不能访问这部分内存区域,因为目前甚至连 size 都还不知道。后续必须调用 ftruncate() 来设置该区域的 size,然后使用 mmap() 将该 "file" map 到属主进程的地址空间之内。针对这个 mapping 而分配的那些内存 page 会被从 kernel 的 direct map 中移除掉,并被特别标记过,防止它们后续被错误地重新 map 回来。此后,该内存区域是可以被该进程访问的,但对其他任何进程甚至包括内核都不能访问这一区域了。因此,这部分内存的数据就被很好地保护了起来。很好,但这也带来一些缺点。例如,指向这部分秘密内存区域的指针不能用在系统调用里了;该内存区域也不能用于 DMA 操作。

这项工作第一次公开发表是在 2019 年 10 月。在后续两年不断演进这个系统调用的时间里,它已经有了不少改变,不仅仅是变成了一个单独的系统调用(发生于 2020 年 7 月)而已,这是因为人们认为此功能与普通 memfds 操作完全没有什么共同点。在 2020 年 7 月底发布的第二版中 memfd_secret() 支持了预留一块固定区域的 memory,但在 9 月的第 5 版中就被删除了。

早期的 patch set 中包括一个 flag,用来要求从内核的 direct map 中删除这部分秘密 page。这样就可以让内核无法访问这些内存区域了(任何可能黑入内核的人也就无法看到这些数据了),但人们担心把用来形成 direct mapping 的 1GB page 中间到处挖洞的话会降低性能。不过,这些担忧已经随着时间的推移都消退了。实际上 2MB page 的性能表现与 1GB page 的性能并没有什么差异。因此,这个 flag 在 2020 年 8 月的第 4 版中消失了,变成了必须要从 direct map 中移除。

11 月发布的第 8 版中也增加了一些改动。会使用 CMA 来专门用作 memfd_secret() 的内存来源,而不是随便分配一块内核管理的内存了。另一个改动也一直让大家很有争议,它的作用是只要有一个秘密内存区域处于 active 状态时,就禁止系统的休眠(hibernation)。这样做的目的是防止秘密数据被写入持久性存储设备中(persistent storage),但如果一些用户发现他们的系统不能再休眠的话很可能会感到不满。尽管如此,这个行为也还是进入了 5.14 内核。

从一开始,memfd_secret()就支持一个 flag 专门用来申请 uncached mapping 来作为秘密数据区域,也就是说希望绕过 memory cache。这样就可以提供更高的安全性,因为在 cache 中都不会再有这些数据了(毕竟 Spectre 漏洞就可以窃取 cache 内容),但将内存设置为 uncached 的话会大大降低性能。cache 毕竟是非常重要的。Andy Lutomirski 一再反对提供这种 uncached mapping,反对因此引入的性能降低等。RApoport 最终同意把这个 flag 删除掉了。在第 11 版完成了删除,此后到现在的最新版本为止都不再具有这个功能了。

在第 17 版(2021 年 2 月)代码中,memfd_secret() 被默认禁用了,并增加了一个 command-line option(secretmem_enable=),用来在系统启动时启用这个功能。这个决定是出于担心系统性能可能会因为打破了 direct mapping 以及对秘密区域内存进行 locking 操作而受到影响。所以除非系统管理员将其打开,否则该功能就不可用。这个版本也不再使用 CMA 进行内存分配了。

基本上这就是最终版本的 memfd_secret() 的效果了。它已经来到了第 23 个版本,看起来版本非常多了,但一般来说内存管理方面的改动基本上都是这样的。等 5.14 版本发布后,我们将能看到这个功能的用户群体有多大,以及他们还需要哪些进一步的改动。不过目前而言,这项工作似乎最终已经成功实现完毕了。有兴趣的人可以参见这个 man page 草案(https://lwn.net/ml/linux-mm/20210729082900.1581359-1-rppt@kernel.org/)。

全文完
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/)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值