OSRLogo
OSRLogoOSRLogoOSRLogo x Subscribe to The NT Insider
OSRLogo
x

Everything Windows Driver Development

x
x
x
GoToHomePage xLoginx
 
 

    Thu, 14 Mar 2019     118020 members

   Login
   Join


 
 
Contents
  Online Dump Analyzer
OSR Dev Blog
The NT Insider
The Basics
File Systems
Downloads
ListServer / Forum
  Express Links
  · The NT Insider Digital Edition - May-June 2016 Now Available!
  · Windows 8.1 Update: VS Express Now Supported
  · HCK Client install on Windows N versions
  · There's a WDFSTRING?
  · When CAN You Call WdfIoQueueP...ously

Sharing Is Caring - Sharing Events Between Kernel-User Mode

 Click Here to Download: Code Associated With This Article Zip Archive, 10KB

Every once in a while we here at OSR need to step out of the more esoteric areas of driver development to address some fundamental questions that plague neophyte driver writers everywhere. Today we’re going to talk about sharing events between a user mode application and a kernel mode driver and hopefully shed some light on this oft discussed topic.

Shared events, as the name implies, can be used as a means for one component to notify the other that some sort of “event” has happened. Maybe you have a shared memory buffer that your driver has mapped into an app’s address space.  You could use a pair of events to synchronize access to this buffer. One event could be used for the app to signal the driver to fetch some data from the buffer and another could be used for the driver to signal the app to fetch some data from the buffer. Of course this kind of functionality can also be achieved through the use of waiting on an asynchronous IRP to be processed, but the event method has less overhead involved.  If there are going to be fairly frequent notifications this potentially makes it a better choice.

There are a handful of different ways to share events. Some of the most popular include:

  • The user-mode app creates the event, and passes the handle to the event to the driver via an IOCTL;
  • The driver creates an event, and passes the handle to the event to the user mode app via an IOCTL;
  • The user-mode app creates the event with a pre-determined name, which the driver then opens;
  • The driver creates the event with a pre-determined name, which the user-mode app then opens.

There are advantages and (naturally) disadvantages, some of them potentially fatal, to each of these approaches.  Let’s talk about them one at a time

Sharing an Event by Handle

The implementation that seems to be everyone’s favorite is to open a handle to an event in user mode and then pass that handle to the driver via an IOCTL. Within the dispatch routine, a quick call to ObReferenceObjectByHandle(...) increments the reference count on the event (so it won’t be deleted even if the user closes it), and returns you a pointer to a KEVENT that can be used to signal the event to the user mode application. QED.  Now that you’ve solved your programming problem for the day, you pat yourself on the back and head to the break room to try and beat the ungodly scores on the pinball machines.

Well hold on there Tommy. This design has a few subtle flaws and though it will work in some cases it can fail in others. The fly doing the backstroke in this soup is a little thing called context. A user mode handle is nothing but an index into a per process handle table, so you had better be 100% sure that you call ObReferenceObjectByHandle within the application’s process context. There are situations in which you are absolutely sure of this (for extra credit, name two such situations). If, on the other hand, your driver happens to be in the middle of a stack of other drivers and doesn’t directly export a named device object, you really can’t be sure of the context in which your dispatch routine is running. Not to mention the fact that if you follow Microsoft doctrine and use IoRegisterDeviceInterface(…), then IRPs will go to the top of the stack that your device object is in. So if you’re a filter driver sitting just above the disk port driver and you export a device interface, the requests that are sent to that device interface (i.e. “directly to you”) go to the top of the stack…thus you can be in an arbitrary context when you get the IOCTL.

For the sake of argument let’s say that you know that you’re in the correct context because you’re a legacy device driver that has exported a named device object and so your dispatch routine is guaranteed to be in the calling processes’ context. The next thing you need to make sure of is that you correctly use ObReferenceObjectByHandle. The optional ObjectType parameter should be set to *ExEventObjectType.  This will prevent a malicious user from passing something other than an event handle and simply return STATUS_OBJECT_TYPE_ MISMATCH if the handle is not an event.  There’s a funny story about why this parameter is optional. Well, at least I hope there is since I can’t see any reason why you wouldn’t want to validate what kind of handle you have. The other parameter that you need to be aware of is the AccessMode parameter, which should be set to UserMode. What this will do for you is check to make sure that the calling application has sufficient access to this handle; yet another reason to make absolutely sure that you are in the calling processes’ context. So, assuming that the above steps are followed, this option isn’t a bad option but I’m not picking up the whole check at the end of this date, if you know what I mean.

Note that the inverse of this implementation, opening an event in kernel mode and passing down a handle for use in user mode, is an absolutely terrible idea. The first hurdle you’d encounter would be trying to convert the pointer to a KEVENT object returned from IoCreateNotification Event(…) into a HANDLE value with a proper reference count and that a user mode application could use. Why not just use the handle returned by IoCreateNotification Event(…)?  Well, for one reason, because (at least as of Windows .NET) the handle that is returned is a kernel mode only handle!  Of course, you could put yourself through the pain of trying to get a proper handle to return, but the necessary APIs are not documented.  So, you see, the entire process is sort of like trying to solve your pest problem by throwing a disco party. It just doesn’t make much sense and when it’s all over you’re going to be left standing in the middle of a big mess. I’d highly recommend choosing another implementation approach.

Sharing Events by Name

So, tall drink of water with a bowl cut, how the heck else can my app and driver share an event if I don’t share a handle? Actually, it’s quite simple. While handles are extremely volatile and should generally not be trusted, there’s no reason why a user mode app can’t create a named event that you then open from within your driver. This implementation is devoid of all of the restrictions of the previous implementation approaches since context is no longer an issue. Named objects within the object manager are available from all process contexts and carry the security attributes with them, so there is no need to check the permissions within the calling application’s context. This makes this implementation even more appealing because you’ll be able to use it in any driver that you write and not have to worry about whether or not your current driver project adheres to all of the restrictions of the previous implementation.

Enough talk already.  Let’s see some code to demonstrate sharing an event by name. To create the event in user mode we use the SDK function CreateEvent(…)

SharedEvent = CreateEvent(NULL, TRUE, FALSE, "SharedEvent");

If our call to CreateEvent succeeded we should be able to see our named event within the object manager. There are several utilities that will allow you to do this but I’ll shamelessly suggest OSR’s ObjDir (available free at www.osr.com/ resources_downloads.shtml). When you create an event in user mode, it’s put in the \BaseNamedObjects directory, so that’s where you’re going to need to go to look for it. Figure 1 shows what it looked like when I ran it on my system

As you can see, my event SharedEvent has been created.

Figure 1 Viewing Named Event with ObjDir

In some applications, just creating the named event will be sufficient.  In others, we might need to let the driver know that the event has been created and that it’s ready to be opened. In our implementation this is done by a standard call to DeviceIoControl(...)

DeviceIoControl(DeviceDriver,
     IOCTL_OPEN_EVENT,
     NULL, 0,
     NULL, 0,
            &bytesReturned,
     NULL);

The code in the driver is equally simple and is shown in Figure 2. Just don’t forget that the event is in \BaseNamedObjects, which is automagically added if you’re in user mode but must be explicitly added if you’re trying to open the event from kernel mode!

HANDLE        SharedEventHandle = NULL;PKEVENT         SharedEvent   = NULL;
UNICODE_STRING       EventName;

       RtlInitUnicodeString(&EventName, L"\\BaseNamedObjects\\SharedEvent");
       SharedEvent = IoCreateNotificationEvent(&EventName, &SharedEventHandle);
       if (SharedEvent != NULL) {
              Status = STATUS_SUCCESS;
       } else {
              Status = STATUS_UNSUCCESSFUL;
       }

Figure 2: Opening an Event from Kernel Mode

So there you have it. You can now use the variable SharedEvent as a parameter to KeSetEvent(…), which will signal your user mode event handle.

Create the Event in Kernel Mode?

Now there’s only one question left to ask: Is the inverse of this last implementation – creating the named event in kernel mode and opening it in user mode – an absolutely terrible idea also? The definitive answer to this question is, of course, “sort of”. First of all you’d need to make sure that the event is created under \BaseNamedObjects, which we’ve already seen how to do since IoCreateNotificationEvent will create the event if it doesn’t already exist. One caveat to remember: if your driver is going to be loaded at boot time, it’ll be loaded before Win32, which creates the BaseNamedObjects directory. Should you try to create your event in your DriverEntry routine before Win32 initializes, you’re probably not going to end up with the results that you expected.

Then there’s the issue of security.  You’ll notice that IoCreateNotificationEvent doesn’t have a parameter to specify the security attributes of the event and so the security applied to the event is that of the current thread. That means that if you are running in the context of the system process when you create the event (as you will be during DriverEntry, AddDevice, and at several other times) a user application will need to be running under the Administrator account in order to be able to access the event.

But who cares really? If the user creates the event then they have access to it (I’ll have to verify this through intense “objective external analysis”  but I’m pretty sure it’s the case), and because your driver is privileged it’s not going to get hassled when it attempts to get a reference to the object. Since there’s no benefit at all to creating the event in kernel mode, just have the app create it and you’ll be able to get back to working on your pinball scores in no time.

Full source code for a driver that opens a named event previously created in a user mode application can be downloaded from the link at the beginning of this article.

User Comments
Rate this article and give us feedback. Do you find anything missing? Share your opinion with the community!
Post Your Comment

"can't work in win10"
i input the wrong event name,the return always success.

Rating:
10-Jan-17, L zy


"is this code still vaild for Win8.1/Win10?"
I was trying the code as it is in Win10, I debug via kernel log, and timer and ioctl working fine. however userspace not received the signal event (timeout err: 0x0102).

31-May-16, Sukumar Ghorai


"Good article"
very informative and helpful. extra information about context and security issues would be helpful. Thank you for the article

Rating:
09-Apr-15, naga malleswara rao yaganti


"RE: Q"
The HANDLE won't be valid (remember - HANDLEs are per-process values), but your reference to the OBJECT will still be valid (i.e. your PKEVENT). There was a bug in the sample that may have added confusion that was fixed a long time ago, but it looks like the online copy was not updated. I have fixed this so I suggest getting the latest version.

23-Sep-03, Scott Noone


"RE: KeSetEvent: Where may it be called?"
From the DDK:

"If Wait is set to FALSE, the caller can be running at IRQL <= DISPATCH_LEVEL. Otherwise, callers of KeSetEvent must be running at IRQL = PASSIVE_LEVEL and in a nonarbitrary thread context."

So, assuming that you have Wait set to FALSE you should be OK calling KeSetEvent from a DPC.

23-Sep-03, Scott Noone


"Q"
Will the handle of an event shared by handle still be valid in the driver when a counterpart user mode application gets terminated?

Rating:
22-Sep-03, Andrew Shafranov


"KeSetEvent: Where may it be called?"
I try to call KeSetEvent from an ISR generated DPC routine, but even at very low IRQ frequencies (2Hz), clients seem to be too slow to call ResetEvent. Is it a problem to call KeSetEvent from a DPC?

Rating:
21-Aug-03, Tamas Karoly


"Re: .inf File"
Eventsys is a legacy driver, so it needs to be installed via the SCM (Service Control Manager). The easiest way to do this is to download our Driver Loader utility from the Downloads section, which will install and start the driver for you.

16-Jun-03, Scott Noone


".inf File"
Do I need to install the driver eventsys? If so how do I install a driver without a .inf file?

Rating:
16-Jun-03, Shilen Thakrar


"Re: What about W2k/XP"
The methods described here and supplied code sample do work on W2K/WXP/2003. The original poster was calling KeSetEvent at an elevated IRQL, which just ain't gonna work.

12-Jun-03, Scott Noone


"What about W2k/XP"
Well, I use shared by name events in NT project. But under W2k/XP after about 100000 times user-mode app call WaitForSingleObject for this event and... never return from it. Under NT everything OK. I try 1, 3 and 4 method from this article - just the same :-(

Rating:
04-Jun-03, Sergey Skosyrev


"Re: Link to the article..."
Thanks...all fixed.

29-May-03, Daniel Root


"Link to the article at the end of it not needed or should be corrected, IMHO."

Rating:
28-May-03, Eugene Lomovsky


Post Your Comments.
Print this article.
Email this article.
bottom nav links