已开启
[Bug]: procmgr 查找进程节点后缺少生命周期保护,退出并发时可能访问已释放对象 #38
CupCupSir创建于 18 天前
openharmony_ci
18 天前 评论:
18 天前 评论:
感谢提交Issue!关于Issue的交互操作,请访问OpenHarmony社区支持命令清单。如果有问题,请联系 [@peterli](https://gitcode.com/peterli) [@heyanhong](https://gitcode.com/heyanhong) [@sjtugjy](https://gitcode.com/sjtugjy) 。如果需要调整订阅PR、Issue的变更状态,请访问链接。
Thanks for submitting the issue. For more commands, please visit OpenHarmony Command List. If you have any questions, please refer to committer gitcode for help. If you need to change the subscription of a Pull Request or Issue, please visit the link.


18 天前 添加了label:waiting_for_assign
问题概述
procmgr使用哈希表保存struct proc_node。get_proc_node在proc_nodes_lock保护下找到节点,但会先释放锁,再把裸指针返回给调用者。调用者使用完毕后也没有对应的引用释放操作。进程回收路径使用同一把锁把节点从哈希表移除并释放内部资源,但释放锁后会立即执行
free(proc)。因此,查找线程可能已经拿到指针,回收线程随后移除并释放同一节点,查找线程再访问节点字段或节点内的互斥锁。这不是每次进程退出都会发生的问题。只有相应节点实际进入释放分支,并且线程调度落在“查找返回之后、首次或后续访问之前”的窗口内才会触发。当前静态证据支持罕见并发时序下的 use-after-free 和
procmgr进程异常,不支持把影响直接扩大为权限提升、可控代码执行或信息泄露。该问题仍存在于已验证的
master:问题详情
查找锁只覆盖哈希表遍历,不覆盖返回后的引用
文件:
user/system-services/system-servers/procmgr/proc_node.cstruct proc_node *get_proc_node(badge_t client_badge) { struct proc_node *proc; struct hlist_head *buckets; pthread_mutex_lock(&proc_nodes_lock); buckets = htable_get_bucket(&badge2proc, client_badge); for_each_in_hlist (proc, hash_node, buckets) { if (client_badge == proc->badge) { goto out; } } pthread_mutex_unlock(&proc_nodes_lock); return NULL; out: pthread_mutex_unlock(&proc_nodes_lock); return proc; }锁能保证遍历哈希链时节点不会同时被摘除,但返回值不是快照,也没有引用计数。
struct proc_node中没有引用计数字段或其他延迟释放状态,API 也没有put_proc_node一类的配对操作。TEE 配置下的
get_proc_node_by_pid使用相同模式:在pid2proc中找到节点后释放proc_nodes_lock,再返回裸指针。它是同一生命周期缺陷的相邻入口。回收路径会在移除哈希项后释放节点
同一文件中的
free_proc_node_resource在proc_nodes_lock下移除 badge 和 pid 哈希项、释放 ID 和名称,然后释放锁:void free_proc_node_resource(struct proc_node *proc) { pthread_mutex_lock(&proc_nodes_lock); htable_del(&proc->hash_node); #ifdef CHCORE_OH_TEE htable_del(&proc->pid_hash_node); clean_sharemem(proc->badge); #endif /* release pid, pcid and name */ pthread_mutex_unlock(&proc_nodes_lock); }del_proc_node随后在两类情况下释放结构体本身:for_each_in_list_safe (child, tmp, node, &proc->children) { child->parent = NULL; if (child->state == PROC_STATE_EXIT) { free_proc_node_resource(child); free(child); } } if (!proc->parent) { free_proc_node_resource(proc); free(proc); }因此,退出节点仍有父进程时通常不会由自己的回收事件立即释放。实际释放发生在以下情况之一:
del_proc_node直接释放它。这一区分决定了真实触发条件。仅有并发退出但没有进入上述释放分支,不足以形成悬空指针。
独立回收线程与 IPC handler 并发运行
文件:
user/system-services/system-servers/procmgr/procmgr.cint main(int argc, char *argv[], char *envp[]) { pthread_create(&recycle_thread, NULL, recycle_routine, NULL); init_procmgr(); cap = chcore_pthread_create( &procmgr_handler_tid, NULL, handler_thread_routine, NULL); /* ... */ }handler_thread_routine通过ipc_register_server(procmgr_dispatch, DEFAULT_CLIENT_REGISTER_HANDLER)注册服务。默认注册回调会为客户端创建被动 IPC handler 线程。回收线程则独立接收内核回收通知。文件:
user/system-services/system-servers/procmgr/recycle.cproc_to_recycle = get_proc_node(msg.badge); assert(proc_to_recycle != 0); pthread_mutex_lock(&recycle_lock); /* set exit status and recycle capability group */ del_proc_node(proc_to_recycle); pthread_mutex_unlock(&recycle_lock);recycle_lock在查找之后才获取。它会串行化已知的回收和子节点释放操作,但普通查找调用者没有持有这把锁。因此,它不能阻止其他调用者在节点释放后继续使用早先取得的指针。节点自身的
lock只用于协调子进程列表。生产调用者也是在get_proc_node返回后才执行pthread_mutex_lock(&client_proc->lock)。如果对象在两者之间被释放,尝试获取这个互斥锁本身已经访问了失效内存。wait_lock保护退出状态和条件变量,同样不是对象生命周期引用。生产调用者会在解锁后继续解引用节点
procmgr_dispatch的多个请求处理函数使用该查找接口。例如,handle_wait和handle_get_thread_cap都先取得client_proc,再访问节点锁和子进程列表:client_proc = get_proc_node(client_badge); assert(client_proc); pthread_mutex_lock(&client_proc->lock); /* traverse client_proc->children */srvmgr.c中的do_launch_process根据父进程 badge 查找父节点,稍后把该指针传给new_proc_node。后者会访问parent->lock并修改父节点的子进程列表。TEE 内存请求路径
oh_mem_ops.c还会在查找后读取proc_node->pid、proc_node->proc_cap或proc_node->puuid。按 pid 查找的handle_kill、handle_info_proc_by_pid、任务映射和任务取消映射路径也存在同一返回后使用窗口。user/system-services/system-servers/procmgr/Makefile通过SRCS := $(wildcard *.c)编译目录中的 C 文件,并链接生成procmgr.srv。因此,proc_node.c、recycle.c、procmgr.c、srvmgr.c和oh_mem_ops.c都属于实际procmgr构建,不是未引用的辅助代码。根本原因
proc_nodes_lock同时被当作哈希表结构锁和节点存在性检查锁,但 API 在释放这把锁后仍把节点地址暴露给调用者。节点没有引用计数、读侧临界区或其他延迟回收机制,释放路径也不会等待现有调用者结束。recycle_lock、proc->lock和wait_lock分别保护回收操作、子进程列表及退出状态。它们都没有覆盖从哈希查找到最后一次节点访问的完整区间,所以不能承担对象生命周期保护。触发条件
必须同时满足以下条件:
procmgr的 IPC handler 或其他生产调用路径通过 badge 或 pid 查找到一个节点,并释放proc_nodes_lock。free_proc_node_resource和free。进程正常退出、崩溃或被终止都可能产生内核回收通知,但调度窗口很短,且非孤儿退出节点会暂时保留到父进程处理。因此,源码确认的是可发生的生命周期竞态,不是稳定或可重复的外部触发方式。
普通客户端进程可能参与与自身或子进程生命周期相关的 IPC 操作,但当前静态分析没有证明普通应用可以精确控制释放时刻、重分配内容或悬空指针的后续用途。该问题不应按已验证的恶意利用链描述。
影响分析
一旦命中窗口,
procmgr会对已经释放的struct proc_node执行字段读取、互斥锁操作或链表访问。最直接的结果包括:procmgr崩溃、断言失败或挂起。procmgr自身状态。procmgr是系统服务,其异常可能阻止后续进程创建、等待、回收或 TEE 内存管理请求,具体恢复方式取决于产品如何监控和重启该服务。当前没有运行时证据表明释放后的内存可以被外部请求按需要重新占用,也没有确认可控字段、稳定堆布局或安全边界跨越。因此,不应据此声称权限提升、任意代码执行、敏感信息泄露或 TEE 内核崩溃。受影响的是用户态
procmgr服务的内存生命周期;更强影响需要单独的设备测试和可控性证据。验证状态
已对
master提交789b0fec1597c80ca6cb2a11aab3222b02e333b6进行静态源码复核:get_proc_node和get_proc_node_by_pid在哈希表锁外返回无引用保护的指针。free_proc_node_resource在同一哈希表锁下移除节点,但del_proc_node会在解锁后释放结构体。recycle_routine是独立线程,IPC 服务使用按客户端创建的 handler 线程。recycle_lock不由普通查找调用者持有,节点锁也在裸指针返回后才获取。procmgr/Makefile编入procmgr.srv。本次没有运行或引用 PoC,也没有在设备上人为控制线程调度。因此,当前确认范围是源码中的释放后访问窗口及其最直接的服务异常风险,不声称已经观测到崩溃或其他运行时结果。
当前查找与释放实现由仓库最早可验证的代码导入提交引入。已检查的发布标签
OpenHarmony-v6.0-Beta1、OpenHarmony-v6.0-Release、OpenHarmony-v6.0.0.1-Release、OpenHarmony-v6.0.0.2-Release、OpenHarmony-v6.1-Release、OpenHarmony-v6.1-LTS、OpenHarmony-v7.0-Beta1和OpenHarmony-v7.0-Release均保留相同的生命周期语义。当前master和已获取的 2026 年 8 月 weekly 分支也仍保留该行为。2026 年 7 月的
fix: avoid NULL deref on stale TASK_UNMAP_NS after TA exit只在按 pid 查找已经返回NULL时提前结束请求。它不能保护“查找成功后才被并发释放”的指针,不是本问题的完整修复。所有可用分支历史中未发现为proc_node增加引用计数、读侧保护或延迟释放的变更。已检索目标仓库全部 29 个公开 GitCode Issue、Issue 评论以及 21 个公开 Merge Request。以
get_proc_node、proc_node、use-after-free、UAF、悬空指针、生命周期、竞态和 recycle 等关键词检查标题与正文,未发现描述同一根因的条目。Issue #15 讨论handle_spawn的输入边界校验,Merge Request !12 处理procmgr编译告警,均不是本问题的重复项。修复建议
建议为
proc_node建立明确的引用生命周期:get_proc_node和get_proc_node_by_pid必须在proc_nodes_lock内确认节点仍可获取,并为返回值增加引用。put_proc_node。需要逐一更新所有 badge 和 pid 查找调用点,避免部分路径仍返回无保护指针。proc_nodes_lock内把节点标记为退出或正在销毁,并从两个哈希表移除,阻止新查找取得引用。不要简单地让所有调用者在执行完整 IPC 操作期间一直持有
proc_nodes_lock。现有路径会获取节点锁、recycle_lock、共享内存锁并调用可能阻塞的系统接口,扩大全局锁范围容易造成死锁或长时间阻塞。建议增加可控制调度点的并发回归测试:
put前不会释放。handle_wait、handle_get_thread_cap、进程启动和 TEE 内存请求等实际调用点。NULL,而已有引用仍保持有效。涉及文件
user/system-services/system-servers/procmgr/proc_node.cuser/system-services/system-servers/procmgr/include/proc_node.huser/system-services/system-servers/procmgr/recycle.cuser/system-services/system-servers/procmgr/procmgr.cuser/system-services/system-servers/procmgr/srvmgr.cuser/system-services/system-servers/procmgr/oh_mem_ops.cuser/system-services/system-servers/procmgr/Makefile版本信息
master789b0fec1597c80ca6cb2a11aab3222b02e333b6OpenHarmony-v6.0-Beta1至OpenHarmony-v7.0-Release