Injeção de falha baseada em pilha
Nota As instruções para habilitar esse recurso se aplicam somente ao WDK para Windows 8. Por Windows 8.1, esse recurso foi integrado ao Verificador de Driver. Em computadores que executam Windows 8.1, use a opção Simulação sistemática de poucos recursos.
A opção Injeção de Falha Baseada em Pilha injeta falhas de recurso em drivers de modo kernel. Essa opção usa um driver especial, KmAutoFail.sys, em conjunto com o Verificador de Driver para penetrar nos caminhos de tratamento de erros do driver. Testar esses caminhos historicamente tem sido muito difícil. A opção Injeção de Falha Baseada em Pilha injeta falhas de recursos de maneira previsível, o que torna os problemas que ele considera reproduzíveis. Como os caminhos de erro são fáceis de reproduzir, também facilita a verificação de correções para esses problemas.
Para ajudá-lo a determinar a causa raiz do erro, uma extensão do depurador é fornecida que pode informar exatamente quais falhas foram injetadas e em que ordem.
Quando a opção Injeção de Falha Baseada em Pilha está habilitada em um driver específico, ela intercepta algumas chamadas desse driver para o kernel e Ndis.sys. A Injeção de Falha Baseada em Pilha examina a pilha de chamadas, especificamente, na parte da pilha de chamadas proveniente do driver em que ela está habilitada. Se esta for a primeira vez que ele já viu essa pilha, ela falhará na chamada de acordo com a semântica dessa chamada. Caso contrário, se ele já viu essa chamada antes, ela passará intocada. Injeção de falha baseada em pilha contém lógica para lidar com o fato de que um driver pode ser carregado e descarregado várias vezes. Ele reconhecerá que uma pilha de chamadas é a mesma mesmo se o driver for recarregado em um local de memória diferente.
Ativando essa opção
Você pode ativar o recurso injeção de falha baseada em pilha para um ou mais drivers ao implantar um driver em um computador de teste. Você pode selecionar a opção Injeção de Falha Baseada em Pilha ao configurar as Propriedades do Verificador de Driver para Projetos de Pacote de Driver. Você deve reiniciar o computador para ativar ou desativar a opção Injeção de Falha Baseada em Pilha. Você também pode executar um utilitário de teste para habilitar o Verificador de Driver e esse recurso no computador de teste.
Importante Ao ativar a Injeção de Falha Baseada em Pilha no computador de teste, certifique-se de não selecionar também Simulação de Poucos Recursos.
Usando a página De propriedade do verificador de driver
- Abra as páginas de propriedades do pacote de driver. Clique com o botão direito do mouse no projeto do pacote de driver no Gerenciador de Soluções e selecione Propriedades.
- Nas páginas de propriedades do pacote de driver, clique em Propriedades de Configuração, em Instalação do Driver e em Verificador de Driver.
- Selecione Habilitar Verificador de Driver. Ao habilitar o Verificador de Driver no computador de teste, você pode optar por habilitar o Verificador de Driver para todos os drivers no computador, somente para o projeto de driver ou para uma lista de drivers especificados.
- Em Injetor de Falha Baseada em Pilha, selecione (marcar) Injeção de Falha Baseada em Pilha.
- Clique em Aplicar ou em OK.
- Consulte Implantando um driver em um computador de teste para obter mais informações. O computador de teste deve reiniciar para ativar essa opção.
Usando o teste Habilitar e Desabilitar o Verificador de Driver
Você também pode habilitar o Verificador de Driver executando um teste de utilitário. Siga as instruções descritas em Como testar um driver em runtime usando o Visual Studio. Na categoria de teste Todos os Testes\Verificador de Driver, selecione os testes Habilitar Verificador de Driver (possível reinicialização necessária) e Desabilitar Verificador de Driver (possível reinicialização necessária).
Selecione as opções verificador de driver clicando no nome do teste Habilitar Verificador de Driver (possível reinicialização necessária) na janela Grupo de Testes de Driver .
Selecione (marcar) Injeção de falha baseada em pilha.
Depois de adicionar esses testes a um grupo de teste, você pode salvar o grupo de teste. Para habilitar a Injeção de Falha Baseada em Pilha, execute o teste Habilitar Verificador de Driver (possível reinicialização necessária) no computador que você configurou para teste.
Para desativar o Verificador de Driver, execute o teste Desabilitar Verificador de Driver (possível reinicialização necessária).
Usando a opção injeção de falha baseada em pilha
Uma consideração importante ao testar com a Injeção de Falha Baseada em Pilha é que a maioria dos bugs encontrados resultará em um bug marcar. Isso pode ser um pouco doloroso se o driver for um driver carregado de inicialização. Por isso, desabilitaremos automaticamente a injeção de falha baseada em pilha se o Verificador de Driver estiver desabilitado. Isso significa que você pode desabilitar a Injeção de Falha Baseada em Pilha no momento da inicialização do depurador desabilitando o Verificador de Driver usando o comando !verifier –disable.
Se for possível, para seus testes iniciais com Injeção de Falha Baseada em Pilha, configure o driver para que ele não seja carregado no momento da inicialização. Em seguida, você pode executar alguns testes de carregamento e descarregamento simples. Muitos dos bugs encontrados pela injeção de falha baseada em pilha ocorrem durante a inicialização ou limpeza. Carregar e descarregar repetidamente o driver é uma boa maneira de encontrá-los.
Depois de fazer as correções necessárias para que os testes de descarregamento de carga sejam bem-sucedidos, você pode passar para testes baseados em IOCTL, testes funcionais completos e, por fim, teste de estresse. Em geral, se você seguir essa progressão de teste, não descobrirá muitos problemas novos durante o teste de estresse, pois a maioria dos caminhos de código já terá sido executada antes disso.
Usando a extensão do depurador SBFI (injeção de falha baseada em pilha)
A maioria dos problemas encontrados com a injeção de falha baseada em pilha resulta em verificações de bugs. Para ajudar a determinar a causa desses bugs de código, o WDK fornece a extensão do depurador de injeção de falha baseada em pilha e os símbolos necessários. O procedimento de instalação instalará ambos no sistema do depurador. O local padrão é C:\Program Files (x86)\Windows Kits\8.0\Debuggers\<arch>.
Para executar a extensão do depurador
No prompt de comando do depurador, digite o seguinte comando: !<path>\kmautofaildbg.dll.autofail. Por exemplo, supondo que as extensões do depurador estejam instaladas em c:\dbgext e que kmautofail.pdb esteja no caminho do símbolo, você digitaria o seguinte comando:
!c:\dbgext\kmautofaildbg.dll.autofail
Isso despejará informações para o depurador mostrando as pilhas de chamadas das falhas mais recentes injetadas. Cada entrada é semelhante à seguinte, obtida de uma execução de teste real. No exemplo a seguir, a injeção de falha baseada em pilha está habilitada no 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
Na parte superior da saída, o número de sequência conta o número de falhas injetadas. Este exemplo mostra a segunda falha injetada durante esta execução de teste. A ID do processo é 0, portanto, esse foi o processo do sistema. IRQL é 2, portanto, isso é chamado no nível de expedição.
Na pilha, KmAutoFail é o driver de injeção de falha baseada em pilha. O nome da função KmAutoFail indica qual chamada de função do Mydriver.sys foi interceptada e a falha foi injetada. Aqui, a função que falhou foi ExAllocatePoolWithTag. Todas as funções no KmAutoFail que interceptam chamadas para Ntoskrnl.sys ou Ndis.sys usam essa convenção de nomenclatura. Em seguida, vemos a pilha de chamadas com o driver sendo testado (Mydriver.sys). Essa é a parte da pilha de chamadas usada para determinar a exclusividade da pilha. Portanto, cada entrada despejada pela extensão do depurador será exclusiva nesta parte da pilha de chamadas. O restante da pilha de chamadas indica quem chamou o driver. A importância main disso é se o driver é chamado do modo de usuário (por meio de um IOCTL) ou de um driver de modo kernel.
Observe que, se um driver tiver retornado uma falha de sua rotina DriverEntry , uma tentativa de recarregamento normalmente ocorrerá em um local de memória diferente. Nesse caso, a pilha de chamadas do local anterior provavelmente conterá "lixo" em vez de informações de pilha do driver. Mas isso não é um problema; ele informa que o driver lidou corretamente com a falha injetada.
Essa próxima entrada mostra uma chamada para o driver por meio de um IOCTL do modo de usuário. Observe a ID do processo e o nível do IRQ. Como Mydriver.sys é um driver de filtro NDIS, o IOCTL passou por Ndis.sys. Observe que nt! NtDeviceIoControlFile está na pilha. Qualquer teste executado no driver que usa IOCTLs passará por essa função.
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
Analisando os resultados da injeção de falha baseada em pilha
Você está executando seus testes em seu driver, e de repente você teve um problema. Provavelmente foi um bug marcar, mas também pode ser porque o computador não respondeu. Como você encontra a causa? Supondo que seja um bug marcar, primeiro use a extensão acima para localizar a lista de falhas injetadas e, em seguida, use o comando do depurador: !analyze –v.
O bug mais comum marcar é causado por não verificar o sucesso de uma alocação. Nesse caso, a pilha do bug marcar análise provavelmente é quase idêntica à da última falha injetada. Em algum momento após a alocação com falha (geralmente a próxima linha), o driver acessará o ponteiro nulo. Esse tipo de bug é muito fácil de corrigir. Às vezes, a alocação com falha é uma ou duas acima da lista, mas esse tipo ainda é muito fácil de localizar e corrigir.
O segundo bug mais comum marcar ocorre durante a limpeza. Nesse caso, o driver provavelmente detectou a falha de alocação e saltou para limpeza; mas durante a limpeza, o driver não marcar o ponteiro e mais uma vez acessou um ponteiro nulo. Um caso intimamente relacionado é onde a limpeza pode ser chamada duas vezes. Se a limpeza não definir o ponteiro como uma estrutura como nula depois de liberá-la, na segunda vez que a função de limpeza for chamada, ela tentará liberar a estrutura uma segunda vez, resultando em um bug marcar.
Erros que fazem com que o computador não responda são mais difíceis de diagnosticar, mas o procedimento para depurá-los é semelhante. Esses erros geralmente são causados por problemas de contagem de referência ou de bloqueio de rotação. Felizmente, o Verificador de Driver detectará muitos problemas de bloqueio de rotação antes que eles levem a problemas. Nesses casos, interrompa o depurador e use a extensão do depurador para despejar a lista de falhas que foram injetadas pela injeção de falha baseada em pilha. Uma rápida olhada no código em torno das falhas mais recentes pode mostrar uma contagem de referência que é feita antes da falha, mas não liberada depois. Caso contrário, procure um thread no driver que esteja aguardando um bloqueio de rotação ou por qualquer contagem de referência obviamente errada.