共用方式為


無法驗證 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 的倍數,則當位在乘法期間向左移動時, NumEntries 值 (例如 0x80000000) 會導致溢位。 不過,緩衝區大小仍會通過驗證測試,因為溢位會導致 dwSize 看起來相當小。 若要避免此問題,請如上一個範例中減去長度,除以 sizeofSET_VALUE_INFO),並將結果與 NumEntries 進行比較。