CVE-2025-39941CVE-2025-39941是Linux内核zram子系统中存在的一个竞争条件漏洞。该漏洞位于zram槽位写入路径中,当多个CPU并发地对同一个zram索引执行写入操作时,会导致zsmalloc句柄(handle)泄漏问题。zram是Linux内核中用于将内存页面压缩后存储在RAM中的块设备驱动,常用于嵌入式设备和内存受限的场景。zsmalloc则是内核中专门为zram设计的内存分配器,用于管理压缩后的小对象存储。
该漏洞的根本原因是zram_slot_lock锁的保护范围不足。在并发写入场景中,多个CPU可能在不同的时间点获取和释放zram_slot_lock,导致zs_free()调用过早发生。具体而言,当CPU0在释放旧句柄之后、设置新句柄之前,另一个CPU可能已经完成了压缩并设置了其自己的句柄,从而导致其中一个CPU的zsmalloc句柄永远不会被释放,形成内存泄漏。
该漏洞的CVSS评分为4.7,属于中危级别。虽然攻击向量为本地(AV:L),需要低权限(PR:L),但其对系统可用性影响为高(A:H),长期运行可能导致系统内存耗尽。攻击复杂度较高(AC:H),需要特定的并发时序条件才能触发。此漏洞影响所有使用zram功能的Linux系统,包括各类Linux发行版和嵌入式系统。
该漏洞的核心技术原理在于zram槽位写入路径中锁的临界区设计不当。在正常情况下,zram写入流程应该遵循以下顺序:1)获取zram_slot_lock;2)释放旧的zsmalloc句柄(zs_free);3)压缩新数据;4)分配新的zsmalloc句柄(zs_malloc);5)设置新的句柄到zram条目(zram_set_handle);6)释放zram_slot_lock。
然而,实际代码实现中,zs_free()被放置在zram_slot_lock()的保护范围之外,导致以下竞争场景:
CPU0首先获取zram_slot_lock并执行zs_free(handle)释放旧句柄,然后释放锁。CPU1此时获取zram_slot_lock,同样执行zs_free(handle)(释放同一个旧句柄,造成double-free风险或无效释放),然后释放锁。接下来两个CPU并发执行compress和zs_malloc操作,最终各自设置新的句柄。由于旧句柄已经被提前释放,最终只有一个CPU的新句柄被正确设置,另一个CPU的zs_malloc分配的句柄将永远丢失,造成内存泄漏。
正确的修复方案是将zram条目的重置操作(reset)与新句柄的设置操作(zram_set_handle)放在同一个zram_slot_lock锁的保护范围内执行,确保zs_free和zram_set_handle在同一个原子操作中完成,从而避免句柄泄漏。
利用方式方面,攻击者可以通过编写多线程程序,对同一个zram设备文件执行并发写入操作来触发该漏洞。需要创建大量并发线程,使用相同的偏移量写入不同的数据,从而在压缩和句柄分配阶段产生竞争条件。