Errors in Referencing User-Space Addresses

Any driver, whether supporting IRPs or fast I/O operations, should validate any address in user space before trying to use it. The I/O manager does not validate such addresses, nor does it validate pointers that are embedded in buffers passed to drivers.

Failure to Validate Addresses Passed in METHOD_NEITHER IOCTLs and FSCTLs

The I/O manager does no validation whatsoever for METHOD_NEITHER IOCTLs and FSCTLs. To ensure that user-space addresses are valid, the driver must use the ProbeForRead and ProbeForWrite routines, enclosing all buffer references in try/except blocks.

In the following example, the driver assumes that the value passed in the Type3InputBuffer represents a valid address.

   case IOCTL_GET_HANDLER:
   {
      PULONG EntryPoint;

      EntryPoint =
         IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; 
      *EntryPoint = (ULONG)DriverEntryPoint; 
      ...
   }

The following code avoids this problem:

   case IOCTL_GET_HANDLER:
   {
      PULONG_PTR EntryPoint;

      EntryPoint =
         IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
 
      try
      {
         if (Irp->RequestorMode != KernelMode)
         { 
            ProbeForWrite(EntryPoint,
                          sizeof(ULONG_PTR),
                          TYPE_ALIGNMENT(ULONG_PTR));
         }
         *EntryPoint = (ULONG_PTR)DriverEntryPoint;
      }
      except(EXCEPTION_EXECUTE_HANDLER)
      {
        ...
      }
      ...
   }

Note also that the correct code casts DriverEntryPoint to a ULONG_PTR, instead of a ULONG. This change allows for use in a 64-bit Windows environment.

Failure to validate pointers embedded in buffered I/O requests

Often drivers embed pointers within buffered requests, as in the following example:

   struct ret_buf
   {
      void  *arg;  // Pointer embedded in request
      int  rval;
   };

   pBuf = Irp->AssociatedIrp.SystemBuffer;
   ...
   arg = pBuf->arg;  // Fetch the embedded pointer
   ...
   // If the arg pointer is not valid, the following
   // statement can corrupt the system:
   RtlMoveMemory(arg, &info, sizeof(info));

In this example, the driver should validate the embedded pointer by using the ProbeXxx routines enclosed in a try/except block in the same way as for the METHOD_NEITHER IOCTLs described earlier. Although embedding a pointer allows a driver to return extra information, a driver can more efficiently achieve the same result by using a relative offset or a variable length buffer.

For more information about using try/except blocks to handle invalid addresses, see Handling Exceptions.