From: Maxim S. Shatskih [maxim@storagecraft.com] Sent: Tuesday, July 11, 2000 2:32 PM To: NT Developers Interest List Subject: [ntdev] Nt and Zw functions and buffer checks (was STATUS_ACCESS_VIOLATION) > But by calling ZwOpenFile I'm bypassing any code in the > real NtOpenFile which to my understanding checks that the > incoming parameters are valid - hence I have to implement The difference between Ntxxx and Zwxxx functions: - in umode - NO difference AFAIK. Both are exports from ntdll.dll. Both are tiny code snippets which call int 2Eh. - in kmode - Zwxxx (BTW - what does "Zw" letters stand for? A dark mystery :-) ) is a tiny snippet of code which calls int 2Eh. - Ntxxx is the real syscall body called from the int 2Eh interrupt handler. The interrupt prolog in int 2Eh handler will a) determine the previous mode (the mode from which the interrupt was called) by examining the trap frame on the stack and b) set the "previous mode" field in the current thread (ETHREAD? TEB?) according to this. Then it calls one of the Ntxxx functions. The previous mode field is accessible for kmode code by ExGetPreviousMode(). IO-initiating functions like NtReadFile will set: Irp->RequestorMode = ExGetPreviousMode(); this is used by the filesystems to determine how to handle Irp->UserBuffer. The filesystem cannot use ExGetPreviousMode to determine this because the IRP could be posted to FSP thread, thus losing the per-thread context like the previous mode field. Thus - the existence of Irp->RequestorMode field. It's obvious that syscalls in _any_ OS kernel must check the pointers specified as the syscall parameters - otherwise, any app could crash the whole OS by passing a bogus pointer to the syscall. Linux goes even further - it sets up the processor's DS register to a special kernel segment (with the appropriate base & limit) in trap prologs, thus disabling any user space pointer dereferences in the kernel/driver code. Any user space pointer access in the driver in Linux leads to a fatal kernel panic (surely immediately detectable by the developer). The only way for accessing user memory in Linux are special macros like copy_to_user() - which contains some primitive thing which resembles try/catch frames. copy_to_user() guarantees (by use of this try/catch-like mechanism) that the OS will not panic even if the user pointer is not valid - copy_to_user() will just return EFAULT in this case. NT does not provides this. Drivers are free to access user space memory directly by pointer like Irp->UserBuffer or like the "non-self-containing IOCTL buffer" case - the IOCTL buffer which contains some pointers to some other structures. NT requires the kmode code to use try/catch while accessing these pointers - but it can be forgotten, thus opening a stability hole (like in afd.sys in some older NT builds - TransmitFile called with invalid header pointers caused BSOD). Generally, NT's way is faster, but Linux way is more reliable. Surely the previous mode info is used to check the buffer and IOSB pointers specified to the syscall. If the previous mode is KernelMode, pointers are not checked (kmode code is trusted). If the previous mode is UserMode, then pointers are checked to be a valid user space pointer by ProbeForRead macro - which fails if this is a kernel space pointer. Now imagine you called Ntxxx from the driver code which is executed by the user thread context with previous mode = UserMode. In this case, Ntxxx and the filesystem stuff it calls will do the pointer check. And it will fail - because you specified the kernel pointer to Ntxxx. This is the cause of your bug. If you call Zwxxx - all will be OK. The trap prolog will set previous mode to KernelMode and call Ntxxx. But there is one more gotcha with ZwCreateFile. IIRC ZwCreateFile called by kmode code will result in a _kernel handle_ (a special kind of a handle), not usable for the apps. If your task is to obtain the handle in kmode and then use the same handle value in the app - the following can possibly help: - call ZwCreateFile - call ObReferenceObjectByHandle to obtain the file object pointer - call ObOpenObjectByPointer with AccessMode = UserMode. This will create a user handle for the file. Pass it to the app. - dereference the file and close the original handle. Max --- You are currently subscribed to ntdev as: GlennEverhart@FirstUSA.com To unsubscribe send a blank email to leave-ntdev-247T@lists.osr.com