-
Windows Minifilter 드라이버에서 Fast I/O와 FLT_PREOP_PENDING 충돌 문제 해결윈도우/커널 덤프분석 2025. 12. 14. 11:50반응형
개요
Windows 커널 모드 minifilter 드라이버를 개발하면서 Driver Verifier 환경에서 BSOD가 발생하는 문제를 겪었습니다. 이 글에서는 Fast I/O 경로에서
FLT_PREOP_PENDING을 반환할 때 발생하는 문제와 그 해결 방법을 공유합니다.
문제 상황
증상
- BSOD 코드:
SYSTEM_SERVICE_EXCEPTION (0x3B) - Exception 코드:
0x80000003(Breakpoint) - 발생 조건: Driver Verifier 활성화 상태에서 파일 쓰기 작업 수행 시
크래시 덤프 분석
STACK_TEXT: nt!DebugPrompt+0x17 nt!DbgPrompt+0x44 FLTMGR!FltpvPrintErrors+0x188 FLTMGR!FltpvVerifyPreOperationStatus+0x27f ← 여기서 검증 실패 FLTMGR!FltvPreOperation+0x290 FLTMGR!FltpPerformPreCallbacksWorker+0x36c FLTMGR!FltpPassThroughFastIo+0xc3 ← Fast I/O 경로 FLTMGR!FltpFastIoWrite+0x165 nt!IopWriteFile+0x137 nt!NtWriteFile+0xd0핵심은
FltpvVerifyPreOperationStatus- Filter Manager의 Verifier가 PreOperation 콜백의 반환값을 검증하다가 실패한 것입니다.
원인 분석
Fast I/O란?
Windows 파일 시스템에는 두 가지 I/O 경로가 있습니다:
구분 IRP-based I/O Fast I/O 처리 방식 IRP 패킷 생성 후 처리 직접 함수 호출 동기성 비동기 가능 항상 동기 용도 일반적인 I/O 캐시된 데이터 빠른 접근 PENDING 지원 ✅ 가능 ❌ 불가능 문제의 코드
저희 드라이버는 파일 변조 전 백업을 위해 비동기 큐 시스템을 사용했습니다:
// PreWrite 콜백에서... if (NT_SUCCESS(RTQueueBackupRequest(...))) { // 백업 큐에 추가 성공 → PENDING 반환 return FLT_PREOP_PENDING; // ← Fast I/O에서는 이게 문제! }IRP-based I/O에서는 이 방식이 정상 동작합니다. 하지만 Fast I/O는 동기적 작업이므로
FLT_PREOP_PENDING을 반환하면 안 됩니다.Driver Verifier는 이 규칙 위반을 감지하고 시스템을 중단시킨 것입니다.
해결 방법
FLT_IS_FASTIO_OPERATION 매크로 활용
Filter Manager는 현재 작업이 Fast I/O인지 확인하는 매크로를 제공합니다:
NTSTATUS RTQueueBackupRequest( _In_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, ... ) { // Paging I/O 체크 if (IsPagingIo) { return STATUS_NOT_SUPPORTED; } // // [핵심] Fast I/O는 PENDING 불가! // Fast I/O 경로에서 FLT_PREOP_PENDING 반환 시 Driver Verifier가 BSOD 발생 // Fast I/O는 동기적이어야 하므로 백업을 건너뛰고 I/O 진행 // if (FLT_IS_FASTIO_OPERATION(Data)) { DbgPrint("BackupQueue: Fast I/O - cannot PENDING, skipping backup\n"); return STATUS_NOT_SUPPORTED; } // IRP-based I/O만 큐에 추가하여 PENDING 처리 ... }호출부 수정
// PreWrite 콜백 NTSTATUS statusQ = RTQueueBackupRequest(Data, FltObjects, ...); if (NT_SUCCESS(statusQ)) { // IRP-based I/O만 여기 도달 return FLT_PREOP_PENDING; } // Fast I/O는 STATUS_NOT_SUPPORTED로 실패 → 백업 없이 진행
설계 고려사항
Fast I/O에서 백업을 건너뛰어도 괜찮을까?
결론: 대부분의 경우 괜찮습니다.
- 랜섬웨어 특성: 대부분의 랜섬웨어는 대용량 파일을 암호화하므로 IRP-based I/O를 사용합니다. Fast I/O는 주로 작은 캐시된 데이터 접근에 사용됩니다.
- 캐시 일관성: Fast I/O로 수정된 데이터도 결국 디스크에 쓰일 때는 IRP-based I/O를 거칩니다. 이때 백업이 수행됩니다.
- 성능 최적화: Fast I/O는 성능이 중요한 경로이므로 동기적 백업이 오히려 시스템 성능을 저하시킬 수 있습니다.
대안: 동기 백업
만약 Fast I/O에서도 반드시 백업이 필요하다면:
if (FLT_IS_FASTIO_OPERATION(Data)) { // 동기적으로 즉시 백업 수행 (PENDING 없이) RTBackupFileSync(FltObjects, OriginalPath); return FLT_PREOP_SUCCESS_WITH_CALLBACK; }단, 이 방식은 성능 저하를 유발할 수 있습니다.
추가 발견: IRQL 문제
같은 프로젝트에서 발견한 또 다른 BSOD 원인도 공유합니다.
문제
DRIVER_IRQL_NOT_LESS_OR_EQUAL (0xD1)- SpinLock 내에서 Paged Pool 메모리 접근원인
Fast I/O 경로에서 minifilter 콜백이 호출될 때, 파일 경로 정보(
UNICODE_STRING)가 Paged Pool에 있을 수 있습니다. SpinLock을 획득하면 IRQL이 DISPATCH_LEVEL로 상승하는데, 이 상태에서 Paged Pool에 접근하면 BSOD가 발생합니다.해결
SpinLock 획득 전에 데이터를 스택 버퍼(NonPaged)로 복사:
BOOLEAN RTIsSelfProtectedPath(PCUNICODE_STRING FilePath) { // [IRQL 안전] FilePath를 스택 버퍼에 복사 WCHAR LocalPathBuffer[520]; USHORT CopyLength; CopyLength = min(FilePath->Length, sizeof(LocalPathBuffer) - sizeof(WCHAR)); __try { RtlCopyMemory(LocalPathBuffer, FilePath->Buffer, CopyLength); } __except (EXCEPTION_EXECUTE_HANDLER) { return FALSE; } // 이제 SpinLock 획득 후에도 안전하게 접근 가능 KeAcquireSpinLock(&gProtectedPathLock, &OldIrql); // LocalPathBuffer 사용 (스택 = NonPaged) ... KeReleaseSpinLock(&gProtectedPathLock, OldIrql); }
정리
Minifilter PreOperation 콜백에서 주의할 점
상황 FLT_PREOP_PENDING 권장 처리 IRP-based I/O ✅ 가능 비동기 큐 사용 가능 Fast I/O ❌ 불가 동기 처리 또는 건너뛰기 Paging I/O ❌ 불가 항상 건너뛰기 IRQL 관련 주의사항
IRQL Paged Pool 접근 권장 처리 PASSIVE_LEVEL ✅ 가능 일반적인 처리 APC_LEVEL ✅ 가능 일반적인 처리 DISPATCH_LEVEL ❌ 불가 스택/NonPaged 버퍼 사용 디버깅 팁
- Driver Verifier 활용: 개발 초기부터 Driver Verifier를 켜고 테스트하면 잠재적 문제를 조기에 발견할 수 있습니다.
- FLT_IS_FASTIO_OPERATION 체크: PENDING을 반환하기 전에 항상 확인하세요.
- IRQL 체크: SpinLock 사용 전에 외부에서 전달된 포인터가 가리키는 데이터를 로컬 버퍼로 복사하세요.
참고 자료
반응형'윈도우 > 커널 덤프분석' 카테고리의 다른 글
Windows Minifilter 드라이버에서 발생한 BSOD 0x7F (Double Fault) 커널 스택 오버플로우 분석 및 해결 (0) 2025.12.22 Windows 커널 드라이버에서 파일 시스템 콜백 내 로깅으로 인한 데드락 문제 (0) 2025.12.18 Windows Minifilter 드라이버에서 PreAcquireForSectionSync와 FLT_PREOP_PENDING 충돌로 인한 BSOD 분석 (0) 2025.12.15 윈도우 커널 드라이버를 서비스로 등록하고 실행/정지 시키는 방법 (0) 2025.05.15 - BSOD 코드: