First, I'd like to describe the architecture of the project I’m working on to make my question clearer.
I have a WDM kernel driver and two user-mode applications that communicate with the driver using the inverted call model combined with the cancel-safe queue (CSQ) framework.
Each user application runs 8 threads, and each thread sends a request to the driver and waits for a completion packet via IOCP after the IRP has been pended, and when it received that the request was complete by the driver, it will do its job then send another request to the driver and repeat. Each application communicates with the driver using a different IOCTL code.
Initially, I had implemented the driver with only one user application, so I didn’t care much about the IRP type — I simply marked the IRP as pending and pushed it to the queue without using any context.
But now that I’ve added a second user application (which sends different types of IRPs), I realized I need to differentiate IRPs using a context.
So here's my question:
Do I need to define a context structure (such as an enum value identifying the IRP type) and pass it to IoCsqInsertIrpEx via the PeekContext parameter — allowing all IRPs from the same application to share the same context address?
Inside the CsqInsertIrpEx function I implemented, I plan to assign this context to the DriverContext[] field of the IRP, so it can later be accessed during peeking or removal.
Or, I thought of ignoring the context when calling IoCsqInsertIrpEx, and instead providing a simple value (like a BOOL) when calling IoCsqRemoveNextIrp. Then, inside my implementation of CsqPeekNextIrp, I would loop through the queue, retrieve the IRP stack location, check the IOCTL code, and decide whether it matches the type I'm looking for before removing it.
Or... is there a much simpler and cleaner way to handle this?
If your queue should be dispatched in any other other than 'give me the next one', then you'll want multiple queues
If you need to store extra information about where the request came from, then think about if that extra info should be stored on the HANDLE (i.e. every request using this HANDLE should have the same context) or per IRP.
remember that drivers are expected to expose services to applications, and handle requests from any application that manages to open a HANDLE. Including applications that you don't expect like security scanners and malware.
If you have two applications, then you will have two instances of the driver, each with its own driver context structure. Each context will have its own queue. Right?
So I will first make another queue to handle that different type of IRP. But do I need to create another set of IO_CSQ routines to handle that queue too? Or can I just create another queue, initialize another IO_CSQ but use the same routines that I'm using with the first queue since the logic of pushing and pulling out the IRPs from the queue won't be different?
If you need to store extra information about where the request came from, then think about if that extra info should be stored on the HANDLE (i.e. every request using this HANDLE should have the same context) or per IRP.
Sorry, I didn't catch what you meant by this. Do you mean that when I receive a request for opening a handle to my driver in my IRP_MJ_CREATE dispatch routine, I should do something specific to mark that new HANDLE so every new IRP coming from it should be marked as well? And if so, what should I do then?
Or do you mean that when I receive an IRP, I should check which handle it came from and act accordingly?
remember that drivers are expected to expose services to applications, and handle requests from any application that manages to open a HANDLE. Including applications that you don't expect like security scanners and malware.
Really thanks for this advice. I haven't thought about that this may happen. I made this driver to communicate only with those 2 user-applications that I made. Is it good practice to ignore any incoming request from other user-applications, or do I have to handle them too in a different way?
I haven't actually written any "code" that would create another instance of the driver when it's opened by another application. Or do you mean that IF I open the same driver from different apps, that will create another instance of the driver?
If so, that would make handling different types of IRPs easier since each instance would have its own queue, so I could just remove the next IRP in the queue without caring about the context, right?
Yes. If you catch IRP_MJ_CREATE, you can store a pointer to a context structure in the FILE_OBJECT, and you'll get that FILE_OBJECT with any read/write/ioctl IRP you get.