Mail Archives: cygwin-developers/1999/09/07/03:58:08
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 AT 8
- pushl $0
- call _SetLastError AT 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 AT 1c DOT ru * egorovv AT 1c DOT ru
*********************************************
- Raw text -