DeviceIoControl error code 1

Hi,

Can you tell me what are possible causes of this error code from calling this function, or at least what may be relevant to my particular case?

I’ve been doing a bit of rewriting of a kernel driver I wrote, and now the user side gets error code 1 (incorrect function) from DeviceIoControl. It used to work before.

The IOCTL control code on the user side and the driver side is identical, in fact, from the same source file. The driver’s creation of the device calls
s2 = IoCreateDevice (pDriverObject, sizeof (DeviceExt), &internalName, FILE_DEVICE_UNKNOWN, 0, false, &pDeviceObject);
So the device is created with shared access.
On the user side, the file handle is successfully created with
HANDLE handle = CreateFile (deviceName.c_str (), GENERIC_READ + GENERIC_WRITE,
exclusive ? 0 : FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
The ‘exclusive’ parameter is either true or false, with the same result.

The driver’s DeviceControl routine handles an unknown IOCTL code (or any incorrect parameters for the correct IOCTL code) by setting the IRP with a STATUS_INVALID_DEVICE_REQUEST, or C0000010h. So I suspect that DeviceControl is not even getting called.

I haven’t found anything useful searching online.

I just added a new IOCTL code, and used it with DeviceIoControl, in this case with some input data and no output data. I haven’t yet changed the driver to recognize this new code.
Result is still code 1 (incorrect function). This further indicates that the driver’s MJ_CONTROL routine isn’t getting called.

Also, … I have a second driver with similar code, and I can perform DeviceIoControl OK with that driver. This is encouraging, because I can compare the user and kernel side code between the two and hopefully see what I changed recently in the former that broke it. If I find something, I’ll post it.

On Feb 9, 2018, at 10:43 PM, xxxxx@rolle.name wrote:
>
> Can you tell me what are possible causes of this error code from calling this function, or at least what may be relevant to my particular case?

Either STATUS_NOT_IMPLEMENTED or STATUS_INVALID_DEVICE_REQUEST will map to ERROR_INVALID_FUNCTION in user mode.

> The driver’s DeviceControl routine handles an unknown IOCTL code (or any incorrect parameters for the correct IOCTL code) by setting the IRP with a STATUS_INVALID_DEVICE_REQUEST, or C0000010h. So I suspect that DeviceControl is not even getting called.

Oh, I suspect it is. STATUS_INVALID_DEVICE_REQUEST will bubble up as ERROR_INVALID_FUNCTION.

How much validation do you do? One common problem is to have a 32-bit app calling a 64-bit driver, where one of the fields in the buffer structure has a different size or causes it to have different padding.

Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Problem solved!

Hi again, Tim,

I just figured that out. I called the second driver with an unknown control code, to which it responds by posting STATUS_INVALID_DEVICE_REQUEST to the IRP and also returning the same. And sure enough, on the user side I saw ERROR_INVALID_FUNCTION, just like you said.

Pity that this behavior is not documented by Microsoft. It would have saved me from going down the rabbit hole.

By the way, what’s the difference between the return value from my DispatchControl and the value I store in pIrp->IoStatus.Status? I’m planning to use a DPC to complete the DispatchControl in some cases, so the pIrp->IoStatus.Status value presumably won’t be used until later when the DPC calls IoCompleteRequest (). What is DispatchControl supposed to return in this case?

Thanks.

I just read the Microsoft docs article “Completing the IRP”. My reading of this is:

  1. If the dispatch routine does complete the IRP, either successfully or otherwise, then it stores the status in the IRP and returns the same status value.
  2. If the dispatch routine does not complete the IRP, then it returns STATUS_PENDING.

This seems simple enough. Is my understanding correct?

I’m curious about a possible race condition. If I queue a DPC to complete the IRP, is it possible that the DPC will start running immediately and will call IoCompleteRequest before the dispatch routine returns with STATUS_PENDING. I suppose this is not a problem, either because the kernel won’t run any new DPCs until the dispatch routine returns, or because the kernel realizes the IRP has already been completed and does the right thing. Can you confirm this?

Thanks.

On Feb 10, 2018, at 12:03 AM, xxxxx@rolle.name wrote:
>
> I just figured that out. I called the second driver with an unknown control code, to which it responds by posting STATUS_INVALID_DEVICE_REQUEST to the IRP and also returning the same. And sure enough, on the user side I saw ERROR_INVALID_FUNCTION, just like you said.
>
> Pity that this behavior is not documented by Microsoft. It would have saved me from going down the rabbit hole.

It IS documented. Or at least it was. There is an MSDN page somewhere that documents the mapping from NTSTATUS codes to Win32 error codes.

> By the way, what’s the difference between the return value from my DispatchControl and the value I store in pIrp->IoStatus.Status?

The driver is required by contract to return the same value you put in the IoStatus, except for IRPs you pend. That’s why KMDF doesn’t give you the chance to get that wrong.

> I’m planning to use a DPC to complete the DispatchControl in some cases, so the pIrp->IoStatus.Status value presumably won’t be used until later when the DPC calls IoCompleteRequest (). What is DispatchControl supposed to return in this case?

STATUS_PENDING. You’ll need to call IoMarkIrpPending in that case as well.

Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

On Feb 10, 2018, at 12:28 AM, xxxxx@rolle.name wrote:
>
> I just read the Microsoft docs article “Completing the IRP”. My reading of this is:
> 1. If the dispatch routine does complete the IRP, either successfully or otherwise, then it stores the status in the IRP and returns the same status value.
> 2. If the dispatch routine does not complete the IRP, then it returns STATUS_PENDING.
>
> This seems simple enough. Is my understanding correct?

Yep, with the additional requirement that you call IoMarkIrpPending in the 2nd case.

> I’m curious about a possible race condition. If I queue a DPC to complete the IRP, is it possible that the DPC will start running immediately and will call IoCompleteRequest before the dispatch routine returns with STATUS_PENDING. I suppose this is not a problem, either because the kernel won’t run any new DPCs until the dispatch routine returns, or because the kernel realizes the IRP has already been completed and does the right thing. Can you confirm this?

Yes, as long as you have marked the IRP as pending. I assume you wouldn’t just be queuing a DPC; that would be silly, because you could just complete the IRP in the dispatch routine. I assume you’re waiting for a timer or some piece of hardware, which will then cause the DPC to be queued.

Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

> Either STATUS_NOT_IMPLEMENTED or STATUS_INVALID_DEVICE_REQUEST will map to ERROR_INVALID_FUNCTION in user mode.
I get NON-ZERO return from DeviceIoControl() using
IOCTL_STORAGE_QUERY_PROPERTY, QueryType = PropertyExistsQuery (which I
take to mean that said property is available)

but then get ERROR_INVALID_FUNCTION with PropertyStandardQuery.

The semantics of “STATUS_NOT_IMPLEMENTED” and
“STATUS_INVALID_DEVICE_REQUEST” suggest that information is lost in
mapping both to the same error code.
If it’s NOT_IMPLEMENTED then the positive result from the initial query
seems to be in error (standard Windows 10 disk drivers).
If it’s INVALID_DEVICE_REQUEST then perhaps I’m doing something wrong
with this particular device?

Which seems more likely, given that with other devices I get consistent
results?