Posted by: Sue Loh
I’ve talked about this before but I want to really highlight it because I still see people wrestling with it.
In Windows CE 5.0 and earlier, “kernel mode” is an access level attached to a thread. If a thread is “in kernel mode” it can access kernel address space. You could call SetKMode to put your thread into or out of kernel mode, whenever you wanted (it required trust, of course). Most system APIs were not implemented by the kernel (nk.exe), so calling an API didn’t put your thread into kernel mode. Unless you called an API that was implemented by nk.exe, in which case your thread would temporarily enter kernel mode for the duration of the call.
In Windows CE 6.0 the implementation is actually the same, except that the SetKMode API is no longer supported. You can’t put threads into or out of kernel mode at a time of your choice. However, most system APIs are implemented by modules that are now loaded into the kernel process, so calling an API usually puts your thread into kernel mode.
While the implementation is the same, the result is effectively different meanings. In CE 5.0, “kernel mode” was a property a thread could acquire or release on demand. In CE 6.0, the rule is fairly simple: your thread is in kernel mode while executing code inside the kernel process, and not in kernel mode when executing code inside a user process. We use the term loosely, like talking about “kernel mode drivers,” “kernel mode code” and “kernel mode addresses.” Whenever people use these phrases they’re trying to talk about code and addresses that are only accessible to the kernel process, that are at addresses above 0x80000000. (Side note, you also have to be clear about whether you’re talking about everything inside the kernel process vs. only the kernel module, kernel.dll.)
The one remaining gotcha in the CE 6.0 rule is callbacks. If a user application passes a function pointer to kernel mode code, and the kernel mode code calls the function pointer directly, then the thread is STILL in kernel mode (remember it is still a property of the thread, just not so obviously) while executing user mode code. And that is very bad for security. Because the user mode code could do anything; it could access kernel addresses or call kernel-only APIs. If you write a driver that takes a function pointer from the caller and later calls it, make sure your driver only does so by using the new CEDDK function, CeDriverPerformCallback. That way your thread jumps back to the user process properly before calling the function, so that the function call can’t do anything that the user process itself couldn’t do already. Or even better — find a different way to implement what you’re doing. Ask yourself this: if an attacker passed me an evil function pointer, could they get me to do something bad on their behalf? If you don’t know the answer, don’t take function pointers from your callers.
You might wonder about function pointers and memory marshalling. “Marshalling” only applies to passing data buffers between processes — not to passing function pointers between processes. You can’t “marshal” a function so that you can safely call it from kernel code. The CeDriverPerformCallback function is the only way to shed the privileges that a kernel mode driver has, for the duration of the call. I suppose you could consider that a form of “marshalling” but I don’t.
UPDATE Jan 31, 2007: Andrew Tuck, one of our support engineers, pointed out another detail that can lead to confusion. (Thanks, Andrew!) Often (at least in Windows environments, if not all OS’), the most privileged processor mode is referred to as “kernel mode.” This usage of the term refers to when the CPU is operating with additional privileges, such as the ability to call special instructions that aren’t normally legal. For example interrupt handling often happens in this CPU “kernel mode.” This terminology is unrelated to the “kernel mode” concept I’ve been talking about in this article. When a Windows CE thread is in “kernel mode” it is not running in the most privileged processor mode. Only very restricted parts of the CE kernel and OAL run in that mode. The Windows CE “kernel mode” thread property controls whether the page tables for kernel address space are mapped, and some other things. (For example, in CE 5.0 it controlled whether the thread could make fast-path API calls, like I described in the API call post I wrote a while back.)