无法检查缓冲区大小

处理实现缓冲 I/O 的 IOCTL 和 FSCL 时,驱动程序应始终检查输入和输出缓冲区的大小,以确保缓冲区可以保存所有请求的数据。 如果请求指定FILE_ANY_ACCESS,就像大多数驱动程序 IOCTL 和 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 管理器假定 “信息 ”字段中的值有效。 如果调用方为输出缓冲区传递有效的内核模式地址,并且字节大小为零,则驱动程序未检查输出缓冲区大小,从而发现错误时,可能会出现严重问题。