Windows2000/private/ntos/ex/event.c
2020-09-30 17:12:32 +02:00

537 lines
20 KiB
C

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
event.c
Abstract:
This module implements the executive event object. Functions are provided
to create, open, set, reset, pulse, and query event objects.
Author:
David N. Cutler (davec) 8-May-1989
Environment:
Kernel mode only.
--*/
#include "exp.h"
// Temporary so boost is patchable
ULONG ExpEventBoost = EVENT_INCREMENT;
// Address of event object type descriptor.
POBJECT_TYPE ExEventObjectType;
// Structure that describes the mapping of generic access rights to object
// specific access rights for event objects.
GENERIC_MAPPING ExpEventMapping = {
STANDARD_RIGHTS_READ | EVENT_QUERY_STATE,
STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE,
STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
EVENT_ALL_ACCESS
};
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, ExpEventInitialization)
#pragma alloc_text(PAGE, NtClearEvent)
#pragma alloc_text(PAGE, NtCreateEvent)
#pragma alloc_text(PAGE, NtOpenEvent)
#pragma alloc_text(PAGE, NtPulseEvent)
#pragma alloc_text(PAGE, NtQueryEvent)
#pragma alloc_text(PAGE, NtResetEvent)
#pragma alloc_text(PAGE, NtSetEvent)
#endif
BOOLEAN ExpEventInitialization ()
/*++
Routine Description:
This function creates the event object type descriptor at system
initialization and stores the address of the object type descriptor in global storage.
Arguments:
None.
Return Value:
A value of TRUE is returned if the event object type descriptor is
successfully initialized. Otherwise a value of FALSE is returned.
--*/
{
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
NTSTATUS Status;
UNICODE_STRING TypeName;
// Initialize string descriptor.
RtlInitUnicodeString(&TypeName, L"Event");
// Create event object type descriptor.
RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
ObjectTypeInitializer.GenericMapping = ExpEventMapping;
ObjectTypeInitializer.PoolType = NonPagedPool;
ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KEVENT);
ObjectTypeInitializer.ValidAccessMask = EVENT_ALL_ACCESS;
Status = ObCreateObjectType(&TypeName, &ObjectTypeInitializer, (PSECURITY_DESCRIPTOR)NULL, &ExEventObjectType);
// If the event object type descriptor was successfully created, then return a value of TRUE. Otherwise return a value of FALSE.
return (BOOLEAN)(NT_SUCCESS(Status));
}
NTSTATUS NtClearEvent (IN HANDLE EventHandle)
/*++
Routine Description:
This function sets an event object to a Not-Signaled state.
Arguments:
EventHandle - Supplies a handle to an event object.
Return Value:
TBS
--*/
{
PVOID Event;
NTSTATUS Status;
// Reference event object by handle.
Status = ObReferenceObjectByHandle(EventHandle, EVENT_MODIFY_STATE, ExEventObjectType, KeGetPreviousMode(), &Event, NULL);
// If the reference was successful, then set the state of the event
// object to Not-Signaled and dereference event object.
if (NT_SUCCESS(Status)) {
KeClearEvent((PKEVENT)Event);
ObDereferenceObject(Event);
}
return Status;// Return service status.
}
NTSTATUS NtCreateEvent (
OUT PHANDLE EventHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN EVENT_TYPE EventType,
IN BOOLEAN InitialState
)
/*++
Routine Description:
This function creates an event object, sets it initial state to the
specified value, and opens a handle to the object with the specified desired access.
Arguments:
EventHandle - Supplies a pointer to a variable that will receive the event object handle.
DesiredAccess - Supplies the desired types of access for the event object.
ObjectAttributes - Supplies a pointer to an object attributes structure.
EventType - Supplies the type of the event (autoclearing or notification).
InitialState - Supplies the initial state of the event object.
Return Value:
TBS
--*/
{
PVOID Event;
HANDLE Handle;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
// Establish an exception handler, probe the output handle address, and
// attempt to create an event object. If the probe fails, then return the
// exception code as the service status. Otherwise return the status value
// returned by the object insertion routine.
try {
// Get previous processor mode and probe output handle address if necessary.
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
ProbeForWriteHandle(EventHandle);
}
// Check argument validity.
if ((EventType != NotificationEvent) && (EventType != SynchronizationEvent)) {
return STATUS_INVALID_PARAMETER;
}
// Allocate event object.
Status = ObCreateObject(PreviousMode,
ExEventObjectType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(KEVENT),
0,
0,
(PVOID *)&Event);
// If the event object was successfully allocated, then initialize the
// event object and attempt to insert the event object in the current process' handle table.
if (NT_SUCCESS(Status)) {
KeInitializeEvent((PKEVENT)Event, EventType, InitialState);
Status = ObInsertObject(Event, NULL, DesiredAccess, 0, (PVOID *)NULL, &Handle);
// If the event object was successfully inserted in the current
// process' handle table, then attempt to write the event object
// handle value. If the write attempt fails, then do not report
// an error. When the caller attempts to access the handle value, an access violation will occur.
if (NT_SUCCESS(Status)) {
try {
*EventHandle = Handle;
} except(ExSystemExceptionFilter()) {
}
}
}
// If an exception occurs during the probe of the output handle address,
// then always handle the exception and return the exception code as the status value.
} except(ExSystemExceptionFilter()) {
return GetExceptionCode();
}
return Status;// Return service status.
}
NTSTATUS NtOpenEvent (OUT PHANDLE EventHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes)
/*++
Routine Description:
This function opens a handle to an event object with the specified desired access.
Arguments:
EventHandle - Supplies a pointer to a variable that will receive the event object handle.
DesiredAccess - Supplies the desired types of access for the event object.
ObjectAttributes - Supplies a pointer to an object attributes structure.
Return Value:
TBS
--*/
{
HANDLE Handle;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
// Establish an exception handler, probe the output handle address, and
// attempt to open the event object. If the probe fails, then return the
// exception code as the service status. Otherwise return the status value returned by the object open routine.
try {
// Get previous processor mode and probe output handle address if necessary.
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
ProbeForWriteHandle(EventHandle);
}
// Open handle to the event object with the specified desired access.
Status = ObOpenObjectByName(ObjectAttributes,
ExEventObjectType,
PreviousMode,
NULL,
DesiredAccess,
NULL,
&Handle);
// If the open was successful, then attempt to write the event object
// handle value. If the write attempt fails, then do not report an
// error. When the caller attempts to access the handle value, an access violation will occur.
if (NT_SUCCESS(Status)) {
try {
*EventHandle = Handle;
} except(ExSystemExceptionFilter()) {
}
}
// If an exception occurs during the probe of the output event handle,
// then always handle the exception and return the exception code as the status value.
} except(ExSystemExceptionFilter()) {
return GetExceptionCode();
}
return Status;// Return service status.
}
NTSTATUS NtPulseEvent (IN HANDLE EventHandle, OUT PLONG PreviousState OPTIONAL)
/*++
Routine Description:
This function sets an event object to a Signaled state, attempts to
satisfy as many waits as possible, and then resets the state of the event object to Not-Signaled.
Arguments:
EventHandle - Supplies a handle to an event object.
PreviousState - Supplies an optional pointer to a variable that will receive the previous state of the event object.
Return Value:
TBS
--*/
{
PVOID Event;
KPROCESSOR_MODE PreviousMode;
LONG State;
NTSTATUS Status;
// Establish an exception handler, probe the previous state address if
// specified, reference the event object, and pulse the event object. If
// the probe fails, then return the exception code as the service status.
// Otherwise return the status value returned by the reference object by handle routine.
try {
// Get previous processor mode and probe previous state address if necessary.
PreviousMode = KeGetPreviousMode();
if ((PreviousMode != KernelMode) && (ARGUMENT_PRESENT(PreviousState))) {
ProbeForWriteLong(PreviousState);
}
// Reference event object by handle.
Status = ObReferenceObjectByHandle(EventHandle, EVENT_MODIFY_STATE, ExEventObjectType, PreviousMode, &Event, NULL);
// If the reference was successful, then pulse the event object,
// dereference event object, and write the previous state value if
// specified. If the write of the previous state fails, then do not
// report an error. When the caller attempts to access the previous state value, an access violation will occur.
if (NT_SUCCESS(Status)) {
State = KePulseEvent((PKEVENT)Event, ExpEventBoost, FALSE);
ObDereferenceObject(Event);
if (ARGUMENT_PRESENT(PreviousState)) {
try {
*PreviousState = State;
} except(ExSystemExceptionFilter()) {
}
}
}
// If an exception occurs during the probe of the previous state, then
// always handle the exception and return the exception code as the status value.
} except(ExSystemExceptionFilter()) {
return GetExceptionCode();
}
return Status;// Return service status.
}
NTSTATUS NtQueryEvent (
IN HANDLE EventHandle,
IN EVENT_INFORMATION_CLASS EventInformationClass,
OUT PVOID EventInformation,
IN ULONG EventInformationLength,
OUT PULONG ReturnLength OPTIONAL
)
/*++
Routine Description:
This function queries the state of an event object and returns the requested information in the specified record structure.
Arguments:
EventHandle - Supplies a handle to an event object.
EventInformationClass - Supplies the class of information being requested.
EventInformation - Supplies a pointer to a record that is to receive the requested information.
EventInformationLength - Supplies the length of the record that is to receive the requested information.
ReturnLength - Supplies an optional pointer to a variable that is to receive the actual length of information that is returned.
Return Value:
TBS
--*/
{
PKEVENT Event;
KPROCESSOR_MODE PreviousMode;
LONG State;
NTSTATUS Status;
EVENT_TYPE EventType;
// Check argument validity.
if (EventInformationClass != EventBasicInformation) {
return STATUS_INVALID_INFO_CLASS;
}
if (EventInformationLength != sizeof(EVENT_BASIC_INFORMATION)) {
return STATUS_INFO_LENGTH_MISMATCH;
}
// Establish an exception handler, probe the output arguments, reference
// the event object, and return the specified information. If the probe
// fails, then return the exception code as the service status. Otherwise
// return the status value returned by the reference object by handle routine.
try {
// Get previous processor mode and probe output arguments if necessary.
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
ProbeForWrite(EventInformation, sizeof(EVENT_BASIC_INFORMATION), sizeof(ULONG));
if (ARGUMENT_PRESENT(ReturnLength)) {
ProbeForWriteUlong(ReturnLength);
}
}
// Reference event object by handle.
Status = ObReferenceObjectByHandle(EventHandle, EVENT_QUERY_STATE, ExEventObjectType, PreviousMode, (PVOID *)&Event, NULL);
// If the reference was successful, then read the current state of
// the event object, deference event object, fill in the information
// structure, and return the length of the information structure if
// specified. If the write of the event information or the return
// length fails, then do not report an error. When the caller accesses
// the information structure or length an access violation will occur.
if (NT_SUCCESS(Status)) {
State = KeReadStateEvent(Event);
EventType = Event->Header.Type;
ObDereferenceObject(Event);
try {
((PEVENT_BASIC_INFORMATION)EventInformation)->EventType = EventType;
((PEVENT_BASIC_INFORMATION)EventInformation)->EventState = State;
if (ARGUMENT_PRESENT(ReturnLength)) {
*ReturnLength = sizeof(EVENT_BASIC_INFORMATION);
}
} except(ExSystemExceptionFilter()) {
}
}
// If an exception occurs during the probe of the output arguments, then
// always handle the exception and return the exception code as the status value.
} except(ExSystemExceptionFilter()) {
return GetExceptionCode();
}
return Status;// Return service status.
}
NTSTATUS NtResetEvent (IN HANDLE EventHandle, OUT PLONG PreviousState OPTIONAL)
/*++
Routine Description:
This function sets an event object to a Not-Signaled state.
Arguments:
EventHandle - Supplies a handle to an event object.
PreviousState - Supplies an optional pointer to a variable that will receive the previous state of the event object.
Return Value:
TBS
--*/
{
PVOID Event;
KPROCESSOR_MODE PreviousMode;
LONG State;
NTSTATUS Status;
// Establish an exception handler, probe the previous state address if
// specified, reference the event object, and reset the event object. If
// the probe fails, then return the exception code as the service status.
// Otherwise return the status value returned by the reference object by handle routine.
try {
// Get previous processor mode and probe previous state address if necessary.
PreviousMode = KeGetPreviousMode();
if ((PreviousMode != KernelMode) && (ARGUMENT_PRESENT(PreviousState))) {
ProbeForWriteLong(PreviousState);
}
// Reference event object by handle.
Status = ObReferenceObjectByHandle(EventHandle, EVENT_MODIFY_STATE, ExEventObjectType, PreviousMode, &Event, NULL);
// If the reference was successful, then set the state of the event
// object to Not-Signaled, dereference event object, and write the
// previous state value if specified. If the write of the previous
// state fails, then do not report an error. When the caller attempts
// to access the previous state value, an access violation will occur.
if (NT_SUCCESS(Status)) {
State = KeResetEvent((PKEVENT)Event);
ObDereferenceObject(Event);
if (ARGUMENT_PRESENT(PreviousState)) {
try {
*PreviousState = State;
} except(ExSystemExceptionFilter()) {
}
}
}
// If an exception occurs during the probe of the previous state, then
// always handle the exception and return the exception code as the status value.
} except(ExSystemExceptionFilter()) {
return GetExceptionCode();
}
return Status;// Return service status.
}
NTSTATUS NtSetEvent (IN HANDLE EventHandle, OUT PLONG PreviousState OPTIONAL)
/*++
Routine Description:
This function sets an event object to a Signaled state and attempts to satisfy as many waits as possible.
Arguments:
EventHandle - Supplies a handle to an event object.
PreviousState - Supplies an optional pointer to a variable that will receive the previous state of the event object.
Return Value:
TBS
--*/
{
PVOID Event;
KPROCESSOR_MODE PreviousMode;
LONG State;
NTSTATUS Status;
#if DBG
// Sneaky trick here to catch sleazy apps (csrss) that erroneously call
// NtSetEvent on an event that happens to be somebody else's
// critical section. Only allow setting a protected handle if the low bit of PreviousState is set.
OBJECT_HANDLE_INFORMATION HandleInfo;
#endif
// Establish an exception handler, probe the previous state address if
// specified, reference the event object, and set the event object. If
// the probe fails, then return the exception code as the service status.
// Otherwise return the status value returned by the reference object by handle routine.
try {
// Get previous processor mode and probe previous state address if necessary.
PreviousMode = KeGetPreviousMode();
#if DBG
if ((PreviousMode != KernelMode) && (ARGUMENT_PRESENT(PreviousState)) && (PreviousState != (PLONG)1)) {
ProbeForWriteLong(PreviousState);
}
#else
if ((PreviousMode != KernelMode) && (ARGUMENT_PRESENT(PreviousState))) {
ProbeForWriteLong(PreviousState);
}
#endif
// Reference event object by handle.
#if DBG
Status = ObReferenceObjectByHandle(EventHandle, EVENT_MODIFY_STATE, ExEventObjectType, PreviousMode, &Event, &HandleInfo);
if (NT_SUCCESS(Status)) {
if ((HandleInfo.HandleAttributes & 1) && (PreviousState != (PLONG)1)) {
#if 0
// This is a protected handle. If the low bit of PreviousState is NOT set, break into the debugger
DbgPrint("NtSetEvent: Illegal call to NtSetEvent on a protected handle\n");
DbgBreakPoint();
PreviousState = NULL;
#endif
}
} else {
if ((KeGetPreviousMode() != KernelMode) &&
(EventHandle != NULL) &&
((NtGlobalFlag & FLG_ENABLE_CLOSE_EXCEPTIONS) || (PsGetCurrentProcess()->DebugPort != NULL)))
{
Status = KeRaiseUserException(STATUS_INVALID_HANDLE);
}
}
#else
Status = ObReferenceObjectByHandle(EventHandle, EVENT_MODIFY_STATE, ExEventObjectType, PreviousMode, &Event, NULL);
#endif
// If the reference was successful, then set the event object to the
// Signaled state, dereference event object, and write the previous state value if specified.
// If the write of the previous state fails, then do not report an error.
// When the caller attempts to access the previous state value, an access violation will occur.
if (NT_SUCCESS(Status)) {
State = KeSetEvent((PKEVENT)Event, ExpEventBoost, FALSE);
ObDereferenceObject(Event);
if (ARGUMENT_PRESENT(PreviousState)) {
try {
*PreviousState = State;
} except(ExSystemExceptionFilter()) {
}
}
}
// If an exception occurs during the probe of the previous state, then
// always handle the exception and return the exception code as the status value.
} except(ExSystemExceptionFilter()) {
return GetExceptionCode();
}
return Status;// Return service status.
}