-
Windows Minifilter 드라이버에서 PreAcquireForSectionSync와 FLT_PREOP_PENDING 충돌로 인한 BSOD 분석윈도우/커널 덤프분석 2025. 12. 15. 10:33반응형
개요
Windows 커널 모드 미니필터 드라이버 개발 중
RESOURCE_NOT_OWNED (0xE3)BSOD가 발생했습니다. 이 오류는PreAcquireForSectionSync콜백에서FLT_PREOP_PENDING을 반환했을 때 발생하는 리소스 소유권 문제입니다.본 포스트에서는 크래시 덤프 분석을 통해 원인을 파악하고, 섹션 생성 콜백에서의 올바른 I/O 처리 방법을 설명합니다.
문제 상황
증상
- 파일 시스템 필터 드라이버 로드 후 특정 파일 작업 시 시스템 크래시
- Bugcheck Code:
0xE3 (RESOURCE_NOT_OWNED) - 발생 시점: 메모리 매핑(MMAP) 파일 접근 시
크래시 덤프 분석
BUGCHECK_CODE: e3 BUGCHECK_P1: ffffa68c645613c0 (Address of resource) BUGCHECK_P2: ffffa68c653f3080 (Address of thread) BUGCHECK_P3: 0000000000000000 (Address of owner table) BUGCHECK_P4: 0000000000000002 PROCESS_NAME: Dbgview.exe FAILURE_BUCKET_ID: 0xE3_nt!ExpReleaseResourceSharedForThreadLite콜 스택 분석
STACK_TEXT: fffffe02`6bb385c8 fffff804`5b057e03 : nt!KeBugCheckEx fffffe02`6bb385d0 fffff804`5aecbbf3 : nt!ExpReleaseResourceSharedForThreadLite+0x18c133 fffffe02`6bb38690 fffff804`5e2a3c7d : nt!ExReleaseResourceLite+0xf3 fffffe02`6bb386f0 fffff804`5b255256 : Ntfs!NtfsReleaseForCreateSection+0x4d fffffe02`6bb38720 fffff804`5b2554fb : nt!FsRtlReleaseFile+0x156 fffffe02`6bb389e0 fffff804`5b25474d : nt!MiShareExistingControlArea+0x7f fffffe02`6bb38a10 fffff804`5b252e94 : nt!MiCreateImageOrDataSection+0x1ad fffffe02`6bb38b00 fffff804`5b254cc7 : nt!MiCreateSection+0xf4 fffffe02`6bb38c80 fffff804`5b254eac : nt!MiCreateSectionCommon+0x207 fffffe02`6bb38d60 fffff804`5b011505 : nt!NtCreateSection+0x5c fffffe02`6bb38dd0 00007ff9`fa40dee4 : nt!KiSystemServiceCopyEnd+0x25핵심 포인트:
Ntfs!NtfsReleaseForCreateSection- NTFS가 섹션 생성을 위해 획득한 리소스를 해제하려 함nt!MiCreateSection- 메모리 매핑 섹션 생성 중- 스레드가 소유하지 않은 리소스를 해제하려고 시도하여 BSOD 발생
원인 분석
문제의 코드
PreAcquireForSectionSync콜백에서 비동기 백업을 위해FLT_PREOP_PENDING을 반환한 것이 문제였습니다:FLT_PREOP_CALLBACK_STATUS RTCoreD_PreAcquireForSectionSync( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext ) { // ... 생략 ... // [문제 코드] 비동기 백업 큐에 추가하고 PENDING 반환 NTSTATUS statusQ = RTQueueBackupRequest( Data, FltObjects, hEffectivePid, &pNameInfo->Name, NULL, RT_OP_MODIFY, FALSE ); if (NT_SUCCESS(statusQ)) { FltReleaseContext(pStreamCtx); FltReleaseFileNameInformation(pNameInfo); return FLT_PREOP_PENDING; // ★ 이것이 문제! } // ... }왜 RESOURCE_NOT_OWNED가 발생하는가?
PreAcquireForSectionSync는 파일 시스템이 섹션 생성을 위해 파일 리소스 락(ERESOURCE)을 획득하기 직전에 호출됩니다.단계 정상 흐름 PENDING 반환 시 1 PreAcquireForSectionSync 호출 PreAcquireForSectionSync 호출 2 콜백에서 SUCCESS 반환 콜백에서 PENDING 반환 3 NTFS가 리소스 락 획득 I/O가 보류됨 (락 획득 안 됨) 4 섹션 생성 비동기 워커에서 I/O 완료 처리 5 NTFS가 리소스 락 해제 NTFS가 리소스 락 해제 시도 → BSOD! 문제의 핵심:
FLT_PREOP_PENDING반환 시, Filter Manager는 I/O를 보류 상태로 만듦- 하지만 NTFS는 이미 리소스 락을 획득했다고 가정하고
NtfsReleaseForCreateSection에서 해제 시도 - 소유하지 않은 리소스를 해제하려 하여 BSOD 발생
PreAcquireForSectionSync의 특수성
콜백 종류 FLT_PREOP_PENDING 지원 이유 PreCreate O 일반적인 IRP 기반 I/O PreWrite O 일반적인 IRP 기반 I/O PreCleanup △ (제한적) 파일 닫힘 직전, 복잡함 PreAcquireForSectionSync X 파일 시스템 리소스 락과 연동 PreRead O 일반적인 IRP 기반 I/O PreAcquireForSectionSync는 메모리 매니저와 파일 시스템 간의 동기화를 위한 특수 콜백으로, PENDING을 지원하지 않습니다.
해결 방법
수정된 코드
FLT_PREOP_PENDING대신 Fire-and-Forget 방식의RTQueueDeferredBackup을 사용합니다:FLT_PREOP_CALLBACK_STATUS RTCoreD_PreAcquireForSectionSync( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext ) { // ... 생략 ... if (bFirstModify && RT_DEFAULT_BACKUP_ENABLE && pStreamCtx->BackupDone == FALSE && (gRollbackInProgress == 0)) { // // [수정] RTQueueDeferredBackup으로 비동기 백업 요청 // PreAcquireForSectionSync에서는 PENDING 불가하므로 Deferred 방식 사용 // 워커가 FltCreateFileEx로 파일을 다시 열어 백업 수행 // NTSTATUS statusQ = RTQueueDeferredBackup( FltObjects->Filter, FltObjects->Instance, hEffectivePid, &pNameInfo->Name, NULL, RT_OP_MODIFY ); if (NT_SUCCESS(statusQ)) { pStreamCtx->BackupPending = TRUE; DbgPrint(RT_LOG_PREFIX "MMAP: Deferred backup QUEUED pid=%lu file=%wZ\n", (ULONG)(ULONG_PTR)hPid, &pNameInfo->Name); } } // ... 정상적으로 SUCCESS 반환 ... FltReleaseContext(pStreamCtx); FltReleaseFileNameInformation(pNameInfo); return FLT_PREOP_SUCCESS_NO_CALLBACK; }RTQueueBackupRequest vs RTQueueDeferredBackup 비교
특성 RTQueueBackupRequest RTQueueDeferredBackup 반환값 FLT_PREOP_PENDING 필요 즉시 반환 (Fire-and-Forget) I/O 보류 O (백업 완료까지 원본 I/O 대기) X (원본 I/O 즉시 진행) 원본 파일 보존 보장됨 보장 안 됨 (이미 변경될 수 있음) 사용 가능 콜백 PreCreate, PreWrite 등 모든 콜백 PreAcquireForSectionSync 사용 불가 사용 가능
설계 고려사항
Deferred 백업의 한계
RTQueueDeferredBackup은 I/O를 보류하지 않으므로, 백업 시점에 파일이 이미 변경되었을 수 있습니다:[타임라인] 1. PreAcquireForSectionSync 호출 2. RTQueueDeferredBackup으로 백업 요청 큐 추가 3. FLT_PREOP_SUCCESS 반환 → I/O 진행 4. MMAP 쓰기로 파일 변경됨 ★ 5. 백업 워커가 파일 열어서 백업 → 이미 변경된 파일 백업됨대안적 접근법
MMAP 케이스에서 원본 파일을 보존하려면:
- PreWrite에서 처리: MMAP 쓰기는 결국 PreWrite를 통해 디스크에 기록됨
- PostCreate에서 선제적 백업: 파일이 쓰기 모드로 열릴 때 미리 백업
- Copy-on-Write 전략: 페이지 폴트 핸들러 수준에서 처리 (복잡함)
정리
콜백별 FLT_PREOP_PENDING 지원 여부
콜백 PENDING 지원 권장 백업 방식 PreCreate O RTQueueBackupRequest PreWrite O RTQueueBackupRequest PreSetInformation O RTQueueBackupRequest PreCleanup △ RTQueueDeferredBackup PreAcquireForSectionSync X RTQueueDeferredBackup PostCreate X (PostOp) RTQueueDeferredBackup 핵심 교훈
- PreAcquireForSectionSync에서 FLT_PREOP_PENDING을 절대 사용하지 마세요
- 파일 시스템 리소스 락과 연동되어 RESOURCE_NOT_OWNED BSOD 발생
- 콜백의 특성을 이해하세요
- 모든 Pre 콜백이 PENDING을 지원하는 것은 아님
- 특히 동기화 관련 콜백(AcquireForSection, AcquireForModWrite 등)은 주의
- 크래시 덤프의 콜 스택을 분석하세요
Ntfs!NtfsReleaseForCreateSection이 보이면 섹션 관련 리소스 문제ExpReleaseResourceSharedForThreadLite는 ERESOURCE 소유권 문제
참고 자료
반응형'윈도우 > 커널 덤프분석' 카테고리의 다른 글
Windows Minifilter 드라이버에서 발생한 BSOD 0x7F (Double Fault) 커널 스택 오버플로우 분석 및 해결 (0) 2025.12.22 Windows 커널 드라이버에서 파일 시스템 콜백 내 로깅으로 인한 데드락 문제 (0) 2025.12.18 Windows Minifilter 드라이버에서 Fast I/O와 FLT_PREOP_PENDING 충돌 문제 해결 (0) 2025.12.14 윈도우 커널 드라이버를 서비스로 등록하고 실행/정지 시키는 방법 (0) 2025.05.15