Compartilhar via


Falha em validar buffers de Variable-Length

Os drivers geralmente aceitam buffers de entrada com cabeçalhos fixos e dados de comprimento variável ao final, como no exemplo a seguir:

   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 );
   }

Se WaitBuffer-NameLength> for um valor ULONG muito elevado, adicioná-lo ao deslocamento poderá causar um estouro de inteiro. Em vez disso, um driver deve subtrair o deslocamento (tamanho fixo do cabeçalho) do InputBufferLength (tamanho do buffer) e testar se o resultado deixa espaço suficiente para o WaitBuffer-NameLength> (dados de comprimento variável), como no exemplo a seguir:

   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 );
   }

Se o tamanho do buffer menos o tamanho do cabeçalho fixo deixar menos bytes do que os necessários para os dados de comprimento variável, retornamos falha.

A subtração mencionada acima não pode resultar em transbordo negativo pois a primeira instrução if garante que o InputBufferLength seja maior ou igual ao tamanho de WAIT_FOR_BUFFER.

O seguinte mostra um problema de sobrecarga mais complicado:

   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;
      }

Neste exemplo, um estouro de inteiro pode ocorrer durante a multiplicação. Se o tamanho da estrutura SET_VALUE_INFO for um múltiplo de 2, um valor NumEntries como 0x80000000 resultará em um overflow, quando os bits são "shiftados" para a esquerda durante a multiplicação. No entanto, o tamanho do buffer será aprovado no teste de validação, pois o estouro faz com que o dwSize pareça bastante pequeno. Para evitar esse problema, subtraia os comprimentos como no exemplo anterior, divida por sizeof(SET_VALUE_INFO) e compare o resultado com NumEntries.