1 /*
2  * PROJECT:     ReactOS api tests
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Tests for FLS implementation details
5  * COPYRIGHT:   Copyright 2018 Mark Jansen (mark.jansen@reactos.org)
6  */
7 
8 #include "precomp.h"
9 #include <ndk/pstypes.h>
10 #include <ndk/rtlfuncs.h>
11 
12 #include <pseh/pseh2.h>
13 
14 /* XP does not have these functions */
15 static DWORD (WINAPI *pFlsAlloc)(PFLS_CALLBACK_FUNCTION);
16 static BOOL (WINAPI *pFlsFree)(DWORD);
17 static PVOID (WINAPI *pFlsGetValue)(DWORD);
18 static BOOL (WINAPI *pFlsSetValue)(DWORD,PVOID);
19 static BOOL (WINAPI *pRtlIsCriticalSectionLockedByThread)(RTL_CRITICAL_SECTION *);
20 
21 
22 #define NtCurrentPeb() (NtCurrentTeb()->ProcessEnvironmentBlock)
23 #define WINVER_2003    0x0502
24 
25 static DWORD g_WinVersion = 0;
26 PVOID g_FlsData1 = NULL;
27 LONG g_FlsCalled1 = 0;
28 PVOID g_FlsData2 = NULL;
29 LONG g_FlsCalled2 = 0;
30 PVOID g_FlsData3 = NULL;
31 LONG g_FlsCalled3 = 0;
32 BOOL g_FlsExcept3 = FALSE;
33 
34 
FlsCallback1(PVOID lpFlsData)35 VOID WINAPI FlsCallback1(PVOID lpFlsData)
36 {
37     ok(lpFlsData == g_FlsData1, "Expected g_FlsData1(%p), got %p\n", g_FlsData1, lpFlsData);
38     InterlockedIncrement(&g_FlsCalled1);
39 }
40 
FlsCallback2(PVOID lpFlsData)41 VOID WINAPI FlsCallback2(PVOID lpFlsData)
42 {
43     ok(lpFlsData == g_FlsData2, "Expected g_FlsData2(%p), got %p\n", g_FlsData2, lpFlsData);
44     InterlockedIncrement(&g_FlsCalled2);
45 }
46 
FlsCallback3(PVOID lpFlsData)47 VOID WINAPI FlsCallback3(PVOID lpFlsData)
48 {
49     ok(lpFlsData == g_FlsData3, "Expected g_FlsData3(%p), got %p\n", g_FlsData3, lpFlsData);
50 
51     if (g_WinVersion <= WINVER_2003)
52         ok(pRtlIsCriticalSectionLockedByThread(NtCurrentPeb()->FastPebLock), "Expected lock on PEB\n");
53     InterlockedIncrement(&g_FlsCalled3);
54     if (g_FlsExcept3)
55     {
56         RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, NULL);
57     }
58 }
59 
60 typedef struct _FLS_CALLBACK_INFO
61 {
62     PFLS_CALLBACK_FUNCTION lpCallback;
63     PVOID Unknown;
64 } FLS_CALLBACK_INFO, *PFLS_CALLBACK_INFO;
65 
66 
ok_fls_(DWORD dwIndex,PVOID pValue,PFLS_CALLBACK_FUNCTION lpCallback)67 void ok_fls_(DWORD dwIndex, PVOID pValue, PFLS_CALLBACK_FUNCTION lpCallback)
68 {
69     PFLS_CALLBACK_INFO FlsCallback;
70     PVOID* FlsData;
71     PVOID gotValue;
72 
73     FlsCallback = (PFLS_CALLBACK_INFO)NtCurrentPeb()->FlsCallback;
74     FlsData = (PVOID*)NtCurrentTeb()->FlsData;
75 
76     winetest_ok(FlsData != NULL, "Expected FlsData\n");
77     winetest_ok(FlsCallback != NULL, "Expected FlsCallback\n");
78 
79     if (FlsData == NULL || FlsCallback == NULL)
80     {
81         winetest_skip("Unable to continue test\n");
82         return;
83     }
84 
85     if (g_WinVersion <= WINVER_2003)
86     {
87         winetest_ok(NtCurrentPeb()->FlsCallback[dwIndex] == lpCallback,
88                     "Expected NtCurrentPeb()->FlsCallback[%lu] to be %p, was %p\n",
89                     dwIndex,
90                     lpCallback,
91                     NtCurrentPeb()->FlsCallback[dwIndex]);
92     }
93     else
94     {
95         winetest_ok(FlsCallback[dwIndex].lpCallback == lpCallback,
96                     "Expected FlsCallback[%lu].lpCallback to be %p, was %p\n",
97                     dwIndex,
98                     lpCallback,
99                     FlsCallback[dwIndex].lpCallback);
100         if (lpCallback != &FlsCallback3 || !g_FlsExcept3)
101         {
102             winetest_ok(FlsCallback[dwIndex].Unknown == NULL,
103                         "Expected FlsCallback[%lu].Unknown to be %p, was %p\n",
104                         dwIndex,
105                         NULL,
106                         FlsCallback[dwIndex].Unknown);
107         }
108     }
109     winetest_ok(FlsData[dwIndex + 2] == pValue,
110                 "Expected FlsData[%lu + 2] to be %p, was %p\n",
111                 dwIndex,
112                 pValue,
113                 FlsData[dwIndex + 2]);
114 
115     gotValue = pFlsGetValue(dwIndex);
116     winetest_ok(gotValue == pValue, "Expected FlsGetValue(%lu) to be %p, was %p\n", dwIndex, pValue, gotValue);
117 }
118 
119 #define ok_fls         (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : ok_fls_
120 
init_funcs(void)121 static VOID init_funcs(void)
122 {
123     HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
124     HMODULE hNTDLL = GetModuleHandleA("ntdll.dll");
125 
126 #define X(f) p##f = (void*)GetProcAddress(hKernel32, #f);
127     X(FlsAlloc);
128     X(FlsFree);
129     X(FlsGetValue);
130     X(FlsSetValue);
131 #undef X
132     pRtlIsCriticalSectionLockedByThread = (void*)GetProcAddress(hNTDLL, "RtlIsCriticalSectionLockedByThread");
133 }
134 
135 
136 
START_TEST(FLS)137 START_TEST(FLS)
138 {
139     RTL_OSVERSIONINFOW rtlinfo = { sizeof(rtlinfo) };
140     DWORD dwIndex1, dwIndex2, dwIndex3, dwErr;
141     BOOL bRet;
142 
143     init_funcs();
144     if (!pFlsAlloc || !pFlsFree || !pFlsGetValue || !pFlsSetValue)
145     {
146         skip("Fls functions not available\n");
147         return;
148     }
149     if (!pRtlIsCriticalSectionLockedByThread)
150     {
151         skip("RtlIsCriticalSectionLockedByThread function not available\n");
152         return;
153     }
154 
155     RtlGetVersion(&rtlinfo);
156     g_WinVersion = (rtlinfo.dwMajorVersion << 8) | rtlinfo.dwMinorVersion;
157 
158     dwIndex1 = pFlsAlloc(FlsCallback1);
159     ok(dwIndex1 != FLS_OUT_OF_INDEXES, "Unable to allocate FLS index\n");
160     dwIndex2 = pFlsAlloc(FlsCallback2);
161     ok(dwIndex2 != FLS_OUT_OF_INDEXES, "Unable to allocate FLS index\n");
162     ok(dwIndex1 != dwIndex2, "Expected different indexes, got %lu\n", dwIndex1);
163 
164     dwIndex3 = pFlsAlloc(FlsCallback3);
165     ok(dwIndex3 != FLS_OUT_OF_INDEXES, "Unable to allocate FLS index\n");
166     ok(dwIndex1 != dwIndex3, "Expected different indexes, got %lu\n", dwIndex1);
167 
168     if (dwIndex1 == FLS_OUT_OF_INDEXES || dwIndex2 == FLS_OUT_OF_INDEXES || dwIndex3 == FLS_OUT_OF_INDEXES)
169     {
170         skip("Unable to continue test\n");
171         return;
172     }
173 
174     ok_fls(dwIndex1, g_FlsData1, &FlsCallback1);
175     ok_fls(dwIndex2, g_FlsData2, &FlsCallback2);
176     ok_fls(dwIndex3, g_FlsData3, &FlsCallback3);
177 
178     g_FlsData1 = (PVOID)0x123456;
179     ok(pFlsSetValue(dwIndex1, g_FlsData1), "FlsSetValue(%lu, %p) failed\n", dwIndex1, g_FlsData1);
180 
181     ok_fls(dwIndex1, g_FlsData1, &FlsCallback1);
182     ok_fls(dwIndex2, g_FlsData2, &FlsCallback2);
183     ok_fls(dwIndex3, g_FlsData3, &FlsCallback3);
184 
185     ok_int(g_FlsCalled1, 0);
186     ok_int(g_FlsCalled2, 0);
187     ok_int(g_FlsCalled3, 0);
188 
189     g_FlsData2 = (PVOID)0x9876112;
190     ok(pFlsSetValue(dwIndex2, g_FlsData2), "FlsSetValue(%lu, %p) failed\n", dwIndex2, g_FlsData2);
191 
192     ok_fls(dwIndex1, g_FlsData1, &FlsCallback1);
193     ok_fls(dwIndex2, g_FlsData2, &FlsCallback2);
194     ok_fls(dwIndex3, g_FlsData3, &FlsCallback3);
195 
196 
197     ok_int(g_FlsCalled1, 0);
198     ok_int(g_FlsCalled2, 0);
199     ok_int(g_FlsCalled3, 0);
200 
201     g_FlsData3 = (PVOID)0x98762;
202     ok(pFlsSetValue(dwIndex3, g_FlsData3), "FlsSetValue(%lu, %p) failed\n", dwIndex3, g_FlsData3);
203 
204     ok_fls(dwIndex1, g_FlsData1, &FlsCallback1);
205     ok_fls(dwIndex2, g_FlsData2, &FlsCallback2);
206     ok_fls(dwIndex3, g_FlsData3, &FlsCallback3);
207 
208     ok_int(g_FlsCalled1, 0);
209     ok_int(g_FlsCalled2, 0);
210     ok_int(g_FlsCalled3, 0);
211 
212     ok(pFlsFree(dwIndex1) == TRUE, "FlsFree(%lu) failed\n", dwIndex1);
213     g_FlsData1 = NULL;
214 
215     ok_fls(dwIndex1, g_FlsData1, NULL);
216     ok_fls(dwIndex2, g_FlsData2, &FlsCallback2);
217     ok_fls(dwIndex3, g_FlsData3, &FlsCallback3);
218 
219     ok_int(g_FlsCalled1, 1);
220     ok_int(g_FlsCalled2, 0);
221     ok_int(g_FlsCalled3, 0);
222 
223     g_FlsExcept3 = TRUE;
224     _SEH2_TRY
225     {
226         bRet = pFlsFree(dwIndex3);
227         dwErr = GetLastError();
228     }
229     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
230     {
231         bRet = 12345;
232         dwErr = 0xdeaddead;
233     }
234     _SEH2_END;
235     ok(pRtlIsCriticalSectionLockedByThread(NtCurrentPeb()->FastPebLock) == FALSE, "Expected no lock on PEB\n");
236 
237     ok(bRet == 12345, "FlsFree(%lu) should have failed, got %u\n", dwIndex3, bRet);
238     ok(dwErr == 0xdeaddead, "Expected GetLastError() to be 0xdeaddead, was %lx\n", dwErr);
239 
240     ok_fls(dwIndex1, g_FlsData1, NULL);
241     ok_fls(dwIndex2, g_FlsData2, &FlsCallback2);
242     ok_fls(dwIndex3, g_FlsData3, &FlsCallback3);
243 
244     ok_int(g_FlsCalled1, 1);
245     ok_int(g_FlsCalled2, 0);
246     ok_int(g_FlsCalled3, 1);
247 }
248