Windows 7 introduced two new object types: UserApcReserve and IoCompletionReserve. What do these object types have in common? They’re both created using NtAllocateReserveObject. If we look inside this system call we can see that the third argument is an index into two arrays, PspMemoryReserveObjectSizes and PspMemoryReserveObjectTypes. Notice that PspInitPhase0 creates a set number (currently two) of object types, drawing the names from another array: PspMemoryReserveObjectNames. Here’s my reversed definition of NtAllocateReserveObject:
#define USER_APC_RESERVE_TYPE 0 #define IO_COMPLETION_RESERVE_TYPE 1 NTSYSCALLAPI NTSTATUS NTAPI NtAllocateReserveObject( __out PHANDLE MemoryReserveHandle, __in_opt POBJECT_ATTRIBUTES ObjectAttributes, __in ULONG Type );
Two new system calls take advantage of the UserApcReserve object and IoCompletionReserve object: NtQueueApcThreadEx and NtSetIoCompletionEx, respectively. In NtQueueApcThreadEx we can see that if a user APC reserve handle/object is supplied, the function uses the space allocated for that object to store the APC, instead of allocating from the pool. Similarly NtSetIoCompletionEx uses the I/O completion reserve object’s already allocated space to store the I/O completion mini-packet, instead of allocating from the pool. It is now clear what the purpose of these reserve objects are: to allow processes to reserve memory before performing certain system calls in order to avoid out-of-memory problems occurring at bad spots (or critical code). Here’s my reversed definitions for the two system calls:
NTSYSCALLAPI NTSTATUS NTAPI NtQueueApcThreadEx( __in HANDLE ThreadHandle, __in_opt HANDLE UserApcReserveHandle, __in PPS_APC_ROUTINE ApcRoutine, __in_opt PVOID ApcArgument1, __in_opt PVOID ApcArgument2, __in_opt PVOID ApcArgument3 ); NTSYSCALLAPI NTSTATUS NTAPI NtSetIoCompletionEx( __in HANDLE IoCompletionHandle, __in HANDLE IoCompletionReserveHandle, __in PVOID KeyContext, __in_opt PVOID ApcContext, __in NTSTATUS IoStatus, __in ULONG_PTR IoStatusInformation );
Note that NtQueueApcThread now calls NtQueueApcThreadEx, which is why the reserve object is optional.