xref: /reactos/sdk/lib/rtl/thread.c (revision 4561998a)
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 __cdecl
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 }
339