Hi,
This is my first attempt to post patches to Wine and it would be great if
you could review these.
First patch
(0001-2-2-ntdll-Added-try-except-blocks-to-RtlCaptureStackBa.txt) is a
correction to a crash situation where application crashes if
RtlCaptureStackBackTrace() is called and one of the frame pointers of call
stack is invalid, pointing usually to 0xffffffff, sometimes to other invalid
locations.
The correction wraps the offending code inside __try – __except blocks so
that a resulting access violation is caught before it can crash the calling
application.
Second patch
(0002-1-2-ntdll-tests-Added-a-test-for-RtlCaptureStackBackTr.txt) is a test
case for the correction.
It changes one of the frame pointers to invalid value and then calls
RtlCaptureStackBackTrace(). The test fails on Wine’s HEAD (without the first
patch), but passes successfully on Windows 7. Haven’t tested this on other
OSes. I created a new signal.c file for the test case under ntdll/tests
since I couldn’t find more fitting place.
This crash can be reproduced with Anarchy Online game when trying to open an
in-game browser. Also reproducible with AwesomiumGL sample application from
Awesomium’s SDK. The crash (among other problems) is reported to bugzilla
at: http://bugs.winehq.org/show_bug.cgi?id=25206.
Best Regards,
Janne Hakonen
diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index aff4509..e80a0af 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -2434,16 +2434,32 @@ USHORT WINAPI RtlCaptureStackBackTrace( ULONG skip,
ULONG count, PVOID *buffer,
{
if (((void *)frame < NtCurrentTeb()->Tib.StackLimit) ||
((void *)(frame + 1) >= NtCurrentTeb()->Tib.StackBase)) return
0;
- frame = (ULONG *)*frame;
+ __TRY /* protect againts corrupted frame pointer */
+ {
+ frame = (ULONG *)*frame;
+ }
+ __EXCEPT_PAGE_FAULT
+ {
+ return 0;
+ }
+ __ENDTRY
}
for (i = 0; i < count; i++)
{
if (((void *)frame < NtCurrentTeb()->Tib.StackLimit) ||
((void *)(frame + 1) >= NtCurrentTeb()->Tib.StackBase)) break;
- buffer[i] = (void *)frame[1];
- if (hash) *hash += frame[1];
- frame = (ULONG *)*frame;
+ __TRY /* protect againts corrupted frame pointer */
+ {
+ buffer[i] = (void *)frame[1];
+ if (hash) *hash += frame[1];
+ frame = (ULONG *)*frame;
+ }
+ __EXCEPT_PAGE_FAULT
+ {
+ return i;
+ }
+ __ENDTRY
}
return i;
}
diff --git a/dlls/ntdll/tests/Makefile.in b/dlls/ntdll/tests/Makefile.in
index 3b88897..43bdada 100644
--- a/dlls/ntdll/tests/Makefile.in
+++ b/dlls/ntdll/tests/Makefile.in
@@ -19,6 +19,7 @@ C_SRCS = \
rtl.c \
rtlbitmap.c \
rtlstr.c \
+ signal.c \
string.c \
time.c
diff --git a/dlls/ntdll/tests/signal.c b/dlls/ntdll/tests/signal.c
new file mode 100644
index 0000000..deacdf2
--- /dev/null
+++ b/dlls/ntdll/tests/signal.c
@@ -0,0 +1,100 @@
+/*
+ * Unit test suite for functions in ntdll signal_*.c files
+ *
+ * Copyright 2011 Janne Hakonen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
USA
+ */
+
+#include "ntdll_test.h"
+#include <wine/exception.h>
+
+/* function pointers */
+static USHORT (WINAPI *pRtlCaptureStackBackTrace)(ULONG, ULONG, PVOID *,
ULONG *);
+
+/* function prototypes */
+static void test_pRtlCaptureStackBackTrace(void);
+static BOOL get_back_trace_with_invalid_frame_pointer(void);
+static void capture_stack_back_trace(void);
+
+/* Test function for RtlCaptureStackBackTrace. */
+static void test_pRtlCaptureStackBackTrace(void)
+{
+ ok(TRUE == get_back_trace_with_invalid_frame_pointer(),
"get_back_trace_with_invalid_frame_pointer() failed\n" );
+}
+
+/* This function attempts to invalidify one frame pointer and then calls
+ * capture_stack_back_trace(). If all goes well TRUE is returned,
+ * but if SEH exception is thrown FALSE is returned instead. */
+static BOOL get_back_trace_with_invalid_frame_pointer(void)
+{
+ BOOL result = FALSE;
+ void **frame;
+ void *old;
+
+ /* copy base pointer to frame */
+ __asm__ ("movl %%ebp, %0\n\t" : "=r" (frame));
+
+ /* invalidify next frame pointer */
+ old = *frame;
+ *frame = (void*)0xffffffff;
+
+ __TRY
+ {
+ /* try to get back trace with invalid call stack, exception is
thrown
+ if something breaks
+ */
+ capture_stack_back_trace();
+ result = TRUE;
+ }
+ __EXCEPT_ALL
+ {
+ trace("capture_stack_back_trace() threw SEH exception, returning
FALSE\n");
+ result = FALSE;
+ }
+ __ENDTRY
+
+ *frame = old;
+ return result;
+}
+
+/* This function just calls RtlCaptureStackBackTrace and doesn't return
anything. */
+static void capture_stack_back_trace(void)
+{
+ ULONG framesToSkip = 1;
+ ULONG maxCallers = 62 - framesToSkip;
+ PULONG hash = NULL;
+
+ void** callers = (void**) calloc(maxCallers, sizeof(void*));
+
+ pRtlCaptureStackBackTrace(framesToSkip, maxCallers, callers, hash);
+
+ free(callers);
+}
+
+START_TEST(signal)
+{
+ HMODULE mod = GetModuleHandleA("ntdll.dll");
+ pRtlCaptureStackBackTrace = (void *)GetProcAddress(mod,
"RtlCaptureStackBackTrace");
+ if (pRtlCaptureStackBackTrace)
+ {
+ test_pRtlCaptureStackBackTrace();
+ }
+ else
+ {
+ win_skip("Required RtlCaptureStackBackTrace function is not
available\n");
+ }
+}
+