You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows内核内存映射异常:为何MmMapLockedPagesSpecifyCache映射值不符?

内核缓冲区映射后用户态无法获取正确值的原因与修复方案

嘿,我来帮你拆解这个问题——你遇到的核心问题是MDL没有被正确初始化就用来做用户态映射,导致用户态拿到的地址根本没指向你在内核里填充数据的那块连续内存。

问题出在哪?

你用IoAllocateMdl创建了MDL,但这个函数只负责分配并初始化MDL的基础结构,不会自动把内存对应的物理页面信息填充进去。对于MmAllocateContiguousMemorySpecifyCache分配的内核内存,必须在创建MDL后调用MmBuildMdlForNonPagedPool来补全MDL的页面条目,否则这个MDL就是个“空架子”,后续MmMapLockedPagesSpecifyCache根本找不到正确的内存区域,自然映射出来的是垃圾数据。

看你这段代码,正好缺了这个关键步骤:

queueContext->mdl = IoAllocateMdl( ptr, static_cast<ULONG>(queueContext->stage_buffer_byte_size), false, false, nullptr );
// ↓ 这里少了 MmBuildMdlForNonPagedPool(queueContext->mdl);

另外还有个次要的潜在问题:缓存一致性。虽然你内核和用户态都用了MmCached缓存类型,但内核修改内存后,CPU缓存可能没同步到用户态地址空间。不过从WinDbg的结果来看,用户态地址完全是乱码,说明映射本身就错了,这个缓存问题是后续才需要考虑的,但修复MDL后如果还有异常,可以调用KeFlushIoBuffers来同步缓存。

修复后的代码示例

1. 分配内存并正确初始化MDL

auto ptr = MmAllocateContiguousMemorySpecifyCache( BUFFER_SIZE, lowestAcceptible, highestAcceptible, lowestAcceptible, MmCached);
PQUEUE_CONTEXT queueContext = QueueGetContext(queue);
if (ptr) {
    RtlZeroMemory(ptr, BUFFER_SIZE);
    queueContext->stage_buffer_ptr = ptr;
    queueContext->stage_buffer_byte_size = BUFFER_SIZE;
    queueContext->stage_buffer_bus_address = MmGetPhysicalAddress(ptr).QuadPart;
    
    // 创建MDL
    queueContext->mdl = IoAllocateMdl( ptr, static_cast<ULONG>(queueContext->stage_buffer_byte_size), false, false, nullptr );
    if (queueContext->mdl) {
        // 关键步骤:填充MDL的页面信息,锁定内存
        MmBuildMdlForNonPagedPool(queueContext->mdl);
    } else {
        // 分配MDL失败,清理已分配的连续内存
        MmFreeContiguousMemory(ptr);
        queueContext->stage_buffer_ptr = nullptr;
        // 这里可以添加错误处理逻辑
    }
}

2. 映射到用户空间并记得清理

用完映射的内存后,一定要解除映射,驱动卸载时也要释放MDL和连续内存:

auto queueContext = QueueGetContext(Queue);
if (queueContext->mdl) {
    auto user_ptr = MmMapLockedPagesSpecifyCache( queueContext->mdl, UserMode, MmCached, nullptr, false, MM_PAGE_PRIORITY::HighPagePriority );
    if (user_ptr) {
        // 可以把user_ptr返回给用户态,或者执行其他操作
        // ...
        
        // 使用完毕后解除映射
        MmUnmapLockedPages(user_ptr, queueContext->mdl);
    }
}

额外提醒

  • 物理连续内存是系统里的稀缺资源,不要长时间持有,用完尽快释放。
  • METHOD_NEITHER类型的IOCTL要格外注意内存安全,不过你这里是内核分配内存映射到用户态,相对风险低,但也要注意权限和资源泄漏问题。

内容的提问来源于stack exchange,提问作者Elad Maimoni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 14:52:33