ReactOS 0.4.17-dev-1183-g3b30a2a
fls.c
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS runtime library
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: Implementation of fiber local storage (FLS) functions
5 * COPYRIGHT: Copyright 2026 Timo Kreuzer <timo.kreuzer@reactos.org>
6 */
7
8#include <rtl.h>
9
10#define NDEBUG
11#include <debug.h>
12
13//
14// References:
15// - https://devblogs.microsoft.com/oldnewthing/20191011-00?p=102989
16// - https://dronesec.net/blog/code-execution-via-fiber-local-storage/
17// - https://wine-devel.winehq.narkive.com/63970k7i/implementing-fiber-local-storage-callbacks-sharing-data-between-dlls
18// - https://github.com/ElliotKillick/operating-system-design-review/blob/main/code/windows/fls-concurrency/fls-experiment.c
19// - https://ntquery.wordpress.com/2014/03/29/anti-debug-fiber-local-storage-fls/
20//
21
22/* The FLS lock. We don't use it yet since kernel32 uses the PEB lock and we need to synchronize with this for now */
23//RTL_SRWLOCK RtlpFlsLock = RTL_SRWLOCK_INIT;
24
25typedef struct _FLS_CALLBACK_INFO
26{
30
31static
32VOID
34{
35 /* Acquire the lock */
36 //RtlAcquireSRWLockExclusive(&RtlpFlsLock);
38}
39
40static
41VOID
43{
44 /* Release the lock */
45 //RtlReleaseSRWLockExclusive(&RtlpFlsLock);
47}
48
49static
52{
53 PPEB peb = NtCurrentPeb();
54 PVOID* newCallbacks;
55
56 /* Allocate a new callback array */
57 newCallbacks = RtlAllocateHeap(RtlGetProcessHeap(),
60 if (newCallbacks == NULL)
61 {
62 return STATUS_NO_MEMORY;
63 }
64
66
67 if (peb->FlsCallback == NULL)
68 {
69 peb->FlsCallback = newCallbacks;
70 newCallbacks = NULL;
71 }
72
74
75 /* Clean up, if needed */
76 if (newCallbacks != NULL)
77 {
78 RtlFreeHeap(RtlGetProcessHeap(), 0, newCallbacks);
79 }
80
81 return STATUS_SUCCESS;
82}
83
84static
87{
88 PTEB teb = NtCurrentTeb();
89 PRTL_FLS_DATA newFlsData;
90
91 ASSERT(teb->FlsData == NULL);
92
93 /* Allocate a new thread FLS data structure */
94 newFlsData = RtlAllocateHeap(RtlGetProcessHeap(),
96 sizeof(RTL_FLS_DATA));
97 if (newFlsData == NULL)
98 {
99 return STATUS_NO_MEMORY;
100 }
101
102 PPEB peb = NtCurrentPeb();
103
105 InsertTailList(&peb->FlsListHead, &newFlsData->ListEntry);
107
108 teb->FlsData = newFlsData;
109
110 return STATUS_SUCCESS;
111}
112
113// Windows 11
115NTAPI
119 _Out_ PULONG FlsIndex)
120{
121 PPEB peb = NtCurrentPeb();
122 PTEB teb = NtCurrentTeb();
124 ULONG index;
125
126 /* Check if we have a thread FLS data structure */
127 if (teb->FlsData == NULL)
128 {
129 /* Allocate a new thread FLS data structure */
131 if (!NT_SUCCESS(status))
132 {
133 return status;
134 }
135 }
136
137 /* Check if we need to allocate the callback array */
138 if (peb->FlsCallback == NULL)
139 {
141 if (!NT_SUCCESS(status))
142 {
143 return status;
144 }
145 }
146
148
149 /* Find a free index */
151 if (index == -1)
152 {
154 return STATUS_NO_MEMORY;
155 }
156
157 /* Set the callback */
159 callbacks[index].Callback = Callback;
160
161 /* Update high index */
162 peb->FlsHighIndex = max(peb->FlsHighIndex, index);
163
165
166 /* Return the index */
167 *FlsIndex = index;
168
169 return STATUS_SUCCESS;
170}
171
173NTAPI
176 _Out_ PULONG FlsIndex)
177{
178 return RtlFlsAllocEx(Callback, NULL, FlsIndex);
179}
180
182NTAPI
184 _In_ ULONG FlsIndex)
185{
186 PPEB peb = NtCurrentPeb();
187 PRTL_BITMAP flsBitmap = peb->FlsBitmap;
188
189 /* Check if the FLS index is within the valid range */
190 if ((FlsIndex == 0) || (FlsIndex >= FLS_MAXIMUM_AVAILABLE))
191 {
193 }
194
196
197 /* Validate that the bit is set */
198 if (!RtlCheckBit(flsBitmap, FlsIndex))
199 {
202 }
203
204 /* Check if there is a callback */
206 PFLS_CALLBACK_FUNCTION callbackFunction = callbackArray[FlsIndex].Callback;
207
208 /* Clear the FLS index bit */
209 RtlClearBit(peb->FlsBitmap, FlsIndex);
210
211 /* Protect with SEH to guard against a broken callback.
212 Note: Vista doesn't do that and will leak the FLS lock!
213 Windows 10 protects the lock and still manages to clear the values as well.
214 We replicate Windows 10 behavior. */
215 _SEH2_VOLATILE PLIST_ENTRY listEntry;
217 {
218 /* Loop all thread FLS data structures */
219 for (listEntry = peb->FlsListHead.Flink;
220 listEntry != &peb->FlsListHead;
221 listEntry = listEntry->Flink)
222 {
223 /* Get the thread FLS data structure */
224 PRTL_FLS_DATA flsData = CONTAINING_RECORD(listEntry, RTL_FLS_DATA, ListEntry);
225
226 /* Check if the value is set (!= NULL) */
227 if (flsData->Data[FlsIndex] != NULL)
228 {
229 if (callbackFunction != NULL)
230 {
231 callbackFunction(flsData->Data[FlsIndex]);
232 }
233
234 flsData->Data[FlsIndex] = NULL;
235 }
236 }
237
238 /* Clear the callback */
239 callbackArray[FlsIndex].Callback = NULL;
240 }
242 {
243 /* Check if we got an exception */
245 {
246 /* We need to finish clearing the values */
247 for (/* the current entry */;
248 listEntry != &peb->FlsListHead;
249 listEntry = listEntry->Flink)
250 {
251 /* Get the thread FLS data structure and clear the value */
252 PRTL_FLS_DATA flsData = CONTAINING_RECORD(listEntry, RTL_FLS_DATA, ListEntry);
253 flsData->Data[FlsIndex] = NULL;
254 }
255
256 /* Clear the callback */
257 callbackArray[FlsIndex].Callback = NULL;
258 }
259
261 }
262 _SEH2_END;
263
264 return STATUS_SUCCESS;
265}
266
267// This function has different signatures between Vista and Windows 10
268// See https://debugactiveprocess.medium.com/rtlprocessflsdata-as-anti-debugging-technique-c531174c6dc8
269// and https://ntquery.wordpress.com/2014/03/29/anti-debug-fiber-local-storage-fls/#more-18
270#if (DLL_EXPORT_VERSION < _WIN32_WINNT_WIN10)
272NTAPI
274 _In_ PRTL_FLS_DATA FlsData)
275{
278}
279#else
281NTAPI
284 _Out_ PVOID* FlsData)
285{
287}
288#endif
289
290#if (DLL_EXPORT_VERSION >= _WIN32_WINNT_WIN10)
291
292// Windows 10
294NTAPI
296 _In_ ULONG FlsIndex,
297 _Out_ PVOID* FlsData)
298{
299 /* Check if the FLS index is within the valid range */
300 if ((FlsIndex == 0) || (FlsIndex >= FLS_MAXIMUM_AVAILABLE))
301 {
303 }
304
305 /* Check if we have a thread FLS data structure */
306 PTEB teb = NtCurrentTeb();
307 if (teb->FlsData == NULL)
308 {
310 }
311
312 /* Return the value (the function doesnt check the bitmap!) */
313 PRTL_FLS_DATA flsData = teb->FlsData;
314 *FlsData = flsData->Data[FlsIndex];
315
316 return STATUS_SUCCESS;
317}
318
319// Windows 11
320PVOID
321WINAPI
323 _In_ ULONG FlsIndex)
324{
326 PVOID flsData;
327
328 status = RtlFlsGetValue(FlsIndex, &flsData);
329 if (!NT_SUCCESS(status))
330 {
331 return NULL;
332 }
333
334 return flsData;
335}
336
337// Windows 10
339NTAPI
341 _In_ ULONG FlsIndex,
342 _In_ PVOID FlsData)
343{
345
346 /* Check if the FLS index is within the valid range */
347 if ((FlsIndex == 0) || (FlsIndex >= FLS_MAXIMUM_AVAILABLE))
348 {
350 }
351
352 /* Check if we have a thread FLS data structure */
353 PTEB teb = NtCurrentTeb();
354 if (teb->FlsData == NULL)
355 {
356 /* Allocate a new thread FLS data structure */
358 if (!NT_SUCCESS(status))
359 {
360 return status;
361 }
362 }
363
364 /* Set the value (the function doesnt check the bitmap!) */
365 PRTL_FLS_DATA flsData = teb->FlsData;
366 flsData->Data[FlsIndex] = FlsData;
367
368 return STATUS_SUCCESS;
369}
370
371#endif // (DLL_EXPORT_VERSION >= DLL_EXPORT_VERSION_WIN10)
#define NtCurrentPeb()
Definition: FLS.c:22
LONG NTSTATUS
Definition: precomp.h:26
#define index(s, c)
Definition: various.h:29
#define UNIMPLEMENTED
Definition: ntoskrnl.c:15
PVOID NTAPI RtlAllocateHeap(IN PVOID HeapHandle, IN ULONG Flags, IN SIZE_T Size)
Definition: heap.c:616
BOOLEAN NTAPI RtlFreeHeap(IN PVOID HeapHandle, IN ULONG Flags, IN PVOID HeapBase)
Definition: heap.c:634
#define STATUS_NOT_IMPLEMENTED
Definition: d3dkmdt.h:42
#define STATUS_NO_MEMORY
Definition: d3dkmdt.h:51
#define RtlClearBit
Definition: dbgbitmap.h:330
#define RtlFindClearBitsAndSet
Definition: dbgbitmap.h:333
#define RtlCheckBit
Definition: dbgbitmap.h:349
_In_ ULONG _In_ ULONG _In_opt_ DXGKCB_MIRACAST_SEND_MESSAGE_CALLBACK Callback
Definition: dispmprt.h:620
#define NULL
Definition: types.h:112
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:33
#define ERROR_INVALID_PARAMETER
Definition: compat.h:101
#define HEAP_ZERO_MEMORY
Definition: compat.h:134
#define InsertTailList(ListHead, Entry)
static VOID RtlpAcquireFlsLock(VOID)
Definition: fls.c:33
NTSTATUS NTAPI RtlFlsAllocEx(_In_ PFLS_CALLBACK_FUNCTION Callback, _Out_opt_ PULONG Unknown, _Out_ PULONG FlsIndex)
Definition: fls.c:116
struct _FLS_CALLBACK_INFO * PFLS_CALLBACK_INFO
static NTSTATUS RtlpAllocateFlsCallbackArray(VOID)
Definition: fls.c:51
struct _FLS_CALLBACK_INFO FLS_CALLBACK_INFO
NTSTATUS NTAPI RtlFlsFree(_In_ ULONG FlsIndex)
Definition: fls.c:183
NTSTATUS NTAPI RtlFlsAlloc(_In_ PFLS_CALLBACK_FUNCTION Callback, _Out_ PULONG FlsIndex)
Definition: fls.c:174
NTSTATUS NTAPI RtlProcessFlsData(_In_ HANDLE ProcessHandle, _Out_ PVOID *FlsData)
Definition: fls.c:282
static VOID RtlpReleaseFlsLock(VOID)
Definition: fls.c:42
static NTSTATUS RtlpAllocateThreadFlsData(VOID)
Definition: fls.c:86
NTSTATUS NTAPI RtlFlsGetValue(_In_ ULONG FlsIndex, _Out_ PVOID *FlsData)
Definition: fls.c:295
NTSTATUS NTAPI RtlFlsSetValue(_In_ ULONG FlsIndex, _In_ PVOID FlsData)
Definition: fls.c:340
PVOID WINAPI RtlFlsGetValue2(_In_ ULONG FlsIndex)
Definition: fls.c:322
GLuint index
Definition: glext.h:6031
@ Unknown
Definition: i8042prt.h:114
#define NtCurrentTeb
#define ASSERT(a)
Definition: mode.c:44
_In_ HANDLE ProcessHandle
Definition: mmfuncs.h:407
#define RTL_FLS_MAXIMUM_AVAILABLE
Definition: rtltypes.h:1225
#define _Out_opt_
Definition: no_sal2.h:214
#define _Out_
Definition: no_sal2.h:160
#define _In_
Definition: no_sal2.h:158
#define _SEH2_AbnormalTermination()
Definition: pseh2_64.h:205
#define _SEH2_FINALLY
Definition: pseh2_64.h:153
#define _SEH2_VOLATILE
Definition: pseh2_64.h:208
#define _SEH2_END
Definition: pseh2_64.h:194
#define _SEH2_TRY
Definition: pseh2_64.h:93
#define STATUS_SUCCESS
Definition: shellext.h:65
PFLS_CALLBACK_FUNCTION Callback
Definition: RtlFls.c:19
PVOID Unknown
Definition: FLS.c:63
Definition: typedefs.h:120
struct _LIST_ENTRY * Flink
Definition: typedefs.h:121
PVOID * FlsCallback
Definition: winternl.h:551
LIST_ENTRY FlsListHead
Definition: winternl.h:552
PRTL_BITMAP FlsBitmap
Definition: winternl.h:555
ULONG FlsHighIndex
Definition: winternl.h:561
LIST_ENTRY ListEntry
Definition: rtltypes.h:1228
PVOID Data[RTL_FLS_MAXIMUM_AVAILABLE]
Definition: rtltypes.h:1229
Definition: compat.h:836
Definition: ps.c:97
#define max(a, b)
Definition: svc.c:63
uint32_t * PULONG
Definition: typedefs.h:59
#define NTAPI
Definition: typedefs.h:36
#define CONTAINING_RECORD(address, type, field)
Definition: typedefs.h:260
uint32_t ULONG
Definition: typedefs.h:59
#define STATUS_INVALID_PARAMETER
Definition: udferr_usr.h:135
VOID(WINAPI * PFLS_CALLBACK_FUNCTION)(PVOID)
Definition: winbase.h:1178
#define WINAPI
Definition: msvc.h:6
NTSYSAPI void WINAPI RtlReleasePebLock(void)
Definition: libsupp.c:95
NTSYSAPI void WINAPI RtlAcquirePebLock(void)
Definition: libsupp.c:85
#define FLS_MAXIMUM_AVAILABLE
Definition: winnt_old.h:1141