/*++ 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. Revision History: --*/ #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. // #ifdef ALLOC_DATA_PRAGMA #pragma const_seg("INITCONST") #endif const GENERIC_MAPPING ExpEventMapping = { STANDARD_RIGHTS_READ | EVENT_QUERY_STATE, STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE, STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE, EVENT_ALL_ACCESS }; #ifdef ALLOC_DATA_PRAGMA #pragma const_seg() #endif #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) #pragma alloc_text(PAGE, NtSetEventBoostPriority) #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: NTSTATUS. --*/ { 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)) { PERFINFO_DECLARE_OBJECT(Event); KeClearEvent((PKEVENT)Event); ObDereferenceObject(Event); } // // Return service status. // return 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: NTSTATUS. --*/ { 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. // PreviousMode = KeGetPreviousMode(); // // Get previous processor mode and probe output handle address if // necessary. // if (PreviousMode != KernelMode) { try { ProbeForWriteHandle(EventHandle); } except(ExSystemExceptionFilter()) { return GetExceptionCode(); } } // // 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)) { if (PreviousMode != KernelMode) { try { *EventHandle = Handle; } except(ExSystemExceptionFilter()) { NOTHING; } } else { *EventHandle = Handle; } } } // // Return service status. // return 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: NTSTATUS. --*/ { 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. // // // Get previous processor mode and probe output handle address // if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { try { ProbeForWriteHandle(EventHandle); } except(ExSystemExceptionFilter()) { return GetExceptionCode(); } } // // 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)) { if (PreviousMode != KernelMode) { try { *EventHandle = Handle; } except(ExSystemExceptionFilter()) { NOTHING; } } else { *EventHandle = Handle; } } // // Return service status. // return 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: NTSTATUS. --*/ { 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. // // // Get previous processor mode and probe previous state address // if necessary. // PreviousMode = KeGetPreviousMode(); if (ARGUMENT_PRESENT(PreviousState) && (PreviousMode != KernelMode)) { try { ProbeForWriteLong(PreviousState); } except(ExSystemExceptionFilter()) { return GetExceptionCode(); } } // // 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)) { PERFINFO_DECLARE_OBJECT(Event); State = KePulseEvent((PKEVENT)Event, ExpEventBoost, FALSE); ObDereferenceObject(Event); if (ARGUMENT_PRESENT(PreviousState)) { if (PreviousMode != KernelMode) { try { *PreviousState = State; } except(ExSystemExceptionFilter()) { NOTHING; } } else { *PreviousState = State; } } } // // Return service status. // return 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: NTSTATUS. --*/ { 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. // // // Get previous processor mode and probe output arguments if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { try { ProbeForWrite(EventInformation, sizeof(EVENT_BASIC_INFORMATION), sizeof(ULONG)); if (ARGUMENT_PRESENT(ReturnLength)) { ProbeForWriteUlong(ReturnLength); } } except(ExSystemExceptionFilter()) { return GetExceptionCode(); } } // // 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); if (PreviousMode != KernelMode) { try { ((PEVENT_BASIC_INFORMATION)EventInformation)->EventType = EventType; ((PEVENT_BASIC_INFORMATION)EventInformation)->EventState = State; if (ARGUMENT_PRESENT(ReturnLength)) { *ReturnLength = sizeof(EVENT_BASIC_INFORMATION); } } except(ExSystemExceptionFilter()) { NOTHING; } } else { ((PEVENT_BASIC_INFORMATION)EventInformation)->EventType = EventType; ((PEVENT_BASIC_INFORMATION)EventInformation)->EventState = State; if (ARGUMENT_PRESENT(ReturnLength)) { *ReturnLength = sizeof(EVENT_BASIC_INFORMATION); } } } // // Return service status. // return 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: NTSTATUS. --*/ { 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. // // // Get previous processor mode and probe previous state address // if necessary. // PreviousMode = KeGetPreviousMode(); if ((ARGUMENT_PRESENT(PreviousState)) && (PreviousMode != KernelMode)) { try { ProbeForWriteLong(PreviousState); } except(ExSystemExceptionFilter()) { return GetExceptionCode(); } } // // 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)) { PERFINFO_DECLARE_OBJECT(Event); State = KeResetEvent((PKEVENT)Event); ObDereferenceObject(Event); if (ARGUMENT_PRESENT(PreviousState)) { if (PreviousMode != KernelMode) { try { *PreviousState = State; } except(ExSystemExceptionFilter()) { NOTHING; } } else { *PreviousState = State; } } } // // Return service status. // return 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: NTSTATUS. --*/ { 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. // // // Get previous processor mode and probe previous state address // if necessary. // PreviousMode = KeGetPreviousMode(); try { #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))) { return 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)) { PERFINFO_DECLARE_OBJECT(Event); 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 service status. // return Status; } NTSTATUS NtSetEventBoostPriority ( IN HANDLE EventHandle ) /*++ Routine Description: This function sets an event object to a Signaled state and performs a special priority boost operation. N.B. This service can only be performed on synchronization events. 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: NTSTATUS. --*/ { PKEVENT Event; NTSTATUS Status; // // Reference event object by handle. // Status = ObReferenceObjectByHandle(EventHandle, EVENT_MODIFY_STATE, ExEventObjectType, KeGetPreviousMode(), &Event, NULL); // // If the reference was successful, then check the type of event object. // If the event object is a notification event, then return an object // type mismatch status. Otherwise, set the specified event and boost // the unwaited thread priority as appropriate. // if (NT_SUCCESS(Status)) { if (Event->Header.Type == NotificationEvent) { Status = STATUS_OBJECT_TYPE_MISMATCH; } else { KeSetEventBoostPriority(Event, NULL); } ObDereferenceObject(Event); } // // Return service status. // return Status; }