Partager via


Défaut de validation des tampons de longueur variable

Les pilotes acceptent souvent des mémoires tampons d’entrée avec des en-têtes fixes et des données de longueur variable en fin de message, comme dans l’exemple suivant :

   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> est une valeur ULONG très volumineuse, l’ajout à l’offset peut entraîner un dépassement d’entier. Au lieu de cela, un pilote doit soustraire le décalage (taille d’en-tête fixe) de InputBufferLength (taille de la mémoire tampon) et tester si le résultat laisse suffisamment de place pour WaitBuffer->NameLength (données de longueur variable), comme dans l’exemple suivant :

   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 d'autres termes, si la taille de la mémoire tampon moins la taille de l’en-tête fixe est inférieure au nombre d’octets requis pour les données de longueur variable, nous retournons un échec.

La soustraction ci-dessus ne peut pas provoquer de débordement négatif, car la première condition if garantit que InputBufferLength est supérieur ou égal à la taille de WAIT_FOR_BUFFER.

Voici un problème de dépassement de capacité plus compliqué :

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

Dans cet exemple, un dépassement d’entier peut se produire pendant la multiplication. Si la taille de la structure SET_VALUE_INFO est un multiple de 2, une valeur NumEntries telle que 0x80000000 entraîne un dépassement de capacité lorsque les bits sont déplacés vers la gauche pendant la multiplication. Toutefois, la taille de la mémoire tampon passe néanmoins le test de validation, car le dépassement a pour conséquence que dwSize semble très petit. Pour éviter ce problème, soustrait les longueurs comme dans l’exemple précédent, divisez par sizeof(SET_VALUE_INFO) et comparez le résultat avec NumEntries.