Windows Server 2008 SP2: Print spooler crashes on Terminal Server when deleting ports


Windows Server 2008 SP2 with role of Terminal Server will experience the print spooler crashing intermittently.

Event ID 1000 Application Error

Faulting application spoolsv.exe, version 6.0.6002.18005, time stamp 0x49e03626, faulting module localspl.dll, version 6.0.6002.18005, time stamp 0x49e04141, exception code 0xc0000005, fault offset 0x0000000000002082, process id 0x7608, application start time 0x01c9f5afb3feb721.


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
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
   +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.




The code in the Windows Print spooler was fixed by applying the QFE bits from Security update 961501. 

961501  MS09-022: Vulnerabilities in the Windows Print Spooler could allow remote code execution;EN-US;961501


How to install QFE Bits:

On a Windows Vista or Windows Server 2008 based system:

1. Click Start , type cmd in the Start Search box, right-click cmd.exe in the

Programs list, and then click Run as administrator


If you are prompted for an administrator password or for confirmation, type your

password, or click Continue.

2. At the command prompt, create a msu_expand_folder and a cab_expand_folder and

then type the following commands


a) C:\> Expand -f:* "<path of the .msu file> <msu_expand_folder>

b) C:\> Expand -f:* <path of the file> <cab_expand_folder>

c) C:\> pkgmgr /ip /m:cab_expand_folder\update-bf.mum


More Information