While teaching a Windows Internals class recently, I came across a situation which looked like a bug to me, but turned out to be something I didn’t know about – dynamic symbolic links.
Symbolic links are Windows kernel objects that point to another object. The weird situation in question was when running WinObj from Sysinternals and navigating to the KenrelObjects object manager directory.

You’ll notice some symbolic link objects that look weird: MemoryErrors, PhysicalMemoryChange, HighMemoryCondition, LowMemoryCondition and a few others. The weird thing that is fairly obvious is that these symbolic link objects have empty targets. Double-clicking any one of them confirms no target, and also shows a curious zero handles, as well as quota change of zero:

To add to the confusion, searching for any of them with Process Explorer yields something like this:

It seems these objects are events, and not symbolic links!
My first instinct was that there is a bug in WinObj (I rewrote it recently for Sysinternals, so was certain I introduced a bug). I ran an old WinObj version, but the result was the same. I tried other tools with similar functionality, and still got the same results. Maybe a bug in Process Explorer? Let’s see in the kernel debugger:

lkd> !object 0xFFFF988110EC0C20
Object: ffff988110ec0c20 Type: (ffff988110efb400) Event
ObjectHeader: ffff988110ec0bf0 (new version)
HandleCount: 4 PointerCount: 117418
Directory Object: ffff828b10689530 Name: HighCommitCondition
Definitely an event and not a symbolic link. What’s going on? I debugged it in WinObj, and indeed the reported object type is a symbolic link. Maybe it’s a bug in the NtQueryDirectoryObject
used to query a directory object for an object.
I asked Mark Russinovich, could there be a bug in Windows? Mark remembered that this is not a bug, but a feature of symbolic links, where objects can be created/resolved dynamically when accessing the symbolic link. Let’s see if we can see something in the debugger:
lkd> !object \kernelobjects\highmemorycondition
Object: ffff828b10659510 Type: (ffff988110e9ba60) SymbolicLink
ObjectHeader: ffff828b106594e0 (new version)
HandleCount: 0 PointerCount: 1
Directory Object: ffff828b10656ce0 Name: HighMemoryCondition
Flags: 0x000010 ( Local )
Target String is '*** target string unavailable ***'
Clearly, there is target, but notice the flags value 0x10. This is the flag indicating the symbolic link is a dynamic one. To get further information, we need to look at the object with a “symbolic link lenses” by using the data structure the kernel uses to represent symbolic links:
lkd> dt nt!_OBJECT_SYMBOLIC_LINK ffff828b10659510
+0x000 CreationTime : _LARGE_INTEGER 0x01d73d87`21bd21e5
+0x008 LinkTarget : _UNICODE_STRING "--- memory read error at address 0x00000000`00000005 ---"
+0x008 Callback : 0xfffff802`08512250 long nt!MiResolveMemoryEvent+0
+0x010 CallbackContext : 0x00000000`00000005 Void
+0x018 DosDeviceDriveIndex : 0
+0x01c Flags : 0x10
+0x020 AccessMask : 0x24
The Callback
member shows the function that is being called (MiResolveMemoryEvent
) that “resolves” the symbolic link to the relevant event. There are currently 11 such events, their names visible with the following:
lkd> dx (nt!_UNICODE_STRING*)&nt!MiMemoryEventNames,11
(nt!_UNICODE_STRING*)&nt!MiMemoryEventNames,11 : 0xfffff80207e02e90 [Type: _UNICODE_STRING *]
[0] : "\KernelObjects\LowPagedPoolCondition" [Type: _UNICODE_STRING]
[1] : "\KernelObjects\HighPagedPoolCondition" [Type: _UNICODE_STRING]
[2] : "\KernelObjects\LowNonPagedPoolCondition" [Type: _UNICODE_STRING]
[3] : "\KernelObjects\HighNonPagedPoolCondition" [Type: _UNICODE_STRING]
[4] : "\KernelObjects\LowMemoryCondition" [Type: _UNICODE_STRING]
[5] : "\KernelObjects\HighMemoryCondition" [Type: _UNICODE_STRING]
[6] : "\KernelObjects\LowCommitCondition" [Type: _UNICODE_STRING]
[7] : "\KernelObjects\HighCommitCondition" [Type: _UNICODE_STRING]
[8] : "\KernelObjects\MaximumCommitCondition" [Type: _UNICODE_STRING]
[9] : "\KernelObjects\MemoryErrors" [Type: _UNICODE_STRING]
[10] : "\KernelObjects\PhysicalMemoryChange" [Type: _UNICODE_STRING]
Creating dynamic symbolic links is only possible from kernel mode, of course, and is undocumented anyway.
At least the conundrum is solved.