Skip to content

Commit 360b579

Browse files
committed
[NTDLL_APITEST] Add more tests for NtCreateThread
1 parent 2634278 commit 360b579

4 files changed

Lines changed: 411 additions & 0 deletions

File tree

‎modules/rostests/apitests/ntdll/CMakeLists.txt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,13 @@ if(ARCH STREQUAL "i386")
156156
add_asm_files(ntdll_apitest_asm
157157
../crt/i386/setjmp_helper.s
158158
i386/NtContinue.S
159+
i386/NtCreateThread.S
159160
)
160161
elseif(ARCH STREQUAL "amd64")
161162
add_asm_files(ntdll_apitest_asm
162163
../crt/amd64/setjmp_helper.s
163164
amd64/NtContinue.S
165+
amd64/NtCreateThread.S
164166
)
165167
endif()
166168

‎modules/rostests/apitests/ntdll/NtCreateThread.c‎

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,139 @@
33
* LICENSE: See COPYING in the top level directory
44
* PURPOSE: Test for NtCreateThread
55
* PROGRAMMER: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
6+
* PROGRAMMER: Timo Kreuzer <timo.kreuzer@reactos.org>
67
*/
78

89
#include "precomp.h"
910

11+
PVOID TestThreadProcPtr;
12+
CONTEXT TestThreadStartupContext;
13+
DECLSPEC_ALIGN(16) UCHAR TestThreadStartupStack[PAGE_SIZE];
14+
BOOLEAN TestThreadFunctionCalled;
15+
extern void ThreadStartupThunk(void*);
16+
17+
#define RPL_MASK 0x0003
18+
#define MODE_MASK 0x0001
19+
#define EFLAGS_INTERRUPT_MASK 0x200L
20+
#define KGDT_R3_CODE 0x0018
21+
#define KGDT_R3_DATA 0x0020
22+
#define KGDT_R3_TEB 0x0038
23+
#define KGDT64_R3_CMCODE 0x0020
24+
#define KGDT64_R3_DATA 0x0028
25+
#define KGDT64_R3_CODE 0x0030
26+
#define KGDT64_R3_CMTEB 0x0050
27+
28+
static
29+
VOID
30+
InitializeTestContext(PCONTEXT Context)
31+
{
32+
RtlFillMemory(Context, sizeof(*Context), 0xAA);
33+
34+
Context->ContextFlags = CONTEXT_ALL;
35+
36+
#ifdef _M_AMD64
37+
38+
/* Control */
39+
Context->Rsp = (ULONG64)(TestThreadStartupStack + PAGE_SIZE - 16);
40+
Context->Rip = (ULONG64)ThreadStartupThunk;
41+
42+
/* Set Eflags. These get sanitized, but 0x100 (trap flag) makes it trap instantly. */
43+
Context->EFlags = 0xFFFFFFFF;
44+
Context->EFlags &= ~0x100;
45+
46+
/* Integer (these are copied) */
47+
Context->Rax = 0xF000000000000000;
48+
Context->Rcx = 0xF000000000000001;
49+
Context->Rdx = 0xF000000000000002;
50+
Context->Rbx = 0xF000000000000003;
51+
Context->Rbp = 0xF000000000000005;
52+
Context->Rsi = 0xF000000000000006;
53+
Context->Rdi = 0xF000000000000007;
54+
Context->R8 = 0xF000000000000008;
55+
Context->R9 = 0xF000000000000009;
56+
Context->R10 = 0xF00000000000000A;
57+
Context->R11 = 0xF00000000000000B;
58+
Context->R12 = 0xF00000000000000C;
59+
Context->R13 = 0xF00000000000000D;
60+
Context->R14 = 0xF00000000000000E;
61+
Context->R15 = 0xF00000000000000F;
62+
63+
#elif defined(_M_IX86)
64+
65+
/* Control */
66+
Context->Esp = (ULONG)(TestThreadStartupStack + PAGE_SIZE - 16);
67+
Context->Eip = (ULONG)ThreadStartupThunk;
68+
69+
/* Set Eflags. These get sanitized, but 0x100 (trap flag) makes it trap instantly. */
70+
Context->EFlags = 0xFFFFFFFF;
71+
Context->EFlags &= ~0x100; // Disable trap flag, because it would make the thread trap instantly
72+
Context->EFlags &= ~0x00020000; // Disable V86 flag
73+
74+
/* Integer (these are copied) */
75+
Context->Eax = 0xF0000000;
76+
Context->Ecx = 0xF0000001;
77+
Context->Edx = 0xF0000002;
78+
Context->Ebx = 0xF0000003;
79+
Context->Ebp = 0xF0000005;
80+
Context->Esi = 0xF0000006;
81+
Context->Edi = 0xF0000007;
82+
83+
/* FIXME: Initialize segments to prevent ReactOS from crashing in the kernel */
84+
Context->SegCs = KGDT_R3_CODE | RPL_MASK;
85+
Context->SegSs = KGDT_R3_DATA | RPL_MASK;
86+
Context->SegDs = KGDT_R3_DATA | RPL_MASK;
87+
Context->SegEs = KGDT_R3_DATA | RPL_MASK;
88+
Context->SegFs = KGDT_R3_TEB | RPL_MASK;
89+
Context->SegGs = 0;
90+
91+
#else
92+
93+
#pragma message("Missing CONTEXT setup in NtCreateThread test")
94+
ASSERT(FALSE);
95+
96+
#endif
97+
}
98+
99+
VOID
100+
NTAPI
101+
TestThreadProc(IN PVOID StartContext)
102+
{
103+
TestThreadFunctionCalled = TRUE;
104+
105+
#if defined(_M_IX86) || defined(_M_AMD64)
106+
CONTEXT Context;
107+
108+
/* Get the debug registers and floating point */
109+
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT;
110+
GetThreadContext(NtCurrentThread(), &Context);
111+
TestThreadStartupContext.Dr0 = Context.Dr0;
112+
TestThreadStartupContext.Dr1 = Context.Dr1;
113+
TestThreadStartupContext.Dr2 = Context.Dr2;
114+
TestThreadStartupContext.Dr3 = Context.Dr3;
115+
TestThreadStartupContext.Dr6 = Context.Dr6;
116+
TestThreadStartupContext.Dr7 = Context.Dr7;
117+
#if defined(_M_IX86)
118+
TestThreadStartupContext.FloatSave = Context.FloatSave;
119+
#endif
120+
#endif
121+
122+
/* Terminate current thread */
123+
NtTerminateThread(NtCurrentThread(), STATUS_SUCCESS);
124+
}
125+
10126
START_TEST(NtCreateThread)
11127
{
12128
NTSTATUS Status;
13129
INITIAL_TEB InitialTeb;
14130
HANDLE ThreadHandle;
15131
OBJECT_ATTRIBUTES Attributes;
132+
DECLSPEC_ALIGN(16) ULONG_PTR Stack[128];
133+
PULONG_PTR StackPtr = &Stack[ARRAYSIZE(Stack) - 2];
134+
CONTEXT Context;
135+
CLIENT_ID ClientId;
136+
137+
BOOL IsWow64 = FALSE;
138+
IsWow64Process(GetCurrentProcess(), &IsWow64);
16139

17140
InitializeObjectAttributes(&Attributes, NULL, 0, NULL, NULL);
18141
ZeroMemory(&InitialTeb, sizeof(INITIAL_TEB));
@@ -27,4 +150,196 @@ START_TEST(NtCreateThread)
27150
FALSE);
28151

29152
ok_hex(Status, STATUS_ACCESS_VIOLATION);
153+
154+
InitialTeb.PreviousStackBase = NULL;
155+
InitialTeb.PreviousStackLimit = NULL;
156+
InitialTeb.StackBase = StackPtr;
157+
InitialTeb.StackLimit = Stack;
158+
InitialTeb.AllocatedStackBase = Stack;
159+
160+
TestThreadProcPtr = TestThreadProc;
161+
162+
RtlFillMemory(&TestThreadStartupContext, sizeof(TestThreadStartupContext), 0xCC);
163+
InitializeTestContext(&Context);
164+
165+
Status = NtCreateThread(&ThreadHandle,
166+
THREAD_ALL_ACCESS,
167+
NULL,
168+
NtCurrentProcess(),
169+
&ClientId,
170+
&Context,
171+
&InitialTeb,
172+
TRUE);
173+
ok_eq_hex(Status, STATUS_SUCCESS);
174+
175+
NtResumeThread(ThreadHandle, NULL);
176+
WaitForSingleObject(ThreadHandle, INFINITE);
177+
CloseHandle(ThreadHandle);
178+
179+
ok_eq_bool(TestThreadFunctionCalled, TRUE);
180+
TestThreadFunctionCalled = FALSE;
181+
182+
#ifdef _M_AMD64
183+
184+
/* Control */
185+
ok_eq_hex64(TestThreadStartupContext.Rsp, Context.Rsp - 0x28);
186+
ok_eq_hex64(TestThreadStartupContext.SegCs, 0x0033);
187+
ok_eq_hex64(TestThreadStartupContext.EFlags, 0x200ED7);
188+
ok_eq_hex(TestThreadStartupContext.SegSs, 0x0002B);
189+
190+
/* Integer (copied) */
191+
ok_eq_hex64(TestThreadStartupContext.Rax, Context.Rax);
192+
ok_eq_hex64(TestThreadStartupContext.Rcx, Context.Rcx);
193+
ok_eq_hex64(TestThreadStartupContext.Rdx, Context.Rdx);
194+
ok_eq_hex64(TestThreadStartupContext.Rbx, Context.Rbx);
195+
ok_eq_hex64(TestThreadStartupContext.Rbp, (GetNTVersion() >= _WIN32_WINNT_WIN10) ? Context.Rbp : 0);
196+
ok_eq_hex64(TestThreadStartupContext.Rsi, Context.Rsi);
197+
ok_eq_hex64(TestThreadStartupContext.Rdi, Context.Rdi);
198+
ok_eq_hex64(TestThreadStartupContext.R8, Context.R8);
199+
ok_eq_hex64(TestThreadStartupContext.R9, Context.R9);
200+
ok_eq_hex64(TestThreadStartupContext.R10, Context.R10);
201+
ok_eq_hex64(TestThreadStartupContext.R11, Context.R11);
202+
ok_eq_hex64(TestThreadStartupContext.R12, Context.R12);
203+
ok_eq_hex64(TestThreadStartupContext.R13, Context.R13);
204+
ok_eq_hex64(TestThreadStartupContext.R14, Context.R14);
205+
ok_eq_hex64(TestThreadStartupContext.R15, Context.R15);
206+
207+
/* Segments (hardcoded) */
208+
ok_eq_hex(TestThreadStartupContext.SegDs, KGDT64_R3_DATA | RPL_MASK);
209+
ok_eq_hex(TestThreadStartupContext.SegEs, KGDT64_R3_DATA | RPL_MASK);
210+
ok_eq_hex(TestThreadStartupContext.SegFs, KGDT64_R3_CMTEB | RPL_MASK);
211+
ok_eq_hex(TestThreadStartupContext.SegGs, KGDT64_R3_DATA | RPL_MASK);
212+
213+
/* Floating point (hardcoded) */
214+
ok_eq_hex64(TestThreadStartupContext.MxCsr, INITIAL_MXCSR);
215+
ok_eq_hex(TestThreadStartupContext.FltSave.ControlWord, INITIAL_FPCSR);
216+
ok_eq_hex(TestThreadStartupContext.FltSave.StatusWord, 0x0000);
217+
ok_eq_hex(TestThreadStartupContext.FltSave.TagWord, 0x00);
218+
ok_eq_hex(TestThreadStartupContext.FltSave.Reserved1, 0x00);
219+
ok_eq_hex(TestThreadStartupContext.FltSave.ErrorOpcode, 0x0000);
220+
ok_eq_hex(TestThreadStartupContext.FltSave.ErrorOffset, 0x00000000);
221+
ok_eq_hex(TestThreadStartupContext.FltSave.ErrorSelector, 0x0000);
222+
ok_eq_hex(TestThreadStartupContext.FltSave.Reserved2, 0x0000);
223+
ok_eq_hex(TestThreadStartupContext.FltSave.DataOffset, 0x00000000);
224+
ok_eq_hex(TestThreadStartupContext.FltSave.DataSelector, 0x0000);
225+
ok_eq_hex(TestThreadStartupContext.FltSave.Reserved3, 0x0000);
226+
ok_eq_hex(TestThreadStartupContext.FltSave.MxCsr, INITIAL_MXCSR);
227+
ok_eq_hex(TestThreadStartupContext.FltSave.MxCsr_Mask, 0x0002FFFF);
228+
for (ULONG i = 0; i < ARRAYSIZE(TestThreadStartupContext.FltSave.FloatRegisters); i++)
229+
{
230+
ok_eq_hex64(TestThreadStartupContext.FltSave.FloatRegisters[i].Low, 0x0000000000000000ull);
231+
ok_eq_hex64(TestThreadStartupContext.FltSave.FloatRegisters[i].High, 0x0000000000000000ull);
232+
}
233+
PM128A XmmRegisters = &TestThreadStartupContext.Xmm0;
234+
for (ULONG i = 0; i < 16; i++)
235+
{
236+
ok_eq_hex64(XmmRegisters[i].Low, 0x0000000000000000ull);
237+
ok_eq_hex64(XmmRegisters[i].High, 0x0000000000000000ull);
238+
}
239+
for (ULONG i = 0; i < ARRAYSIZE(TestThreadStartupContext.VectorRegister); i++)
240+
{
241+
ok_eq_hex64(TestThreadStartupContext.VectorRegister[i].Low, 0xCCCCCCCCCCCCCCCCull);
242+
ok_eq_hex64(TestThreadStartupContext.VectorRegister[i].High, 0xCCCCCCCCCCCCCCCCull);
243+
}
244+
245+
/* Debug registers (reset to 0) */
246+
ok_eq_hex64(TestThreadStartupContext.Dr0, 0x0000000000000000ull);
247+
ok_eq_hex64(TestThreadStartupContext.Dr1, 0x0000000000000000ull);
248+
ok_eq_hex64(TestThreadStartupContext.Dr2, 0x0000000000000000ull);
249+
ok_eq_hex64(TestThreadStartupContext.Dr3, 0x0000000000000000ull);
250+
ok_eq_hex64(TestThreadStartupContext.Dr6, 0x0000000000000000ull);
251+
ok_eq_hex64(TestThreadStartupContext.Dr7, 0x0000000000000000ull);
252+
253+
#elif defined(_M_IX86)
254+
255+
/* Control */
256+
ok_eq_hex(TestThreadStartupContext.Esp, Context.Esp);
257+
ok_eq_hex(TestThreadStartupContext.EFlags, IsWow64 ? 0x00240ED7 : 0x003C4ED7);
258+
259+
/* Integer (copied) */
260+
ok_eq_hex(TestThreadStartupContext.Eax, Context.Eax);
261+
ok_eq_hex(TestThreadStartupContext.Ecx, Context.Ecx);
262+
ok_eq_hex(TestThreadStartupContext.Edx, Context.Edx);
263+
ok_eq_hex(TestThreadStartupContext.Ebx, Context.Ebx);
264+
ok_eq_hex(TestThreadStartupContext.Ebp, Context.Ebp);
265+
ok_eq_hex(TestThreadStartupContext.Esi, Context.Esi);
266+
ok_eq_hex(TestThreadStartupContext.Edi, Context.Edi);
267+
268+
/* Segments (hardcoded, the upper 16 bits are left uninitialized by RtlCaptureContext) */
269+
ok_eq_hex((USHORT)TestThreadStartupContext.SegCs, IsWow64 ? KGDT64_R3_CMCODE | RPL_MASK : KGDT_R3_CODE | RPL_MASK);
270+
ok_eq_hex((USHORT)TestThreadStartupContext.SegDs, IsWow64 ? KGDT64_R3_DATA | RPL_MASK : KGDT_R3_DATA | RPL_MASK);
271+
ok_eq_hex((USHORT)TestThreadStartupContext.SegEs, IsWow64 ? KGDT64_R3_DATA | RPL_MASK : KGDT_R3_DATA | RPL_MASK);
272+
ok_eq_hex((USHORT)TestThreadStartupContext.SegFs, IsWow64 ? KGDT64_R3_CMTEB | RPL_MASK : KGDT_R3_TEB | RPL_MASK);
273+
ok_eq_hex((USHORT)TestThreadStartupContext.SegGs, IsWow64 ? KGDT64_R3_DATA | RPL_MASK : 0x0000);
274+
ok_eq_hex((USHORT)TestThreadStartupContext.SegSs, IsWow64 ? KGDT64_R3_DATA | RPL_MASK : KGDT_R3_DATA | RPL_MASK);
275+
276+
/* Floating point (hardcoded) */
277+
ok_eq_hex(TestThreadStartupContext.FloatSave.ControlWord, IsWow64 ? 0x27F : 0xFFFF0A6A);
278+
ok_eq_hex(TestThreadStartupContext.FloatSave.StatusWord, IsWow64 ? 0 : 0xFFFF2A2A);
279+
ok_eq_hex(TestThreadStartupContext.FloatSave.TagWord, IsWow64 ? 0x0000FFFF : 0xFFFF0000);
280+
ok_eq_hex(TestThreadStartupContext.FloatSave.ErrorOffset, IsWow64 ? 0 : 0xAAAAAAAA);
281+
ok_eq_hex(TestThreadStartupContext.FloatSave.ErrorSelector, IsWow64 ? 0 : 0x02AAAAAA);
282+
ok_eq_hex(TestThreadStartupContext.FloatSave.DataOffset, IsWow64 ? 0 : 0xAAAAAAAA);
283+
ok_eq_hex(TestThreadStartupContext.FloatSave.DataSelector, IsWow64 ? 0 : 0xFFFFAAAA);
284+
285+
/* Debug registers (reset to 0) */
286+
ok_eq_hex(TestThreadStartupContext.Dr0, 0x00000000);
287+
ok_eq_hex(TestThreadStartupContext.Dr1, 0x00000000);
288+
ok_eq_hex(TestThreadStartupContext.Dr2, 0x00000000);
289+
ok_eq_hex(TestThreadStartupContext.Dr3, 0x00000000);
290+
ok_eq_hex(TestThreadStartupContext.Dr6, 0x00000000);
291+
ok_eq_hex(TestThreadStartupContext.Dr7, 0x00000000);
292+
293+
#else
294+
295+
#pragma message("Missing CONTEXT validation in NtCreateThread test")
296+
ASSERT(FALSE);
297+
298+
#endif
299+
300+
#if defined(_M_AMD64) || defined(_M_IX86)
301+
/* Test Eflags set to 0 */
302+
Context.EFlags = 0;
303+
TestThreadStartupContext.EFlags = 0xCCCCCCCC;
304+
Status = NtCreateThread(&ThreadHandle,
305+
THREAD_ALL_ACCESS,
306+
NULL,
307+
NtCurrentProcess(),
308+
&ClientId,
309+
&Context,
310+
&InitialTeb,
311+
TRUE);
312+
ok_eq_hex(Status, STATUS_SUCCESS);
313+
NtResumeThread(ThreadHandle, NULL);
314+
WaitForSingleObject(ThreadHandle, INFINITE);
315+
CloseHandle(ThreadHandle);
316+
ok_eq_hex(TestThreadStartupContext.EFlags, 0x202);
317+
#endif
318+
319+
#if !defined(_M_IX86) // FIXME: This crashes on x86
320+
/* Test different InitialTeb values */
321+
InitialTeb.PreviousStackBase = Stack;
322+
InitialTeb.PreviousStackLimit = NULL;
323+
Status = NtCreateThread(&ThreadHandle,
324+
THREAD_ALL_ACCESS,
325+
NULL,
326+
NtCurrentProcess(),
327+
&ClientId,
328+
&Context,
329+
&InitialTeb,
330+
FALSE);
331+
ok_eq_hex(Status, (GetNTVersion() >= _WIN32_WINNT_WIN10) ? STATUS_NOT_SUPPORTED : STATUS_SUCCESS);
332+
333+
InitialTeb.PreviousStackBase = NULL;
334+
InitialTeb.PreviousStackLimit = Stack;
335+
Status = NtCreateThread(&ThreadHandle,
336+
THREAD_ALL_ACCESS,
337+
NULL,
338+
NtCurrentProcess(),
339+
&ClientId,
340+
&Context,
341+
&InitialTeb,
342+
FALSE);
343+
ok_eq_hex(Status, (GetNTVersion() >= _WIN32_WINNT_WIN10) ? STATUS_NOT_SUPPORTED : STATUS_SUCCESS);
344+
#endif
30345
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* PROJECT: ReactOS API tests
3+
* LICENSE: MIT (https://spdx.org/licenses/MIT)
4+
* PURPOSE: Helper functions for NtCreateThread test
5+
* COPYRIGHT: Copyright 2026 Timo Kreuzer <timo.kreuzer@reactos.org>
6+
*/
7+
8+
#include <asm.inc>
9+
#include <ksamd64.inc>
10+
11+
.code64
12+
13+
EXTERN RtlCaptureContext:PROC
14+
EXTERN TestThreadProcPtr:QWORD
15+
EXTERN TestThreadStartupContext:QWORD
16+
17+
PUBLIC ThreadStartupThunk
18+
.PROC ThreadStartupThunk
19+
20+
pushfq
21+
push rcx
22+
sub rsp, 5 * 8
23+
.ENDPROLOG
24+
25+
lea rcx, TestThreadStartupContext[rip]
26+
call RtlCaptureContext
27+
28+
/* Save original rcx */
29+
mov rcx, [rsp + 5 * 8]
30+
mov TestThreadStartupContext[rip + CxRcx], rcx
31+
32+
/* Save original eflags */
33+
mov rcx, [rsp + 6 * 8]
34+
mov TestThreadStartupContext[rip + CxEFlags], rcx
35+
36+
/* Fix Rsp */
37+
lea rcx, [rsp + 7 * 8]
38+
mov qword ptr TestThreadStartupContext[rip + CxRsp], rcx
39+
40+
add rsp, 7 * 8
41+
jmp qword ptr TestThreadProcPtr[rip]
42+
43+
.ENDP
44+
45+
END

0 commit comments

Comments
 (0)