2026-06-28 内核社区日报

今天,内核社区在内存管理方面有多项进展:针对 AMD 15h 家族的 hugetlb 映射 get_unmapped_area regression 收到测试反馈,tmpfs 长符号链接的未初始化尾部泄露问题得到修复,shmem_fault 数据竞争修复和 numa_memblks 清理系列也已提交。此外,DAMON 的零长度 region 校验和用户空间 autotuner 也值得关注。

🧠 重点 · 内存

1. hugetlb get_unmapped_area 回归修复系列:Andrew Morton 建议重新发送

发生了什么: Oscar Salvador 发布了系列 [PATCH 0/8] 以修复 AMD 15h Family 上 align_va_addr 设置后 hugetlb 映射的回归,并停止在 get_unmapped_area 中对 hugetlb 映射做特殊处理。Andrew Morton 回复指出该系列发送时机不佳(接近 -rc 周期末),导致缺乏反馈,建议刷新/重新测试/重新发送,并考虑提供一个最小化修复用于 stable 内核(定位 Fixes: 提交,该提交距今约 1.5 年)。他建议在系列说明中明确指出该回归会导致运行时 BUG() 以吸引注意。

为什么重要: 该系列旨在统一普通映射和 hugetlb 映射在 get_unmapped_area 中的路径,消除历史上的特殊分支,并修复一个可能引发运行时 BUG() 的回归,使代码更健壮。

来源

2. tmpfs 长符号链接:修正未初始化的 folio tail 导致的信息泄露

发生了什么: shmem_symlink() 在拷贝 NUL 终止的链接目标后直接标记整个 folio 为 uptodate,但新分配的 folio 尾部未被初始化。回收时,未初始化的尾部可能传递给 swap 压缩器(KMSAN 观察到 sw842_compress() 对其计算校验和),写入 swap 设备时也可能泄露内核数据。补丁在标记 uptodate 前调用 folio_zero_range() 清零尾部未使用部分。

为什么重要: 该补丁修复了通过 swap 路径泄露未初始化内核内存的潜在问题,避免由 folio 尾部未清零导致的 kernel memory disclosure,属于内存安全加固。

来源

3. shmem_fault 数据竞争修复

发生了什么: shmem_fault() 在获取 folio 后检查 folio_test_swapbacked(folio),但这与 shmem_writepage() 中设置 swapbacked 的操作存在数据竞争。KCSAN 会报告该 race。补丁通过在 shmem_fault 中正确使用 READ_ONCE/数据依赖屏障来消除竞争。具体地,patch 为 shmem_fault 中读取 shmem_inode_info->swapped 和 folio swapbacked 标志添加了适当的注解,避免编译器或 CPU 重排导致读到 stale 值。

为什么重要: 这是一个经过 KCSAN 确认的真实 data-race,尽管大多数情况下不会导致崩溃,但理论上可能让 fault 路径观察到错误的 folio 状态(如将一个 non-swapbacked folio 误判为 swapbacked),导致错误的分支(如误发 copy_user_highpage 而非 shmem_swapin),进而引发内存内容错误或用户态数据泄漏。KCSAN 报告的修复有助于提高内核数据竞态安全性。

来源

4. DAMON 核心:在 damon_set_regions() 中校验零长度 region

发生了什么: DAMON 核心逻辑假设零长度 region 不存在,但 DAMON_SYSFS、DAMON_RECLAIM、DAMON_LRU_SORT 等调用者允许用户设置空监控目标 region。零长度 region 在启用 CONFIG_DAMON_DEBUG_SANITY 时触发 WARN_ONCE(),并在 damon_merge_two_regions() 中导致除零错误。补丁在 damon_set_regions() 中增加校验:若 region start 按 min_region_sz 对齐后大于等于 end 对齐后的值,则返回 -EINVAL,阻止无效 region 进入后续逻辑。

为什么重要: 此修复消除了 DAMON 核心因零长度 region 产生的 WARN_ONCE 和除零未定义行为,提升了内核内存监控路径的稳健性。

来源

5. numa_memblks: 移除 NUMA 初始化期间的冗余工作

发生了什么: 该系列包含 9 个 patch,清理 numa_memblks。核心改动有:
(1)在 numa_add_memblk() 内部自动将新 node 标记到 numa_nodes_parsed,消除每个调用者额外调用 node_set() 的冗余;
(2)移除 numa_nodemask_from_meminfo() 函数,因为它所做的工作(从 numa_meminfo 中收集 node)现在完全可由 numa_add_memblk() 内的逻辑替代;
(3)在 numa_cleanup_meminfo() 中使用 numa_add_reserved_memblk() 替代直接调用内部函数,使代码接口更统一。涉及 ACPI、of/numa、x86、arch_numa、LoongArch 等调用方。

为什么重要: 这些清理消除了多个架构(如 x86、LoongArch)及通用子系统(ACPI、of/numa、arch_numa)在 NUMA 初始化阶段对 numa_nodes_parsed 的手动维护,移除了隐含依赖调用者正确执行 node_set() 的冗余,使 NUMA 初始化路径更清晰、更健壮。

来源

6. vmstat: 节点上线时 fold 滞留的 per-CPU 统计,消除永久 skew

发生了什么: 当使用 numa_memblks 或类似机制在新节点上线时(如热插拔场景),原有的 per-CPU nodestats 计数器可能还包含之前分配活动留下的差值。现有代码只是简单地清零 per-CPU 计数器,导致永久丢失这些 delta,最终使该节点的全局 vm_stat 偏低。Gregory Price 提交的 v2 patch 改为在节点上线前将所有 per-CPU 的 delta 折叠(fold)到节点级统计中,然后再清零 per-CPU 缓存,确保数据不丢失。

为什么重要: 该问题对内存热插拔和管理员跟踪节点级内存使用统计影响显著。未折叠的 delta 可能导致 vmstat 输出的 node 统计值低于真实值,影响控制组和 NUMA 策略的调度决策。这是一个正确性修复,对使用 CXL 内存池或非对称 NUMA 系统的用户尤其关键。

来源

7. numa_memblks 清理

发生了什么: Sang-Heon Jeon 提交补丁,将 numa_cleanup_meminfo() 中对内部函数 numa_add_memblk_to() 的直接调用替换为封装函数 numa_add_reserved_memblk()

为什么重要: 该补丁统一了 numa_add_memblk_to() 的调用入口,使其仅通过两个封装函数被调用,提升了 numa_memblks 子系统的代码一致性,无功能变化。

来源

8. 用户空间 DAMON_RECLAIM min_age 自动调谐器

发生了什么: aethernet65535 提交 RFC [PATCH],提出用户空间程序 DAMA(DAMOS Autotuner/Assistant),通过动态调整 DAMON_RECLAIM 的 min_age 参数来减少不必要的主动回收。测试使用 Masim 模拟两种场景:场景一在 30 秒全访问与 10% 访问间切换(理论最优 min_age > 30s),场景二每 30 秒切换访问四个区域之一(理论最优 min_age < 90s)。

为什么重要: 该方案将调谐逻辑从内核移至用户空间,降低了内核复杂度,并允许基于工作负载特征(如访问模式周期)自适应调整回收阈值,有望提升内存回收效率。

来源

🔧 其它子系统

文件系统

  • minix 文件系统转用 iomap 并添加 direct I/O:v2 系列共 4 个 patch,将 minix 的 address space 操作从 buffer_head 迁移到 iomap,同时添加 direct I/O 支持。经 syzbot 测试发现 4 个问题已修复(空指针解引用、truncate 页回写错误等)。这是老旧文件系统现代化的重要一步。来源
  • NBD NBD_CLEAR_SOCK 与 buffered write 之间的竞态:syzbot 报告在 NBD_CLEAR_SOCK 与并发 buffered write 之间发生竞态,导致 block_commit_write 中已设置 BH_Uptodate 的 buffer_head 被随后完成的 NBD 请求(因 BLK_STS_IOERR)通过 end_buffer_async_write 清除 BH_Uptodate,进而在 mark_buffer_dirty() 中触发 WARN_ON_ONCE(!buffer_uptodate(bh))。根源在于 blkdev_write_iter 使用 inode_lock_shared(bd_inode)NBD_CLEAR_SOCK 使用 nbd->config_lock,两者不互斥。暂无修复 patch。来源
  • NTFS(in-kernel)目录索引越界/释放后读:在 ntfs_index_next()ntfs_index_walk_down() 中,遍历器在检查条目是否超出索引块边界之前就解引用 entryflags 和子 VCN,导致可能读入相邻已释放的 slab 对象。由 Bakabaka_9 报告并提供 PoC 与根因分析。来源

网络

  • ppp stats ioctl UAFppp_get_stats()(SIOCGPPPSTATS)和 SIOCGPPPCSTATS 在不持锁的情况下分别解引用 ppp->vjppp->xc_state/ppp->rc_state,而 PPPIOCSMAXCID、PPPIOCSCOMPRESS、ppp_ccp_closed() 等路径会在持锁时释放这些结构,从而产生 slab-use-after-free 且将已释放内容复制至用户空间。问题由 Doyensec 报告。来源
  • sctp auth_enable sysctl UAFproc_sctp_do_auth() 更新 SCTP 控制套接字时,若并发删除 network namespace,可能访问已释放的 net->sctp.ctl_sock。修复方法是在 sctp_ctrlsock_exit() 中先调用 sctp_sysctl_net_unregister() 注销 per-net sysctl 表,再销毁控制套接字,并在注销后清除 sysctl_header 指针。来源
  • tcp SYN-ACK 定时器边界检查tcp_synack_retries 传入请求套接字定时器后,若用户设置过大值可能导致定时器超时过长。新 patch 将定时器值约束在 reqsk_timeout 范围内。来源
  • teql 锁保护sch_teql 的 master→slaves 链表无锁保护,并发操作(xmit、dequeue、init、destroy)可导致 UAF。引入 slaves_lock spinlock 保护。来源
  • xdp tracepoint 扩展提议:Masashi Honma 以 RFC 形式提议扩展 bpf_xdp_link_attach_failed tracepoint,在其输出中增加设备 ifindex,从而将 attach 失败与具体网卡设备关联;示例中 TP_printk 改为 "ifindex=%d errmsg=%s"来源

eBPF

  • RISC-V BPF exceptions v2:为 riscv64 实现 arch_bpf_stack_walk() 并更新 BPF JIT 序言/尾声以支持 BPF 异常,使 bpf_jit_supports_exceptions() 返回 true。该支持依赖 CONFIG_FRAME_POINTER,使用帧指针展开器;除混合尾调用与 bpf-to-bpf 调用的测试外,其余异常测试均通过。来源
  • gotox 目标跨 subprog 边界检查 v3:补丁强制 gotox 指令的目标不能跳出当前 subprog 的指令范围,避免 CFG 构建时忽略无效 target 导致的 verifier 误判。来源

Rust / DRM

  • DRM rust 驱动:引入 Higher-Ranked Lifetime private data v5:该系列(19 patches)通过引入 RegistrationGuard(表示 drm_dev_enter/exit SRCU 临界区的守卫)来解决 DRM ioctl 运行在进程上下文且父总线设备可能在 ioctl 处理期间解绑的问题。核心改动:重命名 UninitNormal,建立 Device<T, Registered>Device<T, Normal> 的 Deref 链,新增 Ioctl 设备上下文类型状态,并在 drm::Driver 上添加 RegistrationData 作为 GAT,允许驱动存储生命周期绑定到父总线设备绑定作用域的数据。ioctl 分发宏现在将每个处理程序包装在 RegistrationGuard 中,若设备已拔出则返回 ENODEV,并将注册数据传递给处理程序。该系列已在 nova 驱动中应用,演示了生命周期感知的注册数据(例如 &'bound auxiliary::Device<Bound>)的使用。来源

👀 值得追的讨论 / Patch

1. DAMA:用户态 DAMON_RECLAIM min_age 自动调优器

Rui Yan 发布了 DAMA(DAMOS Autotuner/Assistant),一个通过定期监测 DAMON 回收页、系统回收(kswapd + direct)和 refault 的增量来自动调节 DAMON_RECLAIM 的 min_age 参数的用户空间程序。其算法采用累积衰减、阈值门控与迟滞机制,在 DAMON 回收过多时提高 min_age,在系统回收过多时降低 min_age。合成测试结果表明,DAMA 相比固定 min_age 配置能显著降低系统回收、refault 和 major fault 数量,同时保持较低的内存压力。作者希望社区就如何使 benchmark 更具代表性以及该用户态调优方法提供反馈。来源

2. NBD NBD_CLEAR_SOCK 竞态触发 mark_buffer_dirty() WARNING

NBD NBD_CLEAR_SOCK ioctl 与 buffered block-device 写入之间存在竞态:blkdev_write_iterinode_lock_shared(bd_inode) 下运行,而 NBD_CLEAR_SOCKnbd->config_lock 下处理,两者不互斥。竞态使正在写入的 buffer_head 的 BH_Uptodate 位被清除后又被 mark_buffer_dirty() 检测到,触发 WARN_ON_ONCE(!buffer_uptodate(bh)) 警告。来源

⚡ 一句话速览

  • slabobj_ext 与 kmalloc cache 大小提升条件细化:Shakeel Butt 在讨论中建议,当内存分配分析(mem_alloc_profiling_enabled())启用时,若 obj_exts_cache->object_size <= s->object_size 则返回 s->object_size + 1;否则(仅 memcg 场景)仅在两者相等时提升大小。此讨论围绕避免 kmalloc cache 间循环引用及减少内存浪费展开。来源
  • zsmalloc lock contention 优化 v6:Andrew Morton 回复 v6 系列表示“现在看起来不错”,可望合并。来源
  • page/folio nid/zonenum 独占位断言 v6:Andrew 回复指出 ASSERT_EXCLUSIVE_BITS 在 kcsan-checks.h 中定义,但 mm.h 和 mmzone.h 当前并未可靠包含该头文件,并询问 Sashiko 报告的合法性。来源
  • DAMON core 减少 kernel stack 使用:Arnd Bergmann 提交补丁将 kdamond_tune_intervals 标记为 noinline_for_stack,以解决 kdamond_fn 栈帧大小超过警告限制的问题。Andrew 回应无需添加 Fixes: 和 Cc: stable,因内核中此类问题较多。来源
  • hugetlb 访问路径使用 huge_ptep_get():Lance Yang 的系列收到 Dev Jain 关于 addr 对齐的评论,继续讨论。来源
  • debugobjects:OOM 时跳过 activate fixup:Andrew 回复同意,已合入 mm tree。来源
  • cpuset mempolicy rebind 修复:修正 mpol_rebind_mm() 在子 cpuset 未设置 mems 时的除零问题。Andrew 回复表示接受。来源
  • fork 动态栈:在 unpoupulated stack ptes 中存储 task pointer:Thomas Gleixner 回复指出原补丁的 changelog 未解释为何不能使用 current,并提出使用 raw_cpu_write(next_task, next) 的简化方案,该方案可在所有架构上正确工作且开销可忽略。来源
  • selftests/mm:pagemap_ioctl 使用正确页面大小:修复 transact_test() 中硬编码 4K 页的问题。来源
  • selftests/mm:hmm-tests 包含 linux/mman.h:修复旧 glibc 下未定义 MADV_COLLAPSE 的编译错误。来源