无法检查缓冲区的大小

处理实现缓冲 I/O 的 IOCTL 和 FSCTL 时,驱动程序应始终检查输入和输出缓冲区的大小,以确保缓冲区可以保存所有请求的数据。 如果请求指定FILE_ANY_ACCESS,与大多数驱动程序 IOCTLs 和 FSCTL 一样,具有设备句柄的任何调用方都可以访问该设备的缓冲 IOCTL 或 FSCTL 请求,并且可以读取或写入缓冲区末尾以外的数据。

输入缓冲区大小

例如,假设以下代码出现在从 Dispatch 例程调用的例程中,并且驱动程序尚未验证 IRP 中传递的缓冲区大小:

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

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

该示例不会检查赋值语句(突出显示)之前的缓冲区大小。 因此,如果输入缓冲区不足以包含tNEW_ADDRESS结构,则下一行中的 pNewAddress-Address> 引用可能会出错。

以下代码检查缓冲区大小,避免潜在问题:

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

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

处理其他缓冲 I/O 的代码(例如使用可变大小缓冲区的 WMI 请求)可能会有类似的错误。

输出缓冲区大小

输出缓冲区问题类似于输入缓冲区问题。 它们很容易损坏池,用户模式调用方可能不知道发生了任何错误。

在以下示例中,驱动程序无法检查 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;
   }

假设系统缓冲区的 NumIF 字段指定输入项的数目,本示例可以将 IoStatus.Information 设置为大于输出缓冲区的值,从而将太多信息返回到用户模式代码。 如果应用程序编码不当,并且调用的输出缓冲区太小,则上述代码可以通过在系统缓冲区末尾外写入来损坏池。

请记住,I/O 管理器假定 信息 字段中的值有效。 如果调用方传入输出缓冲区的有效内核模式地址和大小为零字节,则驱动程序不检查输出缓冲区大小并找到错误时,可能会出现严重问题。