How GUI Thread Conversion on Svr03 Breaks the SEH Chain

The Windows kernel maintains two types of threads — Non-GUI threads, and GUI threads. Non-GUI threads threads use the default stack size of 12KB (on i386, which this this discussion applies to) and the default System Service Descriptor table (SSDT), KeServiceDescriptorTable. GUI threads, in contrast, are expected to have much larger stack requirements and thus use an extended stack size of 60 KB (Note: these are the numbers for Svr03 and may vary among releases). More importantly, however, GUI threads use a different SSDT — KeServiceDescriptorTableShadow. Unlike KeServiceDescriptorTable, which only supports the basic set of system calls, this SSDT also includes all the User and GDI system services.

All threads start off as Non-GUI threads. Once the application makes a call to a system service that does not fall within the default range, however, the NT kernel will suspect this thread to be about to do GUI stuff — and will convert the thread into a GUI thread.

Converting a thread to a GUI thread naturally has to entail two things — swapping the SSDT, and enlarging the stack. While swapping the SSDT is not really interesting, enlarging the stack size poses a challenge — you cannot really enlarge a stack as the nearby pages that would need to be acquired may not be available.

As a consequence, enlarging the stack works by swapping the stack. The old, small stack is exchanged against a newly allocated, larger stack. Now swapping a stack is not really a common thing to do and is pretty easy to get wrong. And well, as it turns out, the Svr03 kernel did in fact get it wrong.

But let’s start at the beginning.

When the number of the requested system service is found to be beyond the range supported by the default SSDT, KiConvertToGuiThread is called to perform the thread conversion. KiConvertToGuiThread itself is pretty dumb and lets PsConvertToGuiThread do the actual work.

The following pseudo code illustrates what PsConvertToGuiThread does:

NTSTATUS PsConvertToGuiThread()
  // Create the new stack.
  LargeStack = MmCreateKernelStack( ... )
  if ( LargeStack == NULL )
      // Allocation failed -- set last error value.
      NtCurrentTeb()->LastErrorValue = ERROR_NOT_ENOUGH_MEMORY;
    __except( ... )
    // N.B. We are still on the old stack.
    // This will copy the old thread's contents to the new stack and 
    // migrate the context of the current thread to the new stack.
    SmallStack = KeSwitchKernelStack( LargeStack, ... );

    // Now we are on the new stack.
    MmDeleteKernelStack( SmallStack, ... );
  // Notify Win32k.
  ( PspW32ProcessCallout )( ... )
  ( PspW32ThreadCallout ) ( ... )

This code looks innocent enough, but infact, it is lying. Too see why, you have to recall how Structured Exception Handling is implemented on i386 and how the C compiler makes use of it (I think I have spent way too much time with SEH over the past months…): The __try/__except-block at the top of the routine will cause to the compiler to emit the typical SEH prolog at the beginning of the function. The purpose of this prolog is to set up an EXCEPTION_REGISTRATION_RECORD and to put this record onto the current thread’s SEH chain, which in turn is rooted in the PCR. In the same way, the compiler will put an appropriate epilog to the end of the routine.

So while the code above suggests that the SEH stuff is scoped to the very beginning of the function, it will not be until the end of the function has been reached that the EXCEPTION_REGISTRATION_RECORD is torn down and removed from the SEH chain.

And at this point, it should become clear why this becomes a problem in the context of stack swapping. At the point where KeSwitchKernelStack is called, the EXCEPTION_REGISTRATION_RECORD will still be listed in the SEH chain, although it does not serve any particular purpose any more. So KeSwitchKernelStack is called, which will, as indicated before, copy the contents of the old stack to the new stack — which, of course, includes the EXCEPTION_REGISTRATION_RECORD.


neither KeSwitchKernelStack, nor PsConvertToGuiThread updates the SEH pointer in the PCR! After the swapping has been conducted and MmDeleteKernelStack has returned, the root of the SEH chain will point to freed memory — memory where the EXCEPTION_REGISTRATION_RECORD once has been.

Now two things are worth noting. First, PsConvertToGuiThread can be expected to occupy the bottommost stack frame of the kernel stack. A situation where the dangling pointer could harm a caller of PsConvertToGuiThread is thus not possible.

Secondly, PsConvertToGuiThread makes callouts to Win32k by invoking the callbacks pointed to by PspW32ProcessCallout and PspW32ThreadCallout. And in fact, it is only PsConvertToGuiThread‘s luck that these routines are so well behaved that they do not cause the system to bugcheck because of the dangling pointer. If one of these routines (or routines called by these) did anything with the SEH chain going beyond adding another record to the chain and removing it later, odds were that this routine would dereference a stray pointer… and would bugcheck the system…

It is worth noting that the implementation of PsConvertToGuiThread has changed in Windows Vista, so that the above discussion does not apply to this and later releases.


7 Responses to “How GUI Thread Conversion on Svr03 Breaks the SEH Chain”

  1. 1 cardis.harding December 13, 2009 at 8:08 am

    Hi guys and girls

    I need some extra money for christmas so I started my own webcame at home, yeah I know but I talk to alot of cool people
    everyday doing this and the money are great, well normally its great it all depends on how many people stop by.

    If you have time and I am online please stop by a chat with me its a free service that is provided by the webcam
    operator he pays me per hour and I will do what you tell me to do. If you are local maybe we can hook up later
    I am still in college and I need some money for the christmas gifts so come on by and say HI.

  2. 2 appopurgy March 22, 2010 at 12:38 pm

    Your welcome everyone,
    My computer worked slowly, many errors. Please, help me to fix buggs on my PC. On format please.
    My operation system is Windows XP.

  3. 3 fourberie June 4, 2010 at 5:32 am

    Hi I just lost my job. I have applied to most of the job sites more times than I’d care to recall and applied to 100s of positions. However, i have not been able to find a single good response to my applications. If anyone knows about particular job site where i can look for a good job, please revert me with the location details. I will be thankful to you for your early response.

  4. 4 Silapolxxzz October 20, 2010 at 12:48 pm

    Hello !

    I’m new on this forum so I introduce me…

    My name is Jason I’m 21 years old, I’m Belgian.

    I like: horses and baseball…

    Nice to meet you

  5. 5 egawydotcom January 27, 2011 at 7:17 am

    Are you ready to try a totally different weight loss plan?

  6. 6 plardiree September 22, 2011 at 4:19 am

    Guy .. Beautiful .. Amazing .. I’ll bookmark your blog and take the feeds alsoI’m satisfied to search out numerous useful info here in the publish, we need develop extra techniques in this regard, thanks for sharing. . . . . .

  7. 7 gamesgirl January 6, 2012 at 8:19 am

    Wowo,great article,it’s so useful to me,and your article is very nice,I’ve had a lot from your blog there,Keep on going,my friend,I will keep eyes on it.By the way,thanks for your post!welcome to Best Game for Girl

Comments are currently closed.


About me

Johannes Passing lives in Berlin, Germany and works as a Solutions Architect at Google Cloud.

While mostly focusing on Cloud-related stuff these days, Johannes still enjoys the occasional dose of Win32, COM, and NT kernel mode development.

He also is the author of cfix, a C/C++ unit testing framework for Win32 and NT kernel mode, Visual Assert, a Visual Studio Unit Testing-AddIn, and NTrace, a dynamic function boundary tracing toolkit for Windows NT/x86 kernel/user mode code.

Contact Johannes: jpassing (at) hotmail com

LinkedIn Profile
Xing Profile
Github Profile

%d bloggers like this: