[KERNEL32] FlsAlloc - fiber local storage list was not initialized before using
[reactos.git] / dll / win32 / kernel32 / client / fiber.c
index 22ece1b..a1a5ffe 100644 (file)
@@ -1,39 +1,57 @@
 /*
- * COPYRIGHT:   See COPYING in the top level directory
  * PROJECT:     ReactOS System Libraries
- * FILE:        lib/kernel32/thread/fiber.c
+ * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
  * PURPOSE:     Fiber Implementation
- * PROGRAMMERS: 
- *              Alex Ionescu (alex@relsoft.net)
- *              KJK::Hyperion <noog@libero.it>
+ * COPYRIGHT:   Copyright 2005-2011 Alex Ionescu (alex@relsoft.net)
+ *              Copyright 2003-2008 KJK::Hyperion (noog@libero.it)
+ *              Copyright 2018 Mark Jansen (mark.jansen@reactos.org)
  */
+
 #include <k32.h>
+#include <ndk/rtltypes.h>
 
 #define NDEBUG
 #include <debug.h>
 
-typedef struct _FIBER                                      /* Field offsets:  */
-{                                                          /* 32 bit   64 bit */
-    /* this must be the first field */
-    PVOID Parameter;                                       /*   0x00     0x00 */
-    PEXCEPTION_REGISTRATION_RECORD ExceptionList;          /*   0x04     0x08 */
-    PVOID StackBase;                                       /*   0x08     0x10 */
-    PVOID StackLimit;                                      /*   0x0C     0x18 */
-    PVOID DeallocationStack;                               /*   0x10     0x20 */
-    CONTEXT Context;                                       /*   0x14     0x28 */
-    ULONG GuaranteedStackBytes;                            /*   0x2E0         */
-    PVOID FlsData;                                         /*   0x2E4         */
-    PACTIVATION_CONTEXT_STACK ActivationContextStack;      /*   0x2E8         */
-} FIBER, *PFIBER;
+#ifdef _M_IX86
+C_ASSERT(FIELD_OFFSET(FIBER, ExceptionList) == 0x04);
+C_ASSERT(FIELD_OFFSET(FIBER, StackBase) == 0x08);
+C_ASSERT(FIELD_OFFSET(FIBER, StackLimit) == 0x0C);
+C_ASSERT(FIELD_OFFSET(FIBER, DeallocationStack) == 0x10);
+C_ASSERT(FIELD_OFFSET(FIBER, FiberContext) == 0x14);
+C_ASSERT(FIELD_OFFSET(FIBER, GuaranteedStackBytes) == 0x2E0);
+C_ASSERT(FIELD_OFFSET(FIBER, FlsData) == 0x2E4);
+C_ASSERT(FIELD_OFFSET(FIBER, ActivationContextStackPointer) == 0x2E8);
+C_ASSERT(RTL_FLS_MAXIMUM_AVAILABLE == FLS_MAXIMUM_AVAILABLE);
+#endif // _M_IX86
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
 VOID
 WINAPI
-BaseRundownFls(IN PVOID FlsData)
+BaseRundownFls(_In_ PVOID FlsData)
 {
-    /* No FLS support yet */
-    
+    ULONG n, FlsHighIndex;
+    PRTL_FLS_DATA pFlsData;
+    PFLS_CALLBACK_FUNCTION lpCallback;
+
+    pFlsData = FlsData;
+
+    RtlAcquirePebLock();
+    FlsHighIndex = NtCurrentPeb()->FlsHighIndex;
+    RemoveEntryList(&pFlsData->ListEntry);
+    RtlReleasePebLock();
+
+    for (n = 1; n <= FlsHighIndex; ++n)
+    {
+        lpCallback = NtCurrentPeb()->FlsCallback[n];
+        if (lpCallback && pFlsData->Data[n])
+        {
+            lpCallback(pFlsData->Data[n]);
+        }
+    }
+
+    RtlFreeHeap(RtlGetProcessHeap(), 0, FlsData);
 }
 
 /* PUBLIC FUNCTIONS ***********************************************************/
@@ -58,16 +76,18 @@ ConvertFiberToThread(VOID)
         return FALSE;
     }
 
-    /* this thread won't run a fiber anymore */
+    /* This thread won't run a fiber anymore */
     Teb->HasFiberData = FALSE;
     FiberData = Teb->NtTib.FiberData;
     Teb->NtTib.FiberData = NULL;
 
     /* Free the fiber */
     ASSERT(FiberData != NULL);
-    RtlFreeHeap(GetProcessHeap(), 0, FiberData);
+    RtlFreeHeap(RtlGetProcessHeap(),
+                0,
+                FiberData);
 
-    /* success */
+    /* Success */
     return TRUE;
 }
 
@@ -76,15 +96,15 @@ ConvertFiberToThread(VOID)
  */
 LPVOID
 WINAPI
-ConvertThreadToFiberEx(LPVOID lpParameter, 
-                       DWORD dwFlags)
+ConvertThreadToFiberEx(_In_opt_ LPVOID lpParameter,
+                       _In_ DWORD dwFlags)
 {
     PTEB Teb;
     PFIBER Fiber;
     DPRINT1("Converting Thread to Fiber\n");
 
     /* Check for invalid flags */
-    if (dwFlags &FIBER_FLAG_FLOAT_SWITCH)
+    if (dwFlags & ~FIBER_FLAG_FLOAT_SWITCH)
     {
         /* Fail */
         SetLastError(ERROR_INVALID_PARAMETER);
@@ -101,29 +121,30 @@ ConvertThreadToFiberEx(LPVOID lpParameter,
     }
 
     /* Allocate the fiber */
-    Fiber = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(FIBER));
+    Fiber = RtlAllocateHeap(RtlGetProcessHeap(),
+                            0,
+                            sizeof(FIBER));
     if (!Fiber)
     {
+        /* Fail */
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
         return NULL;
     }
 
     /* Copy some contextual data from the thread to the fiber */
-    Fiber->Parameter = lpParameter;
+    Fiber->FiberData = lpParameter;
     Fiber->ExceptionList = Teb->NtTib.ExceptionList;
     Fiber->StackBase = Teb->NtTib.StackBase;
     Fiber->StackLimit = Teb->NtTib.StackLimit;
     Fiber->DeallocationStack = Teb->DeallocationStack;
     Fiber->FlsData = Teb->FlsData;
     Fiber->GuaranteedStackBytes = Teb->GuaranteedStackBytes;
-    Fiber->ActivationContextStack = Teb->ActivationContextStackPointer;
-    Fiber->Context.ContextFlags = CONTEXT_FULL;
+    Fiber->ActivationContextStackPointer = Teb->ActivationContextStackPointer;
 
-    /* Save FPU State if requested */
-    if (dwFlags & FIBER_FLAG_FLOAT_SWITCH)
-    {
-        Fiber->Context.ContextFlags |= CONTEXT_FLOATING_POINT;
-    }
+    /* Save FPU State if requested, otherwise just the basic registers */
+    Fiber->FiberContext.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ?
+                                       (CONTEXT_FULL | CONTEXT_FLOATING_POINT) :
+                                       CONTEXT_FULL;
 
     /* Associate the fiber to the current thread */
     Teb->NtTib.FiberData = Fiber;
@@ -138,10 +159,11 @@ ConvertThreadToFiberEx(LPVOID lpParameter,
  */
 LPVOID
 WINAPI
-ConvertThreadToFiber(LPVOID lpParameter)
+ConvertThreadToFiber(_In_opt_ LPVOID lpParameter)
 {
     /* Call the newer function */
-    return ConvertThreadToFiberEx(lpParameter, 0);
+    return ConvertThreadToFiberEx(lpParameter,
+                                  0);
 }
 
 /*
@@ -149,12 +171,16 @@ ConvertThreadToFiber(LPVOID lpParameter)
  */
 LPVOID
 WINAPI
-CreateFiber(SIZE_T dwStackSize,
-            LPFIBER_START_ROUTINE lpStartAddress,
-            LPVOID lpParameter)
+CreateFiber(_In_ SIZE_T dwStackSize,
+            _In_ LPFIBER_START_ROUTINE lpStartAddress,
+            _In_opt_ LPVOID lpParameter)
 {
     /* Call the Newer Function */
-    return CreateFiberEx(dwStackSize, 0, 0, lpStartAddress, lpParameter);
+    return CreateFiberEx(dwStackSize,
+                         0,
+                         0,
+                         lpStartAddress,
+                         lpParameter);
 }
 
 /*
@@ -162,20 +188,20 @@ CreateFiber(SIZE_T dwStackSize,
  */
 LPVOID
 WINAPI
-CreateFiberEx(SIZE_T dwStackCommitSize,
-              SIZE_T dwStackReserveSize,
-              DWORD dwFlags,
-              LPFIBER_START_ROUTINE lpStartAddress,
-              LPVOID lpParameter)
+CreateFiberEx(_In_ SIZE_T dwStackCommitSize,
+              _In_ SIZE_T dwStackReserveSize,
+              _In_ DWORD dwFlags,
+              _In_ LPFIBER_START_ROUTINE lpStartAddress,
+              _In_opt_ LPVOID lpParameter)
 {
     PFIBER Fiber;
     NTSTATUS Status;
     INITIAL_TEB InitialTeb;
-    PACTIVATION_CONTEXT_STACK ActivationContextStack = NULL;
+    PACTIVATION_CONTEXT_STACK ActivationContextStackPointer;
     DPRINT("Creating Fiber\n");
 
     /* Check for invalid flags */
-    if (dwFlags &FIBER_FLAG_FLOAT_SWITCH)
+    if (dwFlags & ~FIBER_FLAG_FLOAT_SWITCH)
     {
         /* Fail */
         SetLastError(ERROR_INVALID_PARAMETER);
@@ -183,7 +209,8 @@ CreateFiberEx(SIZE_T dwStackCommitSize,
     }
 
     /* Allocate the Activation Context Stack */
-    Status = RtlAllocateActivationContextStack(&ActivationContextStack);
+    ActivationContextStackPointer = NULL;
+    Status = RtlAllocateActivationContextStack(&ActivationContextStackPointer);
     if (!NT_SUCCESS(Status))
     {
         /* Fail */
@@ -192,11 +219,13 @@ CreateFiberEx(SIZE_T dwStackCommitSize,
     }
 
     /* Allocate the fiber */
-    Fiber = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(FIBER));
+    Fiber = RtlAllocateHeap(RtlGetProcessHeap(),
+                            0,
+                            sizeof(FIBER));
     if (!Fiber)
     {
         /* Free the activation context stack */
-        RtlFreeActivationContextStack(ActivationContextStack);
+        RtlFreeActivationContextStack(ActivationContextStackPointer);
 
         /* Fail */
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
@@ -211,10 +240,12 @@ CreateFiberEx(SIZE_T dwStackCommitSize,
     if (!NT_SUCCESS(Status))
     {
         /* Free the fiber */
-        RtlFreeHeap(GetProcessHeap(), 0, Fiber);
+        RtlFreeHeap(GetProcessHeap(),
+                    0,
+                    Fiber);
 
         /* Free the activation context stack */
-        RtlFreeActivationContextStack(ActivationContextStack);
+        RtlFreeActivationContextStack(ActivationContextStackPointer);
 
         /* Failure */
         BaseSetLastNTError(Status);
@@ -222,24 +253,26 @@ CreateFiberEx(SIZE_T dwStackCommitSize,
     }
 
     /* Clear the context */
-    RtlZeroMemory(&Fiber->Context, sizeof(CONTEXT));
+    RtlZeroMemory(&Fiber->FiberContext,
+                  sizeof(CONTEXT));
 
     /* Copy the data into the fiber */
     Fiber->StackBase = InitialTeb.StackBase;
     Fiber->StackLimit = InitialTeb.StackLimit;
     Fiber->DeallocationStack = InitialTeb.AllocatedStackBase;
-    Fiber->Parameter = lpParameter;
+    Fiber->FiberData = lpParameter;
     Fiber->ExceptionList = EXCEPTION_CHAIN_END;
     Fiber->GuaranteedStackBytes = 0;
     Fiber->FlsData = NULL;
-    Fiber->ActivationContextStack = ActivationContextStack;
-    Fiber->Context.ContextFlags = CONTEXT_FULL;
+    Fiber->ActivationContextStackPointer = ActivationContextStackPointer;
 
-    /* Save FPU State if requested */
-    Fiber->Context.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ? CONTEXT_FLOATING_POINT : 0;
+    /* Save FPU State if requested, otherwise just the basic registers */
+    Fiber->FiberContext.ContextFlags = (dwFlags & FIBER_FLAG_FLOAT_SWITCH) ?
+                                       (CONTEXT_FULL | CONTEXT_FLOATING_POINT) :
+                                       CONTEXT_FULL;
 
-    /* initialize the context for the fiber */
-    BaseInitializeContext(&Fiber->Context,
+    /* Initialize the context for the fiber */
+    BaseInitializeContext(&Fiber->FiberContext,
                           lpParameter,
                           lpStartAddress,
                           InitialTeb.StackBase,
@@ -254,17 +287,24 @@ CreateFiberEx(SIZE_T dwStackCommitSize,
  */
 VOID
 WINAPI
-DeleteFiber(LPVOID lpFiber)
+DeleteFiber(_In_ LPVOID lpFiber)
 {
-    SIZE_T Size = 0;
-    PFIBER Fiber = (PFIBER)lpFiber;
+    SIZE_T Size;
+    PFIBER Fiber;
     PTEB Teb;
 
-    /* First, exit the thread */
+    /* Are we deleting ourselves? */
     Teb = NtCurrentTeb();
-    if ((Teb->HasFiberData) && (Teb->NtTib.FiberData == Fiber)) ExitThread(1);
+    Fiber = (PFIBER)lpFiber;
+    if ((Teb->HasFiberData) &&
+        (Teb->NtTib.FiberData == Fiber))
+    {
+        /* Just exit */
+        ExitThread(1);
+    }
 
-    /* Now de-allocate the stack */
+    /* Not ourselves, de-allocate the stack */
+    Size = 0 ;
     NtFreeVirtualMemory(NtCurrentProcess(),
                         &Fiber->DeallocationStack,
                         &Size,
@@ -274,10 +314,12 @@ DeleteFiber(LPVOID lpFiber)
     if (Fiber->FlsData) BaseRundownFls(Fiber->FlsData);
 
     /* Get rid of the activation context stack */
-    RtlFreeActivationContextStack(Fiber->ActivationContextStack);
+    RtlFreeActivationContextStack(Fiber->ActivationContextStackPointer);
 
     /* Free the fiber data */
-    RtlFreeHeap(GetProcessHeap(), 0, lpFiber);
+    RtlFreeHeap(RtlGetProcessHeap(),
+                0,
+                lpFiber);
 }
 
 /*
@@ -287,36 +329,127 @@ BOOL
 WINAPI
 IsThreadAFiber(VOID)
 {
+    /* Return flag in the TEB */
     return NtCurrentTeb()->HasFiberData;
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 DWORD
 WINAPI
 FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback)
 {
-   (void)lpCallback;
+    DWORD dwFlsIndex;
+    PPEB Peb = NtCurrentPeb();
+    PRTL_FLS_DATA pFlsData;
+
+    RtlAcquirePebLock();
 
-   UNIMPLEMENTED;
-   SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-   return FLS_OUT_OF_INDEXES;
+    pFlsData = NtCurrentTeb()->FlsData;
+
+    if (!Peb->FlsCallback &&
+        !(Peb->FlsCallback = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY,
+                                             FLS_MAXIMUM_AVAILABLE * sizeof(PVOID))))
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        dwFlsIndex = FLS_OUT_OF_INDEXES;
+    }
+    else
+    {
+        dwFlsIndex = RtlFindClearBitsAndSet(Peb->FlsBitmap, 1, 1);
+        if (dwFlsIndex != FLS_OUT_OF_INDEXES)
+        {
+            if (!pFlsData &&
+                !(pFlsData = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RTL_FLS_DATA))))
+            {
+                RtlClearBits(Peb->FlsBitmap, dwFlsIndex, 1);
+                dwFlsIndex = FLS_OUT_OF_INDEXES;
+                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+            }
+            else
+            {
+                if (!NtCurrentTeb()->FlsData)
+                {
+                    NtCurrentTeb()->FlsData = pFlsData;
+                    InitializeListHead(&Peb->FlsListHead);
+                    InsertTailList(&Peb->FlsListHead, &pFlsData->ListEntry);
+                }
+
+                pFlsData->Data[dwFlsIndex] = NULL; /* clear the value */
+                Peb->FlsCallback[dwFlsIndex] = lpCallback;
+
+                if (dwFlsIndex > Peb->FlsHighIndex)
+                    Peb->FlsHighIndex = dwFlsIndex;
+            }
+        }
+        else
+        {
+            SetLastError(ERROR_NO_MORE_ITEMS);
+        }
+    }
+    RtlReleasePebLock();
+    return dwFlsIndex;
 }
 
 
 /*
- * @unimplemented
+ * @implemented
  */
 BOOL
 WINAPI
 FlsFree(DWORD dwFlsIndex)
 {
-    (void)dwFlsIndex;
+    BOOL ret;
+    PPEB Peb = NtCurrentPeb();
+
+    if (dwFlsIndex >= FLS_MAXIMUM_AVAILABLE)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    RtlAcquirePebLock();
+
+    _SEH2_TRY
+    {
+        ret = RtlAreBitsSet(Peb->FlsBitmap, dwFlsIndex, 1);
+        if (ret)
+        {
+            PLIST_ENTRY Entry;
+            PFLS_CALLBACK_FUNCTION lpCallback;
+
+            RtlClearBits(Peb->FlsBitmap, dwFlsIndex, 1);
+            lpCallback = Peb->FlsCallback[dwFlsIndex];
+
+            for (Entry = Peb->FlsListHead.Flink; Entry != &Peb->FlsListHead; Entry = Entry->Flink)
+            {
+                PRTL_FLS_DATA pFlsData;
+
+                pFlsData = CONTAINING_RECORD(Entry, RTL_FLS_DATA, ListEntry);
+                if (pFlsData->Data[dwFlsIndex])
+                {
+                    if (lpCallback)
+                    {
+                        lpCallback(pFlsData->Data[dwFlsIndex]);
+                    }
+                    pFlsData->Data[dwFlsIndex] = NULL;
+                }
+            }
+            Peb->FlsCallback[dwFlsIndex] = NULL;
+        }
+        else
+        {
+            SetLastError(ERROR_INVALID_PARAMETER);
+        }
+    }
+    _SEH2_FINALLY
+    {
+        RtlReleasePebLock();
+    }
+    _SEH2_END;
 
-    UNIMPLEMENTED;
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
+    return ret;
 }
 
 
@@ -327,23 +460,17 @@ PVOID
 WINAPI
 FlsGetValue(DWORD dwFlsIndex)
 {
-    PVOID *ppFlsSlots;
-    PVOID pRetVal;
-
-    if(dwFlsIndex >= 128) goto l_InvalidParam;
-
-    ppFlsSlots = NtCurrentTeb()->FlsData;
-
-    if(ppFlsSlots == NULL) goto l_InvalidParam;
-
-    SetLastError(0);
-    pRetVal = ppFlsSlots[dwFlsIndex + 2];
+    PRTL_FLS_DATA pFlsData;
 
-    return pRetVal;
+    pFlsData = NtCurrentTeb()->FlsData;
+    if (!dwFlsIndex || dwFlsIndex >= FLS_MAXIMUM_AVAILABLE || !pFlsData)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return NULL;
+    }
 
-l_InvalidParam:
-    SetLastError(ERROR_INVALID_PARAMETER);
-    return NULL;
+    SetLastError(ERROR_SUCCESS);
+    return pFlsData->Data[dwFlsIndex];
 }
 
 
@@ -352,46 +479,35 @@ l_InvalidParam:
  */
 BOOL
 WINAPI
-FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData)
+FlsSetValue(DWORD dwFlsIndex,
+            PVOID lpFlsData)
 {
-    PVOID *ppFlsSlots;
-    TEB *pTeb = NtCurrentTeb();
+    PRTL_FLS_DATA pFlsData;
 
-    if(dwFlsIndex >= 128) goto l_InvalidParam;
-
-    ppFlsSlots = pTeb->FlsData;
-
-    if (ppFlsSlots == NULL)
+    if (!dwFlsIndex || dwFlsIndex >= FLS_MAXIMUM_AVAILABLE)
     {
-        PEB *pPeb = pTeb->ProcessEnvironmentBlock;
-
-        ppFlsSlots = RtlAllocateHeap(pPeb->ProcessHeap,
-                                     HEAP_ZERO_MEMORY,
-                                     (128 + 2) * sizeof(PVOID));
-        if(ppFlsSlots == NULL) goto l_OutOfMemory;
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
 
-        pTeb->FlsData = ppFlsSlots;
+    pFlsData = NtCurrentTeb()->FlsData;
 
+    if (!NtCurrentTeb()->FlsData &&
+        !(NtCurrentTeb()->FlsData = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY,
+                                                    sizeof(RTL_FLS_DATA))))
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return FALSE;
+    }
+    if (!pFlsData)
+    {
+        pFlsData = NtCurrentTeb()->FlsData;
         RtlAcquirePebLock();
-
-        /* TODO: initialization */
-
+        InsertTailList(&NtCurrentPeb()->FlsListHead, &pFlsData->ListEntry);
         RtlReleasePebLock();
     }
-
-    ppFlsSlots[dwFlsIndex + 2] = lpFlsData;
-
+    pFlsData->Data[dwFlsIndex] = lpFlsData;
     return TRUE;
-
-l_OutOfMemory:
-    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-    goto l_Fail;
-
-l_InvalidParam:
-    SetLastError(ERROR_INVALID_PARAMETER);
-
-l_Fail:
-    return FALSE;
 }
 
 /* EOF */