I am in the process of developing a WDM device driver which already
handles pretty well several IOCTL codes when sent by a user-mode
application, using DeviceIoControl().

Now I want to make sure that driver can handle any crazy input that an
application programmer may throw at it. For that purpose I placed in
the switch case for the pIoStackLocation-
>Parameters.DeviceIoControl.IoControlCode a default case that all it
does is this:

default:
{
pIRP->IoStatus.Information = 0;
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
pIRP->IoStatus.Status = ntStatus;
IoCompleteRequest(pIRP, IO_NO_INCREMENT);
return ntStatus;
}

This works great (i.e. returns an error with a suitable GetLastError()
code) when I test it with an unrecognized IOCTL as follows:

int dummy;
if ( !DeviceIoControl(hWdm, IOCTL_UNRECOGNIZED,
NULL, 0, //
input
&dummy, sizeof(dummy), // output
&BytesReturned, NULL))

but when I pass the output paramter as NULL, too (see below), the
system hangs (i.e. only a hard reset would get it out of that state):

if ( !DeviceIoControl(hWdm, IOCTL_UNRECOGNIZED,
NULL, 0, // input
NULL, 0, // output
&BytesReturned, NULL))

Notice, the only difference is "NULL, 0" vs. "&dummy,
sizeof(dummy)" ...

On the WinDbg side, all I can see is the following:

Access violation - code c0000005 (!!! second chance !!!)
*** ERROR: Symbol file could not be found. Defaulted to export
symbols for ks.sys -
ks!KsDispatchIrp+0x3b:
f82b9f0d ff10 call dword ptr [eax]


Which doesn't give me enough information about what's happening in the
driver.

So, for now, my workaround is to never pass a NULL output buffer
pointer to DeviceIoControl() - but is it really impossible to protect
a driver against something like this?

I thought that (not) handling an unrecognized IOCTL is a no brainer
(as described above in the 'default' case).

What am I missing?

Thanks,
Don

Re: No way to full-proof DeviceIoControl() ??? by 0dbell

0dbell
Mon Apr 16 23:44:32 CDT 2007

On Apr 16, 11:07 pm, 0db...@gmail.com wrote:
> I am in the process of developing a WDM device driver which already
> handles pretty well several IOCTL codes when sent by a user-mode
> application, using DeviceIoControl().
>
> Now I want to make sure that driver can handle any crazy input that an
> application programmer may throw at it. For that purpose I placed in
> the switch case for the pIoStackLocation->Parameters.DeviceIoControl.IoControlCode a default case that all it
>
> does is this:
>
> default:
> {
> pIRP->IoStatus.Information = 0;
> ntStatus = STATUS_INVALID_DEVICE_REQUEST;
> pIRP->IoStatus.Status = ntStatus;
> IoCompleteRequest(pIRP, IO_NO_INCREMENT);
> return ntStatus;
> }
>
> This works great (i.e. returns an error with a suitable GetLastError()
> code) when I test it with an unrecognized IOCTL as follows:
>
> int dummy;
> if ( !DeviceIoControl(hWdm, IOCTL_UNRECOGNIZED,
> NULL, 0, //
> input
> &dummy, sizeof(dummy), // output
> &BytesReturned, NULL))
>
> but when I pass the output paramter as NULL, too (see below), the
> system hangs (i.e. only a hard reset would get it out of that state):
>
> if ( !DeviceIoControl(hWdm, IOCTL_UNRECOGNIZED,
> NULL, 0, // input
> NULL, 0, // output
> &BytesReturned, NULL))
>
> Notice, the only difference is "NULL, 0" vs. "&dummy,
> sizeof(dummy)" ...
>
> On the WinDbg side, all I can see is the following:
>
> Access violation - code c0000005 (!!! second chance !!!)
> *** ERROR: Symbol file could not be found. Defaulted to export
> symbols for ks.sys -
> ks!KsDispatchIrp+0x3b:
> f82b9f0d ff10 call dword ptr [eax]
>
> Which doesn't give me enough information about what's happening in the
> driver.
>
> So, for now, my workaround is to never pass a NULL output buffer
> pointer to DeviceIoControl() - but is it really impossible to protect
> a driver against something like this?
>
> I thought that (not) handling an unrecognized IOCTL is a no brainer
> (as described above in the 'default' case).
>
> What am I missing?
>
> Thanks,
> Don

OK - problem found and fixed.

The problem was that execution never reached the 'default:' case
because at an outer scope I checked for (pIRP-
>AssociatedIrp.SystemBuffer == NULL) and immediately called
PcDispatchIrp(pFDO, pIRP) if it were indeed NULL.

I now let my IRP_MJ_DEVICE_CONTROL handler reach my custom
dwIoControlCode even when pIRP->AssociatedIrp.SystemBuffer == NULL and
all is well now.

It seems that PortCls cannot handle unrecognized dwIoControlCode,
when
pIRP->AssociatedIrp.SystemBuffer == NULL. Is this a correct
observation?

Thanks,
Don