This issue occurs due to a bug in RemovePrinterFromAllPorts().
The crashing stack:
00000000`0eb7e6d0 000007fe`ec384399 localspl!RemovePrinterFromPort+0x18
00000000`0eb7e700 000007fe`ec3845da localspl!RemovePrinterFromAllPorts+0x41
00000000`0eb7e730 000007fe`ec37b781 localspl!DeletePrinterForReal+0x14e
00000000`0eb7e9c0 000007fe`ec36f6fb localspl!CleanupDeletedPrinters+0x3a
00000000`0eb7e9f0 000007fe`ec36c021 localspl!BuildPrinterInfo+0x1287
00000000`0eb7f3b0 000007fe`ee0d3974 localspl!SplCreateSpooler+0xc5c
00000000`0eb7f880 000007fe`ee0a5b1c win32spl!NCSRCommon::TIniSpooler::CreateSpooler+0x2e8
00000000`0eb7f9b0 000007fe`ee09e531 win32spl!TPrintOpen::InitializeServer+0x13c
00000000`0eb7fa60 000007fe`ee0991d6 win32spl!TPrintOpen::InitializeSpoolers+0x12c
00000000`0eb7fb00 000007fe`ee09a6e1 win32spl!TProviderInitWork::Run+0x16
00000000`0eb7fb30 00000000`77a37313 win32spl!NThreadingLibrary::TWorkCrew::tpSimpleCallback+0x21
00000000`0eb7fb60 00000000`77a47fd0 ntdll!TppSimplepExecuteCallback+0x83
00000000`0eb7fbb0 00000000`7784be3d ntdll!TppWorkerThread+0x3d6
00000000`0eb7fe30 00000000`77a56a51 kernel32!BaseThreadInitThunk+0xd
00000000`0eb7fe60 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
0:007> r
rax=0000000000000000 rbx=0000000013c42ed0 rcx=000000001c44ee30
rdx=0000000013c42ed0 rsi=000000001c44ee30 rdi=0000000000000000
rip=000007feec3832e4 rsp=000000000eb7e6d0 rbp=0000000000000000
r8=000000000eb7e688 r9=00000000098d0e60 r10=0000000077a76e0a
r11=000000001c484e38 r12=0000000000000001 r13=0000000000000000
r14=0000000000000001 r15=0000000000000004
iopl=0 nv up ei pl zr na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
localspl!RemovePrinterFromPort+0x18:
000007fe`ec3832e4 39424c cmp dword ptr [rdx+4Ch],eax ds:00000000`13c42f1c=00000002
Even though we see the memory contents intact for the address 00000000`13c42f1c, it is
actually an address to freed memory.
During the BuildPrintInfo, the spooler traverses the registry to build a list of printers stored in it.
We see that spooler enumerates 1179 printers on the print server.
For each printer whose status is marked as PRINTER_PENDING_DELETION (this value comes from the registry),
spooler calls CleanupDeletedPrinters.
During CleanupDeletedPrinters, each printer is de-linked from the INIPort and DeletePortEntry is called on to
that INIPORT. In this case, there are multiple instances of a printer (identified by a GUID and a unique number, for e.g.
"{E543DC73-D392-45E8-9189-C1FFB6327A1F},857" ) which share the same INIPORT.
Out of those multiple instances, if there are two or more which are marked as PRINTER_PENDING_DELETION,
that printer instance is de-linked from the INIPORT and DeletePortEntry is called for the INIPORT.
The crash occurred from the GUID E543DC73-D392-45E8-9189-C1FFB6327A1F.
There are 3 printer instances for the same GUID E543DC73-D392-45E8-9189-C1FFB6327A1F
and all of them share the INIPORT.
********** 1c469e30 *******
+0x008 pNext : 0x00000000`1c44ee30 _INIPRINTER
+0x018 pName : 0x00000000`1c46bfa0 "{E543DC73-D392-45E8-9189-C1FFB6327A1F},857"
+0x078 Status : 0x1b0 <--------------------- marked for deletion
********** 1c44ee30 *******
+0x008 pNext : 0x00000000`1c433e30 _INIPRINTER
+0x018 pName : 0x00000000`1c450fa0 "{E543DC73-D392-45E8-9189-C1FFB6327A1F},1290"
+0x078 Status : 0x1b0 <--------------------- marked for deletion
********** 1c433e30 *******
+0x008 pNext : 0x00000000`1c416e30 _INIPRINTER
+0x018 pName : 0x00000000`1c435fb0 "{E543DC73-D392-45E8-9189-C1FFB6327A1F}"
+0x078 Status : 0x180
The INIPRINTER which was first deleted was 1c469e30.
0:007> dt localspl!_iniprinter 000000001c469e30
+0x018 pName : 0x00000000`1c46bfa0 "{E543DC73-D392-45E8-9189-C1FFB6327A1F},857"
+0x124 cPorts : 1
+0x128 ppIniPorts : 0x00000000`1c471ff0 -> 0x00000000`13c42ed0 _INIPORT
This caused a DeletePortEntry to be called on the INIPORT 0x00000000`13c42ed0
0:007> dt iniport 0000000013c42ed0
localspl!INIPORT
+0x000 signature : 0x4f50
+0x008 pNext : 0x00000000`13c40ed0 _INIPORT
+0x010 cRef : 0 <------------------------------------- this is 0. which caused it to be free
+0x018 pName : 0x00000000`13c42fb0 "{E543DC73-D392-45E8-9189-C1FFB6327A1F}"
. . .
+0x04c cPrinters : 2 <--- There are still 2 printers which refer to this INIPORT
+0x050 ppIniPrinter : 0x00000000`1c482fe0 -> 0x00000000`1c433e30 _INIPRINTER
ppIniPrinter is a pointer to an array of pointers to INIPRINTER.
After the removal of 1c469e30, there are two entries left:
0:007> dps 0x00000000`1c482fe0
00000000`1c482fe0 00000000`1c433e30
00000000`1c482fe8 00000000`1c44ee30 <---- this is marked for deletion, as shown above
The cRef on that INIPORT was 0 at that time. This caused the INIPORT 0x00000000`13c42ed0 to be released
even though there are two other printer instances which are still referring to this pointer.
The INIPORT 0x00000000`13c42ed0 was deleted at Time Travel Position: 29BC8F8000015C.
The next printer to be deleted is 1c44ee30.
0:007> dt localspl!_iniprinter 000000001c44ee30
. . .
+0x018 pName : 0x00000000`1c450fa0 "{E543DC73-D392-45E8-9189-C1FFB6327A1F},1290"
. . .
+0x124 cPorts : 1
+0x128 ppIniPorts : 0x00000000`1c456ff0 -> 0x00000000`13c42ed0 _INIPORT
While processing the deletion of this printer instance, spooler tries to access INIPORT->cPrinters.
Because the INIPORT was freed during the deletion of the previous instance, this will cause access
to invalid memory and thus crash.
It seems that if there are multiple instances of a printer GUID and if there are more than one which are
marked for deletion, the spooler will crash during the deletion of the second instance.
In the list of all the printers, the GUID E543DC73-D392-45E8-9189-C1FFB6327A1F
is the first one to have multiple instances. It seems that if this GUID were to be absent, the crash would have
occurred for some other GUID.
The underlying problem is: INIPORT cRef does not take into account the fact that there are other
printers still referring it. This fact is taken into account by the field cPrinters, but this value
is not checked on every path to the DeletePortEntry. This thought seems to support a belief that
there is a bug in spooler around the DeletePortEntry.
Because the values come from the registry, it is possible that registry corruption could induce the problem.