[KERNEL32] FlsAlloc - fiber local storage list was not initialized before using
[reactos.git] / dll / win32 / kernel32 / client / fiber.c
1 /*
2 * PROJECT: ReactOS System Libraries
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Fiber Implementation
5 * COPYRIGHT: Copyright 2005-2011 Alex Ionescu (alex@relsoft.net)
6 * Copyright 2003-2008 KJK::Hyperion (noog@libero.it)
7 * Copyright 2018 Mark Jansen (mark.jansen@reactos.org)
8 */
9
10 #include <k32.h>
11 #include <ndk/rtltypes.h>
12
13 #define NDEBUG
14 #include <debug.h>
15
16 #ifdef _M_IX86
17 C_ASSERT(FIELD_OFFSET(FIBER, ExceptionList) == 0x04);
18 C_ASSERT(FIELD_OFFSET(FIBER, StackBase) == 0x08);
19 C_ASSERT(FIELD_OFFSET(FIBER, StackLimit) == 0x0C);
20 C_ASSERT(FIELD_OFFSET(FIBER, DeallocationStack) == 0x10);
21 C_ASSERT(FIELD_OFFSET(FIBER, FiberContext) == 0x14);
22 C_ASSERT(FIELD_OFFSET(FIBER, GuaranteedStackBytes) == 0x2E0);
23 C_ASSERT(FIELD_OFFSET(FIBER, FlsData) == 0x2E4);
24 C_ASSERT(FIELD_OFFSET(FIBER, ActivationContextStackPointer) == 0x2E8);
25 C_ASSERT(RTL_FLS_MAXIMUM_AVAILABLE == FLS_MAXIMUM_AVAILABLE);
26 #endif // _M_IX86
27
28 /* PRIVATE FUNCTIONS **********************************************************/
29
30 VOID
31 WINAPI
32 BaseRundownFls(_In_ PVOID FlsData)
33 {
34 ULONG n, FlsHighIndex;
35 PRTL_FLS_DATA pFlsData;
36 PFLS_CALLBACK_FUNCTION lpCallback;
37
38 pFlsData = FlsData;
39
40 RtlAcquirePebLock();
41 FlsHighIndex = NtCurrentPeb()->FlsHighIndex;
42 RemoveEntryList(&pFlsData->ListEntry);
43 RtlReleasePebLock();
44
45 for (n = 1; n <= FlsHighIndex; ++n)
46 {
47 lpCallback = NtCurrentPeb()->FlsCallback[n];
48 if (lpCallback && pFlsData->Data[n])
49 {
50 lpCallback(pFlsData->Data[n]);
51 }
52 }
53
54 RtlFreeHeap(RtlGetProcessHeap(), 0, FlsData);
55 }
56
57 /* PUBLIC FUNCTIONS ***********************************************************/
58
59 /*
60 * @implemented
61 */
62 BOOL
63 WINAPI
64 ConvertFiberToThread(VOID)
65 {
66 PTEB Teb;
67 PFIBER FiberData;
68 DPRINT1("Converting Fiber to Thread\n");
69
70 /* Check if the thread is already not a fiber */
71 Teb = NtCurrentTeb();
72 if (!Teb->HasFiberData)
73 {
74 /* Fail */
75 SetLastError(ERROR_ALREADY_THREAD);
76 return FALSE;
77 }
78
79 /* This thread won't run a fiber anymore */
80 Teb->HasFiberData = FALSE;
81 FiberData = Teb->NtTib.FiberData;
82 Teb->NtTib.FiberData = NULL;
83
84 /* Free the fiber */
85 ASSERT(FiberData != NULL);
86 RtlFreeHeap(RtlGetProcessHeap(),
87 0,
88 FiberData);
89
90 /* Success */
91 return TRUE;
92 }
93
94 /*
95 * @implemented
96 */
97 LPVOID
98 WINAPI
99 ConvertThreadToFiberEx(_In_opt_ LPVOID lpParameter,
100 _In_ DWORD dwFlags)
101 {
102 PTEB Teb;
103 PFIBER Fiber;
104 DPRINT1("Converting Thread to Fiber\n");
105
106 /* Check for invalid flags */
107 if (dwFlags & ~FIBER_FLAG_FLOAT_SWITCH)
108 {
109 /* Fail */
110 SetLastError(ERROR_INVALID_PARAMETER);
111 return NULL;
112 }
113
114 /* Are we already a fiber? */
115 Teb = NtCurrentTeb();
116 if (Teb->HasFiberData)
117 {
118 /* Fail */
119 SetLastError(ERROR_ALREADY_FIBER);
120 return NULL;
121 }
122
123 /* Allocate the fiber */
124 Fiber = RtlAllocateHeap(RtlGetProcessHeap(),
125 0,
126 sizeof(FIBER));
127 if (!Fiber)
128 {
129 /* Fail */
130 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
131 return NULL;
132 }
133
134 /* Copy some contextual data from the thread to the fiber */
135 Fiber->FiberData = lpParameter;
136 Fiber->ExceptionList = Teb->NtTib.ExceptionList;
137 Fiber->StackBase = Teb->NtTib.StackBase;
138 Fiber->StackLimit = Teb->NtTib.StackLimit;
139 Fiber->DeallocationStack = Teb->DeallocationStack;
140 Fiber->FlsData = Teb->FlsData;
141 Fiber->GuaranteedStackBytes = Teb->GuaranteedStackBytes;
142 Fiber->ActivationContextStackPointer = Teb->ActivationContextStackPointer;
143
144 /* Save FPU State if requested, otherwise just the basic registers */
145 Fiber->FiberContext.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ?
146 (CONTEXT_FULL | CONTEXT_FLOATING_POINT) :
147 CONTEXT_FULL;
148
149 /* Associate the fiber to the current thread */
150 Teb->NtTib.FiberData = Fiber;
151 Teb->HasFiberData = TRUE;
152
153 /* Return opaque fiber data */
154 return (LPVOID)Fiber;
155 }
156
157 /*
158 * @implemented
159 */
160 LPVOID
161 WINAPI
162 ConvertThreadToFiber(_In_opt_ LPVOID lpParameter)
163 {
164 /* Call the newer function */
165 return ConvertThreadToFiberEx(lpParameter,
166 0);
167 }
168
169 /*
170 * @implemented
171 */
172 LPVOID
173 WINAPI
174 CreateFiber(_In_ SIZE_T dwStackSize,
175 _In_ LPFIBER_START_ROUTINE lpStartAddress,
176 _In_opt_ LPVOID lpParameter)
177 {
178 /* Call the Newer Function */
179 return CreateFiberEx(dwStackSize,
180 0,
181 0,
182 lpStartAddress,
183 lpParameter);
184 }
185
186 /*
187 * @implemented
188 */
189 LPVOID
190 WINAPI
191 CreateFiberEx(_In_ SIZE_T dwStackCommitSize,
192 _In_ SIZE_T dwStackReserveSize,
193 _In_ DWORD dwFlags,
194 _In_ LPFIBER_START_ROUTINE lpStartAddress,
195 _In_opt_ LPVOID lpParameter)
196 {
197 PFIBER Fiber;
198 NTSTATUS Status;
199 INITIAL_TEB InitialTeb;
200 PACTIVATION_CONTEXT_STACK ActivationContextStackPointer;
201 DPRINT("Creating Fiber\n");
202
203 /* Check for invalid flags */
204 if (dwFlags & ~FIBER_FLAG_FLOAT_SWITCH)
205 {
206 /* Fail */
207 SetLastError(ERROR_INVALID_PARAMETER);
208 return NULL;
209 }
210
211 /* Allocate the Activation Context Stack */
212 ActivationContextStackPointer = NULL;
213 Status = RtlAllocateActivationContextStack(&ActivationContextStackPointer);
214 if (!NT_SUCCESS(Status))
215 {
216 /* Fail */
217 BaseSetLastNTError(Status);
218 return NULL;
219 }
220
221 /* Allocate the fiber */
222 Fiber = RtlAllocateHeap(RtlGetProcessHeap(),
223 0,
224 sizeof(FIBER));
225 if (!Fiber)
226 {
227 /* Free the activation context stack */
228 RtlFreeActivationContextStack(ActivationContextStackPointer);
229
230 /* Fail */
231 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
232 return NULL;
233 }
234
235 /* Create the stack for the fiber */
236 Status = BaseCreateStack(NtCurrentProcess(),
237 dwStackCommitSize,
238 dwStackReserveSize,
239 &InitialTeb);
240 if (!NT_SUCCESS(Status))
241 {
242 /* Free the fiber */
243 RtlFreeHeap(GetProcessHeap(),
244 0,
245 Fiber);
246
247 /* Free the activation context stack */
248 RtlFreeActivationContextStack(ActivationContextStackPointer);
249
250 /* Failure */
251 BaseSetLastNTError(Status);
252 return NULL;
253 }
254
255 /* Clear the context */
256 RtlZeroMemory(&Fiber->FiberContext,
257 sizeof(CONTEXT));
258
259 /* Copy the data into the fiber */
260 Fiber->StackBase = InitialTeb.StackBase;
261 Fiber->StackLimit = InitialTeb.StackLimit;
262 Fiber->DeallocationStack = InitialTeb.AllocatedStackBase;
263 Fiber->FiberData = lpParameter;
264 Fiber->ExceptionList = EXCEPTION_CHAIN_END;
265 Fiber->GuaranteedStackBytes = 0;
266 Fiber->FlsData = NULL;
267 Fiber->ActivationContextStackPointer = ActivationContextStackPointer;
268
269 /* Save FPU State if requested, otherwise just the basic registers */
270 Fiber->FiberContext.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ?
271 (CONTEXT_FULL | CONTEXT_FLOATING_POINT) :
272 CONTEXT_FULL;
273
274 /* Initialize the context for the fiber */
275 BaseInitializeContext(&Fiber->FiberContext,
276 lpParameter,
277 lpStartAddress,
278 InitialTeb.StackBase,
279 2);
280
281 /* Return the Fiber */
282 return Fiber;
283 }
284
285 /*
286 * @implemented
287 */
288 VOID
289 WINAPI
290 DeleteFiber(_In_ LPVOID lpFiber)
291 {
292 SIZE_T Size;
293 PFIBER Fiber;
294 PTEB Teb;
295
296 /* Are we deleting ourselves? */
297 Teb = NtCurrentTeb();
298 Fiber = (PFIBER)lpFiber;
299 if ((Teb->HasFiberData) &&
300 (Teb->NtTib.FiberData == Fiber))
301 {
302 /* Just exit */
303 ExitThread(1);
304 }
305
306 /* Not ourselves, de-allocate the stack */
307 Size = 0 ;
308 NtFreeVirtualMemory(NtCurrentProcess(),
309 &Fiber->DeallocationStack,
310 &Size,
311 MEM_RELEASE);
312
313 /* Get rid of FLS */
314 if (Fiber->FlsData) BaseRundownFls(Fiber->FlsData);
315
316 /* Get rid of the activation context stack */
317 RtlFreeActivationContextStack(Fiber->ActivationContextStackPointer);
318
319 /* Free the fiber data */
320 RtlFreeHeap(RtlGetProcessHeap(),
321 0,
322 lpFiber);
323 }
324
325 /*
326 * @implemented
327 */
328 BOOL
329 WINAPI
330 IsThreadAFiber(VOID)
331 {
332 /* Return flag in the TEB */
333 return NtCurrentTeb()->HasFiberData;
334 }
335
336 /*
337 * @implemented
338 */
339 DWORD
340 WINAPI
341 FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback)
342 {
343 DWORD dwFlsIndex;
344 PPEB Peb = NtCurrentPeb();
345 PRTL_FLS_DATA pFlsData;
346
347 RtlAcquirePebLock();
348
349 pFlsData = NtCurrentTeb()->FlsData;
350
351 if (!Peb->FlsCallback &&
352 !(Peb->FlsCallback = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY,
353 FLS_MAXIMUM_AVAILABLE * sizeof(PVOID))))
354 {
355 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
356 dwFlsIndex = FLS_OUT_OF_INDEXES;
357 }
358 else
359 {
360 dwFlsIndex = RtlFindClearBitsAndSet(Peb->FlsBitmap, 1, 1);
361 if (dwFlsIndex != FLS_OUT_OF_INDEXES)
362 {
363 if (!pFlsData &&
364 !(pFlsData = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RTL_FLS_DATA))))
365 {
366 RtlClearBits(Peb->FlsBitmap, dwFlsIndex, 1);
367 dwFlsIndex = FLS_OUT_OF_INDEXES;
368 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
369 }
370 else
371 {
372 if (!NtCurrentTeb()->FlsData)
373 {
374 NtCurrentTeb()->FlsData = pFlsData;
375 InitializeListHead(&Peb->FlsListHead);
376 InsertTailList(&Peb->FlsListHead, &pFlsData->ListEntry);
377 }
378
379 pFlsData->Data[dwFlsIndex] = NULL; /* clear the value */
380 Peb->FlsCallback[dwFlsIndex] = lpCallback;
381
382 if (dwFlsIndex > Peb->FlsHighIndex)
383 Peb->FlsHighIndex = dwFlsIndex;
384 }
385 }
386 else
387 {
388 SetLastError(ERROR_NO_MORE_ITEMS);
389 }
390 }
391 RtlReleasePebLock();
392 return dwFlsIndex;
393 }
394
395
396 /*
397 * @implemented
398 */
399 BOOL
400 WINAPI
401 FlsFree(DWORD dwFlsIndex)
402 {
403 BOOL ret;
404 PPEB Peb = NtCurrentPeb();
405
406 if (dwFlsIndex >= FLS_MAXIMUM_AVAILABLE)
407 {
408 SetLastError(ERROR_INVALID_PARAMETER);
409 return FALSE;
410 }
411
412 RtlAcquirePebLock();
413
414 _SEH2_TRY
415 {
416 ret = RtlAreBitsSet(Peb->FlsBitmap, dwFlsIndex, 1);
417 if (ret)
418 {
419 PLIST_ENTRY Entry;
420 PFLS_CALLBACK_FUNCTION lpCallback;
421
422 RtlClearBits(Peb->FlsBitmap, dwFlsIndex, 1);
423 lpCallback = Peb->FlsCallback[dwFlsIndex];
424
425 for (Entry = Peb->FlsListHead.Flink; Entry != &Peb->FlsListHead; Entry = Entry->Flink)
426 {
427 PRTL_FLS_DATA pFlsData;
428
429 pFlsData = CONTAINING_RECORD(Entry, RTL_FLS_DATA, ListEntry);
430 if (pFlsData->Data[dwFlsIndex])
431 {
432 if (lpCallback)
433 {
434 lpCallback(pFlsData->Data[dwFlsIndex]);
435 }
436 pFlsData->Data[dwFlsIndex] = NULL;
437 }
438 }
439 Peb->FlsCallback[dwFlsIndex] = NULL;
440 }
441 else
442 {
443 SetLastError(ERROR_INVALID_PARAMETER);
444 }
445 }
446 _SEH2_FINALLY
447 {
448 RtlReleasePebLock();
449 }
450 _SEH2_END;
451
452 return ret;
453 }
454
455
456 /*
457 * @implemented
458 */
459 PVOID
460 WINAPI
461 FlsGetValue(DWORD dwFlsIndex)
462 {
463 PRTL_FLS_DATA pFlsData;
464
465 pFlsData = NtCurrentTeb()->FlsData;
466 if (!dwFlsIndex || dwFlsIndex >= FLS_MAXIMUM_AVAILABLE || !pFlsData)
467 {
468 SetLastError(ERROR_INVALID_PARAMETER);
469 return NULL;
470 }
471
472 SetLastError(ERROR_SUCCESS);
473 return pFlsData->Data[dwFlsIndex];
474 }
475
476
477 /*
478 * @implemented
479 */
480 BOOL
481 WINAPI
482 FlsSetValue(DWORD dwFlsIndex,
483 PVOID lpFlsData)
484 {
485 PRTL_FLS_DATA pFlsData;
486
487 if (!dwFlsIndex || dwFlsIndex >= FLS_MAXIMUM_AVAILABLE)
488 {
489 SetLastError(ERROR_INVALID_PARAMETER);
490 return FALSE;
491 }
492
493 pFlsData = NtCurrentTeb()->FlsData;
494
495 if (!NtCurrentTeb()->FlsData &&
496 !(NtCurrentTeb()->FlsData = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY,
497 sizeof(RTL_FLS_DATA))))
498 {
499 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
500 return FALSE;
501 }
502 if (!pFlsData)
503 {
504 pFlsData = NtCurrentTeb()->FlsData;
505 RtlAcquirePebLock();
506 InsertTailList(&NtCurrentPeb()->FlsListHead, &pFlsData->ListEntry);
507 RtlReleasePebLock();
508 }
509 pFlsData->Data[dwFlsIndex] = lpFlsData;
510 return TRUE;
511 }
512
513 /* EOF */