Acceso a los búferes de usuario en una rutina de devolución de llamada posterior a la operación
Una rutina de devolución de llamada posterior al controlador de minifiltro debe tratar un búfer en una operación de E/S basada en IRP de la siguiente manera:
Compruebe si existe una MDL para el búfer. El puntero MDL se puede encontrar en el parámetro MdlAddress o OutputMdlAddress en el FLT_PARAMETERS de la operación. Los controladores de minifiltro pueden llamar a FltDecodeParameters para consultar el puntero MDL.
Un método para obtener una MDL válida es buscar la marca de IRP_MN_MDL en el miembro MinorFunction del bloque de parámetros de E/S, FLT_IO_PARAMETER_BLOCK, en los datos de devolución de llamada. En el ejemplo siguiente se muestra cómo comprobar la marca IRP_MN_MDL.
NTSTATUS status; PMDL *ReadMdl = NULL; PVOID ReadAddress = NULL; if (FlagOn(CallbackData->Iopb->MinorFunction, IRP_MN_MDL)) { ReadMdl = &CallbackData->Iopb->Parameters.Read.MdlAddress; }
Sin embargo, la marca de IRP_MN_MDL solo se puede establecer para las operaciones de lectura y escritura. Es mejor usar FltDecodeParameters para recuperar una MDL, ya que la rutina comprueba un MDL válido para cualquier operación. En el ejemplo siguiente, solo se devuelve el parámetro MDL si es válido.
NTSTATUS status; PMDL *ReadMdl = NULL; PVOID ReadAddress = NULL; status = FltDecodeParameters(CallbackData, &ReadMdl, NULL, NULL, NULL);
Si existe una MDL para el búfer, llame a MmGetSystemAddressForMdlSafe para obtener la dirección del sistema del búfer y, a continuación, use esta dirección para acceder al búfer. (Se puede llamar a MmGetSystemAddressForMdlSafe en IRQL <= DISPATCH_LEVEL).
Siguiendo con el ejemplo anterior, el código siguiente obtiene la dirección del sistema.
if (*ReadMdl != NULL) { ReadAddress = MmGetSystemAddressForMdlSafe(*ReadMdl, NormalPagePriority); if (ReadAddress == NULL) { CallbackData->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; CallbackData->IoStatus.Information = 0; } }
Si no hay ningún MDL para el búfer, compruebe si la marca de búfer del sistema está establecida para la operación mediante la macro FLT_IS_SYSTEM_BUFFER .
Si la macro FLT_IS_SYSTEM_BUFFER devuelve TRUE, la operación usa E/S almacenada en búfer y se puede acceder al búfer de forma segura en IRQL = DISPATCH_LEVEL.
Si la macro FLT_IS_SYSTEM_BUFFER devuelve FALSE, no se puede acceder al búfer de forma segura en IRQL = DISPATCH_LEVEL. Si se puede llamar a la rutina de devolución de llamada posterior a la operación en DISPATCH_LEVEL, debe llamar a FltDoCompletionProcessingWhenSafe para lápiz la operación hasta que se pueda procesar en IRQL <= APC_LEVEL. La rutina de devolución de llamada a la que apunta el parámetro SafePostCallback de FltDoCompletionProcessingWhenSafe debe llamar primero a FltLockUserBuffer para bloquear el búfer y, a continuación, llamar a MmGetSystemAddressForMdlSafe para obtener la dirección del sistema del búfer.
Una rutina de devolución de llamada de postoperación debe tratar un búfer en una operación de E/S rápida como se indica a continuación:
Use la dirección del búfer para acceder al búfer (porque una operación de E/S rápida no puede tener una MDL).
Para asegurarse de que una dirección de búfer de espacio de usuario es válida, el controlador de minifiltro debe usar una rutina como ProbeForRead o ProbeForWrite, encerrando todas las referencias de búfer en try/excepto los bloques.
Se garantiza que se llama a la rutina de devolución de llamada posterior a la operación de E/S rápida en el contexto de subproceso correcto.
Se garantiza que la rutina de devolución de llamada de postoperación para una operación de E/S rápida se llama en IRQL <= APC_LEVEL, por lo que puede llamar de forma segura a rutinas como FltLockUserBuffer.
El siguiente fragmento de código de ejemplo comprueba el búfer del sistema o las marcas de E/S rápidas para una operación de control de directorios y aplaza el procesamiento de finalización si es necesario.
if (*DirectoryControlMdl == NULL)
{
if (FLT_IS_SYSTEM_BUFFER(CallbackData) || FLT_IS_FASTIO_OPERATION(CallbackData))
{
dirBuffer = CallbackData->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer;
}
else
{
// Defer processing until safe.
if (!FltDoCompletionProcessingWhenSafe(CallbackData, FltObjects, CompletionContext, Flags, ProcessPostDirCtrlWhenSafe, &retValue))
{
CallbackData->IoStatus.Status = STATUS_UNSUCCESSFUL;
CallbackData->IoStatus.Information = 0;
}
}
}
En el caso de las operaciones que pueden ser de E/S rápidas o basadas en IRP, todas las referencias de búfer deben incluirse en try/excepto en bloques. Aunque no es necesario incluir estas referencias para las operaciones basadas en IRP que usan E/S almacenada en búfer, el intento/excepto los bloques son una precaución segura.