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

基于Minifilter的透明加密:FltWriteFileEx在PostCloseWhenSafe中死锁求助

Minifilter PostCloseWhenSafe写入死锁分析与解决

死锁根源分析

从日志和代码逻辑来看,死锁的核心是循环等待:

  1. 执行PostCloseWhenSafe的线程先获取streamContext->Resource独占锁,随后调用同步版FltWriteFileEx(未指定异步回调,会阻塞等待I/O完成)。
  2. 此时有PostWrite请求触发,该请求尝试获取同一个独占锁,但锁已被PostCloseWhenSafe线程持有,因此PostWrite线程进入等待状态。
  3. PostCloseWhenSafe线程在等待FltWriteFileEx的I/O完成,而该I/O的完成可能依赖于PostWrite线程的处理(比如底层文件系统I/O完成需要上层例程收尾,或存在其他并发I/O依赖),最终形成循环等待:
    • PostCloseWhenSafe线程:持有锁 → 等待I/O完成
    • PostWrite线程:等待锁 → 无法推进I/O完成

你提到的文档说明FltWriteFileEx的请求不会发给当前实例及以上,但死锁并非源于该请求触发自身的PostWrite,而是同步I/O等待期间,并发的PostWrite请求(或依赖PostWrite处理的其他I/O)与锁的持有逻辑冲突。

解决方案

针对这个场景,有两种可靠的修正思路:

1. 缩小锁的持有范围,避免I/O期间持有锁

将锁的持有仅限定在访问streamContext的临界区,不在I/O操作期间持有锁:

// 第一步:获取锁,准备写入所需的上下文数据
CtxAcquireResourceExclusive(streamContext->Resource);
// 保存写入参数(如果需要)
LARGE_INTEGER savedOffset = ByteOffset;
SIZE_T savedLength = WriteLength;
PVOID savedBuffer = WriteBuffer;
CtxReleaseResource(streamContext->Resource);

// 第二步:执行同步写入,此时不持有锁
DbgPrint("before FltWriteFileEx.\n");
Status = FltWriteFileEx(
    Instance,
    FileObject,
    &savedOffset,
    savedLength,
    savedBuffer,
    FLTFL_IO_OPERATION_NON_CACHED |
    FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET,
    &BytesWritten,
    NULL,
    NULL,
    NULL,
    NULL);

// 第三步:再次获取锁,更新上下文状态
CtxAcquireResourceExclusive(streamContext->Resource);
if (NT_SUCCESS(Status) && BytesWritten != 0) {
    streamContext->something = xxx;
    DbgPrint("after FltWriteFileEx.\n");
} else {
    DbgPrint("tail FltWriteFile failed. status = %08x, bytesWritten = %ld.\n", Status, BytesWritten);
}
CtxReleaseResource(streamContext->Resource);

2. 将FltWriteFileEx改为异步调用

通过指定完成回调,避免在持有锁时阻塞等待I/O完成:

// 定义I/O完成回调函数
VOID WriteTailCompletionRoutine(
    _In_ PFLT_CALLBACK_DATA CallbackData,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_opt_ PVOID CompletionContext,
    _In_ NTSTATUS Status,
    _In_ ULONG_PTR BytesWritten
)
{
    PSTREAM_CONTEXT streamContext = (PSTREAM_CONTEXT)CompletionContext;

    CtxAcquireResourceExclusive(streamContext->Resource);
    if (NT_SUCCESS(Status) && BytesWritten != 0) {
        streamContext->something = xxx;
        DbgPrint("after FltWriteFileEx.\n");
    } else {
        DbgPrint("tail FltWriteFile failed. status = %08x, bytesWritten = %ld.\n", Status, BytesWritten);
    }
    CtxReleaseResource(streamContext->Resource);

    // 若CompletionContext是动态分配的资源,需在此释放
}

// PostCloseWhenSafe中的逻辑
CtxAcquireResourceExclusive(streamContext->Resource);
DbgPrint("before FltWriteFileEx.\n");
Status = FltWriteFileEx(
    Instance,
    FileObject,
    &ByteOffset,
    WriteLength,
    WriteBuffer,
    FLTFL_IO_OPERATION_NON_CACHED |
    FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET,
    NULL, // 异步调用无需同步返回BytesWritten
    WriteTailCompletionRoutine,
    streamContext, // 传递上下文到回调
    NULL,
    NULL);

if (!NT_SUCCESS(Status)) {
    DbgPrint("FltWriteFileEx async failed. status = %08x.\n", Status);
    streamContext->something = xxx;
}
CtxReleaseResource(streamContext->Resource);

额外注意事项

  • 异步调用时要确保WriteBuffer等资源的生命周期覆盖整个I/O过程,避免回调时内存已被释放(可使用非分页池内存或引用计数管理)。
  • 若PostWrite中也有对streamContext的修改,要确保所有临界区的锁逻辑一致,避免其他潜在死锁场景。

内容的提问来源于stack exchange,提问作者Drake Wu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 06:54:55