Why you want to use POBJECT_TYPEs when converting handles to objects
This post concludes my trilogy (see parts 1 and 2) on PBOJECT_TYPE (although I do reserve the right to pull a George Lucas and add more episodes later :) ). Today we shall cover why passing the PBOJECT_TYPE to ObReferenceObjectByHandle, ObReferenceObjectByPointer, or ObOpenObjectByPointer is important. Let's take the following snippet
NTSTATUS status;
PKEVENT pEvent;
status = ObReferenceObjectByHandle(pInputBuf->Handle,
0x0,
NULL, // <-- NULL POBJECT_TYPE!
Irp->RequestorMode,
(PVOID*) &pEvent,
NULL);
Let's assume you put this into your driver and pass a handle to an event in pInputBuf->Handle. You will find that it works just fine with a NULL POBJECT_TYPE. Why is that? Because a NULL POBJECT_TYPE tells the API to not perform any type checking on the handle and just give you back the underlying object. In this example, you would have a valid PKEVENT pointer but if the application passed any other type of handle, you would think you have a valid PKEVENT pointer, but in reality it will be an NT object of another type (which I guess you can then validate with ObOpenObjectByPointer, but I don't know why you would not do that the first time).
Passing a NULL POBJECT_TYPE is a problem for a couple of reasons. First, you should never trust your application to send you the right type of handle. Another application can be written to take advantage of your driver and pass it another handle type, opening the door for attacks and other nastiness. You are protecting your driver against the application and verifying that is passing you valid input. Second, you are protecting yourself from yourself. Let's say that the design requirements for your driver and application changes and you no longer are passing a handle to an event, but a handle to a file. You change either the application or the driver, but not both. By passing the correct PBOJECT_TYPE, you will catch the error during the design phase and prevent a fire drill later on in the development or release cycle when you realize that only one component changed.
One more thing to consider, the size of a HANDLE is different in 32- and 64-bit clients. This means that if you are embedding a HANDLE into a data structure you must either put it into a 64-bit field so that the size is the same from platform to platform or you must switch on the applications "bit"-ness in your driver and take two different paths based on the result.
So to conclude, this would be the correct way to make the call so that you are validating the handle correctly
status = ObReferenceObjectByHandle(pInputBuf->Handle,
0x0,
*ExEventObjectType,
Irp->RequestorMode,
(PVOID*) &pEvent,
NULL);