1 /*
2  * PROJECT:         ReactOS kernel-mode tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Kernel-Mode Test Suite Guarded Mutex test
5  * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
6  */
7 
8 #include <kmt_test.h>
9 
10 #define NDEBUG
11 #include <debug.h>
12 
13 static
14 _IRQL_requires_min_(PASSIVE_LEVEL)
15 _IRQL_requires_max_(DISPATCH_LEVEL)
16 BOOLEAN
17 (NTAPI
18 *pKeAreAllApcsDisabled)(VOID);
19 
20 static
21 _Acquires_lock_(_Global_critical_region_)
22 _Requires_lock_not_held_(*Mutex)
23 _Acquires_lock_(*Mutex)
24 _IRQL_requires_max_(APC_LEVEL)
25 _IRQL_requires_min_(PASSIVE_LEVEL)
26 VOID
27 (FASTCALL
28 *pKeAcquireGuardedMutex)(
29     _Inout_ PKGUARDED_MUTEX GuardedMutex);
30 
31 static
32 _Requires_lock_not_held_(*FastMutex)
33 _Acquires_lock_(*FastMutex)
34 _IRQL_requires_max_(APC_LEVEL)
35 _IRQL_requires_min_(PASSIVE_LEVEL)
36 VOID
37 (FASTCALL
38 *pKeAcquireGuardedMutexUnsafe)(
39     _Inout_ PKGUARDED_MUTEX GuardedMutex);
40 
41 static
42 _Acquires_lock_(_Global_critical_region_)
43 _IRQL_requires_max_(APC_LEVEL)
44 VOID
45 (NTAPI
46 *pKeEnterGuardedRegion)(VOID);
47 
48 static
49 _Releases_lock_(_Global_critical_region_)
50 _IRQL_requires_max_(APC_LEVEL)
51 VOID
52 (NTAPI
53 *pKeLeaveGuardedRegion)(VOID);
54 
55 static
56 _IRQL_requires_max_(APC_LEVEL)
57 _IRQL_requires_min_(PASSIVE_LEVEL)
58 VOID
59 (FASTCALL
60 *pKeInitializeGuardedMutex)(
61     _Out_ PKGUARDED_MUTEX GuardedMutex);
62 
63 static
64 _Requires_lock_held_(*FastMutex)
65 _Releases_lock_(*FastMutex)
66 _IRQL_requires_max_(APC_LEVEL)
67 VOID
68 (FASTCALL
69 *pKeReleaseGuardedMutexUnsafe)(
70     _Inout_ PKGUARDED_MUTEX GuardedMutex);
71 
72 static
73 _Releases_lock_(_Global_critical_region_)
74 _Requires_lock_held_(*Mutex)
75 _Releases_lock_(*Mutex)
76 _IRQL_requires_max_(APC_LEVEL)
77 VOID
78 (FASTCALL
79 *pKeReleaseGuardedMutex)(
80     _Inout_ PKGUARDED_MUTEX GuardedMutex);
81 
82 static
83 _Must_inspect_result_
84 _Success_(return != FALSE)
85 _IRQL_requires_max_(APC_LEVEL)
86 _Post_satisfies_(return == 1 || return == 0)
87 BOOLEAN
88 (FASTCALL
89 *pKeTryToAcquireGuardedMutex)(
90     _When_ (return, _Requires_lock_not_held_(*_Curr_) _Acquires_exclusive_lock_(*_Curr_)) _Acquires_lock_(_Global_critical_region_)
91         _Inout_ PKGUARDED_MUTEX GuardedMutex);
92 
93 #define CheckMutex(Mutex, ExpectedCount, ExpectedOwner, ExpectedContention,     \
94                    ExpectedKernelApcDisable, ExpectedSpecialApcDisable,         \
95                    KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled,    \
96                    ExpectedIrql) do                                             \
97 {                                                                               \
98     ok_eq_long((Mutex)->Count, ExpectedCount);                                  \
99     ok_eq_pointer((Mutex)->Owner, ExpectedOwner);                               \
100     ok_eq_ulong((Mutex)->Contention, ExpectedContention);                       \
101     ok_eq_int((Mutex)->KernelApcDisable, ExpectedKernelApcDisable);             \
102     if (KmtIsCheckedBuild)                                                      \
103         ok_eq_int((Mutex)->SpecialApcDisable, ExpectedSpecialApcDisable);       \
104     else                                                                        \
105         ok_eq_int((Mutex)->SpecialApcDisable, 0x5555);                          \
106     ok_eq_bool(KeAreApcsDisabled(), KernelApcsDisabled || SpecialApcsDisabled); \
107     ok_eq_int(Thread->KernelApcDisable, KernelApcsDisabled);                    \
108     ok_eq_bool(pKeAreAllApcsDisabled(), AllApcsDisabled);                       \
109     ok_eq_int(Thread->SpecialApcDisable, SpecialApcsDisabled);                  \
110     ok_irql(ExpectedIrql);                                                      \
111 } while (0)
112 
113 static
114 VOID
TestGuardedMutex(PKGUARDED_MUTEX Mutex,SHORT KernelApcsDisabled,SHORT SpecialApcsDisabled,SHORT AllApcsDisabled,KIRQL OriginalIrql)115 TestGuardedMutex(
116     PKGUARDED_MUTEX Mutex,
117     SHORT KernelApcsDisabled,
118     SHORT SpecialApcsDisabled,
119     SHORT AllApcsDisabled,
120     KIRQL OriginalIrql)
121 {
122     PKTHREAD Thread = KeGetCurrentThread();
123 
124     ok_irql(OriginalIrql);
125     CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, 0x5555, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
126 
127     /* these ASSERT */
128     if (!KmtIsCheckedBuild || OriginalIrql <= APC_LEVEL)
129     {
130         /* acquire/release normally */
131         pKeAcquireGuardedMutex(Mutex);
132         CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
133         ok_bool_false(pKeTryToAcquireGuardedMutex(Mutex), "KeTryToAcquireGuardedMutex returned");
134         CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
135         pKeReleaseGuardedMutex(Mutex);
136         CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
137 
138         /* try to acquire */
139         ok_bool_true(pKeTryToAcquireGuardedMutex(Mutex), "KeTryToAcquireGuardedMutex returned");
140         CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
141         pKeReleaseGuardedMutex(Mutex);
142         CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
143     }
144     else
145         /* Make the following test happy */
146         Mutex->SpecialApcDisable = SpecialApcsDisabled - 1;
147 
148     /* ASSERT */
149     if (!KmtIsCheckedBuild || OriginalIrql == APC_LEVEL || SpecialApcsDisabled < 0)
150     {
151         /* acquire/release unsafe */
152         pKeAcquireGuardedMutexUnsafe(Mutex);
153         CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
154         pKeReleaseGuardedMutexUnsafe(Mutex);
155         CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
156     }
157 
158     /* Bugchecks >= DISPATCH_LEVEL */
159     if (!KmtIsCheckedBuild)
160     {
161         /* mismatched acquire/release */
162         pKeAcquireGuardedMutex(Mutex);
163         CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
164         pKeReleaseGuardedMutexUnsafe(Mutex);
165         CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
166         pKeLeaveGuardedRegion();
167         CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
168 
169         pKeAcquireGuardedMutexUnsafe(Mutex);
170         CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
171         pKeReleaseGuardedMutex(Mutex);
172         CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled + 1, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -1, OriginalIrql);
173         pKeEnterGuardedRegion();
174         CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
175 
176         /* release without acquire */
177         pKeReleaseGuardedMutexUnsafe(Mutex);
178         CheckMutex(Mutex, 0L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
179         pKeReleaseGuardedMutex(Mutex);
180         CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled, KernelApcsDisabled, SpecialApcsDisabled + 1, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -1, OriginalIrql);
181         pKeReleaseGuardedMutex(Mutex);
182         /* TODO: here we see that Mutex->Count isn't actually just a count. Test the bits correctly! */
183         CheckMutex(Mutex, 0L, NULL, 0LU, 0x5555, SpecialApcsDisabled, KernelApcsDisabled, SpecialApcsDisabled + 2, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -2, OriginalIrql);
184         pKeReleaseGuardedMutex(Mutex);
185         CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled, KernelApcsDisabled, SpecialApcsDisabled + 3, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -3, OriginalIrql);
186         Thread->SpecialApcDisable -= 3;
187     }
188 
189     /* make sure we survive this in case of error */
190     ok_eq_long(Mutex->Count, 1L);
191     Mutex->Count = 1;
192     ok_eq_int(Thread->KernelApcDisable, KernelApcsDisabled);
193     Thread->KernelApcDisable = KernelApcsDisabled;
194     ok_eq_int(Thread->SpecialApcDisable, SpecialApcsDisabled);
195     Thread->SpecialApcDisable = SpecialApcsDisabled;
196     ok_irql(OriginalIrql);
197 }
198 
199 typedef VOID (FASTCALL *PMUTEX_FUNCTION)(PKGUARDED_MUTEX);
200 typedef BOOLEAN (FASTCALL *PMUTEX_TRY_FUNCTION)(PKGUARDED_MUTEX);
201 
202 typedef struct
203 {
204     HANDLE Handle;
205     PKTHREAD Thread;
206     KIRQL Irql;
207     PKGUARDED_MUTEX Mutex;
208     PMUTEX_FUNCTION Acquire;
209     PMUTEX_TRY_FUNCTION TryAcquire;
210     PMUTEX_FUNCTION Release;
211     BOOLEAN Try;
212     BOOLEAN RetExpected;
213     KEVENT InEvent;
214     KEVENT OutEvent;
215 } THREAD_DATA, *PTHREAD_DATA;
216 
217 static
218 VOID
219 NTAPI
AcquireMutexThread(PVOID Parameter)220 AcquireMutexThread(
221     PVOID Parameter)
222 {
223     PTHREAD_DATA ThreadData = Parameter;
224     KIRQL Irql;
225     BOOLEAN Ret = FALSE;
226     NTSTATUS Status;
227 
228     DPRINT("Thread starting\n");
229     KeRaiseIrql(ThreadData->Irql, &Irql);
230 
231     if (ThreadData->Try)
232     {
233         Ret = ThreadData->TryAcquire(ThreadData->Mutex);
234         ok_eq_bool(Ret, ThreadData->RetExpected);
235     }
236     else
237         ThreadData->Acquire(ThreadData->Mutex);
238 
239     ok_bool_false(KeSetEvent(&ThreadData->OutEvent, 0, TRUE), "KeSetEvent returned");
240     DPRINT("Thread now waiting\n");
241     Status = KeWaitForSingleObject(&ThreadData->InEvent, Executive, KernelMode, FALSE, NULL);
242     DPRINT("Thread done waiting\n");
243     ok_eq_hex(Status, STATUS_SUCCESS);
244 
245     if (!ThreadData->Try || Ret)
246         ThreadData->Release(ThreadData->Mutex);
247 
248     KeLowerIrql(Irql);
249     DPRINT("Thread exiting\n");
250 }
251 
252 static
253 VOID
InitThreadData(PTHREAD_DATA ThreadData,PKGUARDED_MUTEX Mutex,PMUTEX_FUNCTION Acquire,PMUTEX_TRY_FUNCTION TryAcquire,PMUTEX_FUNCTION Release)254 InitThreadData(
255     PTHREAD_DATA ThreadData,
256     PKGUARDED_MUTEX Mutex,
257     PMUTEX_FUNCTION Acquire,
258     PMUTEX_TRY_FUNCTION TryAcquire,
259     PMUTEX_FUNCTION Release)
260 {
261     ThreadData->Mutex = Mutex;
262     KeInitializeEvent(&ThreadData->InEvent, NotificationEvent, FALSE);
263     KeInitializeEvent(&ThreadData->OutEvent, NotificationEvent, FALSE);
264     ThreadData->Acquire = Acquire;
265     ThreadData->TryAcquire = TryAcquire;
266     ThreadData->Release = Release;
267 }
268 
269 static
270 NTSTATUS
StartThread(PTHREAD_DATA ThreadData,PLARGE_INTEGER Timeout,KIRQL Irql,BOOLEAN Try,BOOLEAN RetExpected)271 StartThread(
272     PTHREAD_DATA ThreadData,
273     PLARGE_INTEGER Timeout,
274     KIRQL Irql,
275     BOOLEAN Try,
276     BOOLEAN RetExpected)
277 {
278     NTSTATUS Status = STATUS_SUCCESS;
279     OBJECT_ATTRIBUTES Attributes;
280 
281     ThreadData->Try = Try;
282     ThreadData->Irql = Irql;
283     ThreadData->RetExpected = RetExpected;
284     InitializeObjectAttributes(&Attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
285     Status = PsCreateSystemThread(&ThreadData->Handle, GENERIC_ALL, &Attributes, NULL, NULL, AcquireMutexThread, ThreadData);
286     ok_eq_hex(Status, STATUS_SUCCESS);
287     Status = ObReferenceObjectByHandle(ThreadData->Handle, SYNCHRONIZE, *PsThreadType, KernelMode, (PVOID *)&ThreadData->Thread, NULL);
288     ok_eq_hex(Status, STATUS_SUCCESS);
289 
290     return KeWaitForSingleObject(&ThreadData->OutEvent, Executive, KernelMode, FALSE, Timeout);
291 }
292 
293 static
294 VOID
FinishThread(PTHREAD_DATA ThreadData)295 FinishThread(
296     PTHREAD_DATA ThreadData)
297 {
298     NTSTATUS Status = STATUS_SUCCESS;
299 
300     KeSetEvent(&ThreadData->InEvent, 0, TRUE);
301     Status = KeWaitForSingleObject(ThreadData->Thread, Executive, KernelMode, FALSE, NULL);
302     ok_eq_hex(Status, STATUS_SUCCESS);
303 
304     ObDereferenceObject(ThreadData->Thread);
305     Status = ZwClose(ThreadData->Handle);
306     ok_eq_hex(Status, STATUS_SUCCESS);
307     KeClearEvent(&ThreadData->InEvent);
308     KeClearEvent(&ThreadData->OutEvent);
309 }
310 
311 static
312 VOID
TestGuardedMutexConcurrent(PKGUARDED_MUTEX Mutex)313 TestGuardedMutexConcurrent(
314     PKGUARDED_MUTEX Mutex)
315 {
316     NTSTATUS Status;
317     THREAD_DATA ThreadData;
318     THREAD_DATA ThreadData2;
319     THREAD_DATA ThreadDataUnsafe;
320     THREAD_DATA ThreadDataTry;
321     PKTHREAD Thread = KeGetCurrentThread();
322     LARGE_INTEGER Timeout;
323     Timeout.QuadPart = -50 * 1000 * 10; /* 50 ms */
324 
325     InitThreadData(&ThreadData, Mutex, pKeAcquireGuardedMutex, NULL, pKeReleaseGuardedMutex);
326     InitThreadData(&ThreadData2, Mutex, pKeAcquireGuardedMutex, NULL, pKeReleaseGuardedMutex);
327     InitThreadData(&ThreadDataUnsafe, Mutex, pKeAcquireGuardedMutexUnsafe, NULL, pKeReleaseGuardedMutexUnsafe);
328     InitThreadData(&ThreadDataTry, Mutex, NULL, pKeTryToAcquireGuardedMutex, pKeReleaseGuardedMutex);
329 
330     /* have a thread acquire the mutex */
331     Status = StartThread(&ThreadData, NULL, PASSIVE_LEVEL, FALSE, FALSE);
332     ok_eq_hex(Status, STATUS_SUCCESS);
333     CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
334     /* have a second thread try to acquire it -- should fail */
335     Status = StartThread(&ThreadDataTry, NULL, PASSIVE_LEVEL, TRUE, FALSE);
336     ok_eq_hex(Status, STATUS_SUCCESS);
337     CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
338     FinishThread(&ThreadDataTry);
339 
340     /* have another thread acquire it -- should block */
341     Status = StartThread(&ThreadData2, &Timeout, APC_LEVEL, FALSE, FALSE);
342     ok_eq_hex(Status, STATUS_TIMEOUT);
343     CheckMutex(Mutex, 4L, ThreadData.Thread, 1LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
344 
345     /* finish the first thread -- now the second should become available */
346     FinishThread(&ThreadData);
347     Status = KeWaitForSingleObject(&ThreadData2.OutEvent, Executive, KernelMode, FALSE, NULL);
348     ok_eq_hex(Status, STATUS_SUCCESS);
349     CheckMutex(Mutex, 0L, ThreadData2.Thread, 1LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
350 
351     /* block two more threads */
352     Status = StartThread(&ThreadDataUnsafe, &Timeout, APC_LEVEL, FALSE, FALSE);
353     ok_eq_hex(Status, STATUS_TIMEOUT);
354     CheckMutex(Mutex, 4L, ThreadData2.Thread, 2LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
355 
356     Status = StartThread(&ThreadData, &Timeout, PASSIVE_LEVEL, FALSE, FALSE);
357     ok_eq_hex(Status, STATUS_TIMEOUT);
358     CheckMutex(Mutex, 8L, ThreadData2.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
359 
360     /* finish 1 */
361     FinishThread(&ThreadData2);
362     Status = KeWaitForSingleObject(&ThreadDataUnsafe.OutEvent, Executive, KernelMode, FALSE, NULL);
363     ok_eq_hex(Status, STATUS_SUCCESS);
364     CheckMutex(Mutex, 4L, ThreadDataUnsafe.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
365 
366     /* finish 2 */
367     FinishThread(&ThreadDataUnsafe);
368     Status = KeWaitForSingleObject(&ThreadData.OutEvent, Executive, KernelMode, FALSE, NULL);
369     ok_eq_hex(Status, STATUS_SUCCESS);
370     CheckMutex(Mutex, 0L, ThreadData.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
371 
372     /* finish 3 */
373     FinishThread(&ThreadData);
374 
375     CheckMutex(Mutex, 1L, NULL, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
376 }
377 
START_TEST(KeGuardedMutex)378 START_TEST(KeGuardedMutex)
379 {
380     KGUARDED_MUTEX Mutex;
381     KIRQL OldIrql;
382     PKTHREAD Thread = KeGetCurrentThread();
383     struct {
384         KIRQL Irql;
385         SHORT KernelApcsDisabled;
386         SHORT SpecialApcsDisabled;
387         BOOLEAN AllApcsDisabled;
388     } TestIterations[] =
389     {
390         { PASSIVE_LEVEL,   0,  0, FALSE },
391         { PASSIVE_LEVEL,  -1,  0, FALSE },
392         { PASSIVE_LEVEL,  -3,  0, FALSE },
393         { PASSIVE_LEVEL,   0, -1, TRUE },
394         { PASSIVE_LEVEL,  -1, -1, TRUE },
395         { PASSIVE_LEVEL,  -3, -2, TRUE },
396         // 6
397         { APC_LEVEL,       0,  0, TRUE },
398         { APC_LEVEL,      -1,  0, TRUE },
399         { APC_LEVEL,      -3,  0, TRUE },
400         { APC_LEVEL,       0, -1, TRUE },
401         { APC_LEVEL,      -1, -1, TRUE },
402         { APC_LEVEL,      -3, -2, TRUE },
403         // 12
404         { DISPATCH_LEVEL,  0,  0, TRUE },
405         { DISPATCH_LEVEL, -1,  0, TRUE },
406         { DISPATCH_LEVEL, -3,  0, TRUE },
407         { DISPATCH_LEVEL,  0, -1, TRUE },
408         { DISPATCH_LEVEL, -1, -1, TRUE },
409         { DISPATCH_LEVEL, -3, -2, TRUE },
410         // 18
411         { HIGH_LEVEL,      0,  0, TRUE },
412         { HIGH_LEVEL,     -1,  0, TRUE },
413         { HIGH_LEVEL,     -3,  0, TRUE },
414         { HIGH_LEVEL,      0, -1, TRUE },
415         { HIGH_LEVEL,     -1, -1, TRUE },
416         { HIGH_LEVEL,     -3, -2, TRUE },
417     };
418     int i;
419 
420     pKeAreAllApcsDisabled = KmtGetSystemRoutineAddress(L"KeAreAllApcsDisabled");
421     pKeInitializeGuardedMutex = KmtGetSystemRoutineAddress(L"KeInitializeGuardedMutex");
422     pKeAcquireGuardedMutex = KmtGetSystemRoutineAddress(L"KeAcquireGuardedMutex");
423     pKeAcquireGuardedMutexUnsafe = KmtGetSystemRoutineAddress(L"KeAcquireGuardedMutexUnsafe");
424     pKeEnterGuardedRegion = KmtGetSystemRoutineAddress(L"KeEnterGuardedRegion");
425     pKeLeaveGuardedRegion = KmtGetSystemRoutineAddress(L"KeLeaveGuardedRegion");
426     pKeReleaseGuardedMutex = KmtGetSystemRoutineAddress(L"KeReleaseGuardedMutex");
427     pKeReleaseGuardedMutexUnsafe = KmtGetSystemRoutineAddress(L"KeReleaseGuardedMutexUnsafe");
428     pKeTryToAcquireGuardedMutex = KmtGetSystemRoutineAddress(L"KeTryToAcquireGuardedMutex");
429 
430     if (skip(pKeAreAllApcsDisabled &&
431              pKeInitializeGuardedMutex &&
432              pKeAcquireGuardedMutex &&
433              pKeAcquireGuardedMutexUnsafe &&
434              pKeEnterGuardedRegion &&
435              pKeLeaveGuardedRegion &&
436              pKeReleaseGuardedMutex &&
437              pKeReleaseGuardedMutexUnsafe &&
438              pKeTryToAcquireGuardedMutex, "No guarded mutexes\n"))
439     {
440         return;
441     }
442 
443     for (i = 0; i < sizeof TestIterations / sizeof TestIterations[0]; ++i)
444     {
445         trace("Run %d\n", i);
446         KeRaiseIrql(TestIterations[i].Irql, &OldIrql);
447         Thread->KernelApcDisable = TestIterations[i].KernelApcsDisabled;
448         Thread->SpecialApcDisable = TestIterations[i].SpecialApcsDisabled;
449 
450         RtlFillMemory(&Mutex, sizeof Mutex, 0x55);
451         pKeInitializeGuardedMutex(&Mutex);
452         CheckMutex(&Mutex, 1L, NULL, 0LU, 0x5555, 0x5555, TestIterations[i].KernelApcsDisabled, TestIterations[i].SpecialApcsDisabled, TestIterations[i].AllApcsDisabled, TestIterations[i].Irql);
453         TestGuardedMutex(&Mutex, TestIterations[i].KernelApcsDisabled, TestIterations[i].SpecialApcsDisabled, TestIterations[i].AllApcsDisabled, TestIterations[i].Irql);
454 
455         Thread->SpecialApcDisable = 0;
456         Thread->KernelApcDisable = 0;
457         KeLowerIrql(OldIrql);
458     }
459 
460     trace("Concurrent test\n");
461     RtlFillMemory(&Mutex, sizeof Mutex, 0x55);
462     pKeInitializeGuardedMutex(&Mutex);
463     TestGuardedMutexConcurrent(&Mutex);
464 }
465