Поделиться через


Сбой при проверке размера буферов

При обработке ioCTL и FSCTL, реализующих буферизированные операции ввода-вывода, драйвер всегда должен проверка размеры входных и выходных буферов, чтобы убедиться, что буферы могут содержать все запрошенные данные. Если в запросе указано FILE_ANY_ACCESS, как и большинство ICTL драйвера и FSCTL, любой вызывающий объект, имеющий дескриптор устройства, имеет доступ к буферизованному запросу IOCTL или FSCTL для этого устройства и может считывать или записывать данные за пределами буфера.

Размер входного буфера

Например, предположим, что следующий код отображается в подпрограмме, вызываемой из подпрограммы Dispatch , и что драйвер не проверил размеры буфера, переданные в IRP:

   switch (ControlCode)
      ...
      ...
      case IOCTL_NEW_ADDRESS:{
         tNEW_ADDRESS *pNewAddress = 
            pIrp->AssociatedIrp.SystemBuffer;

         pDeviceContext->Addr = RtlUlongByteSwap (pNewAddress->Address);

В примере не проверка размеры буфера перед инструкцией присваивания (выделено). В результате ссылка pNewAddress-Address> в следующей строке может привести к сбою, если входной буфер недостаточно велик для tNEW_ADDRESS структуры.

Следующий код проверяет размеры буфера, чтобы избежать потенциальных проблем:

   case IOCTL_NEW_ADDRESS: {
      tNEW_ADDRESS *pNewAddress =
         pIrp->AssociatedIrp.SystemBuffer;

      if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength >=
             sizeof(tNEW_ADDRESS)) {
         pDeviceContext->Addr = RtlUlongByteSwap (pNewAddress->Address);

Код для обработки других буферизированных операций ввода-вывода, например запросов WMI, использующих буферы переменной величины, может иметь аналогичные ошибки.

Размер выходного буфера

Проблемы выходного буфера аналогичны проблемам входного буфера. Они могут легко повредить пул, а вызывающие пользователи пользовательского режима могут не знать, что произошла какая-либо ошибка.

В следующем примере драйверу не удается проверка размер SystemBuffer:

   case IOCTL_GET_INFO: {

       Info = Irp->AssociatedIrp.SystemBuffer;

       Info->NumIF = NumIF;
       ...
       ...
       Irp->IoStatus.Information =
             NumIF*sizeof(GET_INFO_ITEM)+sizeof(ULONG);
       Irp->IoStatus.Status = ntStatus;
   }

Если предположить, что поле NumIF системного буфера указывает количество входных элементов, в этом примере можно задать ioStatus.Information значение больше, чем выходной буфер, и таким образом вернуть слишком много информации в код пользовательского режима. Если приложение закодировано неправильно и вызывает с слишком небольшим выходным буфером, предыдущий код может повредить пул, записав за пределы системного буфера.

Помните, что диспетчер ввода-вывода предполагает, что значение в поле Сведения является допустимым. Если вызывающий объект передает допустимый адрес в режиме ядра для выходного буфера и размер нулевых байтов, могут возникнуть серьезные проблемы, если драйвер не проверка размер выходного буфера и таким образом найти ошибку.