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