reactos/rostests/kmtests/ntos_ke/KeGuardedMutex.c
Thomas Faber e6c56afe8c [KMTESTS]
- Add KmtGetSystemRoutineAddress and use it to avoid importing functions that are not available in some versions of Windows. This allows running kmtests on Windows XP SP3 (and probably Server 2003 SP0/SP2). Note that failures on XP are still not valid bugs, this is purely for convenience!  Dedicated to Jérôme.
ROSTESTS-150

svn path=/trunk/; revision=66281
2015-02-15 10:23:21 +00:00

465 lines
18 KiB
C

/*
* PROJECT: ReactOS kernel-mode tests
* LICENSE: GPLv2+ - See COPYING in the top level directory
* PURPOSE: Kernel-Mode Test Suite Guarded Mutex test
* PROGRAMMER: Thomas Faber <thomas.faber@reactos.org>
*/
#include <kmt_test.h>
#define NDEBUG
#include <debug.h>
static
_IRQL_requires_min_(PASSIVE_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL)
BOOLEAN
(NTAPI
*pKeAreAllApcsDisabled)(VOID);
static
_Acquires_lock_(_Global_critical_region_)
_Requires_lock_not_held_(*Mutex)
_Acquires_lock_(*Mutex)
_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
VOID
(FASTCALL
*pKeAcquireGuardedMutex)(
_Inout_ PKGUARDED_MUTEX GuardedMutex);
static
_Requires_lock_not_held_(*FastMutex)
_Acquires_lock_(*FastMutex)
_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
VOID
(FASTCALL
*pKeAcquireGuardedMutexUnsafe)(
_Inout_ PKGUARDED_MUTEX GuardedMutex);
static
_Acquires_lock_(_Global_critical_region_)
_IRQL_requires_max_(APC_LEVEL)
VOID
(NTAPI
*pKeEnterGuardedRegion)(VOID);
static
_Releases_lock_(_Global_critical_region_)
_IRQL_requires_max_(APC_LEVEL)
VOID
(NTAPI
*pKeLeaveGuardedRegion)(VOID);
static
_IRQL_requires_max_(APC_LEVEL)
_IRQL_requires_min_(PASSIVE_LEVEL)
VOID
(FASTCALL
*pKeInitializeGuardedMutex)(
_Out_ PKGUARDED_MUTEX GuardedMutex);
static
_Requires_lock_held_(*FastMutex)
_Releases_lock_(*FastMutex)
_IRQL_requires_max_(APC_LEVEL)
VOID
(FASTCALL
*pKeReleaseGuardedMutexUnsafe)(
_Inout_ PKGUARDED_MUTEX GuardedMutex);
static
_Releases_lock_(_Global_critical_region_)
_Requires_lock_held_(*Mutex)
_Releases_lock_(*Mutex)
_IRQL_requires_max_(APC_LEVEL)
VOID
(FASTCALL
*pKeReleaseGuardedMutex)(
_Inout_ PKGUARDED_MUTEX GuardedMutex);
static
_Must_inspect_result_
_Success_(return != FALSE)
_IRQL_requires_max_(APC_LEVEL)
_Post_satisfies_(return == 1 || return == 0)
BOOLEAN
(FASTCALL
*pKeTryToAcquireGuardedMutex)(
_When_ (return, _Requires_lock_not_held_(*_Curr_) _Acquires_exclusive_lock_(*_Curr_)) _Acquires_lock_(_Global_critical_region_)
_Inout_ PKGUARDED_MUTEX GuardedMutex);
#define CheckMutex(Mutex, ExpectedCount, ExpectedOwner, ExpectedContention, \
ExpectedKernelApcDisable, ExpectedSpecialApcDisable, \
KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, \
ExpectedIrql) do \
{ \
ok_eq_long((Mutex)->Count, ExpectedCount); \
ok_eq_pointer((Mutex)->Owner, ExpectedOwner); \
ok_eq_ulong((Mutex)->Contention, ExpectedContention); \
ok_eq_int((Mutex)->KernelApcDisable, ExpectedKernelApcDisable); \
if (KmtIsCheckedBuild) \
ok_eq_int((Mutex)->SpecialApcDisable, ExpectedSpecialApcDisable); \
else \
ok_eq_int((Mutex)->SpecialApcDisable, 0x5555); \
ok_eq_bool(KeAreApcsDisabled(), KernelApcsDisabled || SpecialApcsDisabled); \
ok_eq_int(Thread->KernelApcDisable, KernelApcsDisabled); \
ok_eq_bool(pKeAreAllApcsDisabled(), AllApcsDisabled); \
ok_eq_int(Thread->SpecialApcDisable, SpecialApcsDisabled); \
ok_irql(ExpectedIrql); \
} while (0)
static
VOID
TestGuardedMutex(
PKGUARDED_MUTEX Mutex,
SHORT KernelApcsDisabled,
SHORT SpecialApcsDisabled,
SHORT AllApcsDisabled,
KIRQL OriginalIrql)
{
PKTHREAD Thread = KeGetCurrentThread();
ok_irql(OriginalIrql);
CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, 0x5555, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
/* these ASSERT */
if (!KmtIsCheckedBuild || OriginalIrql <= APC_LEVEL)
{
/* acquire/release normally */
pKeAcquireGuardedMutex(Mutex);
CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
ok_bool_false(pKeTryToAcquireGuardedMutex(Mutex), "KeTryToAcquireGuardedMutex returned");
CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
pKeReleaseGuardedMutex(Mutex);
CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
/* try to acquire */
ok_bool_true(pKeTryToAcquireGuardedMutex(Mutex), "KeTryToAcquireGuardedMutex returned");
CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
pKeReleaseGuardedMutex(Mutex);
CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
}
else
/* Make the following test happy */
Mutex->SpecialApcDisable = SpecialApcsDisabled - 1;
/* ASSERT */
if (!KmtIsCheckedBuild || OriginalIrql == APC_LEVEL || SpecialApcsDisabled < 0)
{
/* acquire/release unsafe */
pKeAcquireGuardedMutexUnsafe(Mutex);
CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
pKeReleaseGuardedMutexUnsafe(Mutex);
CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
}
/* Bugchecks >= DISPATCH_LEVEL */
if (!KmtIsCheckedBuild)
{
/* mismatched acquire/release */
pKeAcquireGuardedMutex(Mutex);
CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
pKeReleaseGuardedMutexUnsafe(Mutex);
CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled - 1, TRUE, OriginalIrql);
pKeLeaveGuardedRegion();
CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
pKeAcquireGuardedMutexUnsafe(Mutex);
CheckMutex(Mutex, 0L, Thread, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
pKeReleaseGuardedMutex(Mutex);
CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled + 1, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -1, OriginalIrql);
pKeEnterGuardedRegion();
CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
/* release without acquire */
pKeReleaseGuardedMutexUnsafe(Mutex);
CheckMutex(Mutex, 0L, NULL, 0LU, 0x5555, SpecialApcsDisabled - 1, KernelApcsDisabled, SpecialApcsDisabled, AllApcsDisabled, OriginalIrql);
pKeReleaseGuardedMutex(Mutex);
CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled, KernelApcsDisabled, SpecialApcsDisabled + 1, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -1, OriginalIrql);
pKeReleaseGuardedMutex(Mutex);
/* TODO: here we see that Mutex->Count isn't actually just a count. Test the bits correctly! */
CheckMutex(Mutex, 0L, NULL, 0LU, 0x5555, SpecialApcsDisabled, KernelApcsDisabled, SpecialApcsDisabled + 2, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -2, OriginalIrql);
pKeReleaseGuardedMutex(Mutex);
CheckMutex(Mutex, 1L, NULL, 0LU, 0x5555, SpecialApcsDisabled, KernelApcsDisabled, SpecialApcsDisabled + 3, OriginalIrql >= APC_LEVEL || SpecialApcsDisabled != -3, OriginalIrql);
Thread->SpecialApcDisable -= 3;
}
/* make sure we survive this in case of error */
ok_eq_long(Mutex->Count, 1L);
Mutex->Count = 1;
ok_eq_int(Thread->KernelApcDisable, KernelApcsDisabled);
Thread->KernelApcDisable = KernelApcsDisabled;
ok_eq_int(Thread->SpecialApcDisable, SpecialApcsDisabled);
Thread->SpecialApcDisable = SpecialApcsDisabled;
ok_irql(OriginalIrql);
}
typedef VOID (FASTCALL *PMUTEX_FUNCTION)(PKGUARDED_MUTEX);
typedef BOOLEAN (FASTCALL *PMUTEX_TRY_FUNCTION)(PKGUARDED_MUTEX);
typedef struct
{
HANDLE Handle;
PKTHREAD Thread;
KIRQL Irql;
PKGUARDED_MUTEX Mutex;
PMUTEX_FUNCTION Acquire;
PMUTEX_TRY_FUNCTION TryAcquire;
PMUTEX_FUNCTION Release;
BOOLEAN Try;
BOOLEAN RetExpected;
KEVENT InEvent;
KEVENT OutEvent;
} THREAD_DATA, *PTHREAD_DATA;
static
VOID
NTAPI
AcquireMutexThread(
PVOID Parameter)
{
PTHREAD_DATA ThreadData = Parameter;
KIRQL Irql;
BOOLEAN Ret = FALSE;
NTSTATUS Status;
DPRINT("Thread starting\n");
KeRaiseIrql(ThreadData->Irql, &Irql);
if (ThreadData->Try)
{
Ret = ThreadData->TryAcquire(ThreadData->Mutex);
ok_eq_bool(Ret, ThreadData->RetExpected);
}
else
ThreadData->Acquire(ThreadData->Mutex);
ok_bool_false(KeSetEvent(&ThreadData->OutEvent, 0, TRUE), "KeSetEvent returned");
DPRINT("Thread now waiting\n");
Status = KeWaitForSingleObject(&ThreadData->InEvent, Executive, KernelMode, FALSE, NULL);
DPRINT("Thread done waiting\n");
ok_eq_hex(Status, STATUS_SUCCESS);
if (!ThreadData->Try || Ret)
ThreadData->Release(ThreadData->Mutex);
KeLowerIrql(Irql);
DPRINT("Thread exiting\n");
}
static
VOID
InitThreadData(
PTHREAD_DATA ThreadData,
PKGUARDED_MUTEX Mutex,
PMUTEX_FUNCTION Acquire,
PMUTEX_TRY_FUNCTION TryAcquire,
PMUTEX_FUNCTION Release)
{
ThreadData->Mutex = Mutex;
KeInitializeEvent(&ThreadData->InEvent, NotificationEvent, FALSE);
KeInitializeEvent(&ThreadData->OutEvent, NotificationEvent, FALSE);
ThreadData->Acquire = Acquire;
ThreadData->TryAcquire = TryAcquire;
ThreadData->Release = Release;
}
static
NTSTATUS
StartThread(
PTHREAD_DATA ThreadData,
PLARGE_INTEGER Timeout,
KIRQL Irql,
BOOLEAN Try,
BOOLEAN RetExpected)
{
NTSTATUS Status = STATUS_SUCCESS;
OBJECT_ATTRIBUTES Attributes;
ThreadData->Try = Try;
ThreadData->Irql = Irql;
ThreadData->RetExpected = RetExpected;
InitializeObjectAttributes(&Attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
Status = PsCreateSystemThread(&ThreadData->Handle, GENERIC_ALL, &Attributes, NULL, NULL, AcquireMutexThread, ThreadData);
ok_eq_hex(Status, STATUS_SUCCESS);
Status = ObReferenceObjectByHandle(ThreadData->Handle, SYNCHRONIZE, *PsThreadType, KernelMode, (PVOID *)&ThreadData->Thread, NULL);
ok_eq_hex(Status, STATUS_SUCCESS);
return KeWaitForSingleObject(&ThreadData->OutEvent, Executive, KernelMode, FALSE, Timeout);
}
static
VOID
FinishThread(
PTHREAD_DATA ThreadData)
{
NTSTATUS Status = STATUS_SUCCESS;
KeSetEvent(&ThreadData->InEvent, 0, TRUE);
Status = KeWaitForSingleObject(ThreadData->Thread, Executive, KernelMode, FALSE, NULL);
ok_eq_hex(Status, STATUS_SUCCESS);
ObDereferenceObject(ThreadData->Thread);
Status = ZwClose(ThreadData->Handle);
ok_eq_hex(Status, STATUS_SUCCESS);
KeClearEvent(&ThreadData->InEvent);
KeClearEvent(&ThreadData->OutEvent);
}
static
VOID
TestGuardedMutexConcurrent(
PKGUARDED_MUTEX Mutex)
{
NTSTATUS Status;
THREAD_DATA ThreadData;
THREAD_DATA ThreadData2;
THREAD_DATA ThreadDataUnsafe;
THREAD_DATA ThreadDataTry;
PKTHREAD Thread = KeGetCurrentThread();
LARGE_INTEGER Timeout;
Timeout.QuadPart = -50 * 1000 * 10; /* 50 ms */
InitThreadData(&ThreadData, Mutex, pKeAcquireGuardedMutex, NULL, pKeReleaseGuardedMutex);
InitThreadData(&ThreadData2, Mutex, pKeAcquireGuardedMutex, NULL, pKeReleaseGuardedMutex);
InitThreadData(&ThreadDataUnsafe, Mutex, pKeAcquireGuardedMutexUnsafe, NULL, pKeReleaseGuardedMutexUnsafe);
InitThreadData(&ThreadDataTry, Mutex, NULL, pKeTryToAcquireGuardedMutex, pKeReleaseGuardedMutex);
/* have a thread acquire the mutex */
Status = StartThread(&ThreadData, NULL, PASSIVE_LEVEL, FALSE, FALSE);
ok_eq_hex(Status, STATUS_SUCCESS);
CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
/* have a second thread try to acquire it -- should fail */
Status = StartThread(&ThreadDataTry, NULL, PASSIVE_LEVEL, TRUE, FALSE);
ok_eq_hex(Status, STATUS_SUCCESS);
CheckMutex(Mutex, 0L, ThreadData.Thread, 0LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
FinishThread(&ThreadDataTry);
/* have another thread acquire it -- should block */
Status = StartThread(&ThreadData2, &Timeout, APC_LEVEL, FALSE, FALSE);
ok_eq_hex(Status, STATUS_TIMEOUT);
CheckMutex(Mutex, 4L, ThreadData.Thread, 1LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
/* finish the first thread -- now the second should become available */
FinishThread(&ThreadData);
Status = KeWaitForSingleObject(&ThreadData2.OutEvent, Executive, KernelMode, FALSE, NULL);
ok_eq_hex(Status, STATUS_SUCCESS);
CheckMutex(Mutex, 0L, ThreadData2.Thread, 1LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
/* block two more threads */
Status = StartThread(&ThreadDataUnsafe, &Timeout, APC_LEVEL, FALSE, FALSE);
ok_eq_hex(Status, STATUS_TIMEOUT);
CheckMutex(Mutex, 4L, ThreadData2.Thread, 2LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
Status = StartThread(&ThreadData, &Timeout, PASSIVE_LEVEL, FALSE, FALSE);
ok_eq_hex(Status, STATUS_TIMEOUT);
CheckMutex(Mutex, 8L, ThreadData2.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
/* finish 1 */
FinishThread(&ThreadData2);
Status = KeWaitForSingleObject(&ThreadDataUnsafe.OutEvent, Executive, KernelMode, FALSE, NULL);
ok_eq_hex(Status, STATUS_SUCCESS);
CheckMutex(Mutex, 4L, ThreadDataUnsafe.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
/* finish 2 */
FinishThread(&ThreadDataUnsafe);
Status = KeWaitForSingleObject(&ThreadData.OutEvent, Executive, KernelMode, FALSE, NULL);
ok_eq_hex(Status, STATUS_SUCCESS);
CheckMutex(Mutex, 0L, ThreadData.Thread, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
/* finish 3 */
FinishThread(&ThreadData);
CheckMutex(Mutex, 1L, NULL, 3LU, 0x5555, -1, 0, 0, FALSE, PASSIVE_LEVEL);
}
START_TEST(KeGuardedMutex)
{
KGUARDED_MUTEX Mutex;
KIRQL OldIrql;
PKTHREAD Thread = KeGetCurrentThread();
struct {
KIRQL Irql;
SHORT KernelApcsDisabled;
SHORT SpecialApcsDisabled;
BOOLEAN AllApcsDisabled;
} TestIterations[] =
{
{ PASSIVE_LEVEL, 0, 0, FALSE },
{ PASSIVE_LEVEL, -1, 0, FALSE },
{ PASSIVE_LEVEL, -3, 0, FALSE },
{ PASSIVE_LEVEL, 0, -1, TRUE },
{ PASSIVE_LEVEL, -1, -1, TRUE },
{ PASSIVE_LEVEL, -3, -2, TRUE },
// 6
{ APC_LEVEL, 0, 0, TRUE },
{ APC_LEVEL, -1, 0, TRUE },
{ APC_LEVEL, -3, 0, TRUE },
{ APC_LEVEL, 0, -1, TRUE },
{ APC_LEVEL, -1, -1, TRUE },
{ APC_LEVEL, -3, -2, TRUE },
// 12
{ DISPATCH_LEVEL, 0, 0, TRUE },
{ DISPATCH_LEVEL, -1, 0, TRUE },
{ DISPATCH_LEVEL, -3, 0, TRUE },
{ DISPATCH_LEVEL, 0, -1, TRUE },
{ DISPATCH_LEVEL, -1, -1, TRUE },
{ DISPATCH_LEVEL, -3, -2, TRUE },
// 18
{ HIGH_LEVEL, 0, 0, TRUE },
{ HIGH_LEVEL, -1, 0, TRUE },
{ HIGH_LEVEL, -3, 0, TRUE },
{ HIGH_LEVEL, 0, -1, TRUE },
{ HIGH_LEVEL, -1, -1, TRUE },
{ HIGH_LEVEL, -3, -2, TRUE },
};
int i;
pKeAreAllApcsDisabled = KmtGetSystemRoutineAddress(L"KeAreAllApcsDisabled");
pKeInitializeGuardedMutex = KmtGetSystemRoutineAddress(L"KeInitializeGuardedMutex");
pKeAcquireGuardedMutex = KmtGetSystemRoutineAddress(L"KeAcquireGuardedMutex");
pKeAcquireGuardedMutexUnsafe = KmtGetSystemRoutineAddress(L"KeAcquireGuardedMutexUnsafe");
pKeEnterGuardedRegion = KmtGetSystemRoutineAddress(L"KeEnterGuardedRegion");
pKeLeaveGuardedRegion = KmtGetSystemRoutineAddress(L"KeLeaveGuardedRegion");
pKeReleaseGuardedMutex = KmtGetSystemRoutineAddress(L"KeReleaseGuardedMutex");
pKeReleaseGuardedMutexUnsafe = KmtGetSystemRoutineAddress(L"KeReleaseGuardedMutexUnsafe");
pKeTryToAcquireGuardedMutex = KmtGetSystemRoutineAddress(L"KeTryToAcquireGuardedMutex");
if (skip(pKeAreAllApcsDisabled &&
pKeInitializeGuardedMutex &&
pKeAcquireGuardedMutex &&
pKeAcquireGuardedMutexUnsafe &&
pKeEnterGuardedRegion &&
pKeLeaveGuardedRegion &&
pKeReleaseGuardedMutex &&
pKeReleaseGuardedMutexUnsafe &&
pKeTryToAcquireGuardedMutex, "No guarded mutexes\n"))
{
return;
}
for (i = 0; i < sizeof TestIterations / sizeof TestIterations[0]; ++i)
{
trace("Run %d\n", i);
KeRaiseIrql(TestIterations[i].Irql, &OldIrql);
Thread->KernelApcDisable = TestIterations[i].KernelApcsDisabled;
Thread->SpecialApcDisable = TestIterations[i].SpecialApcsDisabled;
RtlFillMemory(&Mutex, sizeof Mutex, 0x55);
pKeInitializeGuardedMutex(&Mutex);
CheckMutex(&Mutex, 1L, NULL, 0LU, 0x5555, 0x5555, TestIterations[i].KernelApcsDisabled, TestIterations[i].SpecialApcsDisabled, TestIterations[i].AllApcsDisabled, TestIterations[i].Irql);
TestGuardedMutex(&Mutex, TestIterations[i].KernelApcsDisabled, TestIterations[i].SpecialApcsDisabled, TestIterations[i].AllApcsDisabled, TestIterations[i].Irql);
Thread->SpecialApcDisable = 0;
Thread->KernelApcDisable = 0;
KeLowerIrql(OldIrql);
}
trace("Concurrent test\n");
RtlFillMemory(&Mutex, sizeof Mutex, 0x55);
pKeInitializeGuardedMutex(&Mutex);
TestGuardedMutexConcurrent(&Mutex);
}