Skip to content

Commit b6e732e

Browse files
committed
[NTDLL_APITEST] Add simple tests for RtlUnhandledExceptionFilter(2) (reactos#8353)
Based on an example from Whindmar Saksit.
1 parent fa7eca6 commit b6e732e

File tree

3 files changed

+113
-0
lines changed

3 files changed

+113
-0
lines changed

modules/rostests/apitests/ntdll/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ list(APPEND SOURCE
125125
RtlQueryTimeZoneInfo.c
126126
RtlReAllocateHeap.c
127127
RtlRemovePrivileges.c
128+
RtlUnhandledExceptionFilter.c
128129
RtlUnicodeStringToAnsiString.c
129130
RtlUnicodeStringToCountedOemString.c
130131
RtlUnicodeToOemN.c
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* PROJECT: ReactOS API tests
3+
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4+
* PURPOSE: Tests for RtlUnhandledExceptionFilter(2)
5+
* COPYRIGHT: Copyright 2025 Hermès Bélusca-Maïto <[email protected]>
6+
*/
7+
8+
#include "precomp.h"
9+
10+
static NTSTATUS KnownExceptionCodes[] =
11+
{
12+
/* Well-known exception codes, see include/psdk/minwinbase.h */
13+
EXCEPTION_ACCESS_VIOLATION,
14+
EXCEPTION_DATATYPE_MISALIGNMENT,
15+
EXCEPTION_BREAKPOINT,
16+
EXCEPTION_SINGLE_STEP,
17+
EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
18+
EXCEPTION_FLT_DENORMAL_OPERAND,
19+
EXCEPTION_FLT_DIVIDE_BY_ZERO,
20+
EXCEPTION_FLT_INEXACT_RESULT,
21+
EXCEPTION_FLT_INVALID_OPERATION,
22+
EXCEPTION_FLT_OVERFLOW,
23+
EXCEPTION_FLT_STACK_CHECK,
24+
EXCEPTION_FLT_UNDERFLOW,
25+
EXCEPTION_INT_DIVIDE_BY_ZERO,
26+
EXCEPTION_INT_OVERFLOW,
27+
EXCEPTION_PRIV_INSTRUCTION,
28+
EXCEPTION_IN_PAGE_ERROR,
29+
EXCEPTION_ILLEGAL_INSTRUCTION,
30+
EXCEPTION_NONCONTINUABLE_EXCEPTION,
31+
EXCEPTION_STACK_OVERFLOW,
32+
EXCEPTION_INVALID_DISPOSITION,
33+
EXCEPTION_GUARD_PAGE,
34+
EXCEPTION_INVALID_HANDLE,
35+
EXCEPTION_POSSIBLE_DEADLOCK,
36+
/* Additional ones */
37+
STATUS_STACK_BUFFER_OVERRUN,
38+
};
39+
40+
START_TEST(RtlUnhandledExceptionFilter)
41+
{
42+
LONG (NTAPI* pRtlUnhandledExceptionFilter)(_In_ PEXCEPTION_POINTERS ExceptionInfo);
43+
LONG (NTAPI* pRtlUnhandledExceptionFilter2)(_In_ PEXCEPTION_POINTERS ExceptionInfo, _In_ PCSTR Function);
44+
PPEB Peb = NtCurrentPeb();
45+
CONTEXT ctx = {0};
46+
EXCEPTION_RECORD er = {0};
47+
EXCEPTION_POINTERS ep = {&er, &ctx};
48+
49+
/* Load functions */
50+
HMODULE hNtDll = GetModuleHandleW(L"ntdll.dll");
51+
if (!hNtDll)
52+
{
53+
skip("GetModuleHandleW(\"ntdll.dll\") failed with 0x%08lX\n", GetLastError());
54+
return;
55+
}
56+
pRtlUnhandledExceptionFilter = (VOID*)GetProcAddress(hNtDll, "RtlUnhandledExceptionFilter");
57+
pRtlUnhandledExceptionFilter2 = (VOID*)GetProcAddress(hNtDll, "RtlUnhandledExceptionFilter2");
58+
if (!pRtlUnhandledExceptionFilter || !pRtlUnhandledExceptionFilter2)
59+
{
60+
skip("ntdll!RtlUnhandledExceptionFilter(2) not found\n");
61+
return;
62+
}
63+
64+
/* Build a dummy context/exception record. Not really valid on self, don't care. */
65+
ctx.ContextFlags = CONTEXT_CONTROL;
66+
GetThreadContext(GetCurrentThread(), &ctx);
67+
68+
/* Disable BeingDebugged so that RtlUnhandledExceptionFilter(2) doesn't
69+
* unnecessarily break into the debugger with the debugging instructions.
70+
* If you want to see them, comment the following line! */
71+
Peb->BeingDebugged = FALSE;
72+
73+
/* Test the filter routine return value under different exception codes */
74+
// for (er.ExceptionCode = 0; er.ExceptionCode != ~0; ++er.ExceptionCode)
75+
for (UINT i = 0; i < _countof(KnownExceptionCodes); ++i)
76+
{
77+
LONG r = EXCEPTION_CONTINUE_SEARCH;
78+
BOOLEAN fail = TRUE;
79+
er.ExceptionCode = KnownExceptionCodes[i];
80+
81+
/* Skip test if stack overflow code; the filter would invoke another crash handler */
82+
if (er.ExceptionCode == STATUS_STACK_BUFFER_OVERRUN)
83+
continue;
84+
85+
_SEH2_TRY
86+
{
87+
_SEH2_TRY
88+
{
89+
r = pRtlUnhandledExceptionFilter2(&ep, __FUNCTION__);
90+
fail = FALSE;
91+
}
92+
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
93+
{
94+
}
95+
_SEH2_END;
96+
}
97+
_SEH2_FINALLY
98+
{
99+
}
100+
_SEH2_END;
101+
102+
// trace("%#x ret %lu\n", er.ExceptionCode, r);
103+
if (er.ExceptionCode == STATUS_POSSIBLE_DEADLOCK)
104+
ok_long(r, EXCEPTION_CONTINUE_EXECUTION);
105+
else
106+
ok_long(r, EXCEPTION_CONTINUE_SEARCH);
107+
if (fail)
108+
trace("SEH on %#x\n", er.ExceptionCode);
109+
}
110+
}

modules/rostests/apitests/ntdll/testlist.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ extern void func_RtlpEnsureBufferSize(void);
113113
extern void func_RtlQueryTimeZoneInformation(void);
114114
extern void func_RtlReAllocateHeap(void);
115115
extern void func_RtlRemovePrivileges(void);
116+
extern void func_RtlUnhandledExceptionFilter(void);
116117
extern void func_RtlUnicodeStringToAnsiString(void);
117118
extern void func_RtlUnicodeStringToCountedOemString(void);
118119
extern void func_RtlUnicodeToOemN(void);
@@ -238,6 +239,7 @@ const struct test winetest_testlist[] =
238239
{ "RtlQueryTimeZoneInformation", func_RtlQueryTimeZoneInformation },
239240
{ "RtlReAllocateHeap", func_RtlReAllocateHeap },
240241
{ "RtlRemovePrivileges", func_RtlRemovePrivileges },
242+
{ "RtlUnhandledExceptionFilter", func_RtlUnhandledExceptionFilter },
241243
{ "RtlUnicodeStringToAnsiSize", func_RtlxUnicodeStringToAnsiSize }, /* For some reason, starting test name with Rtlx hides it */
242244
{ "RtlUnicodeStringToAnsiString", func_RtlUnicodeStringToAnsiString },
243245
{ "RtlUnicodeStringToCountedOemString", func_RtlUnicodeStringToCountedOemString },

0 commit comments

Comments
 (0)