This is the mail archive of the cygwin-developers@sourceware.cygnus.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

setjmp/longjmp patch update


Hello,

Here is a path which fixes the problem with longjmp when it is called 
from signal handler.
setjmp is modified to store fs:0 (SEH handler) value at offset 44 of
jmp_buf. longjmp in turn checks this value and invokes seh_unwind if 
necessary. 
It handles win32 exceptions raised as signals such as SIGFPE and 
SIGSEGV and preserves SEH handler list in a consistent state.

This makes possible to use malicious SEH for good things.
In this patch I modified signal handler invocation code to call
signal handler guarded by a __try/__finally fashioned block.

It installs additional seh frame around the call which guarantees
that signal state will be restored even if handler doesn't return 
due to the longjmp call.

The patch follows.
Vadim



--- config/i386/setjmp.c	Fri Oct 23 11:23:57 1998
+++ config/i386/setjmp.c.1	Fri Sep 03 08:29:54 1999
@@ -40,4 +40,6 @@ asm("	.globl	_setjmp  		\n"
 "	movw	%ss, %ax		\n"
 "	movw	%ax, 42(%edi)		\n"
+"	movl	%fs:0, %eax		\n"
+"	movl	%eax, 44(%edi)		\n"
 "	popl	%edi			\n"
 "	movl	$0,%eax			\n"
--- config/i386/longjmp.c	Fri Oct 23 11:23:57 1998
+++ config/i386/longjmp.c.1	Fri Sep 03 08:48:20 1999
@@ -11,4 +11,5 @@ details. */
 #ifdef __i386__
 #if 1
+
 asm ("	.globl	_longjmp          \n"
 "_longjmp:                        \n"
@@ -16,4 +17,12 @@ asm ("	.globl	_longjmp          \n"
 "	movl	%esp,%ebp	  \n"
 "	movl	8(%ebp),%edi	  \n"
+"      movl 8(%ebp),%eax         \n"
+"      movl 44(%eax),%eax        \n"
+"      cmpl %fs:0,%eax           \n"
+"      je L1                     \n"
+"      pushl %eax                \n"
+"      call _seh_unwind          \n"
+"      add   $4, %esp            \n"
+"L1:                             \n"
 "	movl	12(%ebp),%eax	  \n"
 "	testl	%eax,%eax	  \n"
--- exceptions.cc	Sun Sep 05 07:55:59 1999
+++ exceptions.cc.1	Tue Sep 07 11:24:49 1999
@@ -25,4 +25,6 @@ static int handle_exceptions (EXCEPTION_
 static void sigreturn ();
 static void sigbegin ();
+
+static void sig_call_try(void (*handler)(int), int sig, sigset_t
oldmask);
 };
 
@@ -578,12 +580,11 @@ got_mutex:
   *(--sp) = orig.Ebx;
   *(--sp) = orig.Eax;
-  *(--sp) = (DWORD)1;
   *(--sp) = oldmask;
   *(--sp) = sig;
-  *(--sp) = (DWORD) sigreturn;
   *(--sp) = (DWORD) thissig.sa_handler;
+  *(--sp) = (DWORD) sigreturn;
 
   orig.Esp = (DWORD) sp;
-  orig.Eip = (DWORD) sigbegin;
+  orig.Eip = (DWORD) sig_call_try;
 
   SetThreadContext (myself->getthread2signal(), &orig); /* Restart the
thread */
@@ -813,8 +814,5 @@ asm ("
 	.text
 _sigreturn:
-	addl	$4,%esp
-	call	_set_process_mask@8
-	pushl	$0
-	call	_SetLastError@4
+	addl	$12,%esp
 	popl	%eax
 	popl	%ebx
@@ -826,3 +824,96 @@ _sigreturn:
 	ret
 ");
+}
+
+extern "C"
+{
+
+/* Light-weight _RtlUnwind API function emulator which 
+does Win32 SEH stack unwinding. Walks from currently 
+installed at fs:0 SEH frame down to given frame and 
+and calls them to do cleanup. */
+
+void 
+seh_unwind(exception_list * frame)
+{
+    EXCEPTION_RECORD ex_rec;
+    CONTEXT ctx;
+    exception_list* dispatched_frame;
+        
+    exception_list* p = _except_list;
+    if (p != frame)
+        debug_printf ("unwinding SEH frames down to %p", frame);
+    else
+        return ;
+
+    ex_rec.ExceptionCode = 0; 
+    ex_rec.ExceptionFlags = 2; 
+    ex_rec.ExceptionRecord = 0; 
+    ex_rec.ExceptionAddress = 0; 
+
+    GetThreadContext(GetCurrentThread(), &ctx); 
+    while (p != (exception_list*)(-1))
+    {
+        if (p == frame)
+        {
+            _except_list = p;
+            return;
+        }
+        else
+        {
+            p->handler(&ex_rec, p, &ctx, &dispatched_frame);
+            debug_printf ("calling SEH frame %p", p);
+            p = p->prev;
+        }
+    }
+    // if we get here something is wrong
+    debug_printf ("failed to find SEH frame %p", frame);
+}
+
+/* extended SEH frame registration*/
+struct exception_list_ex
+{
+  struct _exception_list frame;
+  sigset_t mask;  
+};
+
+/* SEH handler which does nothing except setting stored signal
+mask in a case of seh unwinding */
+static int 
+sig_call_finally (EXCEPTION_RECORD *rec, exception_list_ex * frame_ex, 
+                  CONTEXT *, void *)
+{
+  if (rec->ExceptionFlags == 2) //unwind
+  {
+    set_process_mask (frame_ex->mask, 1);
+    SetLastError(0);
+  }
+  return 0;
+}
+
+/* Performs signal handler invocation within SEH frame
+and restores old signal mask on successful return */
+static void 
+sig_call_try(_sig_func_ptr handler, int sig, sigset_t oldmask)
+{
+  sigbegin();
+
+  //seh frame setup
+  exception_list_ex frame_ex;
+  frame_ex.frame.prev = _except_list;
+  frame_ex.frame.handler = sig_call_finally;
+  frame_ex.mask = oldmask;
+  _except_list = &frame_ex.frame;
+
+  //try
+  handler(sig);
+
+  //restore original seh frame
+  _except_list = frame_ex.frame.prev;
+
+  //finally
+  set_process_mask (oldmask, 1);
+  SetLastError(0);
+}
+
 }


-- 
*********************************************
Vadim Egorov, 1C      *       Вадим Егоров,1C
egorovv@1c.ru         *         egorovv@1c.ru
*********************************************


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]