Partilhar via


Falha ao verificar o tamanho dos buffers

Ao lidar com IOCTLs e FSCTLs que implementam E/S em buffer, um driver deve sempre marcar os tamanhos dos buffers de entrada e saída para garantir que os buffers possam conter todos os dados solicitados. Se a solicitação especificar FILE_ANY_ACCESS, como a maioria dos IOCTLs e FSCTLs do driver, qualquer chamador que tenha um identificador para o dispositivo terá acesso a solicitações IOCTL ou FSCTL em buffer para esse dispositivo e poderá ler ou gravar dados além do final do buffer.

Tamanho do buffer de entrada

Por exemplo, suponha que o código a seguir apareça em uma rotina chamada de uma rotina dispatch e que o driver não validou os tamanhos de buffer passados no IRP:

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

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

O exemplo não marcar os tamanhos de buffer antes da instrução de atribuição (realçada). Como resultado, a referência pNewAddress-Address> na próxima linha poderá falhar se o buffer de entrada não for grande o suficiente para conter uma estrutura de tNEW_ADDRESS.

O código a seguir verifica os tamanhos de buffer, evitando o possível problema:

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

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

O código para lidar com outras E/Ss em buffer, como solicitações WMI que usam buffers de tamanho variável, pode ter erros semelhantes.

Tamanho do buffer de saída

Problemas de buffer de saída são semelhantes a problemas de buffer de entrada. Eles podem facilmente corromper o pool e os chamadores do modo de usuário podem não saber que ocorreu qualquer erro.

No exemplo a seguir, o driver não marcar o tamanho do 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;
   }

Supondo que o campo NumIF do buffer do sistema especifique o número de itens de entrada, este exemplo pode definir IoStatus.Information como um valor maior que o buffer de saída e, portanto, retornar muitas informações para o código de modo de usuário. Se um aplicativo for codificado incorretamente e chamar com um buffer de saída muito pequeno, o código anterior poderá corromper o pool gravando além do final do buffer do sistema.

Lembre-se de que o gerenciador de E/S pressupõe que o valor no campo Informações é válido. Se um chamador passar um endereço válido no modo kernel para o buffer de saída e um tamanho de zero bytes, problemas graves poderão ocorrer se o driver não marcar o tamanho do buffer de saída e, portanto, encontrar o erro.