Compartir a través de


Error al validar búferes de Variable-Length

Los controladores suelen aceptar búferes de entrada con encabezados fijos y datos finales de longitud variable, como en el ejemplo siguiente:

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

Si WaitBuffer-NameLength> es un valor ULONG muy grande, agregarlo al desplazamiento podría provocar un desbordamiento entero. En su lugar, un controlador debe restar el desplazamiento (tamaño fijo de encabezado) de InputBufferLength (tamaño del búfer) y probar si el resultado deja espacio suficiente para WaitBuffer-NameLength> (datos de longitud variable), como en el ejemplo siguiente:

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

En otras palabras, si el tamaño del búfer menos el tamaño fijo del encabezado deja menos el número de bytes necesarios para los datos de longitud variable, se devuelve un error.

La resta anterior no puede subdesbordarse porque la primera instrucción if garantiza que InputBufferLength sea mayor o igual que el tamaño de WAIT_FOR_BUFFER.

A continuación se muestra un problema de desbordamiento más 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;
      }

En este ejemplo, se puede producir un desbordamiento entero durante la multiplicación. Si el tamaño de la estructura de SET_VALUE_INFO es un múltiplo de 2, un valor NumEntries como 0x80000000 da como resultado un desbordamiento, cuando los bits se desplazan a la izquierda durante la multiplicación. Sin embargo, el tamaño del búfer pasará la prueba de validación, ya que el desbordamiento hace que dwSize parezca bastante pequeño. Para evitar este problema, reste las longitudes como en el ejemplo anterior, divida por sizeof(SET_VALUE_INFO) y compare el resultado con NumEntries.