Иллюстрированный самоучитель по программированию систем защиты

Сериализация

Отмена запросов ввода/вывода

Всякий раз, когда запрос ввода/вывода удерживается драйвером в течение продолжительного отрезка времени, драйвер должен быть готов к отмене данного запроса. В случае закрытия потока диспетчер ввода/вывода пытается отменить все запросы ввода/вывода, отправленные этим потоком и еще не завершенные. Пока все такие запросы ввода/вывода не будут завершены устройством, ему не придет запрос IRP_MJ_ CLOSE, и, следовательно, не освободится объект-файл, а впоследствии – и само устройство (драйвер никогда не получит запрос DriverUnload).

Для обеспечения отмены запроса ввода/вывода в пакете IRP, представляющем такой запрос, должен быть указан адрес диспетчерской точки входа драйвера, собственно отменяющей запрос. Для этого служит функция IoSetCancelRoutine().

PDRIVER_CANCEL loSetCancelRoutine (IN PIRP Irp,
PDRIVER_CANCEL CancelRoutine);

Где функция CancelRoutine() имеет такой же прототип, как и все диспетчерские функции.

VOID <strong>CancelRoutine</strong> (IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp);

Она вызывается на уровне IRQL DISPATCH_LEVEL в случайном контексте потока, однако перед ее вызовом происходит захват специальной системной спин-блокировки. До тех пор, пока системная спин-блокировка не будет освобождена, функция CancelRoutine() работает на уровне IRQL DISPATCH_LEVEL. Уровень IRQL, на который нужно перейти после освобождения блокировки указывается при вызове IoReleaseCancelSpinLock() (см. ниже). Принцип реализации функции следующий:

  • Если указанный пакет IRP не может быть отменен или не принадлежит драйверу, то надо освободить системную спин-блокировку и завершить работу функции.
  • В противном случае:
    1. удалить пакет IRP из любых очередей, в которых он присутствует;
    2. установить функцию отмены IRP в NULL с помощью IoSetCancelRoutine();
    3. освободить системную спин-блокировку;
    4. установить в IRP поле loStatus.Information равном 0, а поле loStatus.Status равном STATUS_CANCELLED;
    5. завершить IRP с помощью loCompleteRequest().

Системная спин-блокировка отмены IRP освобождается с помощью loRelease CancelSpinLock():

VOID IoReleaseCancelSpinLock(IN KIRQL Irgl);

Где Irql – уровень IRQL, на который система должна вернуться после освобождения спин-блокировки. Это значение хранится в IRP в поле Cancellrql.

Отмена IRP и Системная Очередь

Пример функции отмены IRP драйвера, использующего системную очередь, показан в следующем листинге. Необходимо отметить, что для удаления IRP из системной очереди используется функция KeRemoveEntryDeviceQueue() так, как это показано в листинге.

VOID Cancel(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
// Обрабатывается ли отменяемый запрос в данный момент?
if (Irp == DeviceOb]ect › Current!rp)
{
// Да. Освободить системную спин-блокировку и указать
// диспетчеру ввода/вывода начать обработку следующего
// пакета. Отмена IRP – в конце функции
loReleaseCancelSpinLock(Irp › CancelIrql);
loStartNextPacket(DeviceOb]ect, TRUE); }
else {
// Нет. Отменяемый IRP находится в очереди. // Удалить его из очереди
KeRemoveEntryDeviceQueue(SDeviceOb]ect › DeviceQueue, &Irp › Tail.Overlay.DeviceQueueEntry);
loReleaseCancelSpinLock(Irp › CancelIrql); }
// Отменить IRP
Irp › IoStatus.Status = STATUS_CANCELLED;
 Irp › IoStatus.Information = 0; loCompleteRequest(Irp, IO_NO_INCREMENT); return; }

Отмена IRP и очереди, управляемые драйвером

VOID Cancel(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
PIRP irpToCancel;
PDEVICE_EXT devExt;
KIRQL oldlrql;
// обнулить указатель на функцию отмены loSetCancelRoutine(Irp, NULL);
// Освободить системную спин-блокировку // как можно быстрее
loReleaseCancelSpinLock(Irp › CancelIrql); devExt = DeviceObject › DeviceExtension;
// Захватить спин-блокировку доступа к очереди,
// удалить IRP и освободить
// спин-блокировку KeAcquireSpinLock(&devExt › QueueLock, soldlrql);
RemoveEntryList(&Irp › Tail.Overlay.ListEntry);
KeReleaseSpinLock(&devExt › QueueLock, oldlrql);
// Отменить IRP
Irp › IoStatus Status = STATUS_CANCELLED;
Irp › IoStatus.Information = 0;
loCompleteRequest(Irp, IO_NO_INCREMENT);
Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.