CORE-6639 #resolve #time 1d #comment Guard pages now work ;-)
[reactos.git] / reactos / lib / rtl / thread.c
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
23 RtlpCreateUserStack(IN HANDLE hProcess,
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 = 0;
33 BOOLEAN UseGuard = FALSE;
34 ULONG Dummy;
35 SIZE_T 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 (hProcess == 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) StackReserve = Headers->OptionalHeader.
53 SizeOfStackReserve;
54 if (!StackCommit) StackCommit = Headers->OptionalHeader.
55 SizeOfStackCommit;
56 }
57 else
58 {
59 /* Use the System Settings if needed */
60 if (!StackReserve) StackReserve = SystemBasicInfo.AllocationGranularity;
61 if (!StackCommit) StackCommit = SystemBasicInfo.PageSize;
62 }
63
64 /* Check if the commit is higher than the reserve*/
65 if (StackCommit >= StackReserve)
66 {
67 /* Grow the reserve beyond the commit, up to 1MB alignment */
68 StackReserve = ROUND_UP(StackCommit, 1024 * 1024);
69 }
70
71 /* Align everything to Page Size */
72 StackReserve = ROUND_UP(StackReserve, SystemBasicInfo.AllocationGranularity);
73 StackCommit = ROUND_UP(StackCommit, SystemBasicInfo.PageSize);
74
75 /* Reserve memory for the stack */
76 Status = ZwAllocateVirtualMemory(hProcess,
77 (PVOID*)&Stack,
78 StackZeroBits,
79 &StackReserve,
80 MEM_RESERVE,
81 PAGE_READWRITE);
82 if (!NT_SUCCESS(Status)) return Status;
83
84 /* Now set up some basic Initial TEB Parameters */
85 InitialTeb->PreviousStackBase = NULL;
86 InitialTeb->PreviousStackLimit = NULL;
87 InitialTeb->AllocatedStackBase = (PVOID)Stack;
88 InitialTeb->StackBase = (PVOID)(Stack + StackReserve);
89
90 /* Update the Stack Position */
91 Stack += StackReserve - StackCommit;
92
93 /* Check if we will need a guard page */
94 if (StackReserve > StackCommit)
95 {
96 /* Remove a page to set as guard page */
97 Stack -= SystemBasicInfo.PageSize;
98 StackCommit += SystemBasicInfo.PageSize;
99 UseGuard = TRUE;
100 }
101
102 /* Allocate memory for the stack */
103 Status = ZwAllocateVirtualMemory(hProcess,
104 (PVOID*)&Stack,
105 0,
106 &StackCommit,
107 MEM_COMMIT,
108 PAGE_READWRITE);
109 if (!NT_SUCCESS(Status)) return Status;
110
111 /* Now set the current Stack Limit */
112 InitialTeb->StackLimit = (PVOID)Stack;
113
114 /* Create a guard page */
115 if (UseGuard)
116 {
117 /* Attempt maximum space possible */
118 GuardPageSize = SystemBasicInfo.PageSize;
119 Status = ZwProtectVirtualMemory(hProcess,
120 (PVOID*)&Stack,
121 &GuardPageSize,
122 PAGE_GUARD | PAGE_READWRITE,
123 &Dummy);
124 if (!NT_SUCCESS(Status)) return Status;
125
126 /* Update the Stack Limit keeping in mind the Guard Page */
127 InitialTeb->StackLimit = (PVOID)((ULONG_PTR)InitialTeb->StackLimit +
128 GuardPageSize);
129 }
130
131 /* We are done! */
132 return STATUS_SUCCESS;
133 }
134
135 NTSTATUS
136 NTAPI
137 RtlpFreeUserStack(IN HANDLE Process,
138 IN PINITIAL_TEB InitialTeb)
139 {
140 SIZE_T Dummy = 0;
141 NTSTATUS Status;
142
143 /* Free the Stack */
144 Status = ZwFreeVirtualMemory(Process,
145 &InitialTeb->AllocatedStackBase,
146 &Dummy,
147 MEM_RELEASE);
148
149 /* Clear the initial TEB */
150 RtlZeroMemory(InitialTeb, sizeof(INITIAL_TEB));
151 return Status;
152 }
153
154 /* FUNCTIONS ***************************************************************/
155
156
157 /*
158 * @implemented
159 */
160 NTSTATUS
161 NTAPI
162 RtlSetThreadIsCritical(IN BOOLEAN NewValue,
163 OUT PBOOLEAN OldValue OPTIONAL,
164 IN BOOLEAN NeedBreaks)
165 {
166 ULONG BreakOnTermination;
167
168 /* Initialize to FALSE */
169 if (OldValue) *OldValue = FALSE;
170
171 /* Fail, if the critical breaks flag is required but is not set */
172 if ((NeedBreaks) &&
173 !(NtCurrentPeb()->NtGlobalFlag & FLG_ENABLE_SYSTEM_CRIT_BREAKS))
174 {
175 return STATUS_UNSUCCESSFUL;
176 }
177
178 /* Check if the caller wants the old value */
179 if (OldValue)
180 {
181 /* Query and return the old break on termination flag for the process */
182 ZwQueryInformationThread(NtCurrentThread(),
183 ThreadBreakOnTermination,
184 &BreakOnTermination,
185 sizeof(ULONG),
186 NULL);
187 *OldValue = (BOOLEAN)BreakOnTermination;
188 }
189
190 /* Set the break on termination flag for the process */
191 BreakOnTermination = NewValue;
192 return ZwSetInformationThread(NtCurrentThread(),
193 ThreadBreakOnTermination,
194 &BreakOnTermination,
195 sizeof(ULONG));
196 }
197
198 /*
199 @implemented
200 */
201 NTSTATUS
202 NTAPI
203 RtlCreateUserThread(IN HANDLE ProcessHandle,
204 IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
205 IN BOOLEAN CreateSuspended,
206 IN ULONG StackZeroBits OPTIONAL,
207 IN SIZE_T StackReserve OPTIONAL,
208 IN SIZE_T StackCommit OPTIONAL,
209 IN PTHREAD_START_ROUTINE StartAddress,
210 IN PVOID Parameter OPTIONAL,
211 OUT PHANDLE ThreadHandle OPTIONAL,
212 OUT PCLIENT_ID ClientId OPTIONAL)
213 {
214 NTSTATUS Status;
215 HANDLE Handle;
216 CLIENT_ID ThreadCid;
217 INITIAL_TEB InitialTeb;
218 OBJECT_ATTRIBUTES ObjectAttributes;
219 CONTEXT Context;
220
221 /* First, we'll create the Stack */
222 Status = RtlpCreateUserStack(ProcessHandle,
223 StackReserve,
224 StackCommit,
225 StackZeroBits,
226 &InitialTeb);
227 if (!NT_SUCCESS(Status)) return Status;
228
229 /* Next, we'll set up the Initial Context */
230 RtlInitializeContext(ProcessHandle,
231 &Context,
232 Parameter,
233 StartAddress,
234 InitialTeb.StackBase);
235
236 /* We are now ready to create the Kernel Thread Object */
237 InitializeObjectAttributes(&ObjectAttributes,
238 NULL,
239 0,
240 NULL,
241 SecurityDescriptor);
242 Status = ZwCreateThread(&Handle,
243 THREAD_ALL_ACCESS,
244 &ObjectAttributes,
245 ProcessHandle,
246 &ThreadCid,
247 &Context,
248 &InitialTeb,
249 CreateSuspended);
250 if (!NT_SUCCESS(Status))
251 {
252 /* Free the stack */
253 RtlpFreeUserStack(ProcessHandle, &InitialTeb);
254 }
255 else
256 {
257 /* Return thread data */
258 if (ThreadHandle)
259 *ThreadHandle = Handle;
260 else
261 NtClose(Handle);
262 if (ClientId) *ClientId = ThreadCid;
263 }
264
265 /* Return success or the previous failure */
266 return Status;
267 }
268
269 /*
270 * @implemented
271 */
272 VOID
273 NTAPI
274 RtlExitUserThread(NTSTATUS Status)
275 {
276 /* Call the Loader and tell him to notify the DLLs */
277 LdrShutdownThread();
278
279 /* Shut us down */
280 NtCurrentTeb()->FreeStackOnTermination = TRUE;
281 NtTerminateThread(NtCurrentThread(), Status);
282 }
283
284 /*
285 @implemented
286 */
287 VOID
288 NTAPI
289 RtlFreeUserThreadStack(HANDLE ProcessHandle,
290 HANDLE ThreadHandle)
291 {
292 NTSTATUS Status;
293 THREAD_BASIC_INFORMATION ThreadBasicInfo;
294 SIZE_T Dummy, Size = 0;
295 PVOID StackLocation;
296
297 /* Query the Basic Info */
298 Status = NtQueryInformationThread(ThreadHandle,
299 ThreadBasicInformation,
300 &ThreadBasicInfo,
301 sizeof(THREAD_BASIC_INFORMATION),
302 NULL);
303 if (!NT_SUCCESS(Status) || !ThreadBasicInfo.TebBaseAddress) return;
304
305 /* Get the deallocation stack */
306 Status = NtReadVirtualMemory(ProcessHandle,
307 &((PTEB)ThreadBasicInfo.TebBaseAddress)->
308 DeallocationStack,
309 &StackLocation,
310 sizeof(PVOID),
311 &Dummy);
312 if (!NT_SUCCESS(Status) || !StackLocation) return;
313
314 /* Free it */
315 NtFreeVirtualMemory(ProcessHandle, &StackLocation, &Size, MEM_RELEASE);
316 }
317
318 PTEB
319 NTAPI
320 _NtCurrentTeb(VOID)
321 {
322 /* Return the TEB */
323 return NtCurrentTeb();
324 }
325
326 NTSTATUS
327 NTAPI
328 RtlRemoteCall(IN HANDLE Process,
329 IN HANDLE Thread,
330 IN PVOID CallSite,
331 IN ULONG ArgumentCount,
332 IN PULONG Arguments,
333 IN BOOLEAN PassContext,
334 IN BOOLEAN AlreadySuspended)
335 {
336 UNIMPLEMENTED;
337 return STATUS_NOT_IMPLEMENTED;
338 }