Échec de la vérification de la taille des mémoires tampons

Lors de la gestion des IOCTL et des FSCTL qui implémentent des E/S mises en mémoire tampon, un pilote doit toujours case activée les tailles des mémoires tampons d’entrée et de sortie pour s’assurer que les mémoires tampons peuvent contenir toutes les données demandées. Si la demande spécifie FILE_ANY_ACCESS, comme le font la plupart des PILOTES IOCTL et FSCTL, tout appelant disposant d’un handle pour l’appareil a accès aux demandes IOCTL ou FSCTL mises en mémoire tampon pour cet appareil, et peut lire ou écrire des données au-delà de la fin de la mémoire tampon.

Taille de la mémoire tampon d’entrée

Par exemple, supposons que le code suivant apparaît dans une routine appelée à partir d’une routine Dispatch et que le pilote n’a pas validé les tailles de mémoire tampon passées dans l’IRP :

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

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

L’exemple ne case activée pas les tailles de mémoire tampon avant l’instruction d’affectation (en surbrillance). Par conséquent, la référence pNewAddress-Address> de la ligne suivante peut être défaillante si la mémoire tampon d’entrée n’est pas assez grande pour contenir une structure tNEW_ADDRESS.

Le code suivant vérifie les tailles de mémoire tampon, évitant ainsi le problème potentiel :

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

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

Le code permettant de gérer d’autres E/S mises en mémoire tampon, telles que les requêtes WMI qui utilisent des tampons de taille variable, peut présenter des erreurs similaires.

Taille de la mémoire tampon de sortie

Les problèmes de mémoire tampon de sortie sont similaires aux problèmes de mémoire tampon d’entrée. Ils peuvent facilement endommager le pool, et les appelants en mode utilisateur peuvent ignorer qu’une erreur s’est produite.

Dans l’exemple suivant, le pilote ne parvient pas à case activée la taille du 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;
   }

En supposant que le champ NumIF de la mémoire tampon système spécifie le nombre d’éléments d’entrée, cet exemple peut définir IoStatus.Information sur une valeur supérieure à la mémoire tampon de sortie et donc renvoyer trop d’informations au code en mode utilisateur. Si une application est mal codée et qu’elle appelle avec une mémoire tampon de sortie trop petite, le code précédent peut endommager le pool en écrivant au-delà de la fin de la mémoire tampon système.

N’oubliez pas que le gestionnaire d’E/S suppose que la valeur dans le champ Informations est valide. Si un appelant transmet une adresse valide en mode noyau pour la mémoire tampon de sortie et une taille de 0 octets, des problèmes sérieux peuvent se produire si le pilote ne case activée pas la taille de la mémoire tampon de sortie et trouve donc l’erreur.