Share via


스택 기반 오류 삽입

참고 이 기능을 사용하도록 설정하는 지침은 Windows 8 WDK에만 적용됩니다. Windows 8.1 이 기능은 드라이버 검증 도구에 통합되었습니다. Windows 8.1 실행하는 컴퓨터에서 체계적인 낮은 리소스 시뮬레이션 옵션을 사용합니다.

스택 기반 실패 삽입 옵션은 커널 모드 드라이버에 리소스 오류를 삽입합니다. 이 옵션은 드라이버 검증 도구 와 함께 특수 드라이버 KmAutoFail.sys 사용하여 드라이버 오류 처리 경로를 관통합니다. 이러한 경로를 테스트하는 것은 역사적으로 매우 어려웠습니다. 스택 기반 오류 주입 옵션은 리소스 오류를 예측 가능한 방식으로 삽입하므로 문제를 재현할 수 있습니다. 오류 경로는 쉽게 재현할 수 있으므로 이러한 문제에 대한 수정 사항을 쉽게 확인할 수 있습니다.

오류의 근본 원인을 파악하는 데 도움이 되도록 삽입된 오류와 순서를 정확하게 알 수 있는 디버거 확장이 제공됩니다.

특정 드라이버에서 스택 기반 오류 주입 옵션을 사용하도록 설정하면 해당 드라이버에서 커널로의 일부 호출을 가로채고 Ndis.sys. 스택 기반 오류 주입은 호출 스택, 특히 호출 스택이 사용하도록 설정된 드라이버에서 제공되는 호출 스택 부분을 확인합니다. 해당 스택을 처음 본 경우 해당 호출의 의미 체계에 따라 호출이 실패합니다. 그렇지 않으면 이전에 해당 호출을 본 경우 그대로 전달됩니다. 스택 기반 오류 주입에는 드라이버를 여러 번 로드하고 언로드할 수 있다는 사실을 처리하는 논리가 포함되어 있습니다. 드라이버가 다른 메모리 위치에 다시 로드되더라도 호출 스택이 동일하다는 것을 인식합니다.

이 옵션 활성화

테스트 컴퓨터에 드라이버를 배포할 때 하나 이상의 드라이버에 대해 스택 기반 오류 주입 기능을 활성화할 수 있습니다. 드라이버 패키지 프로젝트에 대한 드라이버 검증 도구 속성을 구성할 때 스택 기반 오류 주입 옵션을 선택할 수 있습니다. 스택 기반 오류 주입 옵션을 활성화하거나 비활성화하려면 컴퓨터를 다시 시작해야 합니다. 테스트 유틸리티를 실행하여 테스트 컴퓨터에서 드라이버 검증 도구 및 이 기능을 사용하도록 설정할 수도 있습니다.

중요 테스트 컴퓨터에서 스택 기반 오류 주입을 활성화하는 경우 리소스 부족 시뮬레이션도 선택하지 않도록 합니다.

  • 드라이버 검증 도구 속성 사용 페이지

    1. 드라이버 패키지의 속성 페이지를 엽니다. 솔루션 탐색기 드라이버 패키지 프로젝트를 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.
    2. 드라이버 패키지의 속성 페이지에서 구성 속성을 클릭하고 드라이버 설치를 클릭한 다음 드라이버 검증 도구를 클릭합니다.
    3. 드라이버 검증 도구 사용을 선택합니다. 테스트 컴퓨터에서 드라이버 검증 도구를 사용하도록 설정하면 컴퓨터의 모든 드라이버, 드라이버 프로젝트 전용 또는 지정된 드라이버 목록에 대해 드라이버 검증 도구를 사용하도록 선택할 수 있습니다.
    4. 스택 기반 오류 삽입기에서 스택 기반 오류 주입을 선택합니다(검사).
    5. 적용 또는 확인을 클릭합니다.
    6. 자세한 내용은 테스트 컴퓨터에 드라이버 배포 를 참조하세요. 이 옵션을 활성화하려면 테스트 컴퓨터를 다시 시작해야 합니다.
  • 드라이버 검증 도구 사용 및 사용 안 함 테스트 사용

    1. 유틸리티 테스트를 실행하여 드라이버 검증 도구를 사용하도록 설정할 수도 있습니다. Visual Studio를 사용하여 런타임에 드라이버를 테스트하는 방법에 설명된 지침을 따릅니다. 모든 테스트\드라이버 검증 도구 테스트 범주에서 드라이버 검증 도구 사용(다시 부팅 필요 가능)드라이버 검증 도구 사용 안 함(다시 부팅 필요 가능) 테스트를 선택합니다.

    2. 드라이버 테스트 그룹 창에서 드라이버 검증 도구 사용(가능한 재부팅 필요) 테스트의 이름을 클릭하여 드라이버 검증 도구 옵션을 선택합니다.

    3. 스택 기반 오류 주입(검사)을 선택합니다.

    4. 이러한 테스트를 테스트 그룹에 추가한 후 테스트 그룹을 저장할 수 있습니다. 스택 기반 오류 주입을 사용하도록 설정하려면 테스트를 위해 구성한 컴퓨터에서 드라이버 검증 도구 사용(필요한 재부팅 가능) 테스트를 실행합니다.

      드라이버 검증 도구를 비활성화하려면 드라이버 검증 도구 사용 안 함(다시 부팅 필요 가능) 테스트를 실행합니다.

스택 기반 오류 삽입 옵션 사용

스택 기반 오류 주입을 사용하여 테스트할 때 한 가지 중요한 고려 사항은 발견한 대부분의 버그로 인해 버그 검사 발생한다는 것입니다. 드라이버가 부팅 로드 드라이버인 경우 이는 다소 고통스러울 수 있습니다. 이 때문에 드라이버 검증 도구가 사용하지 않도록 설정된 경우 스택 기반 오류 주입을 자동으로 사용하지 않도록 설정합니다. 즉, !verifier –disable 명령을 사용하여 드라이버 검증 도구 를 사용하지 않도록 설정하여 부팅 시 디버거에서 스택 기반 오류 삽입을 사용하지 않도록 설정할 수 있습니다.

가능한 경우 스택 기반 오류 주입을 사용한 초기 테스트의 경우 부팅 시 로드되지 않도록 드라이버를 설정합니다. 그런 다음 몇 가지 간단한 부하 및 언로드 테스트를 실행할 수 있습니다. 스택 기반 오류 주입에서 발견된 대부분의 버그는 초기화 또는 정리 중에 발생합니다. 드라이버를 반복적으로 로드하고 언로드하는 것은 이러한 항목을 찾는 좋은 방법입니다.

부하 언로드 테스트를 성공하는 데 필요한 수정을 수행한 후 IOCTL 기반 테스트, 전체 기능 테스트 및 마지막으로 스트레스 테스트로 이동할 수 있습니다. 일반적으로 이 테스트 진행을 따르는 경우 대부분의 코드 경로가 이전에 이미 실행되었기 때문에 스트레스 테스트 중에 많은 새로운 문제를 발견하지 못할 것입니다.

SBFI(스택 기반 오류 삽입) 디버거 확장 사용

스택 기반 오류 주입과 관련된 대부분의 문제는 버그 검사를 초래합니다. 이러한 코드 버그의 원인을 파악하기 위해 WDK는 스택 기반 오류 주입 디버거 확장 및 필요한 기호를 제공합니다. 설치 절차는 디버거 시스템에 둘 다 설치됩니다. 기본 위치는 C:\Program Files (x86)\Windows Kits\8.0\Debuggers\<arch입니다>.

디버거 확장을 실행하려면

  • 디버거 명령 프롬프트에서 다음 명령을 입력합니다. !<path>\kmautofaildbg.dll.autofail. 예를 들어 디버거 확장이 c:\dbgext에 설치되고 kmautofail.pdb가 기호 경로에 있다고 가정하면 다음 명령을 입력합니다.

    !c:\dbgext\kmautofaildbg.dll.autofail
    

그러면 삽입된 가장 최근 오류의 호출 스택을 보여 주는 정보가 디버거에 덤프됩니다. 각 항목은 실제 테스트 실행에서 가져온 다음과 같습니다. 다음 예제에서는 스택 기반 오류 삽입이 Mydriver.sys

Sequence: 2, Test Number: 0, Process ID: 0, Thread ID: 0
                 IRQ Level: 2, HASH: 0xea98a56083aae93c
 0xfffff8800129ed83 kmautofail!ShimHookExAllocatePoolWithTag+0x37
 0xfffff88003c77566 mydriver!AddDestination+0x66
 0xfffff88003c5eeb2 mydriver!ProcessPacketDestination+0x82
 0xfffff88003c7db82 mydriver!ProcessPacketSource+0x8b2
 0xfffff88003c5d0d8 mydriver!ForwardPackets+0xb8
 0xfffff88003c81102 mydriver!RoutePackets+0x142
 0xfffff88003c83826 mydriver!RouteNetBufferLists+0x306
 0xfffff88003c59a76 mydriver!DeviceSendPackets+0x156
 0xfffff88003c59754 mydriver!ProcessingComplete+0x4a4
 0xfffff88001b69b81 systemdriver2!ProcessEvent+0x1a1
 0xfffff88001b3edc4 systemdriver1!CallChildDriver+0x20
 0xfffff88001b3fc0a systemdriver1!ProcessEvent+0x3e
 0xfffff800c3ea6eb9 nt!KiRetireDpcList+0x209
 0xfffff800c3ea869a nt!KiIdleLoop+0x5a

출력 맨 위에 있는 시퀀스 번호는 삽입된 오류 수를 계산합니다. 이 예제에서는 이 테스트 실행 중에 삽입된 두 번째 오류를 보여줍니다. 프로세스 ID는 0이므로 시스템 프로세스였습니다. IRQL은 2이므로 디스패치 수준에서 호출됩니다.

스택에서 KmAutoFail은 스택 기반 오류 주입 드라이버입니다. KmAutoFail 함수 이름은 가로채고 오류가 주입된 Mydriver.sys 함수 호출을 나타냅니다. 여기서 실패한 함수는 ExAllocatePoolWithTag였습니다. Ntoskrnl.sys 또는 Ndis.sys 호출을 가로채는 KmAutoFail의 모든 함수는 이 명명 규칙을 사용합니다. 다음으로 테스트 중인 드라이버가 있는 호출 스택(Mydriver.sys)이 표시됩니다. 이는 스택의 고유성을 결정하는 데 사용되는 호출 스택의 부분입니다. 따라서 디버거 확장에 의해 덤프된 각 항목은 호출 스택의 이 부분에서 고유합니다. 나머지 호출 스택은 드라이버를 호출한 사용자를 나타냅니다. 이 기본 중요도는 드라이버가 사용자 모드(IOCTL을 통해)에서 호출되는지 또는 커널 모드 드라이버에서 호출되는지 여부입니다.

드라이버가 DriverEntry 루틴에서 오류를 반환한 경우 일반적으로 다시 로드 시도는 다른 메모리 위치에서 수행됩니다. 이 경우 이전 위치의 호출 스택에는 드라이버의 스택 정보가 아닌 "가비지"가 포함될 수 있습니다. 그러나 이것은 문제가 되지 않습니다. 드라이버가 삽입된 오류를 올바르게 처리했음을 알려줍니다.

다음 항목에서는 사용자 모드에서 IOCTL을 통해 드라이버에 대한 호출을 보여 줍니다. 프로세스 ID 및 IRQ 수준을 확인합니다. Mydriver.sys NDIS 필터 드라이버이므로 IOCTL은 Ndis.sys 통과했습니다. 참고로 nt! NtDeviceIoControlFile이 스택에 있습니다. IOCTL을 사용하는 드라이버에서 실행하는 모든 테스트는 이 함수를 통과합니다.

Sequence: 5, Test Number: 0, Process ID: 2052, Thread ID: 4588
                 IRQ Level: 0, HASH: 0xecd4650e9c25ee4
 0xfffff8800129ed83 kmautofail!ShimHookExAllocatePoolWithTag+0x37
 0xfffff88003c6fb39 mydriver!SendMultipleOids+0x41
 0xfffff88003c7157b mydriver!PvtDisconnect+0x437
 0xfffff88003c71069 mydriver!NicDisconnect+0xd9
 0xfffff88003ca3538 mydriver!NicControl+0x10c
 0xfffff88003c99625 mydriver!DeviceControl+0x4c5
 0xfffff88001559d93 NDIS!ndisDummyIrpHandler+0x73
 0xfffff88001559339 NDIS!ndisDeviceControlIrpHandler+0xc9
 0xfffff800c445cc96 nt!IovCallDriver+0x3e6
 0xfffff800c42735ae nt!IopXxxControlFile+0x7cc
 0xfffff800c4274836 nt!NtDeviceIoControlFile+0x56
 0xfffff800c3e74753 nt!KiSystemServiceCopyEnd+0x13

스택 기반 오류 주입 결과 분석

드라이버에서 테스트를 실행하고 갑자기 문제가 발생했습니다. 대부분의 경우 이는 버그 검사 컴퓨터가 응답하지 않게 되었기 때문일 수도 있습니다. 원인을 찾으려면 어떻게 해야 합니까? 버그 검사 가정하고 먼저 위의 확장을 사용하여 삽입된 오류 목록을 찾은 다음 디버거 명령 !analyze –v를 사용합니다.

가장 일반적인 버그 검사 할당의 성공을 확인하지 않아 발생합니다. 이 경우 버그 검사 분석의 스택은 주입된 마지막 오류와 거의 동일합니다. 할당이 실패한 후(종종 다음 줄) 드라이버가 null 포인터에 액세스합니다. 이 유형의 버그를 수정하기가 매우 쉽습니다. 경우에 따라 실패한 할당이 목록의 하나 또는 두 개이지만 이 형식은 여전히 찾기 쉽고 수정할 수 있습니다.

두 번째로 일반적인 버그 검사 정리 중에 발생합니다. 이 경우 드라이버는 할당 오류를 감지하고 정리로 뛰어올랐을 것입니다. 그러나 정리하는 동안 드라이버가 포인터를 검사 않았고 다시 한 번 null 포인터에 액세스했습니다. 밀접하게 관련된 사례는 정리를 두 번 호출할 수 있는 경우입니다. 정리가 해제된 후 구조체에 대한 포인터를 null로 설정하지 않으면 정리 함수가 호출될 때 두 번째로 구조체를 해제하여 버그 검사 발생합니다.

컴퓨터가 응답하지 않는 오류는 진단하기가 더 어렵지만 디버그 절차는 비슷합니다. 이러한 오류는 종종 참조 수 또는 스핀 잠금 문제로 인해 발생합니다. 다행히 드라이버 검증 도구 는 문제가 발생하기 전에 많은 스핀 잠금 문제를 catch합니다. 이러한 경우 디버거에 침입하고 디버거 확장을 사용하여 스택 기반 오류 주입에 의해 주입된 오류 목록을 덤프합니다. 최신 오류와 관련한 코드를 빠르게 살펴보면 오류 전에 수행되었지만 이후에 릴리스되지 않은 참조 수가 표시될 수 있습니다. 그렇지 않은 경우 스핀 잠금을 기다리는 드라이버의 스레드 또는 분명히 잘못된 참조 수를 찾습니다.