Mailing-List: contact cygwin-developers-help AT sourceware DOT cygnus DOT com; run by ezmlm List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-developers-owner AT sourceware DOT cygnus DOT com Delivered-To: mailing list cygwin-developers AT sourceware DOT cygnus DOT com Message-ID: <37D4C510.7C473183@1c.ru> Date: Tue, 07 Sep 1999 11:56:00 +0400 From: Vadim Egorov X-Mailer: Mozilla 4.61 [en] (WinNT; I) X-Accept-Language: ru,en MIME-Version: 1.0 To: "cygwin-developers AT sourceware DOT cygnus DOT com" Subject: setjmp/longjmp patch update Content-Type: text/plain; charset=koi8-r Content-Transfer-Encoding: 8bit X-MDaemon-Deliver-To: cygwin-developers AT sourceware DOT cygnus DOT com X-Return-Path: EgorovV AT 1c DOT ru 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 *********************************************