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


Сбой проверки буферов Variable-Length

Драйверы часто принимают входные буферы с фиксированными заголовками и конечными данными переменной длины, как показано в следующем примере:

   typedef struct _WAIT_FOR_BUFFER {
      LARGE_INTEGER Timeout;
      ULONG NameLength;
      BOOLEAN TimeoutSpecified;
      WCHAR Name[1];
   } WAIT_FOR_BUFFER, *PWAIT_FOR_BUFFER;

   if (InputBufferLength < sizeof(WAIT_FOR_BUFFER)) {
      IoCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
      return( STATUS_INVALID_PARAMETER );
   }

   WaitBuffer = Irp->AssociatedIrp.SystemBuffer;

   if (FIELD_OFFSET(WAIT_FOR_BUFFER, Name[0]) +
          WaitBuffer->NameLength > InputBufferLength) {
       IoCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
       return( STATUS_INVALID_PARAMETER );
   }

Если WaitBuffer-NameLength> имеет очень большое значение ULONG, добавление его в смещение может привести к переполнению целочисленного значения. Вместо этого драйвер должен вычесть смещение (фиксированный размер заголовка) из InputBufferLength (размер буфера) и проверить, оставляет ли результат достаточно места для WaitBuffer-NameLength> (данные переменной длины), как показано в следующем примере:

   if (InputBufferLength < sizeof(WAIT_FOR_BUFFER)) {
      IoCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
      Return( STATUS_INVALID_PARAMETER );
   }

   WaitBuffer = Irp->AssociatedIrp.SystemBuffer;

   if ((InputBufferLength -
         FIELD_OFFSET(WAIT_FOR_BUFFER, Name[0])  <
         WaitBuffer->NameLength) {
      IoCompleteRequest( Irp, STATUS_INVALID_PARAMETER );
      return( STATUS_INVALID_PARAMETER );
   }

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

Приведенное выше вычитание не может быть недостаточно, так как первый оператор if гарантирует, что InputBufferLength больше или равен размеру WAIT_FOR_BUFFER.

Ниже показана более сложная проблема переполнения.

   case IOCTL_SET_VALUE:
      dwSize = sizeof(SET_VALUE);

      if (inputBufferLength < dwSize) {
         ntStatus = STATUS_BUFFER_TOO_SMALL;
         break;
      }

      dwSize = FIELD_OFFSET(SET_VALUE, pInfo[0]) +
                  pSetValue->NumEntries * sizeof(SET_VALUE_INFO);

      if (inputBufferLength < dwSize) {
         ntStatus = STATUS_BUFFER_TOO_SMALL;
         break;
      }

В этом примере во время умножения может произойти переполнение целых чисел. Если размер структуры SET_VALUE_INFO кратен 2, значение NumEntries , например 0x80000000, приводит к переполнению, когда биты смещаются влево во время умножения. Однако размер буфера, тем не менее, пройдет проверку, так как переполнение приводит к тому, что dwSize будет выглядеть довольно мало. Чтобы избежать этой проблемы, вычитайте длину, как в предыдущем примере, разделите на sizeof(SET_VALUE_INFO) и сравните результат с NumEntries.