一封来自社区关于 swap 的 BUG 报告邮件打破了清晨的宁静:
[syzbot] [mm?] KMSAN: uninit-value in swap_writeout
(原始报告链接:https://lore.kernel.org/lkml/6949370f.050a0220.1b4e0c.0038.GAE@google.com/)
报告指出,在系统执行 swap out 操作时,内核内存检测工具 KMSAN 发现了一个未初始化的内存值。具体问题定位在下图所示的代码位置:

对应到代码中,即是在检查 folio 内容是否全为零以决定是否需要实际写出到 swap 时,对 data[last_pos] 的访问读到了未初始化的值。
// 问题大致出现在检查folio是否全零的逻辑中
if (data[last_pos] != 0) {
// 需要执行swap out
}
追溯这个 folio 的申请路径,发现它来源于共享内存(shmem)机制,具体是为一个符号链接(symlink)分配的:

进一步分析相关代码逻辑后发现,问题的根源在于创建 symlink 时,符号链接名称(symname)的长度可能小于一个 folio 的大小。在执行 memcpy 复制名称后,folio 中剩余的部分空间并未被初始化:

memcpy(folio_address(folio), symname, len);
// len 之后到 folio 结束的空间未被清零
初步判断,修复方案是在复制操作后,将 folio 尾部剩余的区域进行清零处理:

在与内核维护者通过邮件列表沟通后,该修复思路得到了认可。对方建议使用内核中更正式的 folio_zero_range() 函数来完成清零操作,而非简单的 memset,这更符合 Linux 内核的内存管理抽象。
因此,最终的修复方案采用了 folio_zero_range():

if (len < PAGE_SIZE)
folio_zero_range(folio, len, PAGE_SIZE - len);
完成补丁的发送与简要记录,这次针对 Linux内核 中 shmem 模块的细微内存管理问题的分析与修复便告一段落。此类问题凸显了在复杂系统编程中,对内存状态进行严格管理的必要性。
|