Variable-Length 버퍼 유효성 검사 실패
드라이버는 다음 예제와 같이 고정 헤더 및 후행 가변 길이 데이터가 있는 입력 버퍼를 수락하는 경우가 많습니다.
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 );
}
WaitBuffer-NameLength>가 매우 큰 ULONG 값인 경우 오프셋에 추가하면 정수 오버플로가 발생할 수 있습니다. 대신 드라이버는 InputBufferLength(버퍼 크기)에서 오프셋(고정 헤더 크기)을 빼고 결과가 WaitBuffer-NameLength>(가변 길이 데이터)에 충분한 공간을 남겨 두는지 테스트해야 합니다.
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 );
}
즉, 버퍼 크기에서 고정 헤더 크기를 뺀 값이 가변 길이 데이터에 필요한 바이트 수보다 적게 남으면 오류를 반환합니다.
첫 번째 if 문은 InputBufferLength 가 WAIT_FOR_BUFFER 크기보다 크거나 같은지 확인하므로 위의 빼기는 언더플로할 수 없습니다.
다음은 보다 복잡한 오버플로 문제를 보여 줍니다.
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;
}
이 예제에서는 곱하기 중에 정수 오버플로가 발생할 수 있습니다. SET_VALUE_INFO 구조체의 크기가 2의 배수인 경우 0x80000000 같은 NumEntries 값은 곱하기 동안 비트가 왼쪽으로 이동하면 오버플로가 발생합니다. 그러나 오버플로로 인해 dwSize 가 매우 작게 표시되므로 버퍼 크기는 유효성 검사 테스트를 통과합니다. 이 문제를 방지하려면 이전 예제와 같이 길이를 빼고 sizeof(SET_VALUE_INFO)로 나누고 결과를 NumEntries와 비교합니다.