xref: /reactos/sdk/lib/rtl/thread.c (revision 53221834)
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 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
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
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
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
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
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
334 _NtCurrentTeb(VOID)
335 {
336     /* Return the TEB */
337     return NtCurrentTeb();
338 }
339 
340 NTSTATUS
341 NTAPI
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