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


Сбой проверки буферов 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.