Échec de la validation des mémoires tampons Variable-Length
Les pilotes acceptent souvent les mémoires tampons d’entrée avec des en-têtes fixes et des données de longueur variable de fin, 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 importante, l’ajout de cette valeur au décalage peut entraîner un dépassement de capacité d’entier. Au lieu de cela, un pilote doit soustraire le décalage (taille d’en-tête fixe) de la valeur InputBufferLength (taille de mémoire tampon) et vérifier si le résultat laisse suffisamment de place pour le 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 d’en-tête fixe laisse moins que le nombre d’octets requis pour les données de longueur variable, nous retournons l’échec.
La soustraction ci-dessus ne peut pas être inférieure, car la première instruction if garantit que la valeur InputBufferLength est supérieure ou égale à la taille de WAIT_FOR_BUFFER.
Voici un problème de dépassement de capacité plus complexe :
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 de capacité 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 réussira néanmoins le test de validation, car le dépassement de capacité entraîne l’apparition de dwSize assez petit. Pour éviter ce problème, soustrayez les longueurs comme dans l’exemple précédent, divisez par sizeof(SET_VALUE_INFO), puis comparez le résultat avec NumEntries.