Inyección de errores basada en pila
Nota Las instrucciones para habilitar esta característica solo se aplican al WDK para Windows 8. Para Windows 8.1, esta característica se ha integrado en el Comprobador de controladores. En los equipos que ejecutan Windows 8.1, use la opción simulación de recursos bajos sistemáticos.
La opción Inserción de errores basada en pila inserta errores de recursos en los controladores del modo kernel. Esta opción usa un controlador especial, KmAutoFail.sys, junto con el Comprobador de controladores para penetrar las rutas de acceso de control de errores del controlador. La prueba de estas rutas ha sido históricamente muy difícil. La opción Inyección de errores basada en pila inserta errores de recursos de forma predecible, lo que hace que los problemas que encuentre reproducibles. Dado que las rutas de acceso de error son fáciles de reproducir, también facilita la comprobación de las correcciones de estos problemas.
Para ayudarle a determinar la causa principal del error, se proporciona una extensión del depurador que puede indicar exactamente qué errores se han insertado y en qué orden.
Cuando la opción Inserción de errores basada en pila está habilitada en un controlador específico, intercepta algunas llamadas de ese controlador al kernel y Ndis.sys. La inyección de errores basada en pila examina la pila de llamadas, en concreto, en la parte de la pila de llamadas que procede del controlador en el que está habilitado. Si es la primera vez que ha visto esa pila, se producirá un error en la llamada según la semántica de esa llamada. De lo contrario, si ha visto esa llamada antes, la pasará sin modificar. La inyección de errores basada en pila contiene lógica para tratar con el hecho de que un controlador se puede cargar y descargar varias veces. Reconocerá que una pila de llamadas es la misma aunque el controlador se vuelva a cargar en una ubicación de memoria diferente.
Activación de esta opción
Puede activar la característica Inserción de errores basada en pila para uno o varios controladores al implementar un controlador en un equipo de prueba. Puede seleccionar la opción Inserción de errores basada en pila al configurar las propiedades del comprobador de controladores para proyectos de paquete de controladores. Debe reiniciar el equipo para activar o desactivar la opción Inserción de errores basada en pila. También puede ejecutar una utilidad de prueba para habilitar el Comprobador de controladores y esta característica en el equipo de prueba.
Importante Al activar la inserción de errores basados en pila en el equipo de prueba, asegúrese de no seleccionar también Simulación de recursos bajos.
Uso de la página de propiedades del comprobador de controladores
- Abra las páginas de propiedades del paquete de controladores. Haga clic con el botón derecho en el proyecto de paquete de controladores en Explorador de soluciones y seleccione Propiedades.
- En las páginas de propiedades del paquete de controladores, haga clic en Propiedades de configuración, en Instalación del controlador y, a continuación, en Comprobador de controladores.
- Seleccione Habilitar comprobador de controladores. Al habilitar Driver Verifier en el equipo de prueba, puede optar por habilitar Driver Verifier para todos los controladores del equipo, solo para el proyecto de controlador o para una lista de controladores especificados.
- En Inyector de error basado en pila, seleccione (comprobar) Inserción de errores basada en pila.
- Haga clic en Aplicar o en Aceptar.
- Consulte Implementación de un controlador en un equipo de prueba para obtener más información. El equipo de prueba debe reiniciarse para activar esta opción.
Uso de la prueba Habilitar y deshabilitar comprobador de controladores
También puede habilitar Driver Verifier ejecutando una prueba de utilidad. Siga las instrucciones que se describen en Cómo probar un controlador en tiempo de ejecución mediante Visual Studio. En la categoría De prueba Todas las pruebas\Comprobador de controladores, seleccione las pruebas Habilitar comprobador de controladores (posible reinicio necesario) y Deshabilitar comprobador de controladores (posible reinicio necesario).
Seleccione las opciones Comprobador de controladores haciendo clic en el nombre de la prueba Habilitar comprobador de controladores (posible reinicio necesario) en la ventana Grupo de pruebas de controladores .
Seleccione (comprobar) Inserción de errores basada en pila.
Después de agregar estas pruebas a un grupo de pruebas, puede guardar el grupo de pruebas. Para habilitar la inserción de errores basados en pila, ejecute la prueba Habilitar comprobador de controladores (posible reinicio necesario) en el equipo que ha configurado para realizar pruebas.
Para desactivar el Comprobador de controladores, ejecute la prueba Deshabilitar comprobador de controladores (posible reinicio necesario).
Uso de la opción Inserción de errores basada en pila
Una consideración importante al probar con la inyección de errores basada en pila es que la mayoría de los errores que encuentra darán lugar a una comprobación de errores. Esto podría resultar algo doloroso si el controlador es un controlador cargado de arranque. Por este motivo, deshabilitaremos automáticamente la inserción de errores basada en pila si el comprobador del controlador está deshabilitado. Esto significa que puede deshabilitar la inserción de errores basada en pila en tiempo de arranque desde el depurador si deshabilita el comprobador de controladores mediante el comando !comprobador –disable.
Si es posible, para las pruebas iniciales con inyección de errores basada en pila, configure el controlador para que no se cargue en tiempo de arranque. Después, puede ejecutar algunas pruebas de carga y descarga sencillas. Muchos de los errores encontrados por la inserción de errores basados en pila se producen durante la inicialización o limpieza. Cargar y descargar repetidamente el controlador es una buena manera de encontrarlos.
Después de realizar las correcciones necesarias para que las pruebas de descarga de carga se realicen correctamente, puede pasar a pruebas basadas en IOCTL, pruebas funcionales completas y, por último, pruebas de esfuerzo. En general, si sigue esta progresión de prueba, no descubrirá muchos problemas nuevos durante las pruebas de esfuerzo, ya que la mayoría de las rutas de acceso de código ya se ejecutarán antes de esto.
Uso de la extensión del depurador de inyección de errores basada en pila (SBFI)
La mayoría de los problemas encontrados con la inyección de errores basada en pila dan como resultado comprobaciones de errores. Para ayudar a determinar la causa de estos errores de código, WDK proporciona la extensión del depurador de inserción de errores basado en pila y los símbolos necesarios. El procedimiento de instalación instalará ambos en el sistema del depurador. La ubicación predeterminada es C:\Archivos de programa (x86)\Windows Kits\8.0\Debuggers\<arch>.
Para ejecutar la extensión del depurador
En el símbolo del sistema del depurador, escriba el siguiente comando: !<path>\kmautofaildbg.dll.autofail. Por ejemplo, suponiendo que las extensiones del depurador están instaladas en c:\dbgext y que kmautofail.pdb está en la ruta de acceso del símbolo, escribiría el siguiente comando:
!c:\dbgext\kmautofaildbg.dll.autofail
Esto volcará la información en el depurador en la que se muestran las pilas de llamadas de los errores más recientes insertados. Cada entrada tiene un aspecto similar al siguiente, tomado de una ejecución de prueba real. En el ejemplo siguiente, la inserción de errores basada en pila está habilitada en 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
En la parte superior de la salida, el número de secuencia cuenta el número de errores que se insertan. En este ejemplo se muestra el segundo error insertado durante esta ejecución de prueba. El identificador de proceso es 0, por lo que este fue el proceso del sistema. IRQL es 2, por lo que se denomina en el nivel de envío.
Desde la pila, KmAutoFail es el controlador de inyección de errores basado en pila. El nombre de la función KmAutoFail indica qué llamada de función de Mydriver.sys se interceptó y se insertó un error. Aquí, la función que produjo un error fue ExAllocatePoolWithTag. Todas las funciones de KmAutoFail que interceptan llamadas a Ntoskrnl.sys o Ndis.sys usan esta convención de nomenclatura. A continuación, vemos la pila de llamadas con el controlador que se está probando (Mydriver.sys). Esta es la parte de la pila de llamadas que se usa para determinar la unicidad de la pila. Por lo tanto, cada entrada volcada por la extensión del depurador será única en esta parte de la pila de llamadas. El resto de la pila de llamadas indica quién llamó al controlador. La importancia principal de esto es si se llama al controlador desde el modo de usuario (mediante un IOCTL) o desde un controlador en modo kernel.
Tenga en cuenta que si un controlador ha devuelto un error de su rutina DriverEntry , normalmente se realizará un intento de recarga en una ubicación de memoria diferente. En ese caso, la pila de llamadas de la ubicación anterior probablemente contendrá "elementos no utilizados" en lugar de la información de pila del controlador. Pero esto no es un problema; indica que el controlador controló correctamente ese error insertado.
En esta entrada siguiente se muestra una llamada al controlador mediante un IOCTL desde el modo de usuario. Anote el identificador de proceso y el nivel de IRQ. Dado que Mydriver.sys es un controlador de filtro NDIS, el IOCTL llegó a través de Ndis.sys. Tenga en cuenta que nt! NtDeviceIoControlFile está en la pila. Cualquier prueba que ejecute en el controlador que use ICTLs pasará por esta función.
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
Análisis de los resultados de la inyección de errores basada en pila
Está ejecutando las pruebas en el controlador y, de repente, se produce un problema. Lo más probable es que se trate de una comprobación de errores, pero también podría deberse a que el equipo no responde. ¿Cómo encuentras la causa? Suponiendo que se trata de una comprobación de errores, use primero la extensión anterior para buscar la lista de errores insertados y, a continuación, use el comando del depurador: !analyze –v.
La comprobación de errores más común se debe a que no se comprueba el éxito de una asignación. En este caso, la pila del análisis de comprobación de errores probablemente sea casi idéntica a la del último error insertado. En algún momento después de la asignación con errores (a menudo la siguiente línea), el controlador accederá al puntero nulo. Este tipo de error es muy fácil de corregir. A veces, la asignación con errores es una o dos de la lista, pero este tipo sigue siendo muy fácil de encontrar y corregir.
La segunda comprobación de errores más común se produce durante la limpieza. En este caso, el controlador probablemente detectó el error de asignación y saltó a la limpieza; pero durante la limpieza, el controlador no ha comprobado el puntero y, una vez más, ha accedido a un puntero nulo. Un caso estrechamente relacionado es donde se puede llamar a limpieza dos veces. Si la limpieza no establece el puntero en una estructura en NULL después de liberarla, la segunda vez que se llama a la función de limpieza intentará liberar la estructura una segunda vez, lo que dará lugar a una comprobación de errores.
Los errores que hacen que el equipo deje de responder son más difíciles de diagnosticar, pero el procedimiento para depurarlos es similar. Estos errores suelen deberse a problemas de recuento de referencias o bloqueo de número de número. Afortunadamente, el Comprobador de controladores detectará muchos problemas de bloqueo de número antes de que se produzcan problemas. En estos casos, interrumpa en el depurador y use la extensión del depurador para volcar la lista de errores insertados por la inyección de errores basada en pila. Un vistazo rápido al código en torno a los errores más recientes podría mostrar un recuento de referencias que se toma antes del error, pero que no se libera después. Si no es así, busque un subproceso en el controlador que esté esperando un bloqueo de giro o para cualquier recuento de referencias que obviamente sea incorrecto.