Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
Merge pull request #150 from klickverbot/fiber-seh
Browse files Browse the repository at this point in the history
Fixed SEH in fibers on Windows Server OSes.
  • Loading branch information
complexmath committed Feb 13, 2012
2 parents 0b1b9c3 + b91eb27 commit e0f90e7
Showing 1 changed file with 79 additions and 12 deletions.
91 changes: 79 additions & 12 deletions src/core/thread.d
Expand Up @@ -3748,22 +3748,71 @@ private:

version( AsmX86_Windows )
{
push( cast(size_t) &fiber_entryPoint ); // EIP
push( cast(size_t) m_ctxt.bstack ); // EBP
push( 0x00000000 ); // EDI
push( 0x00000000 ); // ESI
push( 0x00000000 ); // EBX
push( 0xFFFFFFFF ); // FS:[0]
version( StackGrowsDown )
version( StackGrowsDown ) {} else static assert( false );

// On Windows Server 2008 and 2008 R2, an exploit mitigation
// technique known as SEHOP is activated by default. To avoid
// hijacking of the exception handler chain, the presence of a
// Windows-internal handler (ntdll.dll!FinalExceptionHandler) at
// its end is tested by RaiseException. If it is not present, all
// handlers are disregarded, and the program is thus aborted
// (see http://blogs.technet.com/b/srd/archive/2009/02/02/
// preventing-the-exploitation-of-seh-overwrites-with-sehop.aspx).
// For new threads, this handler is installed by Windows immediately
// after creation. To make exception handling work in fibers, we
// have to insert it for our new stacks manually as well.
//
// To do this, we first determine the handler by traversing the SEH
// chain of the current thread until its end, and then construct a
// registration block for the last handler on the newly created
// thread. We then continue to push all the initial register values
// for the first context switch as for the other implementations.
//
// Note that this handler is never actually invoked, as we install
// our own one on top of it in the fiber entry point function.
// Thus, it should not have any effects on OSes not implementing
// exception chain verification.

alias void function() fp_t; // Actual signature not relevant.
static struct EXCEPTION_REGISTRATION
{
push( cast(size_t) m_ctxt.bstack ); // FS:[4]
push( cast(size_t) m_ctxt.bstack - m_size ); // FS:[8]
EXCEPTION_REGISTRATION* next; // sehChainEnd if last one.
fp_t handler;
}
else
enum sehChainEnd = cast(EXCEPTION_REGISTRATION*) 0xFFFFFFFF;

__gshared static fp_t finalHandler = null;
if ( finalHandler is null )
{
push( cast(size_t) m_ctxt.bstack ); // FS:[4]
push( cast(size_t) m_ctxt.bstack + m_size ); // FS:[8]
static EXCEPTION_REGISTRATION* fs0()
{
asm
{
naked;
mov EAX, FS:[0];
ret;
}
}
auto reg = fs0();
while ( reg.next != sehChainEnd ) reg = reg.next;

// Benign races are okay here, just to avoid re-lookup on every
// fiber creation.
finalHandler = reg.handler;
}

pstack -= EXCEPTION_REGISTRATION.sizeof;
*(cast(EXCEPTION_REGISTRATION*)pstack) =
EXCEPTION_REGISTRATION( sehChainEnd, finalHandler );

push( cast(size_t) &fiber_entryPoint ); // EIP
push( cast(size_t) m_ctxt.bstack - EXCEPTION_REGISTRATION.sizeof ); // EBP
push( 0x00000000 ); // EDI
push( 0x00000000 ); // ESI
push( 0x00000000 ); // EBX
push( cast(size_t) m_ctxt.bstack - EXCEPTION_REGISTRATION.sizeof ); // FS:[0]
push( cast(size_t) m_ctxt.bstack ); // FS:[4]
push( cast(size_t) m_ctxt.bstack - m_size ); // FS:[8]
push( 0x00000000 ); // EAX
}
else version( AsmX86_64_Windows )
Expand Down Expand Up @@ -4070,6 +4119,24 @@ unittest
}
}

// Test exception handling inside fibers.
unittest
{
enum MSG = "Test message.";
string caughtMsg;
(new Fiber({
try
{
throw new Exception(MSG);
}
catch (Exception e)
{
caughtMsg = e.msg;
}
})).call();
assert(caughtMsg == MSG);
}

version( AsmX86_64_Posix )
{
unittest
Expand Down

0 comments on commit e0f90e7

Please sign in to comment.