- May 7, 2013
- 10,400
Rich (BB code):
REFERENCE_BY_POINTER (18)
Arguments:
Arg1: ffff9188026c9f00, Object type of the object whose reference count is being lowered
Arg2: ffff91880ef86080, Object whose reference count is being lowered
Arg3: 0000000000000001, Reserved
Arg4: 0000000000000800, Reserved
The reference count of an object is illegal for the current state of the object.
Each time a driver uses a pointer to an object the driver calls a kernel routine
to increment the reference count of the object. When the driver is done with the
pointer the driver calls another kernel routine to decrement the reference count.
Drivers must match calls to the increment and decrement routines. This bugcheck
can occur because an object's reference count goes to zero while there are still
open handles to the object, in which case the fourth parameter indicates the number
of opened handles. It may also occur when the object's reference count drops below zero
whether or not there are open handles to the object, and in that case the fourth parameter
contains the actual value of the pointer references count.
Every object which is managed by the Object Manager has a reference count associated to it. This reference count is used by the Object Manager to ensure that objects which are still currently in use aren't prematurely deleted. The reference count is a sum of two parts: the handle count and the pointer count. The pointer count, as the name suggests, is the current number of pointers which are referring to the object in question. On the other hand, the handle count is the current number of open handles to the object.
The pointer count is exclusively used by kernel components; only they can use a pointer to refer to an object. However, handles can be opened by both user-mode and kernel-mode code. This something to bear in mind when debugging this bugcheck.
When a handle is opened or a pointer is set to refer to an object address, then the reference count is incremented by 1. Likewise, when the opposite is true, the reference count is decremented by 1. If we attempt deallocate an object whose reference count is still positive, then we will bugcheck as shown above. Additionally, if the reference count were to drop below 0 to any negative integer, then we will also bugcheck with the same error code.
Let's now examine the first two parameters: the object type and the address of the object itself. The first parameter contains a pointer to the object type structure, which contains metadata for that particular object type. In this case, we can see that our object was a thread object.
Rich (BB code):
2: kd> dt _OBJECT_TYPE ffff9188026c9f00
win32k!_OBJECT_TYPE
+0x000 TypeList : _LIST_ENTRY [ 0xffff9188`026c9f00 - 0xffff9188`026c9f00 ]
+0x010 Name : _UNICODE_STRING "Thread"
+0x020 DefaultObject : (null)
+0x028 Index : 0x8 ''
+0x02c TotalNumberOfObjects : 0x13d2
+0x030 TotalNumberOfHandles : 0x1a5c
+0x034 HighWaterNumberOfObjects : 0x1539
+0x038 HighWaterNumberOfHandles : 0x1c8f
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0b8 TypeLock : _EX_PUSH_LOCK
+0x0c0 Key : 0x65726854
+0x0c8 CallbackList : _LIST_ENTRY [ 0xffff9188`026c9fc8 - 0xffff9188`026c9fc8 ]
We can find the reference count for the particular object shown in the second parameter by using the !object command. As we can see, the reference count is 2048, since there is currently 2048 open handles for the object.
Rich (BB code):
2: kd> !object ffff91880ef86080
Object: ffff91880ef86080 Type: (ffff9188026c9f00) Thread
ObjectHeader: ffff91880ef86050 (new version)
HandleCount: 2048 PointerCount: 0
Upon investigating the call stack for the crash, we can see that a handle for a object was being closed. There is a number of important functions to understand here, and therefore we'll examine each one separately.
Rich (BB code):
2: kd> !stack -p
Call Stack : 6 frames
## Stack-Pointer Return-Address Call-Site
00 ffffa20a28a6f908 fffff80259416e66 nt!KeBugCheckEx+0
Parameter[0] = (unknown)
Parameter[1] = (unknown)
Parameter[2] = (unknown)
Parameter[3] = (unknown)
01 ffffa20a28a6f910 fffff802595f372e nt!ObfDereferenceObjectWithTag+20ebd6 (perf)
Parameter[0] = (unknown)
Parameter[1] = (unknown)
Parameter[2] = (unknown)
Parameter[3] = (unknown)
02 ffffa20a28a6f950 fffff802595f73ac nt!ObCloseHandleTableEntry+29e
Parameter[0] = ffffa4870d1cf3c0 << _HANDLE_TABLE
Parameter[1] = ffffa4870e0c3f10 << _HANDLE_TABLE_ENTRY
Parameter[2] = ffff91880f7ba080 << _EPROCESS
Parameter[3] = 00000000000007c4 << Handle value
03 ffffa20a28a6fa90 fffff802594086b8 nt!NtClose+ec (perf)
Parameter[0] = 00000000000007c4 << Handle being closed
Parameter[1] = (unknown)
Parameter[2] = (unknown)
Parameter[3] = (unknown)
04 ffffa20a28a6fb00 00000000772d1cfc nt!KiSystemServiceCopyEnd+28
Parameter[0] = 00000000000007c4
Parameter[1] = 0000000077368850
Parameter[2] = 000000000000002b
Parameter[3] = 00000000773538ec
The NtClose function takes one argument which is the value of the handle. The function will close the handle which has been passed to it and decrement the reference count for the associated object. This function may fail if the handle is kernel-mode only or is protected from being closed.
The second function is ObCloseHandleTableEntry, this is a Object Manager function is called by NtClose. The function looks up the corresponding handle, finds the handle table entry from the process' handle table and then attempts to close it. As we can see, the subsequent call is ObfDereferenceObjectWithTag. This, as the name suggests, decrements the reference count for the associated object.
Rich (BB code):
2: kd> !handle 7c4
PROCESS ffff91880f7ba080
SessionId: 0 Cid: 148c Peb: 00304000 ParentCid: 03f8
DirBase: 1a1ce1000 ObjectTable: ffffa4870d1cf3c0 HandleCount: 2136.
Image: GameManagerService.exe
Handle table at ffffa4870d1cf3c0 with 2136 entries in use
07c4: free handle, Entry address ffffa4870e0c3f10, Next Entry ffffa4870e0c3d60
We can dump the handle which has been closed by using the !handle command. As we can see, the corresponding handle table entry points to a freed handle and the handle table which the handle belongs to is for the GameManagerService.exe process. All processes will have their own handle table and closing a handle occurs within the context of the owning process. This is why it would seem that the GameManagerService.exe process, however, this is necessarily the case and therefore I would recommend running Driver Verifier to be certain, or alternatively, setting object and handle tracing using GFlags and then checking the trace using !obtrace and !htrace respectively.
References:
Windows Internals 6th Edition - Part 1
Last edited: