From 590f17c72d0353c75007c62962737dc5d553884a Mon Sep 17 00:00:00 2001 From: Kevin B Kenny Date: Sat, 10 Nov 2007 17:24:01 +0000 Subject: 2007-11-10 Kevin Kenny * win/tclWin32Dll.c: Rewrote the Windows stack checking algorithm to use information from VirtualQuery to determine the bound of the stack. This change fixes a bug where the guard page of the stack was never restored after an overflow. It also eliminates a nasty piece of assembly code for structured exception handling on mingw. It introduces an assumption that the stack is a single memory arena returned from VirtualAlloc, but the code in MSVCRT makes the same assumption, so it should be fairly safe. --- ChangeLog | 11 +++ win/tclWin32Dll.c | 198 +++++++++++------------------------------------------- 2 files changed, 51 insertions(+), 158 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6560a3b..e7eb651 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2007-11-10 Kevin Kenny + + * win/tclWin32Dll.c: Rewrote the Windows stack checking algorithm + to use information from VirtualQuery to determine the bound of the + stack. This change fixes a bug where the guard page of the stack + was never restored after an overflow. It also eliminates a nasty + piece of assembly code for structured exception handling on + mingw. It introduces an assumption that the stack is a single + memory arena returned from VirtualAlloc, but the code in MSVCRT + makes the same assumption, so it should be fairly safe. + 2007-11-10 Miguel Sofer * generic/tclBasic.c: diff --git a/win/tclWin32Dll.c b/win/tclWin32Dll.c index 8ffb697..49c6f14 100644 --- a/win/tclWin32Dll.c +++ b/win/tclWin32Dll.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclWin32Dll.c,v 1.49 2007/11/10 16:08:10 msofer Exp $ + * RCS: @(#) $Id: tclWin32Dll.c,v 1.50 2007/11/10 17:24:02 kennykb Exp $ */ #include "tclWinInt.h" @@ -21,9 +21,13 @@ */ typedef struct ThreadSpecificData { int *stackBound; /* The current stack boundary */ + SYSTEM_INFO si; /* The system information, used to + * determine the page size */ + MEMORY_BASIC_INFORMATION mbi; + /* The information about the memory + * area in which the stack resides */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; -static int * CheckStackSpace(void) #endif /* TCL_NO_STACK_CHECK */ /* @@ -522,138 +526,6 @@ TclWinNoBackslash( return path; } -#ifndef TCL_NO_STACK_CHECK -/* - *---------------------------------------------------------------------- - * - * CheckStackSpace -- - * - * Detect if we are about to blow the stack. Called before an evaluation - * can happen when nesting depth is checked. - * - * Results: - * A pointer to the deepest safe stack location that was determined, or - * NULL if we are about to blow the stack. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static int * -CheckStackSpace(void) -{ - -#ifdef HAVE_NO_SEH - EXCEPTION_REGISTRATION registration; -#endif - int retval = 0; - - /* - * We can recurse only if there is at least TCL_WIN_STACK_THRESHOLD bytes - * of stack space left. alloca() is cheap on windows; basically it just - * subtracts from the stack pointer causing the OS to throw an exception - * if the stack pointer is set below the bottom of the stack. - */ - -#ifdef HAVE_NO_SEH - __asm__ __volatile__ ( - - /* - * Construct an EXCEPTION_REGISTRATION to protect the call to __alloca - */ - - "leal %[registration], %%edx" "\n\t" - "movl %%fs:0, %%eax" "\n\t" - "movl %%eax, 0x0(%%edx)" "\n\t" /* link */ - "leal 1f, %%eax" "\n\t" - "movl %%eax, 0x4(%%edx)" "\n\t" /* handler */ - "movl %%ebp, 0x8(%%edx)" "\n\t" /* ebp */ - "movl %%esp, 0xc(%%edx)" "\n\t" /* esp */ - "movl %[error], 0x10(%%edx)" "\n\t" /* status */ - - /* - * Link the EXCEPTION_REGISTRATION on the chain - */ - - "movl %%edx, %%fs:0" "\n\t" - - /* - * Attempt a call to __alloca, to determine whether there's sufficient - * memory to be had. - */ - - "movl %[size], %%eax" "\n\t" - "pushl %%eax" "\n\t" - "call __alloca" "\n\t" - - /* - * Come here on a normal exit. Recover the EXCEPTION_REGISTRATION and - * store a TCL_OK status - */ - - "movl %%fs:0, %%edx" "\n\t" - "movl %[ok], %%eax" "\n\t" - "movl %%eax, 0x10(%%edx)" "\n\t" - "jmp 2f" "\n" - - /* - * Come here on an exception. Get the EXCEPTION_REGISTRATION that we - * previously put on the chain. - */ - - "1:" "\t" - "movl %%fs:0, %%edx" "\n\t" - "movl 0x8(%%edx), %%edx" "\n\t" - - /* - * Come here however we exited. Restore context from the - * EXCEPTION_REGISTRATION in case the stack is unbalanced. - */ - - "2:" "\t" - "movl 0xc(%%edx), %%esp" "\n\t" - "movl 0x8(%%edx), %%ebp" "\n\t" - "movl 0x0(%%edx), %%eax" "\n\t" - "movl %%eax, %%fs:0" "\n\t" - - : - /* No outputs */ - : - [registration] "m" (registration), - [ok] "i" (TCL_OK), - [error] "i" (TCL_ERROR), - [size] "i" (TCL_WIN_STACK_THRESHOLD) - : - "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory" - ); - retval = (registration.status == TCL_OK); - -#else /* !HAVE_NO_SEH */ - __try { -#ifdef HAVE_ALLOCA_GCC_INLINE - __asm__ __volatile__ ( - "movl %0, %%eax" "\n\t" - "call __alloca" "\n\t" - : - : "i"(TCL_WIN_STACK_THRESHOLD) - : "%eax"); -#else - alloca(TCL_WIN_STACK_THRESHOLD); -#endif /* HAVE_ALLOCA_GCC_INLINE */ - retval = 1; - } __except (EXCEPTION_EXECUTE_HANDLER) {} -#endif /* HAVE_NO_SEH */ - - if (retval) { - return (int *) ((char *)&retval - TCL_WIN_STACK_THRESHOLD); - } else { - return NULL; - } -} -#endif - /* *---------------------------------------------------------------------- * @@ -679,41 +551,51 @@ int TclpGetCStackParams( int ***stackBoundPtr) { - int localVar, *stackBound; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* Most variables are actually in a * thread-specific data block to minimise the * impact on the stack. */ - if (!tsdPtr->stackBound || (&localVar < tsdPtr->stackBound)) { - /* - * First time through in this thread or apparent stack failure. If we - * didn't already blow up, we are within the safety area. Recheck with - * the OS to get a new estimate. + if (!tsdPtr->stackBound + || ((DWORD_PTR)&tsdPtr < (DWORD_PTR)tsdPtr->stackBound)) { + + /* + * Either we haven't determined the stack bound in this thread, + * or else we've overflowed the bound that we previously + * determined. We need to find a new stack bound from + * Windows. */ - - stackBound = CheckStackSpace(); - if (!stackBound) { - /* - * We are really blowing the stack! Do not update the estimate we - * already had, just return. - */ - + + GetSystemInfo(&tsdPtr->si); + if (VirtualQuery((LPCVOID) &tsdPtr, + &(tsdPtr->mbi), sizeof(tsdPtr->mbi)) == 0) { + + /* For some reason, the system didn't let us query the + * stack size. Nevertheless, we got here and haven't + * blown up yet. Don't update the calculated stack bound. + * If there is no calculated stack bound yet, set it to + * the base of the current page of stack. */ + if (!tsdPtr->stackBound) { - /* - * ??? First time around: this thread was born with a stack - * smaller than TCL_WIN_STACK_THRESHOLD. What do we do now? - * Initialise to something that will always fail? Choose for - * instance 1K ints above the current depth. - */ - - tsdPtr->stackBound = (&localVar + 1024); + tsdPtr->stackBound = + (int*) ((DWORD_PTR)(&tsdPtr) + & ~ (DWORD_PTR)(tsdPtr->si.dwPageSize - 1)); } + } else { - tsdPtr->stackBound = stackBound; + + /* The allocation base of the stack segment has to be advanced + * by one page (to allow for the guard page maintained in the + * C runtime) and then by TCL_WIN_STACK_THRESHOLD (to allow + * for the amount of stack that Tcl needs). + */ + + tsdPtr->stackBound = + (int*) ((DWORD_PTR)(tsdPtr->mbi.AllocationBase) + + (DWORD_PTR)(tsdPtr->si.dwPageSize) + + TCL_WIN_STACK_THRESHOLD); } } - *stackBoundPtr = &(tsdPtr->stackBound); return 1; } -- cgit v0.12