1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * PURPOSE: Rtl user thread functions
5 * FILE: lib/rtl/thread.c
6 * PROGRAMERS:
7 * Alex Ionescu (alex@relsoft.net)
8 * Eric Kohl
9 * KJK::Hyperion
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <rtl.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* PRIVATE FUNCTIONS *******************************************************/
20
21 NTSTATUS
22 NTAPI
RtlpCreateUserStack(IN HANDLE ProcessHandle,IN SIZE_T StackReserve OPTIONAL,IN SIZE_T StackCommit OPTIONAL,IN ULONG StackZeroBits OPTIONAL,OUT PINITIAL_TEB InitialTeb)23 RtlpCreateUserStack(IN HANDLE ProcessHandle,
24 IN SIZE_T StackReserve OPTIONAL,
25 IN SIZE_T StackCommit OPTIONAL,
26 IN ULONG StackZeroBits OPTIONAL,
27 OUT PINITIAL_TEB InitialTeb)
28 {
29 NTSTATUS Status;
30 SYSTEM_BASIC_INFORMATION SystemBasicInfo;
31 PIMAGE_NT_HEADERS Headers;
32 ULONG_PTR Stack;
33 BOOLEAN UseGuard;
34 ULONG Dummy;
35 SIZE_T MinimumStackCommit, GuardPageSize;
36
37 /* Get some memory information */
38 Status = ZwQuerySystemInformation(SystemBasicInformation,
39 &SystemBasicInfo,
40 sizeof(SYSTEM_BASIC_INFORMATION),
41 NULL);
42 if (!NT_SUCCESS(Status)) return Status;
43
44 /* Use the Image Settings if we are dealing with the current Process */
45 if (ProcessHandle == NtCurrentProcess())
46 {
47 /* Get the Image Headers */
48 Headers = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
49 if (!Headers) return STATUS_INVALID_IMAGE_FORMAT;
50
51 /* If we didn't get the parameters, find them ourselves */
52 if (StackReserve == 0)
53 StackReserve = Headers->OptionalHeader.SizeOfStackReserve;
54 if (StackCommit == 0)
55 StackCommit = Headers->OptionalHeader.SizeOfStackCommit;
56
57 MinimumStackCommit = NtCurrentPeb()->MinimumStackCommit;
58 if ((MinimumStackCommit != 0) && (StackCommit < MinimumStackCommit))
59 {
60 StackCommit = MinimumStackCommit;
61 }
62 }
63 else
64 {
65 /* Use the System Settings if needed */
66 if (StackReserve == 0)
67 StackReserve = SystemBasicInfo.AllocationGranularity;
68 if (StackCommit == 0)
69 StackCommit = SystemBasicInfo.PageSize;
70 }
71
72 /* Check if the commit is higher than the reserve */
73 if (StackCommit >= StackReserve)
74 {
75 /* Grow the reserve beyond the commit, up to 1MB alignment */
76 StackReserve = ROUND_UP(StackCommit, 1024 * 1024);
77 }
78
79 /* Align everything to Page Size */
80 StackCommit = ROUND_UP(StackCommit, SystemBasicInfo.PageSize);
81 StackReserve = ROUND_UP(StackReserve, SystemBasicInfo.AllocationGranularity);
82
83 /* Reserve memory for the stack */
84 Stack = 0;
85 Status = ZwAllocateVirtualMemory(ProcessHandle,
86 (PVOID*)&Stack,
87 StackZeroBits,
88 &StackReserve,
89 MEM_RESERVE,
90 PAGE_READWRITE);
91 if (!NT_SUCCESS(Status)) return Status;
92
93 /* Now set up some basic Initial TEB Parameters */
94 InitialTeb->AllocatedStackBase = (PVOID)Stack;
95 InitialTeb->StackBase = (PVOID)(Stack + StackReserve);
96 InitialTeb->PreviousStackBase = NULL;
97 InitialTeb->PreviousStackLimit = NULL;
98
99 /* Update the stack position */
100 Stack += StackReserve - StackCommit;
101
102 /* Check if we can add a guard page */
103 if (StackReserve >= StackCommit + SystemBasicInfo.PageSize)
104 {
105 Stack -= SystemBasicInfo.PageSize;
106 StackCommit += SystemBasicInfo.PageSize;
107 UseGuard = TRUE;
108 }
109 else
110 {
111 UseGuard = FALSE;
112 }
113
114 /* Allocate memory for the stack */
115 Status = ZwAllocateVirtualMemory(ProcessHandle,
116 (PVOID*)&Stack,
117 0,
118 &StackCommit,
119 MEM_COMMIT,
120 PAGE_READWRITE);
121 if (!NT_SUCCESS(Status))
122 {
123 GuardPageSize = 0;
124 ZwFreeVirtualMemory(ProcessHandle, (PVOID*)&Stack, &GuardPageSize, MEM_RELEASE);
125 return Status;
126 }
127
128 /* Now set the current Stack Limit */
129 InitialTeb->StackLimit = (PVOID)Stack;
130
131 /* Create a guard page if needed */
132 if (UseGuard)
133 {
134 GuardPageSize = SystemBasicInfo.PageSize;
135 Status = ZwProtectVirtualMemory(ProcessHandle,
136 (PVOID*)&Stack,
137 &GuardPageSize,
138 PAGE_GUARD | PAGE_READWRITE,
139 &Dummy);
140 if (!NT_SUCCESS(Status)) return Status;
141
142 /* Update the Stack Limit keeping in mind the Guard Page */
143 InitialTeb->StackLimit = (PVOID)((ULONG_PTR)InitialTeb->StackLimit +
144 GuardPageSize);
145 }
146
147 /* We are done! */
148 return STATUS_SUCCESS;
149 }
150
151 VOID
152 NTAPI
RtlpFreeUserStack(IN HANDLE ProcessHandle,IN PINITIAL_TEB InitialTeb)153 RtlpFreeUserStack(IN HANDLE ProcessHandle,
154 IN PINITIAL_TEB InitialTeb)
155 {
156 SIZE_T Dummy = 0;
157
158 /* Free the Stack */
159 ZwFreeVirtualMemory(ProcessHandle,
160 &InitialTeb->AllocatedStackBase,
161 &Dummy,
162 MEM_RELEASE);
163
164 /* Clear the initial TEB */
165 RtlZeroMemory(InitialTeb, sizeof(*InitialTeb));
166 }
167
168 /* FUNCTIONS ***************************************************************/
169
170
171 /*
172 * @implemented
173 */
174 NTSTATUS
175 __cdecl
RtlSetThreadIsCritical(IN BOOLEAN NewValue,OUT PBOOLEAN OldValue OPTIONAL,IN BOOLEAN NeedBreaks)176 RtlSetThreadIsCritical(IN BOOLEAN NewValue,
177 OUT PBOOLEAN OldValue OPTIONAL,
178 IN BOOLEAN NeedBreaks)
179 {
180 ULONG BreakOnTermination;
181
182 /* Initialize to FALSE */
183 if (OldValue) *OldValue = FALSE;
184
185 /* Fail, if the critical breaks flag is required but is not set */
186 if ((NeedBreaks) &&
187 !(NtCurrentPeb()->NtGlobalFlag & FLG_ENABLE_SYSTEM_CRIT_BREAKS))
188 {
189 return STATUS_UNSUCCESSFUL;
190 }
191
192 /* Check if the caller wants the old value */
193 if (OldValue)
194 {
195 /* Query and return the old break on termination flag for the process */
196 ZwQueryInformationThread(NtCurrentThread(),
197 ThreadBreakOnTermination,
198 &BreakOnTermination,
199 sizeof(ULONG),
200 NULL);
201 *OldValue = (BOOLEAN)BreakOnTermination;
202 }
203
204 /* Set the break on termination flag for the process */
205 BreakOnTermination = NewValue;
206 return ZwSetInformationThread(NtCurrentThread(),
207 ThreadBreakOnTermination,
208 &BreakOnTermination,
209 sizeof(ULONG));
210 }
211
212 /*
213 @implemented
214 */
215 NTSTATUS
216 NTAPI
RtlCreateUserThread(IN HANDLE ProcessHandle,IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,IN BOOLEAN CreateSuspended,IN ULONG StackZeroBits OPTIONAL,IN SIZE_T StackReserve OPTIONAL,IN SIZE_T StackCommit OPTIONAL,IN PTHREAD_START_ROUTINE StartAddress,IN PVOID Parameter OPTIONAL,OUT PHANDLE ThreadHandle OPTIONAL,OUT PCLIENT_ID ClientId OPTIONAL)217 RtlCreateUserThread(IN HANDLE ProcessHandle,
218 IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
219 IN BOOLEAN CreateSuspended,
220 IN ULONG StackZeroBits OPTIONAL,
221 IN SIZE_T StackReserve OPTIONAL,
222 IN SIZE_T StackCommit OPTIONAL,
223 IN PTHREAD_START_ROUTINE StartAddress,
224 IN PVOID Parameter OPTIONAL,
225 OUT PHANDLE ThreadHandle OPTIONAL,
226 OUT PCLIENT_ID ClientId OPTIONAL)
227 {
228 NTSTATUS Status;
229 HANDLE Handle;
230 CLIENT_ID ThreadCid;
231 INITIAL_TEB InitialTeb;
232 OBJECT_ATTRIBUTES ObjectAttributes;
233 CONTEXT Context;
234
235 /* First, we'll create the Stack */
236 Status = RtlpCreateUserStack(ProcessHandle,
237 StackReserve,
238 StackCommit,
239 StackZeroBits,
240 &InitialTeb);
241 if (!NT_SUCCESS(Status)) return Status;
242
243 /* Next, we'll set up the Initial Context */
244 RtlInitializeContext(ProcessHandle,
245 &Context,
246 Parameter,
247 StartAddress,
248 InitialTeb.StackBase);
249
250 /* We are now ready to create the Kernel Thread Object */
251 InitializeObjectAttributes(&ObjectAttributes,
252 NULL,
253 0,
254 NULL,
255 SecurityDescriptor);
256 Status = ZwCreateThread(&Handle,
257 THREAD_ALL_ACCESS,
258 &ObjectAttributes,
259 ProcessHandle,
260 &ThreadCid,
261 &Context,
262 &InitialTeb,
263 CreateSuspended);
264 if (!NT_SUCCESS(Status))
265 {
266 /* Free the stack */
267 RtlpFreeUserStack(ProcessHandle, &InitialTeb);
268 }
269 else
270 {
271 /* Return thread data */
272 if (ThreadHandle)
273 *ThreadHandle = Handle;
274 else
275 NtClose(Handle);
276 if (ClientId) *ClientId = ThreadCid;
277 }
278
279 /* Return success or the previous failure */
280 return Status;
281 }
282
283 /*
284 * @implemented
285 */
286 VOID
287 NTAPI
RtlExitUserThread(NTSTATUS Status)288 RtlExitUserThread(NTSTATUS Status)
289 {
290 /* Call the Loader and tell him to notify the DLLs */
291 LdrShutdownThread();
292
293 /* Shut us down */
294 NtCurrentTeb()->FreeStackOnTermination = TRUE;
295 NtTerminateThread(NtCurrentThread(), Status);
296 }
297
298 /*
299 @implemented
300 */
301 VOID
302 NTAPI
RtlFreeUserThreadStack(HANDLE ProcessHandle,HANDLE ThreadHandle)303 RtlFreeUserThreadStack(HANDLE ProcessHandle,
304 HANDLE ThreadHandle)
305 {
306 NTSTATUS Status;
307 THREAD_BASIC_INFORMATION ThreadBasicInfo;
308 SIZE_T Dummy, Size = 0;
309 PVOID StackLocation;
310
311 /* Query the Basic Info */
312 Status = NtQueryInformationThread(ThreadHandle,
313 ThreadBasicInformation,
314 &ThreadBasicInfo,
315 sizeof(THREAD_BASIC_INFORMATION),
316 NULL);
317 if (!NT_SUCCESS(Status) || !ThreadBasicInfo.TebBaseAddress) return;
318
319 /* Get the deallocation stack */
320 Status = NtReadVirtualMemory(ProcessHandle,
321 &((PTEB)ThreadBasicInfo.TebBaseAddress)->
322 DeallocationStack,
323 &StackLocation,
324 sizeof(PVOID),
325 &Dummy);
326 if (!NT_SUCCESS(Status) || !StackLocation) return;
327
328 /* Free it */
329 NtFreeVirtualMemory(ProcessHandle, &StackLocation, &Size, MEM_RELEASE);
330 }
331
332 PTEB
333 NTAPI
_NtCurrentTeb(VOID)334 _NtCurrentTeb(VOID)
335 {
336 /* Return the TEB */
337 return NtCurrentTeb();
338 }
339
340 NTSTATUS
341 NTAPI
RtlRemoteCall(IN HANDLE Process,IN HANDLE Thread,IN PVOID CallSite,IN ULONG ArgumentCount,IN PULONG Arguments,IN BOOLEAN PassContext,IN BOOLEAN AlreadySuspended)342 RtlRemoteCall(IN HANDLE Process,
343 IN HANDLE Thread,
344 IN PVOID CallSite,
345 IN ULONG ArgumentCount,
346 IN PULONG Arguments,
347 IN BOOLEAN PassContext,
348 IN BOOLEAN AlreadySuspended)
349 {
350 UNIMPLEMENTED;
351 return STATUS_NOT_IMPLEMENTED;
352 }
353