LWN:memfd_secret()

本文介绍了两种增强内存保护的技术:memfd_secret() 和 KVM 保护内存。前者允许创建秘密内存区域,用于存储敏感信息如加密密钥;后者则通过软件方法防止虚拟机之间的内存泄露。

memfd_secret()

第一个是 Mike Rapoport 的 memfd_secret()补丁集,之前已经介绍过了(https://lwn.net/Articles/812325/),所以我们这里仅仅再简单介绍一下,详细背景资料请看那篇文章。这项工作的目的是允许用户空间进程创建一个 "秘密 "的内存区域,这个区域尽可能除了此进程之外都无法访问。预期的使用场景包括用在加密函数库中,可以使用秘密区域来保存加密密钥,并保证它们不被窥探。

在最新版本的 patch 中,这个功能已经被移到了一个单独的系统调用中。

int memfd_secret(unsigned long flags);

函数的返回值是文件描述符,可供传递给 mmap()来 map 实际的内存区域。大多数情况下,这个内存看起来(对 map 的进程来说)就像其他内存区域一样,不过还是会有一些区别:

  • 这个范围内的内存 page 将会从内核的 direct map 中移除掉,因为 direct map 会允许内核访问(几乎)系统中任何物理页的部分。这样改动之后,内核更难访问到这些内存了,无论是普通访问还是通过漏洞访问都不可以了。

  • 如果 flags 包含 SECRETMEM_UNCACHED,那么只要底层架构支持,这部分内存将被映射为 uncached。uncached 内存访问速度会慢得多,但它也不会受那些预测执行的漏洞的影响。

秘密区域的内存被 lock 在 RAM 中,无法被 swap。因此,这会被算入此进程的 locked-memory limit 限制之内。

这样的功能一直存在一个问题:从内核的 direct map 中移除 page,这是一个会带来很大性能损失的动作。direct map 使用 huge page,因此使用了尽量少的系统 TLB。从 direct map 中移除这里那里的 page,会破坏这些 huge page,显著增加要使用的 TLB 数量。为了将这种影响降到最低,memfd_secret()patchset 维护了另一个独立的物理连续页面的缓存。

最近这个 patch set 的改动已经不多了,所以它可能已经接近于可以被合入。不过,在实际打上这个 patch 之前,人们很难确定 memory-management patch 是否真的会被合入。

KVM 保护内存

虽然内存泄露漏洞在任何系统上都是坏事,但在运行虚拟化 guest OS 的系统上问题更大。这类机器可能运行着来自完全不相关的人员的 workload,而这些 workload 通常来说是不愿意互相分享秘密信息的。因此,由于很可能与攻击者控制的 guest OS 共享物理系统,使得内存保护成为了一个更加紧迫的问题。作为加固这些系统的一种方式,CPU 厂商一直在增加内存加密机制,使 guest memory 无法被内核以及其他 guest 访问。不过这些功能都会带来一些额外开销,而且目前支持这些功能的硬件还远未普及。

但是, Kirill Shutemov 从这些技术中得出了一个有趣的结论:使用了这些技术的系统仍然可以正常工作,这意味着大多数时候其实内核或 hypervisor 并不需要访问这些内存。所以他整理了一套 patch set,完全采用了软件方法来实现。运行这套代码的系统并不需要对 guest memory 进行加密,而只是简单地将其 unmap 掉。这个功能需要内核和 guest 两边都要支持。

具体来说就是增加了一个 KVM hypercall,允许 guest 请求他们的内存不可被其他人访问到。host kernel 会相应处理,也就是从 direct map 中删除分配给 guest 的这些内存,从而无法访问该内存。用户空间中的方法有点不同:任何属于 guest 的内存仍然保持 mapped 状态,但被标记为 PROT_NONE 这种保护模式,从而使得无法访问了。这将影响像 QEMU 模拟器这样的程序,它会无法直接访问 guest memory。不进行 mapping 的话,自然也会阻止来自其他 guest 的攻击。在 guest 内部,guest kernel 照常控制着内存权限(memory permission)。

这种隔离方式保护了 guest memory,使其不至于因为内核或 QEMU 等组件的漏洞而被意外访问到。但这并不是一种完全的保护。如果 host kernel 被入侵,可以执行任意代码的话,它就可以随意重映射这些 page 并偷取到其中内容。不过对于绝大多数让内核访问野指针指针的漏洞或者 speculative-execution 漏洞来说,这种 unmapping 动作就可以给攻击者增加很大的麻烦。

当然,有些时候,内核必须访问 guest 内部的内存才能执行正常的内核功能。我们为 guest 添加了第二个 hypercall,用来表明它们需要向 host kernel 开放哪些内存。这些范围将被映射到 host kernel 的地址空间内。虚拟化设备的 DMA buffer 就是一个例子,guest 会希望以这种方式与 host kernel 共享内存。

这项工作看起来很有价值,但还是有一些问题需要先解决,才能合入。与 memfd_secret()不同的是,这项工作没有提供避免 page 被移除产生的 direct map 碎片问题的解决方案,因为在这种情况下涉及的内存空间相当大,碎片问题可能会更严重。unmap 的 guest memory 无法被迁移(migration),这就破坏了内核的内存 defragment 机制。这很可能会随着运行的时间变长,而造成各种问题。Shutemov 也承认,需要先解决这个问题再合并 patch。目前如果一个 guest 系统启用了这种内存保护方式的话,也就不可以被 reboot 了。Shutemov 建议这种情况可以直接判定为 "不支持",不过这个想法已经在讨论中引起了抱怨。

这些问题意味着 KVM proected memory 这个功能不会在不久的将来内出现在主线内核中。不过这两个 patch set 很可能都给我们指明了今后发展的一个方向。尽可能的共享内存,这会提高性能,但越来越明显看出与之相关的安全问题很难解决。所以,尽可能地分离地址空间,这看起来是一个相对简单的方法,可以避开许多这类问题。

全文完
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、付费专栏及课程。

余额充值