Help converting a WDM KS (camera) filter driver to KMDF

Hi guys,

After the latest thread, I decided to convert the WDM driver I have to KMDF. I have a filter driver which captures the IOCTL_KS_READ_STREAM control code, and sets a completion routine with WdfRequestSetCompletionRoutine to post-process a camera’s image.

I try to access the image structure and the frame in the callback, but unfortunately I weren’t able to do this using KMDF API. In the WDM API, I accessed the following:
(PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer
MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority)

In the KMDF callback, I tried using these APIs:
WdfRequestRetrieveUnsafeUserInputBuffer
WdfRequestProbeAndLockUserBufferForRead

But that doesn’t work for me.
Could you suggest a way to access and modify the frame data? Thanks.

xxxxx@gmail.com wrote:

After the latest thread, I decided to convert the WDM driver I have to KMDF. I have a filter driver which captures the IOCTL_KS_READ_STREAM control code, and sets a completion routine with WdfRequestSetCompletionRoutine to post-process a camera’s image.

I try to access the image structure and the frame in the callback, but unfortunately I weren’t able to do this using KMDF API. In the WDM API, I accessed the following:
(PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer
MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority)

In the KMDF callback, I tried using these APIs:
WdfRequestRetrieveUnsafeUserInputBuffer
WdfRequestProbeAndLockUserBufferForRead

But that doesn’t work for me.
Could you suggest a way to access and modify the frame data? Thanks.

You have embarked on a non-trivial task.  The issue is that all of the
IOCTL_KS_* ioctls were designed for kernel-to-kernel communication. 
They are METHOD_NEITHER, and they don’t follow any of the defined IRP
rules, which means you can’t really use the KMDF APIs to access the
data.  The rules they do follow are undocumented.  What I know, I know
from reverse engineering.

In IOCTL_KS_READ_STREAM, the Type3InputBuffer and SystemBuffer are not
used.  The address of the KSSTREAM_HEADER is in UserBuffer, and the
MdlAddress points to the actual frame buffer (not the KSSTREAM_HEADER).

However, some of this is set up by ksthunk.sys, which sits in between
64-bit KS drivers and 32-bit apps.  The setup may be slightly different
on a 32-bit system, where ksthunk doesn’t apply.


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

Thank you for the reply Tim, and for the various posts in the forums with information about KS. Your posts gave me some insight about what’s going on.

Your information helped me understand the following code on GitHub, which seems to work:
https://github.com/flowerinthenight/windows-camera-class-filter-driver/blob/master/ccfltr/ccfltr.c#L395

Now I’m trying to upgrade the WDM example to KMDF. I started with the toaster KMDF example, and tried to use WdfRequestSetCompletionRoutine for post-processing:
https://github.com/Microsoft/Windows-driver-samples/blob/master/general/toaster/toastDrv/kmdf/filter/generic/filter.c#L328

Your comment, “you can’t really use the KMDF APIs to access the data”, made me notice that I can use the WdfRequestWdmGetIrp function to get the raw IRP pointer in the completion routine, and use a code which is similar to the WDM example. So far, looks like it works.

Are the any other details that I need to be aware of?

Also, you mentioned WdfSynchronizationScopeNone in one of the threads:
https://www.osronline.com/showthread.cfm?link=212301
Can you please say whether and why it’s needed in my case?

Thank you so much for the help!

Hmmmm… In reference to the (almost 7 year old) thread you linked: If you’re processing METHOD_NEITHER IOCTLs, and you want to be in the context of the current process, relying on Sync Scope None is a poor practice. It works by way of implementation, not by way of architectural correctness.

If you want to be called in the context of the requesting process, you should use the InCallerContext Event Processing Callback.

Are you RECEIVING MTHOD_NEITHER IOCTLs or are you SENDING them? Sorry… I’m not a Ks guy, so I can only be of limited help with that end of things. But I’m reasonably familiar with WDF.

Peter
OSR
@OSRDrivers

xxxxx@osr.com wrote:

Hmmmm… In reference to the (almost 7 year old) thread you linked: If you’re processing METHOD_NEITHER IOCTLs, and you want to be in the context of the current process, relying on Sync Scope None is a poor practice. It works by way of implementation, not by way of architectural correctness.

If you want to be called in the context of the requesting process, you should use the InCallerContext Event Processing Callback.

Are you RECEIVING MTHOD_NEITHER IOCTLs or are you SENDING them? Sorry… I’m not a Ks guy, so I can only be of limited help with that end of things. But I’m reasonably familiar with WDF.

Receiving.  It is the KS subsystem that sends these ioctls to streaming
drivers.

The fact that these ioctls don’t follow the rules makes the usual “in
caller context” advice somewhat squishy.  Although these are
METHOD_NEITHER ioctls, they originate in kernel mode, and they do not
follow the rules of a METHOD_NEITHER ioctl that you would get from user
mode.  The address fields in the IRP are used in a way that is specific
to KS, and all the addresses have already been converted to kernel
addresses.

There is some truly weird voodoo magic in the KS interface.  They open a
file handle to the filter, and a file handle to each pin.  When you get
the IRP_MJ_CREATE call for the pin, the file name in the request not
only includes the device interface reference string, but also the
desired video format, as a binary structure, including embedded zeros. 
That works just fine, as long as the driver knows what it is going to
receive, but it’s certainly odd.


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

xxxxx@gmail.com wrote:

Your information helped me understand the following code on GitHub, which seems to work:
https://github.com/flowerinthenight/windows-camera-class-filter-driver/blob/master/ccfltr/ccfltr.c#L395

Maybe, but I’m suspicious of anyone who writes code like this:

InterlockedAnd(&pDeviceExtension->IsVideoInfoHeader2, 0);
InterlockedAnd(&pDeviceExtension->KsStateRun, 0);
InterlockedAnd(&pDeviceExtension->StreamerPid, 0);

That shows a misunderstanding on several levels.  A DeviceExtension is
automatically zeroed on creation, and a write of a 32-bit value is
already atomic, so this would have had the same effect:

pDeviceExtension->IsVideoInfoHeader2 = 0;
pDeviceExtension->KsStateRun = 0;
pDeviceExtension->StreamerPid = 0;

Your comment, “you can’t really use the KMDF APIs to access the data”, made me notice that I can use the WdfRequestWdmGetIrp function to get the raw IRP pointer in the completion routine, and use a code which is similar to the WDM example. So far, looks like it works.

Yep, that’s the right way to do it.

 

Are the any other details that I need to be aware of?

I’m sure there are lots, but it depends on what you need to do.  If you
need to know the format of the video stream as it was negotiated, that
turns out to be complicated.  Are you able to assume a format?

Also, you mentioned WdfSynchronizationScopeNone in one of the threads:
https://www.osronline.com/showthread.cfm?link=212301
Can you please say whether and why it’s needed in my case?

It’s difficult to come up with general rules.  You need to know what
parts of your shared state are vulnerable and need locking.  If you
access a lot of shared data, then a sync scope can make that easier, but
it’s also easy to overdo it.


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

> I’m suspicious of anyone who writes code like this

Maybe it’s not perfect, but that’s literally the only relevant example that I found in the internet.

a write of a 32-bit value is already atomic

That’s probably true in practice for all relevant processors, but isn’t it true that in theory some processor (e.g. some esoteric ARM) can lack such guarantees?

If you need to know the format of the video stream as it was negotiated, that turns out to be complicated.

The author of the example does this by checking the GUID at VideoInfoHeader.DataFormat.SubFormat. Is there more to it?

My task is to replace the webcam image if a condition is met, as a security measure. I can have a replacement image for common formats (say MJPG and a couple of more), and just have a zero-sized buffer returned for less known formats, which is acceptable as long as the streaming is blocked.

That makes sense. You definitely know KS.

The only other thing I’ll throw in here in CASE it’s useful, is that KMDF “knows about” kernel-mode to kernel-mode METHOD_NEITHER IOCTLs. And, IIRC, actually supports the use of the typical WdfRequestRetrieveOutputBuffer/WdfRequestRetrieveInputBuffer functions in this case.

Assuming the buffer pointers are KVAs, of course, and the lengths are… proper.

Confucius once wrote: “Posting a sample on the Internet doesn’t make the author wise or the sample good.” I think he wrote this in Autumn Annals, just after The September That Never Ended.

Seriously?

Allow me to put your fears to rest: No system on which Windows will run will violate this constraint.

(This reminds me of the arguments people used to give me about assuming endian-ness in my network driver code: “Suppose you’re running on a big endian system,” they would say. My answer was always “Then my driver isn’t running at all, because Windows isn’t going to run on a big endian system anytime soon.”)

Peter
OSR
@OSRDrivers

xxxxx@osr.com wrote:

(This reminds me of the arguments people used to give me about assuming endian-ness in my network driver code: “Suppose you’re running on a big endian system,” they would say. My answer was always “Then my driver isn’t running at all, because Windows isn’t going to run on a big endian system anytime soon.”)

Exactly, and this applies across the board – even to Windows CE. 
Although CE runs on many strange processors that are traditionally
big-endian, the operating system requires them to run in little-endian mode.


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

OK, good to know.

Another question about cameras: can IRP_MJ_CREATE be called for the same camera device object more than once, before the previous handle gets IRP_MJ_CLEANUP? Also, is there any significance to IRP_MJ_CLOSE, or does it always happen right after IRP_MJ_CLEANUP? Which one is better to consider the camera activity to be stopped?

On Apr 22, 2018, at 10:44 AM, xxxxx@gmail.com wrote:
>
> OK, good to know.
>
> Another question about cameras: can IRP_MJ_CREATE be called for the same camera device object more than once, before the previous handle gets IRP_MJ_CLEANUP?

Certainly. A single camera can be opened by multiple processes, or multiple times by a single process. In the KS world, for example, you’ll get separate file opens for the filter and for each of the pins.

> Also, is there any significance to IRP_MJ_CLOSE, or does it always happen right after IRP_MJ_CLEANUP? Which one is better to consider the camera activity to be stopped?

For a KS driver, neither one. Camera activity begins when the pin transitions to the run state (KSSTATE_RUN), and stops when the pin transitions out of run state. A KS driver’s streaming state is controlled by the KSPROPERTY_CONNECTION_STATE property within KSPROPSETID_Connection.

IRP_MJ_CLEANUP happens while the process is still accessible. IRP_MJ_CLOSE can arrive after the process memory is gone. The OSR web site has an extensive article on why it’s better to handle IRP_MJ_CLOSE.

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

I don’t quite understand how it works.

A single camera can be opened by multiple processes, or multiple times by a single process.

I know that only one process at a time can use a given camera. How is this enforced? Where does the second connection fail? I tried debugging the filter driver, but didn’t find clues.

I see that a regular communication for a single process looks like this:

IRP_MJ_CREATE (filename = “\global”)
IRP_MJ_CREATE (filename = “{146F1A80-4791-11D0-A5D6-28DB04C10000}<binary_data>”)
KSSTATE_ACQUIRE
KSSTATE_PAUSE
KSSTATE_RUN
IOCTL_KS_READ_STREAM
IOCTL_KS_READ_STREAM

KSSTATE_PAUSE
KSSTATE_STOP
IRP_MJ_CLEANUP
IRP_MJ_CLOSE
IRP_MJ_CLEANUP
IRP_MJ_CLOSE
=====================================

Now, if I open another app while already using the camera, it looks like this:
=====================================
IRP_MJ_CREATE(1) (filename = “\global”)
IRP_MJ_CREATE(1) (filename = “{146F1A80-4791-11D0-A5D6-28DB04C10000}<binary_data>”)
KSSTATE_ACQUIRE(1)
KSSTATE_PAUSE(1)
KSSTATE_RUN(1)
IOCTL_KS_READ_STREAM(1)
IOCTL_KS_READ_STREAM(1)

IRP_MJ_CREATE(2) (filename = “\global”)
IRP_MJ_CREATE(2) (filename = “{146F1A80-4791-11D0-A5D6-28DB04C10000}<binary_data>”)
KSSTATE_ACQUIRE(2)
KSSTATE_PAUSE(2)
KSSTATE_RUN(2)
IOCTL_KS_READ_STREAM(1)

KSSTATE_PAUSE(1)
KSSTATE_STOP(1)
IRP_MJ_CLEANUP(1)
IRP_MJ_CLOSE(1)
IRP_MJ_CLEANUP(1)
IRP_MJ_CLOSE(1)
=====================================

I’d like to be able to connect the video format in <binary_data> to IOCTL_KS_READ_STREAM, to be able to know what to expect in the frame buffer. But in order for this to work correctly, I need to know to discard the “IRP_MJ_CREATE(2)” IRP.</binary_data></binary_data></binary_data></binary_data>

xxxxx@gmail.com wrote:

I don’t quite understand how it works.

> A single camera can be opened by multiple processes, or multiple times by a single process.
I know that only one process at a time can use a given camera. How is this enforced? Where does the second connection fail? I tried debugging the filter driver, but didn’t find clues.

At the transition to KSSTATE_ACQUIRE, the driver is supposed to fail
that if the hardware resources are already committed.  This convention
was invented during the days of Windows Media Center, because it opened
an instance in a service at boot time, even if it wasn’t recording
anything.  If drivers failed during Create, no other devices could use
the camera.

I see that a regular communication for a single process looks like this:

IRP_MJ_CREATE (filename = “\global”)

That’s the filter.

 

IRP_MJ_CREATE (filename = “{146F1A80-4791-11D0-A5D6-28DB04C10000}<binary_data>”)

That’s the pin. The is the selected video format.

> Now, if I open another app while already using the camera, it looks like this:
> =====================================
> IRP_MJ_CREATE(1) (filename = “\global”)
> IRP_MJ_CREATE(1) (filename = “{146F1A80-4791-11D0-A5D6-28DB04C10000}<binary_data>”)
> KSSTATE_ACQUIRE(1)
> KSSTATE_PAUSE(1)
> KSSTATE_RUN(1)
> IOCTL_KS_READ_STREAM(1)
> IOCTL_KS_READ_STREAM(1)
> …
> IRP_MJ_CREATE(2) (filename = “\global”)
> IRP_MJ_CREATE(2) (filename = “{146F1A80-4791-11D0-A5D6-28DB04C10000}<binary_data>”)
> KSSTATE_ACQUIRE(2)

That should have failed.

> I’d like to be able to connect the video format in <binary_data> to IOCTL_KS_READ_STREAM, to be able to know what to expect in the frame buffer. But in order for this to work correctly, I need to know to discard the “IRP_MJ_CREATE(2)” IRP.

You get a file handle in the IOCTL_KS_READ_STREAM call. You can connect
that to the corresponding IRP_MJ_CREATE.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.</binary_data></binary_data></binary_data></binary_data>

You are right, KSSTATE_ACQUIRE fails with 0xc000009a (STATUS_INSUFFICIENT_RESOURCES). Here’s why I didn’t see it:
https://github.com/flowerinthenight/windows-camera-class-filter-driver/issues/2

Another question:
In the completion routine for IOCTL_KS_READ_STREAM, the example uses ProbeForRead on KSSTREAM_HEADER’s Data pointer. That doesn’t look correct to me, but I’m not sure. Driver Verifier says:
Error String ProbeForRead should only be called at IRQL <= APC_LEVEL.

I think that there are two issues here: first the IRQL mismatch, and second the fact that the Data pointer can be irrelevant since the completion routine is likely to run in the context of a different process. Am I right? And if that’s so, can this check be just removed? I mean, can I trust Irp->MdlAddress without additional checks? Or can it be invalid, and if so what’s the proper way to check this?

Thanks again for your help!

xxxxx@gmail.com wrote:

Another question:
In the completion routine for IOCTL_KS_READ_STREAM, the example uses ProbeForRead on KSSTREAM_HEADER’s Data pointer. That doesn’t look correct to me, but I’m not sure. Driver Verifier says:
Error String ProbeForRead should only be called at IRQL <= APC_LEVEL.

That shouldn’t be necessary.  Maybe this was an issue in Windows 98, but
these days all the addresses in the IRP should be kernel-mode addresses
by the time they get to you.  There’s a filter called “ksthunk” that
sits on top of the main driver and provides a nice, clean IRP to the
drivers below it.  That way, lower drivers don’t have to deal with the
32/64 issues.

How far back do you need to support?  Are you worried about XP?

 

I think that there are two issues here: first the IRQL mismatch, and second the fact that the Data pointer can be irrelevant since the completion routine is likely to run in the context of a different process. Am I right? And if that’s so, can this check be just removed? I mean, can I trust Irp->MdlAddress without additional checks? Or can it be invalid, and if so what’s the proper way to check this?

The Irp->MdlAddress buffer should already be locked by the time you see it.


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

Great, thanks.

How far back do you need to support? Are you worried about XP?

Windows 7 and up should be supported. Windows XP is nice to have if I can get the support for free or almost for free.
What’s the difference regarding Windows XP?

xxxxx@gmail.com wrote:

> How far back do you need to support? Are you worried about XP?
Windows 7 and up should be supported. Windows XP is nice to have if I can get the support for free or almost for free.
What’s the difference regarding Windows XP?

The “ksthunk” helper I mentioned came into being when the 64-bit systems
arrived.  In the XP days, it’s quite possible you WILL have to worry
about locking and mapping user-mode pointers.  That’s ugly.


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