You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
618 lines
12 KiB
618 lines
12 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
object.c
|
|
|
|
Abstract:
|
|
|
|
This is the NT Watchdog driver implementation.
|
|
|
|
Author:
|
|
|
|
Michael Maciesowicz (mmacie) 02-May-2001
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "wd.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text (PAGE, WdpFlushRegistryKey)
|
|
#pragma alloc_text (PAGE, WdpInitializeObject)
|
|
#endif
|
|
|
|
//
|
|
// Exports.
|
|
//
|
|
|
|
WATCHDOGAPI
|
|
PVOID
|
|
WdAttachContext(
|
|
IN PVOID pWatch,
|
|
IN ULONG ulSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates context buffer of given size from non-paged pool
|
|
and associates it with given watchdog object. In case the client code
|
|
doesn't detach the context before freeing watchdog object it will be
|
|
freed automatically when watchdog object is destroyed.
|
|
|
|
This routine helps to handle the case when watchdog object is freed
|
|
but the context must persist till object is actually destroyed and client
|
|
code cannot guarantee that.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
ulSize - Size in bytes of context to allocate and attach.
|
|
|
|
Return Value:
|
|
|
|
A pointer to created and attached context buffer.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWATCHDOG_OBJECT pWatchdogObject;
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdAttachContext);
|
|
|
|
pWatchdogObject = (PWATCHDOG_OBJECT)pWatch;
|
|
|
|
//
|
|
// Check for double attach.
|
|
//
|
|
|
|
ASSERT(NULL == pWatchdogObject->Context);
|
|
|
|
//
|
|
// Allocate storage for attached context from non-paged pool.
|
|
//
|
|
|
|
pWatchdogObject->Context = ExAllocatePoolWithTag(NonPagedPool,
|
|
ulSize,
|
|
pWatchdogObject->OwnerTag);
|
|
|
|
return pWatchdogObject->Context;
|
|
} // WdAttachContext()
|
|
|
|
WATCHDOGAPI
|
|
VOID
|
|
WdCompleteEvent(
|
|
IN PVOID pWatch,
|
|
IN PKTHREAD pThread
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function *MUST* be called from client handler for watchdog timeout event
|
|
before exiting. It removes references from watchdog and thread objects.
|
|
It also reenables watchdog event generation for deferred watchdog objects.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
pThread - Points to KTHREAD object for spinning thread.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Note: pThread is NULL for recovery events.
|
|
//
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdCompleteEvent);
|
|
|
|
//
|
|
// Resume event generation for deferred watchdog.
|
|
//
|
|
|
|
if (WdDeferredWatchdog == ((PWATCHDOG_OBJECT)pWatch)->ObjectType)
|
|
{
|
|
InterlockedExchange(&(((PDEFERRED_WATCHDOG)pWatch)->Trigger), 0);
|
|
}
|
|
|
|
//
|
|
// Drop reference counts.
|
|
//
|
|
|
|
if (NULL != pThread)
|
|
{
|
|
ObDereferenceObject(pThread);
|
|
}
|
|
|
|
WdDereferenceObject(pWatch);
|
|
|
|
return;
|
|
} // WdCompleteEvent()
|
|
|
|
WATCHDOGAPI
|
|
VOID
|
|
WdDereferenceObject(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function decreases reference count of watchdog object.
|
|
If remaining count is zero we will remove object here, since it's
|
|
been freed already.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWATCHDOG_OBJECT pWatchdogObject;
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdDereferenceObject);
|
|
|
|
pWatchdogObject = (PWATCHDOG_OBJECT)pWatch;
|
|
|
|
ASSERT(pWatchdogObject->ReferenceCount > 0);
|
|
|
|
//
|
|
// Drop reference count and remove the object if fully dereferenced.
|
|
//
|
|
|
|
if (InterlockedDecrement(&(pWatchdogObject->ReferenceCount)) == 0)
|
|
{
|
|
//
|
|
// Object already freed - remove it now.
|
|
//
|
|
|
|
WdpDestroyObject(pWatchdogObject);
|
|
}
|
|
|
|
return;
|
|
} // WdDereferenceObject()
|
|
|
|
WATCHDOGAPI
|
|
VOID
|
|
WdDetachContext(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees context attached to watchdog object.
|
|
|
|
NOTE: You don't have to call this routine before freeing watchdog
|
|
object - the context, if attached, will be destroyed automatically
|
|
when watchdog object is destroyed.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWATCHDOG_OBJECT pWatchdogObject;
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdDetachContext);
|
|
|
|
pWatchdogObject = (PWATCHDOG_OBJECT)pWatch;
|
|
|
|
//
|
|
// Check for double detach.
|
|
//
|
|
|
|
ASSERT(NULL != pWatchdogObject->Context);
|
|
|
|
//
|
|
// Free storage for attached context and set reference to NULL.
|
|
//
|
|
// BUGBUG: This is not synchronized with DPCs!
|
|
//
|
|
|
|
ExFreePool(pWatchdogObject->Context);
|
|
pWatchdogObject->Context = NULL;
|
|
|
|
return;
|
|
} // WdDetachContext()
|
|
|
|
WATCHDOGAPI
|
|
PDEVICE_OBJECT
|
|
WdGetDeviceObject(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function return pointer to device object associated with watchdog object.
|
|
This function increases reference count on DEVICE_OBJECT so the caller must call
|
|
ObDereferenceObject() once DEVICE_OBJECT pointer is not needed any more.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
Pointer to DEVICE_OBJECT.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT pDeviceObject;
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdGetDeviceObject);
|
|
|
|
pDeviceObject = ((PWATCHDOG_OBJECT)pWatch)->DeviceObject;
|
|
ASSERT(NULL != pDeviceObject);
|
|
|
|
ObReferenceObject(pDeviceObject);
|
|
return pDeviceObject;
|
|
} // WdGetDeviceObject()
|
|
|
|
WATCHDOGAPI
|
|
WD_EVENT_TYPE
|
|
WdGetLastEvent(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function return last event associated with watchdog object.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
Last event type.
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdGetLastEvent);
|
|
|
|
return ((PWATCHDOG_OBJECT)pWatch)->LastEvent;
|
|
} // WdGetLastEvent()
|
|
|
|
WATCHDOGAPI
|
|
PDEVICE_OBJECT
|
|
WdGetLowestDeviceObject(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function return pointer to the lowest (most likely PDO) DEVICE_OBJECT
|
|
associated with watchdog object. This function increases reference count on
|
|
returned DEVICE_OBJECT - the caller must call ObDereferenceObject() once
|
|
DEVICE_OBJECT pointer is not needed any more.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
Pointer to DEVICE_OBJECT.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT pDeviceObject;
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdGetLowestDeviceObject);
|
|
|
|
//
|
|
// Note: No need to bump reference count here, it is always done when
|
|
// watchdog object is created.
|
|
//
|
|
|
|
pDeviceObject = ((PWATCHDOG_OBJECT)pWatch)->DeviceObject;
|
|
ASSERT(NULL != pDeviceObject);
|
|
|
|
//
|
|
// Now get the pointer to the lowest device object in the stack.
|
|
// Note: This call automatically bumps a reference count on returned object.
|
|
//
|
|
|
|
pDeviceObject = IoGetDeviceAttachmentBaseRef(pDeviceObject);
|
|
ASSERT(NULL != pDeviceObject);
|
|
|
|
return pDeviceObject;
|
|
} // WdGetLowestDeviceObject()
|
|
|
|
WATCHDOGAPI
|
|
VOID
|
|
WdReferenceObject(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function increases reference count of watchdog object.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdReferenceObject);
|
|
|
|
if (InterlockedIncrement(&(((PWATCHDOG_OBJECT)pWatch)->ReferenceCount)) == 1)
|
|
{
|
|
//
|
|
// Somebody referenced removed object.
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
//
|
|
// Check for overflow.
|
|
//
|
|
|
|
ASSERT(((PWATCHDOG_OBJECT)pWatch)->ReferenceCount > 0);
|
|
|
|
return;
|
|
} // WdReferenceObject()
|
|
|
|
//
|
|
// Non-exports.
|
|
//
|
|
|
|
VOID
|
|
WdpDestroyObject(
|
|
IN PVOID pWatch
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function unconditionally frees watchdog object.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWATCHDOG_OBJECT pWatchdogObject;
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
ASSERT_WATCHDOG_OBJECT(pWatch);
|
|
|
|
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdpDestroyObject);
|
|
|
|
pWatchdogObject = (PWATCHDOG_OBJECT)pWatch;
|
|
|
|
ASSERT(0 == pWatchdogObject->ReferenceCount);
|
|
|
|
//
|
|
// Drop reference count on device object.
|
|
//
|
|
|
|
ObDereferenceObject(pWatchdogObject->DeviceObject);
|
|
|
|
//
|
|
// We are freeing non-paged pool, it's OK to be at IRQL <= DISPATCH_LEVEL.
|
|
// Free attached context first, if any, then free watchdog object.
|
|
//
|
|
|
|
if (NULL != pWatchdogObject->Context)
|
|
{
|
|
ExFreePool(pWatchdogObject->Context);
|
|
}
|
|
|
|
ExFreePool(pWatch);
|
|
|
|
return;
|
|
} // WdpDestroyObject()
|
|
|
|
NTSTATUS
|
|
WdpFlushRegistryKey(
|
|
IN PVOID pWatch,
|
|
IN PCWSTR pwszKeyName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function forces a registry key to be committed to disk.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
pwszKeyName - Points to key name string.
|
|
|
|
Return Value:
|
|
|
|
Status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING unicodeKeyName;
|
|
HANDLE keyHandle;
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
UNREFERENCED_PARAMETER(pWatch);
|
|
ASSERT(NULL != pwszKeyName);
|
|
|
|
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdpFlushRegistryKey);
|
|
|
|
RtlInitUnicodeString(&unicodeKeyName, pwszKeyName);
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&unicodeKeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
ntStatus = ZwOpenKey(&keyHandle,
|
|
KEY_READ | KEY_WRITE,
|
|
&objectAttributes);
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
ntStatus = ZwFlushKey(keyHandle);
|
|
ZwClose(keyHandle);
|
|
}
|
|
|
|
return ntStatus;
|
|
} // WdpFlushRegistryKey()
|
|
|
|
VOID
|
|
WdpInitializeObject(
|
|
IN PVOID pWatch,
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN WD_OBJECT_TYPE objectType,
|
|
IN WD_TIME_TYPE timeType,
|
|
IN ULONG ulTag
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes watchdog object.
|
|
|
|
Arguments:
|
|
|
|
pWatch - Points to WATCHDOG_OBJECT.
|
|
|
|
pDeviceObject - Points to DEVICE_OBJECT associated with watchdog.
|
|
|
|
objectType - Type of watchdog object.
|
|
|
|
timeType - Kernel, User, Both thread time to monitor.
|
|
|
|
ulTag - A tag identifying owner.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWATCHDOG_OBJECT pWatchdogObject;
|
|
|
|
PAGED_CODE();
|
|
ASSERT(NULL != pWatch);
|
|
ASSERT(NULL != pDeviceObject);
|
|
ASSERT((objectType == WdStandardWatchdog) || (objectType == WdDeferredWatchdog));
|
|
ASSERT((timeType >= WdKernelTime) && (timeType <= WdFullTime));
|
|
|
|
WDD_TRACE_CALL((PDEFERRED_WATCHDOG)pWatch, WddWdpInitializeObject);
|
|
|
|
pWatchdogObject = (PWATCHDOG_OBJECT)pWatch;
|
|
|
|
//
|
|
// Set initial state of watchdog object.
|
|
//
|
|
|
|
pWatchdogObject->ObjectType = objectType;
|
|
pWatchdogObject->ReferenceCount = 1;
|
|
pWatchdogObject->OwnerTag = ulTag;
|
|
pWatchdogObject->DeviceObject = pDeviceObject;
|
|
pWatchdogObject->TimeType = timeType;
|
|
pWatchdogObject->LastEvent = WdNoEvent;
|
|
pWatchdogObject->LastQueuedThread = NULL;
|
|
pWatchdogObject->Context = NULL;
|
|
|
|
//
|
|
// Bump reference count on device object.
|
|
//
|
|
|
|
ObReferenceObject(pDeviceObject);
|
|
|
|
//
|
|
// Initialize encapsulated KSPIN_LOCK object.
|
|
//
|
|
|
|
KeInitializeSpinLock(&(pWatchdogObject->SpinLock));
|
|
|
|
return;
|
|
} // WdpInitializeObject()
|