Comparteix via


Anotaciones de IRQL para controladores

Todos los desarrolladores de controladores deben tener en cuenta los niveles de solicitud de interrupción (IRQLs). Un IRQL es un entero entre 0 y 31; PASSIVE_LEVEL, DISPATCH_LEVEL y APC_LEVEL normalmente se mencionan de forma simbólica, mientras que los otros se mencionan por sus valores numéricos. La elevación y reducción del IRQL debe seguir una estricta disciplina de pila. Una función debe tener como objetivo retornar al mismo IRQL en el que fue llamada. Los valores IRQL deben no disminuir en la pila. Y una función no puede reducir el IRQL sin aumentarlo primero. Las anotaciones IRQL están diseñadas para ayudar a aplicar esas reglas.

Cuando el código del controlador tiene anotaciones IRQL, las herramientas de análisis de código pueden hacer una mejor inferencia sobre el intervalo de niveles en el que se debe ejecutar una función y pueden encontrar errores con mayor precisión. Por ejemplo, puede agregar anotaciones que especifiquen el IRQL máximo en el que se puede llamar a una función; Si se llama a una función en un IRQL superior, las herramientas de análisis de código pueden identificar las incoherencias.

Las funciones del controlador deben anotarse con tanta información sobre IRQL como sea apropiada. Si la información adicional está disponible, ayuda a las herramientas de análisis de código en la comprobación posterior de la función de llamada y la función llamada. En algunos casos, agregar una anotación es una buena manera de suprimir un falso positivo. Algunas funciones, como una función de utilidad, se pueden llamar en cualquier IRQL. En este caso, no tener ninguna anotación IRQL es la anotación adecuada.

Al anotar una función para IRQL, es especialmente importante tener en cuenta cómo puede evolucionar la función, no solo su implementación actual. Por ejemplo, una función en su implementación actual podría funcionar correctamente a un nivel de IRQL más alto del que el diseñador había previsto. Aunque es tentador anotar la función en función de lo que realmente hace el código, es posible que el diseñador tenga en cuenta los requisitos futuros, como la necesidad de reducir el IRQL máximo para algunas mejoras futuras o requisitos del sistema pendientes. La anotación debe derivarse de la intención del diseñador de funciones, no de la implementación real.

Puede usar las anotaciones de la tabla siguiente para indicar el IRQL correcto para una función y sus parámetros. Los valores IRQL se definen en Wdm.h.

Anotación IRQL Descripción
_IRQL_requires_max_(irql) Irql es el IRQL máximo en el que se puede llamar a la función.
_IRQL_requires_min_(irql) Irql es el IRQL mínimo en el que se puede llamar a la función.
_IRQL_requires_(irql) La función debe escribirse en el IRQL especificado por irql.
_IRQL_raises_(irql) La función sale en el irql especificado, pero solo se puede llamar para aumentar (no disminuir) el IRQL actual.
_IRQL_guarda_ El parámetro anotado guarda el IRQL actual para restaurarlo más adelante.
_IRQL_restores_ El parámetro anotado contiene un valor IRQL de IRQL_saves que se va a restaurar cuando la función devuelve.
_IRQL_saves_global_(kind, param) El IRQL actual se guarda en una ubicación interna de las herramientas de análisis de código, desde donde se restaurará el IRQL. Esta anotación se usa para anotar una función. La ubicación se identifica por tipo y se refina aún más mediante parámetros. Por ejemplo, OldIrql podría ser el tipo y FastMutex podría ser el parámetro que tenía ese valor IRQL antiguo.
_IRQL_restores_global_(kind, param) La IRQL guardada por la función anotada con IRQL_saves_global se restaura desde una ubicación que es interna a las herramientas de análisis de código.
_IRQL_always_function_min_(value) El valor IRQL es el valor mínimo al que la función puede reducir el IRQL.
_IRQL_always_function_max_(value) El valor IRQL es el valor máximo al que la función puede elevar el IRQL.
_IRQL_requires_same_ La función anotada debe entrar y salir en el mismo IRQL. La función puede cambiar el IRQL, pero debe restaurar el valor de IRQL a su valor original antes de que la función termine.
_IRQL_uses_cancel_ El parámetro anotado es el valor de IRQL que debe restaurarse mediante una función de callback de DRIVER_CANCEL. En la mayoría de los casos, use la anotación IRQL_is_cancel en su lugar.

Anotaciones para DRIVER_CANCEL

Hay una diferencia entre las anotaciones _IRQL_uses_cancel_ y _IRQL_is_cancel_. La anotación _IRQL_uses_cancel_ simplemente especifica que el parámetro anotado es el valor IRQL que debería ser restaurado por una función de devolución de llamada DRIVER_CANCEL. La anotación _IRQL_is_cancel_ es una anotación compuesta que consta de _IRQL_uses_cancel_ más otras anotaciones que garantizan el comportamiento correcto de una función de callback DRIVER_CANCEL. Por sí mismo, la anotación _IRQL_uses_cancel_ solo es ocasionalmente útil; por ejemplo, si el resto de las obligaciones descritas por _IRQL_is_cancel_ ya se han cumplido de alguna otra manera.

Anotación IRQL Descripción
_IRQL_is_cancel_ El parámetro anotado es el IRQL, pasado como parte de la llamada a la función de devolución de llamada DRIVER_CANCEL. Esta anotación indica que la función es una utilidad a la que se llama desde rutinas de cancelación y que completa los requisitos de las funciones DRIVER_CANCEL, incluida la liberación del bloqueo de giro de cancelación.

Interacción de las anotaciones IRQL

Las anotaciones de parámetros IRQL interactúan entre sí más que otras anotaciones porque el valor IRQL se establece, restablece, guarda y se restaura mediante las distintas funciones llamadas.

Especificación de IRQL máximo y mínimo

Las anotaciones _IRQL_requires_max_ y _IRQL_requires_min_ especifican que no se debe llamar a la función desde un IRQL superior o inferior al valor especificado. Por ejemplo, cuando PREfast ve una secuencia de llamadas de función que no cambian el IRQL, si encuentra uno con un valor de _IRQL_requires_max_ que está debajo de un _IRQL_requires_min_ cercano, notifica una advertencia en la segunda llamada que encuentra. El error podría producirse realmente en la primera llamada; el mensaje indica dónde se produjo la otra mitad del conflicto.

Si las anotaciones de una función mencionan irQL y no aplican explícitamente _IRQL_requires_max_, la herramienta Análisis de código aplica implícitamente la anotación _IRQL_requires_max_(DISPATCH_LEVEL), que suele ser correcta con excepciones poco frecuentes. La aplicación implícita de esto como valor predeterminado elimina una gran cantidad de desorden de anotaciones y hace que las excepciones sean mucho más visibles.

La anotación _IRQL_requires_min_(PASSIVE_LEVEL) siempre está implícita porque irQL no puede ir más abajo; por lo tanto, no hay ninguna regla explícita correspondiente sobre irQL mínimo. Algunas funciones tienen un límite superior distinto de DISPATCH_LEVEL y un límite inferior distinto de PASSIVE_LEVEL.

Se llama a algunas funciones en un contexto donde no es seguro que la función llamada incremente el IRQL por encima de un cierto máximo o, con más frecuencia, lo reduzca de forma segura por debajo de cierto mínimo. Las anotaciones _IRQL_always_function_max_ y _IRQL_always_function_min_ ayudan a PREfast a encontrar casos en los que esto se produce involuntariamente.

Por ejemplo, las funciones del tipo DRIVER_STARTIO están anotadas con _IRQL_always_function_min_(DISPATCH_LEVEL). Esto significa que durante la ejecución de una función de DRIVER_STARTIO, es un error reducir el IRQL por debajo de DISPATCH_LEVEL. Otras anotaciones indican que la función debe ser ingresada y abandonada en DISPATCH_LEVEL.

Especificar un IRQL explícito

Utilice la anotación _IRQL_raises_ o _IRQL_requires_ para ayudar a PREfast a notificar mejor una incoherencia detectada con las anotaciones _IRQL_requires_max_ o _IRQL_requires_min_, ya que de esta manera conoce el IRQL.

La anotación _IRQL_raises_ indica que una función devuelve con el IRQL establecido en un nuevo valor. Al usar la anotación de _IRQL_raises_, también establece eficazmente la anotación _drv_maxFunctionIRQL en el mismo valor IRQL. Sin embargo, si la función aumenta el IRQL a un nivel más alto que el valor final y luego lo disminuye al valor final, debe agregar una anotación explícita _IRQL_always_function_max_ después de la anotación _IRQL_raises_ para permitir el valor más alto de IRQL.

Elevación o reducción de IRQL

La anotación _IRQL_raises_ indica que la función solo debe usarse para generar IRQL y no debe usarse para reducir IRQL, aunque la sintaxis de la función lo permita. KeRaiseIrql es un ejemplo de una función que no se debe usar para reducir IRQL.

Guardar y restaurar IRQL

Use las anotaciones _IRQL_saves_ y _IRQL_restores_ para indicar que el IRQL actual (si se conoce exactamente o solo aproximadamente) se guarda o restaura desde el parámetro anotado.

Algunas funciones guardan y restauran IRQL implícitamente. Por ejemplo, la función del sistema ExAcquireFastMutex guarda el IRQL en una ubicación opaca asociada al objeto de exclusión mutua rápida que identifica el primer parámetro; el IRQL guardado se restaura mediante la función ExReleaseFastMutex correspondiente para ese objeto de exclusión mutua rápida. Para indicar estas acciones explícitamente, use las anotaciones _IRQL_saves_global_ y _IRQL_restores_global_. Los parámetros kind y param indican dónde se guarda el valor IRQL. La ubicación donde se guarda el valor no tiene que especificarse con precisión, siempre y cuando las anotaciones que guarden y restaure el valor sean coherentes.

Mantener el mismo IRQL

Debe anotar las funciones que cree su controlador que cambian el IRQL, usando bien la anotación _IRQL_requires_same_ o una de las demás anotaciones IRQL, para indicar que se espera el cambio en el IRQL. En ausencia de anotaciones que indican cualquier cambio en IRQL, las herramientas de análisis de código emitirán una advertencia para cualquier función que no salga en el mismo IRQL en el que se especificó la función. Si el cambio en IRQL está previsto, agregue la anotación adecuada para suprimir el error. Si el cambio en IRQL no está previsto, se debe corregir el código.

Guardar y restaurar IRQL para rutinas de cancelación de E/S

Utilice la anotación _IRQL_uses_cancel_ para indicar que el parámetro anotado es el valor IRQL que debe restaurar una función callback de DRIVER_CANCEL. Esta anotación indica que la función es una utilidad que se llama desde rutinas de cancelación y que cumple con los requisitos establecidos para las funciones DRIVER_CANCEL (es decir, libera la obligación del que realiza la llamada).

Por ejemplo, a continuación se muestra la declaración del tipo de función de devolución de llamada DRIVER_CANCEL. Uno de los parámetros es el IRQL que esta función debe restaurar. Las anotaciones indican todos los requisitos de una función cancel.

// Define driver cancel routine type.  //    
__drv_functionClass(DRIVER_CANCEL)  
_Requires_lock_held_(_Global_cancel_spin_lock_)  
_Releases_lock_(_Global_cancel_spin_lock_)  
_IRQL_requires_min_(DISPATCH_LEVEL)  
_IRQL_requires_(DISPATCH_LEVEL)  
typedef  
VOID  
DRIVER_CANCEL (  
    _Inout_ struct _DEVICE_OBJECT *DeviceObject,  
    _Inout_ _IRQL_uses_cancel_ struct _IRP *Irp  
    );  
  
typedef DRIVER_CANCEL *PDRIVER_CANCEL;  

Anotaciones de SAL 2.0 para controladores