無法驗證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進行比較。