/* * @implemented */ NTSTATUS NTAPI NtReplyPort(IN HANDLE PortHandle, IN PPORT_MESSAGE ReplyMessage) { PLPCP_PORT_OBJECT Port; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); NTSTATUS Status; PLPCP_MESSAGE Message; PETHREAD Thread = PsGetCurrentThread(), WakeupThread; //PORT_MESSAGE CapturedReplyMessage; PAGED_CODE(); LPCTRACE(LPC_REPLY_DEBUG, "Handle: %p. Message: %p.\n", PortHandle, ReplyMessage); if (KeGetPreviousMode() == UserMode) { _SEH2_TRY { ProbeForRead(ReplyMessage, sizeof(PORT_MESSAGE), sizeof(ULONG)); /*RtlCopyMemory(&CapturedReplyMessage, ReplyMessage, sizeof(PORT_MESSAGE)); ReplyMessage = &CapturedReplyMessage;*/ } _SEH2_EXCEPT(ExSystemExceptionFilter()) { DPRINT1("SEH crash [1]\n"); DbgBreakPoint(); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; }
/* * @implemented */ NTSTATUS NTAPI NtQueryDefaultUILanguage(OUT LANGID* LanguageId) { NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); /* Enter SEH for probing */ _SEH2_TRY { /* Check if we came from user mode */ if (KeGetPreviousMode() != KernelMode) { /* Probe the Language ID */ ProbeForWriteLangid(LanguageId); } /* Call the executive helper routine */ Status = ExpGetCurrentUserUILanguage(L"MultiUILanguageId", LanguageId); if (NT_SUCCESS(Status)) { /* Success, return the language */ *LanguageId = PsInstallUILanguageId; } } _SEH2_EXCEPT(ExSystemExceptionFilter()) { /* Get exception code */ Status = _SEH2_GetExceptionCode(); } _SEH2_END; /* Return status */ return Status; }
/* * @implemented */ NTSTATUS NTAPI NtQueryInstallUILanguage(OUT LANGID* LanguageId) { NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); /* Enter SEH for probing */ _SEH2_TRY { /* Check if we came from user mode */ if (KeGetPreviousMode() != KernelMode) { /* Probe the Language ID */ ProbeForWriteLangid(LanguageId); } /* Return it */ *LanguageId = PsInstallUILanguageId; } _SEH2_EXCEPT(ExSystemExceptionFilter()) { /* Get exception code */ Status = _SEH2_GetExceptionCode(); } _SEH2_END; /* Return status */ return Status; }
/* * @implemented */ NTSTATUS NTAPI NtReplyPort(IN HANDLE PortHandle, IN PPORT_MESSAGE ReplyMessage) { NTSTATUS Status; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PORT_MESSAGE CapturedReplyMessage; PLPCP_PORT_OBJECT Port; PLPCP_MESSAGE Message; PETHREAD Thread = PsGetCurrentThread(), WakeupThread; PAGED_CODE(); LPCTRACE(LPC_REPLY_DEBUG, "Handle: %p. Message: %p.\n", PortHandle, ReplyMessage); /* Check if the call comes from user mode */ if (PreviousMode != KernelMode) { _SEH2_TRY { ProbeForRead(ReplyMessage, sizeof(*ReplyMessage), sizeof(ULONG)); CapturedReplyMessage = *(volatile PORT_MESSAGE*)ReplyMessage; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; }
CCHAR PsGetCurrentThreadPreviousMode( VOID ) { return KeGetPreviousMode(); }
int ExSystemExceptionFilter( VOID ) { return( KeGetPreviousMode() != KernelMode ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ); }
NTKERNELAPI KPROCESSOR_MODE ExGetPreviousMode( VOID ) /*++ Routine Description: Returns previous mode. This routine is exported from the kernel so that drivers can call it, as they may have to do probing of embedded pointers to user structures on IOCTL calls that the I/O system can't probe for them on the FastIo path, which does not pass previous mode via the FastIo parameters. Arguments: None. Return Value: Either KernelMode or UserMode --*/ { return KeGetPreviousMode(); }
NTSTATUS NtAllocateLocallyUniqueId ( __out PLUID Luid ) /*++ Routine Description: This function returns an LUID value that is unique since the system was last rebooted. It is unique on the system it is generated on only (not network wide). There are no restrictions on who can allocate LUIDs. The LUID space is large enough that this will never present a problem. If one LUID is allocated every 100ns, they will not be exhausted for roughly 15,000 years (100ns * 2^63). Arguments: Luid - Supplies the address of a variable that will receive the new LUID. Return Value: STATUS_SUCCESS is returned if the service is successfully executed. STATUS_ACCESS_VIOLATION is returned if the output parameter for the LUID cannot be written. --*/ { KPROCESSOR_MODE PreviousMode; // // Get previous processor mode and probe argument if necessary. // try { PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { ProbeForWriteSmallStructure((PVOID)Luid, sizeof(LUID), sizeof(ULONG)); } // // Allocate and store a locally unique Id. // ExAllocateLocallyUniqueId(Luid); } except (ExSystemExceptionFilter()) { return GetExceptionCode(); } return STATUS_SUCCESS; }
NTSTATUS NTAPI NtQueryInformationPort( IN HANDLE PortHandle OPTIONAL, IN PORT_INFORMATION_CLASS PortInformationClass, OUT PVOID PortInformation, IN ULONG Length, OUT PULONG ReturnLength OPTIONAL ) { KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PLPCP_PORT_OBJECT PortObject; PAGED_CODE(); // // Get previous processor mode and probe output argument if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { try { ProbeForWrite( PortInformation, Length, sizeof( ULONG ) ); if (ARGUMENT_PRESENT( ReturnLength )) { ProbeForWriteUlong( ReturnLength ); } } except( EXCEPTION_EXECUTE_HANDLER ) { return( GetExceptionCode() ); } } if (ARGUMENT_PRESENT( PortHandle )) { Status = ObReferenceObjectByHandle( PortHandle, GENERIC_READ, LpcPortObjectType, PreviousMode, &PortObject, NULL ); if (!NT_SUCCESS( Status )) { return( Status ); } ObDereferenceObject( PortObject ); return STATUS_SUCCESS; } else { return STATUS_INVALID_INFO_CLASS; } }
/* * @implemented */ NTSTATUS NTAPI NtRequestWaitReplyPort(IN HANDLE PortHandle, IN PPORT_MESSAGE LpcRequest, IN OUT PPORT_MESSAGE LpcReply) { PORT_MESSAGE LocalLpcRequest; ULONG NumberOfDataEntries; PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); NTSTATUS Status; PLPCP_MESSAGE Message; PETHREAD Thread = PsGetCurrentThread(); BOOLEAN Callback; PKSEMAPHORE Semaphore; ULONG MessageType; PLPCP_DATA_INFO DataInfo; PAGED_CODE(); LPCTRACE(LPC_SEND_DEBUG, "Handle: %p. Messages: %p/%p. Type: %lx\n", PortHandle, LpcRequest, LpcReply, LpcpGetMessageType(LpcRequest)); /* Check if the thread is dying */ if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING; /* Check for user mode access */ if (PreviousMode != KernelMode) { _SEH2_TRY { /* Probe the full request message and copy the base structure */ ProbeForRead(LpcRequest, sizeof(*LpcRequest), sizeof(ULONG)); ProbeForRead(LpcRequest, LpcRequest->u1.s1.TotalLength, sizeof(ULONG)); LocalLpcRequest = *LpcRequest; /* Probe the reply message for write */ ProbeForWrite(LpcReply, sizeof(*LpcReply), sizeof(ULONG)); /* Make sure the data entries in the request message are valid */ Status = LpcpVerifyMessageDataInfo(LpcRequest, &NumberOfDataEntries); if (!NT_SUCCESS(Status)) { DPRINT1("LpcpVerifyMessageDataInfo failed\n"); return Status; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPRINT1("Got exception\n"); return _SEH2_GetExceptionCode(); } _SEH2_END; }
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 NtAlertThread( IN HANDLE ThreadHandle ) /*++ Routine Description: This function alerts the target thread using the previous mode as the mode of the alert. Arguments: ThreadHandle - Supplies an open handle to the thread to be alerted Return Value: TBD --*/ { PETHREAD Thread; NTSTATUS st; KPROCESSOR_MODE Mode; PAGED_CODE(); Mode = KeGetPreviousMode(); st = ObReferenceObjectByHandle( ThreadHandle, THREAD_ALERT, PsThreadType, Mode, (PVOID *)&Thread, NULL ); if ( !NT_SUCCESS(st) ) { return st; } (VOID) KeAlertThread(&Thread->Tcb,Mode); ObDereferenceObject(Thread); return STATUS_SUCCESS; }
NTSTATUS NTAPI KiRaiseException(IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT Context, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame, IN BOOLEAN SearchFrames) { KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); CONTEXT LocalContext; EXCEPTION_RECORD LocalExceptionRecord; ULONG ParameterCount, Size; /* Check if we need to probe */ if (PreviousMode != KernelMode) { /* Set up SEH */ _SEH2_TRY { /* Probe the context */ ProbeForRead(Context, sizeof(CONTEXT), sizeof(ULONG)); /* Probe the Exception Record */ ProbeForRead(ExceptionRecord, FIELD_OFFSET(EXCEPTION_RECORD, NumberParameters) + sizeof(ULONG), sizeof(ULONG)); /* Validate the maximum parameters */ if ((ParameterCount = ExceptionRecord->NumberParameters) > EXCEPTION_MAXIMUM_PARAMETERS) { /* Too large */ _SEH2_YIELD(return STATUS_INVALID_PARAMETER); } /* Probe the entire parameters now*/ Size = (sizeof(EXCEPTION_RECORD) - ((EXCEPTION_MAXIMUM_PARAMETERS - ParameterCount) * sizeof(ULONG))); ProbeForRead(ExceptionRecord, Size, sizeof(ULONG)); /* Now make copies in the stack */ RtlCopyMemory(&LocalContext, Context, sizeof(CONTEXT)); RtlCopyMemory(&LocalExceptionRecord, ExceptionRecord, Size); Context = &LocalContext; ExceptionRecord = &LocalExceptionRecord; /* Update the parameter count */ ExceptionRecord->NumberParameters = ParameterCount; }
NTSTATUS NTAPI KiContinue(IN PCONTEXT Context, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame) { NTSTATUS Status = STATUS_SUCCESS; KIRQL OldIrql = APC_LEVEL; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); /* Raise to APC_LEVEL, only if needed */ if (KeGetCurrentIrql() < APC_LEVEL) KeRaiseIrql(APC_LEVEL, &OldIrql); /* Set up SEH to validate the context */ _SEH2_TRY { /* Check the previous mode */ if (PreviousMode != KernelMode) { /* Validate from user-mode */ KiContinuePreviousModeUser(Context, ExceptionFrame, TrapFrame); } else { /* Convert the context into Exception/Trap Frames */ KeContextToTrapFrame(Context, ExceptionFrame, TrapFrame, Context->ContextFlags, KernelMode); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Save the exception code */ Status = _SEH2_GetExceptionCode(); } _SEH2_END; /* Lower the IRQL if needed */ if (OldIrql < APC_LEVEL) KeLowerIrql(OldIrql); /* Return status */ return Status; }
NTSTATUS NTAPI NtQueryDefaultLocale(IN BOOLEAN UserProfile, OUT PLCID DefaultLocaleId) { NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); /* Enter SEH for probing */ _SEH2_TRY { /* Check if we came from user mode */ if (KeGetPreviousMode() != KernelMode) { /* Probe the language ID */ ProbeForWriteLangid(DefaultLocaleId); } /* Check if we have a user profile */ if (UserProfile) { /* Return session wide thread locale */ *DefaultLocaleId = MmGetSessionLocaleId(); } else { /* Return system locale */ *DefaultLocaleId = PsDefaultSystemLocaleId; } } _SEH2_EXCEPT(ExSystemExceptionFilter()) { /* Get exception code */ Status = _SEH2_GetExceptionCode(); } _SEH2_END; /* Return status */ return Status; }
NTSTATUS NTAPI NtRegisterThreadTerminatePort(IN HANDLE PortHandle) { NTSTATUS Status; PTERMINATION_PORT TerminationPort; PVOID TerminationLpcPort; PETHREAD Thread; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "PortHandle: %p\n", PortHandle); /* Get the Port */ Status = ObReferenceObjectByHandle(PortHandle, PORT_ALL_ACCESS, LpcPortObjectType, KeGetPreviousMode(), &TerminationLpcPort, NULL); if (!NT_SUCCESS(Status)) return(Status); /* Allocate the Port and make sure it suceeded */ TerminationPort = ExAllocatePoolWithTag(NonPagedPool, sizeof(TERMINATION_PORT), '=TsP'); if(TerminationPort) { /* Associate the Port */ Thread = PsGetCurrentThread(); TerminationPort->Port = TerminationLpcPort; TerminationPort->Next = Thread->TerminationPort; Thread->TerminationPort = TerminationPort; /* Return success */ return STATUS_SUCCESS; } /* Dereference and Fail */ ObDereferenceObject(TerminationLpcPort); return STATUS_INSUFFICIENT_RESOURCES; }
NTSTATUS NtTestAlert( VOID ) /*++ Routine Description: This function tests the alert flag inside the current thread. If an alert is pending for the previous mode, then the alerted status is returned, pending APC's may also be delivered at this time. Arguments: None Return Value: STATUS_ALERTED - An alert was pending for the current thread at the time this function was called. STATUS_SUCCESS - No alert was pending for this thread. --*/ { PAGED_CODE(); if ( KeTestAlertThread(KeGetPreviousMode()) ) { return STATUS_ALERTED; } else { return STATUS_SUCCESS; } }
NTSYSAPI NTSTATUS NTAPI NtQueueApcThread( IN HANDLE ThreadHandle, IN PPS_APC_ROUTINE ApcRoutine, IN PVOID ApcArgument1, IN PVOID ApcArgument2, IN PVOID ApcArgument3 ) /*++ Routine Description: This function is used to queue a user-mode APC to the specified thread. The APC will fire when the specified thread does an alertable wait Arguments: ThreadHandle - Supplies a handle to a thread object. The caller must have THREAD_SET_CONTEXT access to the thread. ApcRoutine - Supplies the address of the APC routine to execute when the APC fires. ApcArgument1 - Supplies the first PVOID passed to the APC ApcArgument2 - Supplies the second PVOID passed to the APC ApcArgument3 - Supplies the third PVOID passed to the APC Return Value: Returns an NT Status code indicating success or failure of the API --*/ { PETHREAD Thread; NTSTATUS st; KPROCESSOR_MODE Mode; KIRQL Irql; PKAPC Apc; PAGED_CODE(); Mode = KeGetPreviousMode(); st = ObReferenceObjectByHandle( ThreadHandle, THREAD_SET_CONTEXT, PsThreadType, Mode, (PVOID *)&Thread, NULL ); if ( NT_SUCCESS(st) ) { st = STATUS_SUCCESS; if ( IS_SYSTEM_THREAD(Thread) ) { st = STATUS_INVALID_HANDLE; } else { Apc = ExAllocatePoolWithQuotaTag( (NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE), sizeof(*Apc), 'pasP' ); if ( !Apc ) { st = STATUS_NO_MEMORY; } else { KeInitializeApc( Apc, &Thread->Tcb, OriginalApcEnvironment, PspQueueApcSpecialApc, NULL, (PKNORMAL_ROUTINE)ApcRoutine, UserMode, ApcArgument1 ); if ( !KeInsertQueueApc(Apc,ApcArgument2,ApcArgument3,0) ) { ExFreePool(Apc); st = STATUS_UNSUCCESSFUL; } } } ObDereferenceObject(Thread); } return st; }
NTSTATUS NtImpersonateThread( __in HANDLE ServerThreadHandle, __in HANDLE ClientThreadHandle, __in PSECURITY_QUALITY_OF_SERVICE SecurityQos ) /*++ Routine Description: This routine is used to cause the server thread to impersonate the client thread. The impersonation is done according to the specified quality of service parameters. Arguments: ServerThreadHandle - Is a handle to the server thread (the impersonator, or doing the impersonation). This handle must be open for THREAD_IMPERSONATE access. ClientThreadHandle - Is a handle to the Client thread (the impersonatee, or one being impersonated). This handle must be open for THREAD_DIRECT_IMPERSONATION access. SecurityQos - A pointer to security quality of service information indicating what form of impersonation is to be performed. Return Value: STATUS_SUCCESS - Indicates the call completed successfully. --*/ { KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PETHREAD ClientThread, ServerThread; SECURITY_QUALITY_OF_SERVICE CapturedQos; SECURITY_CLIENT_CONTEXT ClientSecurityContext; // // Get previous processor mode and probe and capture arguments if necessary // PreviousMode = KeGetPreviousMode(); try { if (PreviousMode != KernelMode) { ProbeForReadSmallStructure (SecurityQos, sizeof (SECURITY_QUALITY_OF_SERVICE), sizeof (ULONG)); } CapturedQos = *SecurityQos; } except (ExSystemExceptionFilter ()) { return GetExceptionCode (); } // // Reference the client thread, checking for appropriate access. // Status = ObReferenceObjectByHandle (ClientThreadHandle, // Handle THREAD_DIRECT_IMPERSONATION, // DesiredAccess PsThreadType, // ObjectType PreviousMode, // AccessMode &ClientThread, // Object NULL); // GrantedAccess if (!NT_SUCCESS (Status)) { return Status; } // // Reference the client thread, checking for appropriate access. // Status = ObReferenceObjectByHandle (ServerThreadHandle, // Handle THREAD_IMPERSONATE, // DesiredAccess PsThreadType, // ObjectType PreviousMode, // AccessMode &ServerThread, // Object NULL); // GrantedAccess if (!NT_SUCCESS (Status)) { ObDereferenceObject (ClientThread); return Status; } // // Get the client's security context // Status = SeCreateClientSecurity (ClientThread, // ClientThread &CapturedQos, // SecurityQos FALSE, // ServerIsRemote &ClientSecurityContext); // ClientContext if (!NT_SUCCESS (Status)) { ObDereferenceObject (ServerThread); ObDereferenceObject (ClientThread); return Status; } // // Impersonate the client // Status = SeImpersonateClientEx (&ClientSecurityContext, ServerThread); SeDeleteClientSecurity (&ClientSecurityContext); // // Done. // ObDereferenceObject (ServerThread); ObDereferenceObject (ClientThread); return Status ; }
/* * @implemented */ NTSTATUS NTAPI NtRequestWaitReplyPort(IN HANDLE PortHandle, IN PPORT_MESSAGE LpcRequest, IN OUT PPORT_MESSAGE LpcReply) { PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); NTSTATUS Status; PLPCP_MESSAGE Message; PETHREAD Thread = PsGetCurrentThread(); BOOLEAN Callback; PKSEMAPHORE Semaphore; ULONG MessageType; PAGED_CODE(); LPCTRACE(LPC_SEND_DEBUG, "Handle: %lx. Messages: %p/%p. Type: %lx\n", PortHandle, LpcRequest, LpcReply, LpcpGetMessageType(LpcRequest)); /* Check if the thread is dying */ if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING; /* Check if this is an LPC Request */ if (LpcpGetMessageType(LpcRequest) == LPC_REQUEST) { /* Then it's a callback */ Callback = TRUE; } else if (LpcpGetMessageType(LpcRequest)) { /* This is a not kernel-mode message */ return STATUS_INVALID_PARAMETER; } else { /* This is a kernel-mode message without a callback */ LpcRequest->u2.s2.Type |= LPC_REQUEST; Callback = FALSE; } /* Get the message type */ MessageType = LpcRequest->u2.s2.Type; /* Validate the length */ if (((ULONG)LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) > (ULONG)LpcRequest->u1.s1.TotalLength) { /* Fail */ return STATUS_INVALID_PARAMETER; } /* Reference the object */ Status = ObReferenceObjectByHandle(PortHandle, 0, LpcPortObjectType, PreviousMode, (PVOID*)&Port, NULL); if (!NT_SUCCESS(Status)) return Status; /* Validate the message length */ if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength)) { /* Fail */ ObDereferenceObject(Port); return STATUS_PORT_MESSAGE_TOO_LONG; } /* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ ObDereferenceObject(Port); return STATUS_NO_MEMORY; } /* Check if this is a callback */ if (Callback) { /* FIXME: TODO */ Semaphore = NULL; // we'd use the Thread Semaphore here ASSERT(FALSE); } else { /* No callback, just copy the message */ _SEH2_TRY { /* Copy it */ LpcpMoveMessage(&Message->Request, LpcRequest, LpcRequest + 1, MessageType, &Thread->Cid); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Fail */ LpcpFreeToPortZone(Message, 0); ObDereferenceObject(Port); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Right now clear the port context */ Message->PortContext = NULL; /* Check if this is a not connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* We want the connected port */ QueuePort = Port->ConnectedPort; if (!QueuePort) { /* We have no connected port, fail */ LpcpFreeToPortZone(Message, 3); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } /* This will be the rundown port */ ReplyPort = QueuePort; /* Check if this is a communication port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Copy the port context and use the connection port */ Message->PortContext = QueuePort->PortContext; ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, 3); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } } else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Use the connection port for anything but communication ports */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, 3); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } } /* Reference the connection port if it exists */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } else { /* Otherwise, for a connection port, use the same port object */ QueuePort = ReplyPort = Port; } /* No reply thread */ Message->RepliedToThread = NULL; Message->SenderPort = Port; /* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0; /* Set the message ID for our thread now */ Thread->LpcReplyMessageId = Message->Request.MessageId; Thread->LpcReplyMessage = NULL; /* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain); LpcpSetPortToThread(Thread, Port); /* Release the lock and get the semaphore we'll use later */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); Semaphore = QueuePort->MsgQueue.Semaphore; /* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); } } /* Now release the semaphore */ LpcpCompleteWait(Semaphore); KeLeaveCriticalRegion(); /* And let's wait for the reply */ LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode); /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Get the LPC Message and clear our thread's reply data */ Message = LpcpGetMessageFromThread(Thread); Thread->LpcReplyMessage = NULL; Thread->LpcReplyMessageId = 0; /* Check if we have anything on the reply chain*/ if (!IsListEmpty(&Thread->LpcReplyChain)) { /* Remove this thread and reinitialize the list */ RemoveEntryList(&Thread->LpcReplyChain); InitializeListHead(&Thread->LpcReplyChain); } /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); /* Check if we got a reply */ if (Status == STATUS_SUCCESS) { /* Check if we have a valid message */ if (Message) { LPCTRACE(LPC_SEND_DEBUG, "Reply Messages: %p/%p\n", &Message->Request, (&Message->Request) + 1); /* Move the message */ _SEH2_TRY { LpcpMoveMessage(LpcReply, &Message->Request, (&Message->Request) + 1, 0, NULL); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; /* Check if this is an LPC request with data information */ if ((LpcpGetMessageType(&Message->Request) == LPC_REQUEST) && (Message->Request.u2.s2.DataInfoOffset)) { /* Save the data information */ LpcpSaveDataInfoMessage(Port, Message, 0); } else { /* Otherwise, just free it */ LpcpFreeToPortZone(Message, 0); } } else { /* We don't have a reply */ Status = STATUS_LPC_REPLY_LOST; } }
NTSTATUS NtGetContextThread( IN HANDLE ThreadHandle, IN OUT PCONTEXT ThreadContext ) /*++ Routine Description: This function returns the usermode context of the specified thread. This function will fail if the specified thread is a system thread. It will return the wrong answer if the thread is a non-system thread that does not execute in user-mode. Arguments: ThreadHandle - Supplies an open handle to the thread object from which to retrieve context information. The handle must allow THREAD_GET_CONTEXT access to the thread. ThreadContext - Supplies the address of a buffer that will receive the context of the specified thread. Return Value: None. --*/ { ULONG Alignment; ULONG ContextFlags; GETSETCONTEXT ContextFrame; ULONG ContextLength; KIRQL Irql; KPROCESSOR_MODE Mode; NTSTATUS Status; PETHREAD Thread; PAGED_CODE(); // // Get previous mode and reference specified thread. // Mode = KeGetPreviousMode(); Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_GET_CONTEXT, PsThreadType, Mode, (PVOID *)&Thread, NULL); // // If the reference was successful, the check if the specified thread // is a system thread. // if (NT_SUCCESS(Status)) { // // If the thread is not a system thread, then attempt to get the // context of the thread. // if (IS_SYSTEM_THREAD(Thread) == FALSE) { // // Attempt to get the context of the specified thread. // try { // // Set the default alignment, capture the context flags, // and set the default size of the context record. // Alignment = CONTEXT_ALIGN; ContextFlags = ProbeAndReadUlong(&ThreadContext->ContextFlags); ContextLength = sizeof(CONTEXT); #if defined(_X86_) // // CONTEXT_EXTENDED_REGISTERS is SET, then we want sizeof(CONTEXT) set above // otherwise (not set) we only want the old part of the context record. // if ((ContextFlags & CONTEXT_EXTENDED_REGISTERS) != CONTEXT_EXTENDED_REGISTERS) { ContextLength = FIELD_OFFSET(CONTEXT, ExtendedRegisters); } #endif #if defined(_MIPS_) // // The following code is included for backward compatibility // with old code that does not understand extended context // records on MIPS systems. // if ((ContextFlags & CONTEXT_EXTENDED_INTEGER) != CONTEXT_EXTENDED_INTEGER) { Alignment = sizeof(ULONG); ContextLength = FIELD_OFFSET(CONTEXT, ContextFlags) + 4; } #endif if (Mode != KernelMode) { ProbeForWrite(ThreadContext, ContextLength, Alignment); } } except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); } // // If an exception did not occur during the probe of the thread // context, then get the context of the target thread. // if (NT_SUCCESS(Status)) { KeInitializeEvent(&ContextFrame.OperationComplete, NotificationEvent, FALSE); ContextFrame.Context.ContextFlags = ContextFlags; ContextFrame.Mode = Mode; if (Thread == PsGetCurrentThread()) { ContextFrame.Apc.SystemArgument1 = NULL; ContextFrame.Apc.SystemArgument2 = Thread; KeRaiseIrql(APC_LEVEL, &Irql); PspGetSetContextSpecialApc(&ContextFrame.Apc, NULL, NULL, &ContextFrame.Apc.SystemArgument1, &ContextFrame.Apc.SystemArgument2); KeLowerIrql(Irql); // // Move context to specfied context record. If an exception // occurs, then silently handle it and return success. // try { RtlMoveMemory(ThreadContext, &ContextFrame.Context, ContextLength); } except(EXCEPTION_EXECUTE_HANDLER) { } } else { KeInitializeApc(&ContextFrame.Apc, &Thread->Tcb, OriginalApcEnvironment, PspGetSetContextSpecialApc, NULL, NULL, KernelMode, NULL); if (!KeInsertQueueApc(&ContextFrame.Apc, NULL, Thread, 2)) { Status = STATUS_UNSUCCESSFUL; } else { KeWaitForSingleObject(&ContextFrame.OperationComplete, Executive, KernelMode, FALSE, NULL); // // Move context to specfied context record. If an // exception occurs, then silently handle it and // return success. // try { RtlMoveMemory(ThreadContext, &ContextFrame.Context, ContextLength); } except(EXCEPTION_EXECUTE_HANDLER) { } } } } } else {
/* * @implemented */ NTSTATUS NTAPI LpcRequestPort(IN PVOID PortObject, IN PPORT_MESSAGE LpcMessage) { PLPCP_PORT_OBJECT Port = PortObject, QueuePort, ConnectionPort = NULL; ULONG MessageType; PLPCP_MESSAGE Message; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); PAGED_CODE(); LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", Port, LpcMessage); /* Check if this is a non-datagram message */ if (LpcMessage->u2.s2.Type) { /* Get the message type */ MessageType = LpcpGetMessageType(LpcMessage); /* Validate it */ if ((MessageType < LPC_DATAGRAM) || (MessageType > LPC_CLIENT_DIED)) { /* Fail */ return STATUS_INVALID_PARAMETER; } /* Mark this as a kernel-mode message only if we really came from it */ if ((PreviousMode == KernelMode) && (LpcMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE)) { /* We did, this is a kernel mode message */ MessageType |= LPC_KERNELMODE_MESSAGE; } } else { /* This is a datagram */ MessageType = LPC_DATAGRAM; } /* Can't have data information on this type of call */ if (LpcMessage->u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER; /* Validate message sizes */ if (((ULONG)LpcMessage->u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)LpcMessage->u1.s1.TotalLength <= (ULONG)LpcMessage->u1.s1.DataLength)) { /* Fail */ return STATUS_PORT_MESSAGE_TOO_LONG; } /* Allocate a new message */ Message = LpcpAllocateFromPortZone(); if (!Message) return STATUS_NO_MEMORY; /* Clear the context */ Message->RepliedToThread = NULL; Message->PortContext = NULL; /* Copy the message */ LpcpMoveMessage(&Message->Request, LpcMessage, LpcMessage + 1, MessageType, &PsGetCurrentThread()->Cid); /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Check if this is anything but a connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* The queue port is the connected port */ QueuePort = Port->ConnectedPort; if (QueuePort) { /* Check if this is a client port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Then copy the context */ Message->PortContext = QueuePort->PortContext; ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, 3); return STATUS_PORT_DISCONNECTED; } } else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Any other kind of port, use the connection port */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, 3); return STATUS_PORT_DISCONNECTED; } } /* If we have a connection port, reference it */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } } else { /* For connection ports, use the port itself */ QueuePort = PortObject; } /* Make sure we have a port */ if (QueuePort) { /* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0; /* No Message ID for the thread */ PsGetCurrentThread()->LpcReplyMessageId = 0; /* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); /* Release the lock and release the semaphore */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); LpcpCompleteWait(QueuePort->MsgQueue.Semaphore); /* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); } /* We're done */ KeLeaveCriticalRegion(); if (ConnectionPort) ObDereferenceObject(ConnectionPort); LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message); return STATUS_SUCCESS; } /* If we got here, then free the message and fail */ LpcpFreeToPortZone(Message, 3); if (ConnectionPort) ObDereferenceObject(ConnectionPort); return STATUS_PORT_DISCONNECTED; }
/* * @implemented */ NTSTATUS NTAPI NtRequestPort(IN HANDLE PortHandle, IN PPORT_MESSAGE LpcRequest) { PLPCP_PORT_OBJECT Port, QueuePort, ConnectionPort = NULL; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); NTSTATUS Status; PLPCP_MESSAGE Message; PETHREAD Thread = PsGetCurrentThread(); PKSEMAPHORE Semaphore; ULONG MessageType; PAGED_CODE(); LPCTRACE(LPC_SEND_DEBUG, "Handle: %lx. Message: %p. Type: %lx\n", PortHandle, LpcRequest, LpcpGetMessageType(LpcRequest)); /* Get the message type */ MessageType = LpcRequest->u2.s2.Type | LPC_DATAGRAM; /* Can't have data information on this type of call */ if (LpcRequest->u2.s2.DataInfoOffset) return STATUS_INVALID_PARAMETER; /* Validate the length */ if (((ULONG)LpcRequest->u1.s1.DataLength + sizeof(PORT_MESSAGE)) > (ULONG)LpcRequest->u1.s1.TotalLength) { /* Fail */ return STATUS_INVALID_PARAMETER; } /* Reference the object */ Status = ObReferenceObjectByHandle(PortHandle, 0, LpcPortObjectType, PreviousMode, (PVOID*)&Port, NULL); if (!NT_SUCCESS(Status)) return Status; /* Validate the message length */ if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength)) { /* Fail */ ObDereferenceObject(Port); return STATUS_PORT_MESSAGE_TOO_LONG; } /* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ ObDereferenceObject(Port); return STATUS_NO_MEMORY; } /* No callback, just copy the message */ _SEH2_TRY { /* Copy it */ LpcpMoveMessage(&Message->Request, LpcRequest, LpcRequest + 1, MessageType, &Thread->Cid); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Fail */ LpcpFreeToPortZone(Message, 0); ObDereferenceObject(Port); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Right now clear the port context */ Message->PortContext = NULL; /* Check if this is a not connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* We want the connected port */ QueuePort = Port->ConnectedPort; if (!QueuePort) { /* We have no connected port, fail */ LpcpFreeToPortZone(Message, 3); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } /* Check if this is a communication port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Copy the port context and use the connection port */ Message->PortContext = QueuePort->PortContext; ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, 3); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } } else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Use the connection port for anything but communication ports */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, 3); ObDereferenceObject(Port); return STATUS_PORT_DISCONNECTED; } } /* Reference the connection port if it exists */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } else { /* Otherwise, for a connection port, use the same port object */ QueuePort = Port; } /* Reference QueuePort if we have it */ if (QueuePort && ObReferenceObjectSafe(QueuePort)) { /* Set sender's port */ Message->SenderPort = Port; /* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0; /* No Message ID for the thread */ PsGetCurrentThread()->LpcReplyMessageId = 0; /* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); /* Release the lock and get the semaphore we'll use later */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); /* Now release the semaphore */ Semaphore = QueuePort->MsgQueue.Semaphore; LpcpCompleteWait(Semaphore); /* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); } KeLeaveCriticalRegion(); /* Dereference objects */ if (ConnectionPort) ObDereferenceObject(ConnectionPort); ObDereferenceObject(QueuePort); ObDereferenceObject(Port); LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Message: %p\n", QueuePort, Message); return STATUS_SUCCESS; } Status = STATUS_PORT_DISCONNECTED; /* All done with a failure*/ LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Status: %p\n", Port, Status); /* The wait failed, free the message */ if (Message) LpcpFreeToPortZone(Message, 3); ObDereferenceObject(Port); if (ConnectionPort) ObDereferenceObject(ConnectionPort); return Status; }
NTSTATUS NtReplyPort ( IN HANDLE PortHandle, IN PPORT_MESSAGE ReplyMessage ) /*++ Routine Description: A client and server process can send a reply to a previous request message with the NtReplyPort service: The Type field of the message is set to LPC_REPLY by the service. If the MapInfoOffset field of the reply message is non-zero, then the PORT_MAP_INFORMATION structure it points to will be processed and the relevant pages in the caller's address space will be unmapped. The ClientId and MessageId fields of the ReplyMessage structure are used to identify the thread waiting for this reply. If the target thread is in fact waiting for this reply message, then the reply message is copied into the thread's message buffer and the thread's wait is satisfied. If the thread is not waiting for a reply or is waiting for a reply to some other MessageId, then the message is placed in the message queue of the port that is connected to the communication port specified by the PortHandle parameter and the Type field of the message is set to LPC_LOST_REPLY. Arguments: PortHandle - Specifies the handle of the communication port that the original message was received from. ReplyMessage - Specifies a pointer to the reply message to be sent. The ClientId and MessageId fields determine which thread will get the reply. Return Value: Status code that indicates whether or not the operation was successful. --*/ { KPROCESSOR_MODE PreviousMode; PLPCP_PORT_OBJECT PortObject; PORT_MESSAGE CapturedReplyMessage; NTSTATUS Status; PLPCP_MESSAGE Msg; PETHREAD CurrentThread; PETHREAD WakeupThread; PAGED_CODE(); CurrentThread = PsGetCurrentThread(); // // Get previous processor mode and probe output arguments if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { try { ProbeForRead( ReplyMessage, sizeof( *ReplyMessage ), sizeof( ULONG )); CapturedReplyMessage = *ReplyMessage; } except( EXCEPTION_EXECUTE_HANDLER ) { return GetExceptionCode(); } } else {
/* * @implemented */ NTSTATUS NTAPI LpcRequestWaitReplyPort(IN PVOID PortObject, IN PPORT_MESSAGE LpcRequest, OUT PPORT_MESSAGE LpcReply) { PLPCP_PORT_OBJECT Port, QueuePort, ReplyPort, ConnectionPort = NULL; KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); NTSTATUS Status = STATUS_SUCCESS; PLPCP_MESSAGE Message; PETHREAD Thread = PsGetCurrentThread(); BOOLEAN Callback = FALSE; PKSEMAPHORE Semaphore; USHORT MessageType; PAGED_CODE(); Port = (PLPCP_PORT_OBJECT)PortObject; LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Messages: %p/%p. Type: %lx\n", Port, LpcRequest, LpcReply, LpcpGetMessageType(LpcRequest)); /* Check if the thread is dying */ if (Thread->LpcExitThreadCalled) return STATUS_THREAD_IS_TERMINATING; /* Check if this is an LPC Request */ MessageType = LpcpGetMessageType(LpcRequest); switch (MessageType) { /* No type */ case 0: /* Assume LPC request */ MessageType = LPC_REQUEST; break; /* LPC request callback */ case LPC_REQUEST: /* This is a callback */ Callback = TRUE; break; /* Anything else */ case LPC_CLIENT_DIED: case LPC_PORT_CLOSED: case LPC_EXCEPTION: case LPC_DEBUG_EVENT: case LPC_ERROR_EVENT: /* Nothing to do */ break; default: /* Invalid message type */ return STATUS_INVALID_PARAMETER; } /* Set the request type */ LpcRequest->u2.s2.Type = MessageType; /* Validate the message length */ if (((ULONG)LpcRequest->u1.s1.TotalLength > Port->MaxMessageLength) || ((ULONG)LpcRequest->u1.s1.TotalLength <= (ULONG)LpcRequest->u1.s1.DataLength)) { /* Fail */ return STATUS_PORT_MESSAGE_TOO_LONG; } /* Allocate a message from the port zone */ Message = LpcpAllocateFromPortZone(); if (!Message) { /* Fail if we couldn't allocate a message */ return STATUS_NO_MEMORY; } /* Check if this is a callback */ if (Callback) { /* FIXME: TODO */ Semaphore = NULL; // we'd use the Thread Semaphore here ASSERT(FALSE); return STATUS_NOT_IMPLEMENTED; } else { /* No callback, just copy the message */ LpcpMoveMessage(&Message->Request, LpcRequest, LpcRequest + 1, 0, &Thread->Cid); /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Right now clear the port context */ Message->PortContext = NULL; /* Check if this is a not connection port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_CONNECTION_PORT) { /* We want the connected port */ QueuePort = Port->ConnectedPort; if (!QueuePort) { /* We have no connected port, fail */ LpcpFreeToPortZone(Message, 3); return STATUS_PORT_DISCONNECTED; } /* This will be the rundown port */ ReplyPort = QueuePort; /* Check if this is a communication port */ if ((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_CLIENT_PORT) { /* Copy the port context and use the connection port */ Message->PortContext = QueuePort->PortContext; ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, 3); return STATUS_PORT_DISCONNECTED; } } else if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) { /* Use the connection port for anything but communication ports */ ConnectionPort = QueuePort = Port->ConnectionPort; if (!ConnectionPort) { /* Fail */ LpcpFreeToPortZone(Message, 3); return STATUS_PORT_DISCONNECTED; } } /* Reference the connection port if it exists */ if (ConnectionPort) ObReferenceObject(ConnectionPort); } else { /* Otherwise, for a connection port, use the same port object */ QueuePort = ReplyPort = Port; } /* No reply thread */ Message->RepliedToThread = NULL; Message->SenderPort = Port; /* Generate the Message ID and set it */ Message->Request.MessageId = LpcpNextMessageId++; if (!LpcpNextMessageId) LpcpNextMessageId = 1; Message->Request.CallbackId = 0; /* Set the message ID for our thread now */ Thread->LpcReplyMessageId = Message->Request.MessageId; Thread->LpcReplyMessage = NULL; /* Insert the message in our chain */ InsertTailList(&QueuePort->MsgQueue.ReceiveHead, &Message->Entry); InsertTailList(&ReplyPort->LpcReplyChainHead, &Thread->LpcReplyChain); LpcpSetPortToThread(Thread, Port); /* Release the lock and get the semaphore we'll use later */ KeEnterCriticalRegion(); KeReleaseGuardedMutex(&LpcpLock); Semaphore = QueuePort->MsgQueue.Semaphore; /* If this is a waitable port, wake it up */ if (QueuePort->Flags & LPCP_WAITABLE_PORT) { /* Wake it */ KeSetEvent(&QueuePort->WaitEvent, IO_NO_INCREMENT, FALSE); } } /* Now release the semaphore */ LpcpCompleteWait(Semaphore); KeLeaveCriticalRegion(); /* And let's wait for the reply */ LpcpReplyWait(&Thread->LpcReplySemaphore, PreviousMode); /* Acquire the LPC lock */ KeAcquireGuardedMutex(&LpcpLock); /* Get the LPC Message and clear our thread's reply data */ Message = LpcpGetMessageFromThread(Thread); Thread->LpcReplyMessage = NULL; Thread->LpcReplyMessageId = 0; /* Check if we have anything on the reply chain*/ if (!IsListEmpty(&Thread->LpcReplyChain)) { /* Remove this thread and reinitialize the list */ RemoveEntryList(&Thread->LpcReplyChain); InitializeListHead(&Thread->LpcReplyChain); } /* Release the lock */ KeReleaseGuardedMutex(&LpcpLock); /* Check if we got a reply */ if (Status == STATUS_SUCCESS) { /* Check if we have a valid message */ if (Message) { LPCTRACE(LPC_SEND_DEBUG, "Reply Messages: %p/%p\n", &Message->Request, (&Message->Request) + 1); /* Move the message */ LpcpMoveMessage(LpcReply, &Message->Request, (&Message->Request) + 1, 0, NULL); /* Acquire the lock */ KeAcquireGuardedMutex(&LpcpLock); /* Check if we replied to a thread */ if (Message->RepliedToThread) { /* Dereference */ ObDereferenceObject(Message->RepliedToThread); Message->RepliedToThread = NULL; } /* Free the message */ LpcpFreeToPortZone(Message, 3); } else { /* We don't have a reply */ Status = STATUS_LPC_REPLY_LOST; } } else { /* The wait failed, free the message */ if (Message) LpcpFreeToPortZone(Message, 0); } /* All done */ LPCTRACE(LPC_SEND_DEBUG, "Port: %p. Status: %p\n", Port, Status); /* Dereference the connection port */ if (ConnectionPort) ObDereferenceObject(ConnectionPort); return Status; }
NTSTATUS KeUserModeCallback ( IN ULONG ApiNumber, IN PVOID InputBuffer, IN ULONG InputLength, OUT PVOID *OutputBuffer, IN PULONG OutputLength ) /*++ Routine Description: This function call out from kernel mode to a user mode function. Arguments: ApiNumber - Supplies the API number. InputBuffer - Supplies a pointer to a structure that is copied to the user stack. InputLength - Supplies the length of the input structure. Outputbuffer - Supplies a pointer to a variable that receives the address of the output buffer. Outputlength - Supplies a pointer to a variable that receives the length of the output buffer. Return Value: If the callout cannot be executed, then an error status is returned. Otherwise, the status returned by the callback function is returned. --*/ { PUCALLOUT_FRAME CalloutFrame; ULONGLONG OldStack; ULONGLONG NewStack; ULONGLONG OldStIFS; PKTRAP_FRAME TrapFrame; NTSTATUS Status; ULONG Length; SHORT BsFrameSize; USHORT TearPointOffset; ASSERT(KeGetPreviousMode() == UserMode); // // Get the user mode stack pointer and attempt to copy input buffer // to the user stack. // TrapFrame = KeGetCurrentThread()->TrapFrame; OldStack = TrapFrame->IntSp; OldStIFS = TrapFrame->StIFS; try { // // Compute new user mode stack address, probe for writability, // and copy the input buffer to the user stack. // // N.B. EM requires stacks to be 16-byte aligned, therefore // the input length must be rounded up to a 16-byte boundary. // Length = (InputLength + 16 - 1 + sizeof(UCALLOUT_FRAME) + STACK_SCRATCH_AREA) & ~(16 - 1); NewStack = OldStack - Length; CalloutFrame = (PUCALLOUT_FRAME)(NewStack + STACK_SCRATCH_AREA); ProbeForWrite((PVOID)NewStack, Length, sizeof(QUAD)); RtlCopyMemory(CalloutFrame + 1, InputBuffer, InputLength); // // Fill in the callout arguments. // CalloutFrame->Buffer = (PVOID)(CalloutFrame + 1); CalloutFrame->Length = InputLength; CalloutFrame->ApiNumber = ApiNumber; CalloutFrame->IntSp = OldStack; CalloutFrame->RsPFS = TrapFrame->StIFS; CalloutFrame->BrRp = TrapFrame->BrRp; if (PsGetCurrentProcess()->DebugPort != NULL) { KiFlushRse(); BsFrameSize = (SHORT)(TrapFrame->RsBSP - TrapFrame->RsBSPSTORE); if (BsFrameSize) { ULONGLONG TopBound, BottomBound; ULONGLONG UserRnats1, UserRnats2; ULONGLONG Mask; ULONGLONG CurrentRNAT = 0; SHORT RNatSaveIndex; // // Copy the dirty stacked registers back into the // user backing store // TearPointOffset = (USHORT) TrapFrame->RsBSPSTORE & 0x1F8; RtlCopyMemory((PVOID)(TrapFrame->RsBSPSTORE), (PVOID)(PCR->InitialBStore + TearPointOffset), BsFrameSize); TopBound = TrapFrame->RsBSP | RNAT_ALIGNMENT; BottomBound = TrapFrame->RsBSPSTORE | RNAT_ALIGNMENT; RNatSaveIndex = TearPointOffset >> 3; Mask = (((1ULL << (NAT_BITS_PER_RNAT_REG - RNatSaveIndex)) - 1) << RNatSaveIndex); UserRnats1 = TrapFrame->RsRNAT & ((1ULL << RNatSaveIndex) - 1); if (TopBound > BottomBound) { // // user dirty stacked GR span across at least one RNAT // boundary; need to deposit the valid RNAT bits from // the trap frame into the kernel backing store. Also, // the RNAT field in the trap frame has to be updated. // UserRnats2 = *(PULONGLONG)BottomBound & Mask; *(PULONGLONG)BottomBound = UserRnats1 | UserRnats2; TrapFrame->RsRNAT = CurrentRNAT; } else { // // user stacked register region does not span across an // RNAT boundary; combine the RNAT fields from both the // trap frame and the context frame. // UserRnats2 = CurrentRNAT & Mask; TrapFrame->RsRNAT = UserRnats1 | UserRnats2; } TrapFrame->RsBSPSTORE = TrapFrame->RsBSP; } } // // If an exception occurs during the probe of the user stack, then // always handle the exception and return the exception code as the // status value. // } except (EXCEPTION_EXECUTE_HANDLER) {
NTSTATUS NtSuspendThread( IN HANDLE ThreadHandle, OUT PULONG PreviousSuspendCount OPTIONAL ) /*++ Routine Description: This function suspends the target thread, and optionally returns the previous suspend count. Arguments: ThreadHandle - Supplies a handle to the thread object to suspend. PreviousSuspendCount - An optional parameter, that if specified points to a variable that receives the thread's previous suspend count. Return Value: return-value - Description of conditions needed to return value. - or - None. --*/ { PETHREAD Thread; NTSTATUS st; ULONG LocalPreviousSuspendCount; KPROCESSOR_MODE Mode; PAGED_CODE(); try { Mode = KeGetPreviousMode(); if ( Mode != KernelMode ) { if (ARGUMENT_PRESENT(PreviousSuspendCount)) { ProbeForWriteUlong(PreviousSuspendCount); } } } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } st = ObReferenceObjectByHandle( ThreadHandle, THREAD_SUSPEND_RESUME, PsThreadType, Mode, (PVOID *)&Thread, NULL ); if ( !NT_SUCCESS(st) ) { return st; } try { if ( Thread != PsGetCurrentThread() ) { if ( Thread->HasTerminated ) { ObDereferenceObject(Thread); return STATUS_THREAD_IS_TERMINATING; } LocalPreviousSuspendCount = (ULONG) KeSuspendThread(&Thread->Tcb); } else { LocalPreviousSuspendCount = (ULONG) KeSuspendThread(&Thread->Tcb); } ObDereferenceObject(Thread); if (ARGUMENT_PRESENT(PreviousSuspendCount)) *PreviousSuspendCount = LocalPreviousSuspendCount; } except (EXCEPTION_EXECUTE_HANDLER) { st = GetExceptionCode(); // // Either the suspend, or the store could cause an // exception. The store is a partial success, while the // suspend exception is an error // if ( st == STATUS_SUSPEND_COUNT_EXCEEDED ) { ObDereferenceObject(Thread); } else { st = STATUS_SUCCESS; } return st; } return STATUS_SUCCESS; }
NTSTATUS NtSignalAndWaitForSingleObject( IN HANDLE SignalHandle, IN HANDLE WaitHandle, IN BOOLEAN Alertable, IN PLARGE_INTEGER Timeout OPTIONAL ) /*++ Routine Description: This function atomically signals the specified signal object and then waits until the specified wait object attains a state of Signaled. An optional timeout can also be specified. If a timeout is not specified, then the wait will not be satisfied until the wait object attains a state of Signaled. If a timeout is specified, and the wait object has not attained a state of Signaled when the timeout expires, then the wait is automatically satisfied. If an explicit timeout value of zero is specified, then no wait will occur if the wait cannot be satisfied immediately. The wait can also be specified as alertable. Arguments: SignalHandle - supplies the handle of the signal object. WaitHandle - Supplies the handle for the wait object. Alertable - Supplies a boolean value that specifies whether the wait is alertable. Timeout - Supplies an pointer to an absolute or relative time over which the wait is to occur. Return Value: The wait completion status. A value of STATUS_TIMEOUT is returned if a timeout occurred. A value of STATUS_SUCCESS is returned if the specified object satisfied the wait. A value of STATUS_ALERTED is returned if the wait was aborted to deliver an alert to the current thread. A value of STATUS_USER_APC is returned if the wait was aborted to deliver a user APC to the current thread. --*/ { OBJECT_HANDLE_INFORMATION HandleInformation; KPROCESSOR_MODE PreviousMode; PVOID RealObject; PVOID SignalObject; POBJECT_HEADER SignalObjectHeader; NTSTATUS Status; LARGE_INTEGER TimeoutValue; PVOID WaitObject; POBJECT_HEADER WaitObjectHeader; // // Establish an exception handler and probe the specified timeout value // if necessary. If the probe fails, then return the exception code as // the service status. // PreviousMode = KeGetPreviousMode(); if ((ARGUMENT_PRESENT(Timeout)) && (PreviousMode != KernelMode)) { try { TimeoutValue = ProbeAndReadLargeInteger(Timeout); Timeout = &TimeoutValue; } except(EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } } // // Reference the signal object by handle. // Status = ObReferenceObjectByHandle(SignalHandle, 0, NULL, PreviousMode, &SignalObject, &HandleInformation); // // If the reference was successful, then reference the wait object by // handle. // if (NT_SUCCESS(Status)) { Status = ObReferenceObjectByHandle(WaitHandle, SYNCHRONIZE, NULL, PreviousMode, &WaitObject, NULL); // // If the reference was successful, then determine the real wait // object, check the signal object access, signal the signal object, // and wait for the real wait object. // if (NT_SUCCESS(Status)) { WaitObjectHeader = OBJECT_TO_OBJECT_HEADER(WaitObject); RealObject = WaitObjectHeader->Type->DefaultObject; if ((LONG)RealObject >= 0) { RealObject = (PVOID)((PCHAR)WaitObject + (ULONG)RealObject); } // // If the signal object is an event, then check for modify access // and set the event. Otherwise, if the signal object is a mutant, // then attempt to release ownership of the mutant. Otherwise, if // the object is a semaphore, then check for modify access and // release the semaphore. Otherwise, the signal objet is invalid. // SignalObjectHeader = OBJECT_TO_OBJECT_HEADER(SignalObject); Status = STATUS_ACCESS_DENIED; if (SignalObjectHeader->Type == ExEventObjectType) { // // Check for access to the specified event object, // if ((PreviousMode != KernelMode) && (SeComputeDeniedAccesses(HandleInformation.GrantedAccess, EVENT_MODIFY_STATE) != 0)) { goto WaitExit; } // // If the wait object is also an event, the wait is not // alertable, and no timeout was specified, then the event // pair path can be used. Otherwise, set the event and wait // atomically. // if ((WaitObjectHeader->Type == ExEventObjectType) && (Alertable == FALSE) && (Timeout == NULL)) { Status = KiSetServerWaitClientEvent((PKEVENT)SignalObject, (PKEVENT)RealObject, PreviousMode); goto WaitExit; } // // Set the specified event and wait atomically. // KeSetEvent((PKEVENT)SignalObject, EVENT_INCREMENT, TRUE); } else if (SignalObjectHeader->Type == ExMutantObjectType) { // // Release the specified mutant and wait atomically. // // N.B. The release will only be successful if the current // thread is the owner of the mutant. // try { KeReleaseMutant((PKMUTANT)SignalObject, MUTANT_INCREMENT, FALSE, TRUE); } except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); goto WaitExit; } } else if (SignalObjectHeader->Type == ExSemaphoreObjectType) { // // Check for access to the specified semaphore object, // if ((PreviousMode != KernelMode) && (SeComputeDeniedAccesses(HandleInformation.GrantedAccess, SEMAPHORE_MODIFY_STATE) != 0)) { goto WaitExit; } // // Release the specified semaphore and wait atomically. // try { // // If the wait object is also a semaphore, the wait is // not alertable, and no timeout was specified, then // the semaphore path can be used. Otherwise, release // the semaphore and wait atomically. // if ((WaitObjectHeader->Type == ExSemaphoreObjectType) && (Alertable == FALSE) && (Timeout == NULL)) { Status = KeReleaseWaitForSemaphore((PKSEMAPHORE)SignalObject, (PKSEMAPHORE)RealObject, UserRequest, PreviousMode); goto WaitExit; } // // Release the specified semaphore and wait atomically. // KeReleaseSemaphore((PKSEMAPHORE)SignalObject, SEMAPHORE_INCREMENT, 1, TRUE); } except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); goto WaitExit; } } else { Status = STATUS_OBJECT_TYPE_MISMATCH; goto WaitExit; } Status = KeWaitForSingleObject(RealObject, UserRequest, PreviousMode, Alertable, Timeout); WaitExit: ObDereferenceObject(WaitObject); }
NTSTATUS NTAPI NtTerminateThread(IN HANDLE ThreadHandle, IN NTSTATUS ExitStatus) { PETHREAD Thread; PETHREAD CurrentThread = PsGetCurrentThread(); NTSTATUS Status; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "ThreadHandle: %p ExitStatus: %d\n", ThreadHandle, ExitStatus); /* Handle the special NULL case */ if (!ThreadHandle) { /* Check if we're the only thread left */ if (PsGetCurrentProcess()->ActiveThreads == 1) { /* This is invalid */ return STATUS_CANT_TERMINATE_SELF; } /* Terminate us directly */ goto TerminateSelf; } else if (ThreadHandle == NtCurrentThread()) { TerminateSelf: /* Terminate this thread */ return PspTerminateThreadByPointer(CurrentThread, ExitStatus, TRUE); } /* We are terminating another thread, get the Thread Object */ Status = ObReferenceObjectByHandle(ThreadHandle, THREAD_TERMINATE, PsThreadType, KeGetPreviousMode(), (PVOID*)&Thread, NULL); if (!NT_SUCCESS(Status)) return Status; /* Check to see if we're running in the same thread */ if (Thread != CurrentThread) { /* Terminate it */ Status = PspTerminateThreadByPointer(Thread, ExitStatus, FALSE); /* Dereference the Thread and return */ ObDereferenceObject(Thread); } else { /* Dereference the thread and terminate ourselves */ ObDereferenceObject(Thread); goto TerminateSelf; } /* Return status */ return Status; }
/* * @implemented */ NTSTATUS NTAPI NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL, IN NTSTATUS ExitStatus) { NTSTATUS Status; PEPROCESS Process, CurrentProcess = PsGetCurrentProcess(); PETHREAD Thread, CurrentThread = PsGetCurrentThread(); BOOLEAN KillByHandle; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "ProcessHandle: %p ExitStatus: %d\n", ProcessHandle, ExitStatus); /* Were we passed a process handle? */ if (ProcessHandle) { /* Yes we were, use it */ KillByHandle = TRUE; } else { /* We weren't... we assume this is suicide */ KillByHandle = FALSE; ProcessHandle = NtCurrentProcess(); } /* Get the Process Object */ Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_TERMINATE, PsProcessType, KeGetPreviousMode(), (PVOID*)&Process, NULL); if (!NT_SUCCESS(Status)) return(Status); /* Check if this is a Critical Process, and Bugcheck */ if (Process->BreakOnTermination) { /* Break to debugger */ PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n", Process, Process->ImageFileName); } /* Lock the Process */ if (!ExAcquireRundownProtection(&Process->RundownProtect)) { /* Failed to lock, fail */ ObDereferenceObject(Process); return STATUS_PROCESS_IS_TERMINATING; } /* Set the delete flag, unless the process is comitting suicide */ if (KillByHandle) PspSetProcessFlag(Process, PSF_PROCESS_DELETE_BIT); /* Get the first thread */ Status = STATUS_NOTHING_TO_TERMINATE; Thread = PsGetNextProcessThread(Process, NULL); if (Thread) { /* We know we have at least a thread */ Status = STATUS_SUCCESS; /* Loop and kill the others */ do { /* Ensure it's not ours*/ if (Thread != CurrentThread) { /* Kill it */ PspTerminateThreadByPointer(Thread, ExitStatus, FALSE); } /* Move to the next thread */ Thread = PsGetNextProcessThread(Process, Thread); } while (Thread); } /* Unlock the process */ ExReleaseRundownProtection(&Process->RundownProtect); /* Check if we are killing ourselves */ if (Process == CurrentProcess) { /* Also make sure the caller gave us our handle */ if (KillByHandle) { /* Dereference the process */ ObDereferenceObject(Process); /* Terminate ourselves */ PspTerminateThreadByPointer(CurrentThread, ExitStatus, TRUE); } } else if (ExitStatus == DBG_TERMINATE_PROCESS) { /* Disable debugging on this process */ DbgkClearProcessDebugObject(Process, NULL); } /* Check if there was nothing to terminate, or if we have a Debug Port */ if ((Status == STATUS_NOTHING_TO_TERMINATE) || ((Process->DebugPort) && (KillByHandle))) { /* Clear the handle table */ ObClearProcessHandleTable(Process); /* Return status now */ Status = STATUS_SUCCESS; } /* Decrease the reference count we added */ ObDereferenceObject(Process); /* Return status */ return Status; }