Unix垃圾回收器重制版:重写过程、漏洞分析与复现方法揭秘
Unix垃圾回收器重制版资讯
AF_UNIX垃圾回收器是内核中有趣的部分,因套接字发送后可能在用户空间不可访问但内核仍让其存活,造成内存效率低下,垃圾回收器会介入释放。不久前该子系统基于图/强连通分量模型重写,但仍易出现漏洞。本文详细介绍重写过程,探讨使用后释放(Use - After - Free)的漏洞。
AF_UNIX垃圾回收器——背景
每个子系统的垃圾回收器负责回收无法通过用户空间句柄访问的内核对象。AF_UNIX的入口点是`unix_gc()`,实际核心代码在`__unix_gc()`中。
对于`unix_sock`结构体,关键字段是`inflight`。当套接字的`struct file *`作为SCM_RIGHTS有效负载传输时处于“传输中”状态,每次发送`inflight`递增,接收递减。垃圾回收器寻找`file_count == inflight`的套接字。
内核维护全局的`unix_tot_inflight`计数器,每次进入传输状态递增,接收递减。垃圾回收触发条件有两个:一是传输中的套接字过多;二是关闭套接字时,若有套接字处于传输中。
旧的垃圾回收器
2024年之前的垃圾回收器在相关文章中有详细描述,涵盖算法和2021年在Android系统中的实际利用情况。旧的垃圾回收器会遍历传输中的图,标记循环,并检查`inflight != refcount`,以确定每个循环是否可以回收。
新的垃圾回收器
新的垃圾回收器取代当前实现,减少锁定每个套接字队列的影响,在无循环引用或传输中文件描述符图形状未更新时保持轻量级。
图的表示方面,每个传输中的套接字为“顶点”,每个在SCM_RIGHTS控制消息中携带的`struct file *`为有向“边”。使用Tarjan算法将图划分为强连通分量(SCC),循环是顶点可回收的必要但不充分条件。
`__unix_gc`的调度方面,当添加新边且两端点都处于传输中时,`unix_graph_maybe_cyclic`会被置为真。在慢速和快速路径之间进行调度。
慢速路径`unix_walk_scc()`是实际构建SCC的地方,索引从`UNIX_VERTEX_INDEX_START`开始,遍历开始时假设图无环,只有找到循环才标记为有环。
Tarjan算法用于处理有向图并生成其SCC,具体细节可参考维基百科页面。
快速路径`unix_walk_scc_fast()`在图形状未改变时重用现有SCC,以逆序遍历每个缓存的SCC,将顶点移动到已访问列表,并对其运行`unix_vertex_dead()`。
CVE - 2025 - 40214——kCTF漏洞分析
补丁内容对相关代码进行了修改,根本原因是`unix_add_edge()`未初始化`struct unix_vertex`的`scc_index`字段,快速路径的死SCC检查会比较顶点之间的`scc_index`,可能导致活跃套接字被错误标记为死亡,出现使用后释放漏洞。补丁通过分配单调递增的`unix_vertex_max_scc_index`计数器修复问题。
作者描述了发现漏洞的策略,本文作者设计了接近的方法。复现过程分为阶段1 - 喷射顶点、阶段2 - `B ↔ A`循环、阶段3 - 虚假链,然后关闭并触发。在存在漏洞的内核上添加`printk`补丁可确认SCC死亡。
还给出了复现代码,包括发送和接收文件描述符、喷射顶点、触发垃圾回收等功能。
本文详细介绍了重写后的AF_UNIX垃圾回收器、CVE - 2025 - 40214中`scc_index`未初始化字段的漏洞,以及两种复现方法。那么,这些复现方法在实际应用中还会有哪些潜在影响呢?
