Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2072 lines
58 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. lpcsend.c
  5. Abstract:
  6. Local Inter-Process Communication (LPC) request system services.
  7. Author:
  8. Steve Wood (stevewo) 15-May-1989
  9. Revision History:
  10. --*/
  11. #include "lpcp.h"
  12. NTSTATUS
  13. LpcpRequestWaitReplyPort (
  14. IN PVOID PortAddress,
  15. IN PPORT_MESSAGE RequestMessage,
  16. OUT PPORT_MESSAGE ReplyMessage,
  17. IN KPROCESSOR_MODE AccessMode
  18. );
  19. #ifdef ALLOC_PRAGMA
  20. #pragma alloc_text(PAGE,NtRequestPort)
  21. #pragma alloc_text(PAGE,NtRequestWaitReplyPort)
  22. #pragma alloc_text(PAGE,LpcRequestPort)
  23. #pragma alloc_text(PAGE,LpcRequestWaitReplyPort)
  24. #pragma alloc_text(PAGE,LpcpRequestWaitReplyPort)
  25. #pragma alloc_text(PAGE,LpcRequestWaitReplyPortEx)
  26. #endif
  27. NTSTATUS
  28. NtRequestPort (
  29. IN HANDLE PortHandle,
  30. IN PPORT_MESSAGE RequestMessage
  31. )
  32. /*++
  33. Routine Description:
  34. A client and server process send datagram messages using this procedure.
  35. The message pointed to by the RequestMessage parameter is placed in the
  36. message queue of the port connected to the communication port specified
  37. by the PortHandle parameter. This service returns an error if PortHandle
  38. is invalid or if the MessageId field of the PortMessage structure is
  39. non-zero.
  40. Arguments:
  41. PortHandle - Specifies the handle of the communication port to send
  42. the request message to.
  43. RequestMessage - Specifies a pointer to the request message. The Type
  44. field of the message is set to LPC_DATAGRAM by the service.
  45. Return Value:
  46. NTSTATUS - A status code that indicates whether or not the operation was
  47. successful.
  48. --*/
  49. {
  50. PETHREAD CurrentThread;
  51. PLPCP_PORT_OBJECT PortObject;
  52. PLPCP_PORT_OBJECT QueuePort;
  53. PORT_MESSAGE CapturedRequestMessage;
  54. ULONG MsgType;
  55. KPROCESSOR_MODE PreviousMode;
  56. NTSTATUS Status;
  57. PLPCP_MESSAGE Msg;
  58. PLPCP_PORT_OBJECT ConnectionPort = NULL;
  59. PAGED_CODE();
  60. //
  61. // Get previous processor mode and validate parameters
  62. //
  63. PreviousMode = KeGetPreviousMode();
  64. if (PreviousMode != KernelMode) {
  65. try {
  66. ProbeForReadSmallStructure( RequestMessage,
  67. sizeof( *RequestMessage ),
  68. PROBE_ALIGNMENT (PORT_MESSAGE));
  69. CapturedRequestMessage = *RequestMessage;
  70. CapturedRequestMessage.u2.s2.Type &= ~LPC_KERNELMODE_MESSAGE;
  71. } except( EXCEPTION_EXECUTE_HANDLER ) {
  72. return GetExceptionCode();
  73. }
  74. if (CapturedRequestMessage.u2.s2.Type != 0) {
  75. return STATUS_INVALID_PARAMETER;
  76. }
  77. } else {
  78. //
  79. // This is a kernel mode caller
  80. //
  81. CapturedRequestMessage = *RequestMessage;
  82. if ((CapturedRequestMessage.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != 0) {
  83. return STATUS_INVALID_PARAMETER;
  84. }
  85. }
  86. //
  87. // Make sure that the caller has given us some data to send
  88. //
  89. if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
  90. return STATUS_INVALID_PARAMETER;
  91. }
  92. //
  93. // Make sure DataLength is valid with respect to header size and total length
  94. //
  95. if ((((CLONG)CapturedRequestMessage.u1.s1.DataLength) + sizeof( PORT_MESSAGE )) >
  96. ((CLONG)CapturedRequestMessage.u1.s1.TotalLength)) {
  97. return STATUS_INVALID_PARAMETER;
  98. }
  99. //
  100. // Reference the communication port object by handle. Return status if
  101. // unsuccessful.
  102. //
  103. Status = LpcpReferencePortObject( PortHandle,
  104. 0,
  105. PreviousMode,
  106. &PortObject );
  107. if (!NT_SUCCESS( Status )) {
  108. return Status;
  109. }
  110. //
  111. // Validate the message length
  112. //
  113. if (((ULONG)CapturedRequestMessage.u1.s1.TotalLength > PortObject->MaxMessageLength) ||
  114. ((ULONG)CapturedRequestMessage.u1.s1.TotalLength <= (ULONG)CapturedRequestMessage.u1.s1.DataLength)) {
  115. ObDereferenceObject( PortObject );
  116. return STATUS_PORT_MESSAGE_TOO_LONG;
  117. }
  118. //
  119. // Determine which port to queue the message to and get client
  120. // port context if client sending to server. Also validate
  121. // length of message being sent.
  122. //
  123. //
  124. // Allocate and initialize the LPC message to send off
  125. //
  126. Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedRequestMessage.u1.s1.TotalLength );
  127. if (Msg == NULL) {
  128. ObDereferenceObject( PortObject );
  129. return STATUS_NO_MEMORY;
  130. }
  131. Msg->RepliedToThread = NULL;
  132. Msg->PortContext = NULL;
  133. MsgType = CapturedRequestMessage.u2.s2.Type | LPC_DATAGRAM;
  134. CurrentThread = PsGetCurrentThread();
  135. if (PreviousMode != KernelMode) {
  136. try {
  137. LpcpMoveMessage( &Msg->Request,
  138. &CapturedRequestMessage,
  139. (RequestMessage + 1),
  140. MsgType,
  141. &CurrentThread->Cid );
  142. } except( EXCEPTION_EXECUTE_HANDLER ) {
  143. Status = GetExceptionCode();
  144. LpcpFreeToPortZone( Msg, 0 );
  145. ObDereferenceObject( PortObject );
  146. return Status;
  147. }
  148. }
  149. else {
  150. LpcpMoveMessage( &Msg->Request,
  151. &CapturedRequestMessage,
  152. (RequestMessage + 1),
  153. MsgType,
  154. &CurrentThread->Cid );
  155. }
  156. //
  157. // Acquire the global Lpc mutex that guards the LpcReplyMessage
  158. // field of the thread and the request message queue. Stamp the
  159. // request message with a serial number, insert the message at
  160. // the tail of the request message queue and remember the address
  161. // of the message in the LpcReplyMessage field for the current thread.
  162. //
  163. LpcpAcquireLpcpLockByThread(CurrentThread);
  164. //
  165. // Based on what type of port the caller gave us we'll need to get
  166. // the port to actually queue the message off to.
  167. //
  168. if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
  169. //
  170. // The caller didn't give us a connection port so find the
  171. // connection port for this port. If it is null then we'll
  172. // fall through without sending a message
  173. //
  174. QueuePort = PortObject->ConnectedPort;
  175. //
  176. // Check if the queue port is in process of going away
  177. //
  178. if ( QueuePort != NULL) {
  179. //
  180. // If the port is a client communication port then give the
  181. // message the proper port context
  182. //
  183. if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
  184. Msg->PortContext = QueuePort->PortContext;
  185. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  186. if (ConnectionPort == NULL) {
  187. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  188. ObDereferenceObject( PortObject );
  189. return STATUS_PORT_DISCONNECTED;
  190. }
  191. //
  192. // In the case we don't have a CLIENT_COMMUNICATION_PORT nor
  193. // SERVER_COMMUNICATION_PORT we'll use the connection port
  194. // to queue messages.
  195. //
  196. } else if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
  197. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  198. if (ConnectionPort == NULL) {
  199. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  200. ObDereferenceObject( PortObject );
  201. return STATUS_PORT_DISCONNECTED;
  202. }
  203. }
  204. if (ConnectionPort) {
  205. ObReferenceObject( ConnectionPort );
  206. }
  207. }
  208. } else {
  209. //
  210. // The caller supplied a server connection port so that is the port
  211. // we queue off to
  212. //
  213. QueuePort = PortObject;
  214. }
  215. //
  216. // At this point we have an LPC message ready to send and if queue port is
  217. // not null then we have a port to actually send the message off to
  218. //
  219. if (QueuePort != NULL) {
  220. //
  221. // Reference the QueuePort to prevent this port evaporating under us
  222. // Test if the QueuePort isn't in the process of going away
  223. // (i.e. we need to have at least 2 references for this object when
  224. // ObReferenceObject returns). Note the LPC lock is still held.
  225. //
  226. if ( ObReferenceObjectSafe( QueuePort ) ) {
  227. //
  228. // Finish filling in the message and then insert it in the queue
  229. //
  230. Msg->Request.MessageId = LpcpGenerateMessageId();
  231. Msg->Request.CallbackId = 0;
  232. Msg->SenderPort = PortObject;
  233. CurrentThread->LpcReplyMessageId = 0;
  234. InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
  235. LpcpTrace(( "%s Send DataGram (%s) Msg %lx [%08x %08x %08x %08x] to Port %lx (%s)\n",
  236. PsGetCurrentProcess()->ImageFileName,
  237. LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
  238. Msg,
  239. *((PULONG)(Msg+1)+0),
  240. *((PULONG)(Msg+1)+1),
  241. *((PULONG)(Msg+1)+2),
  242. *((PULONG)(Msg+1)+3),
  243. QueuePort,
  244. LpcpGetCreatorName( QueuePort )));
  245. //
  246. // Release the mutex, increment the request message queue
  247. // semaphore by one for the newly inserted request message
  248. // then exit the critical region.
  249. //
  250. // Disable APCs to prevent this thread from being suspended
  251. // before being able to release the semaphore.
  252. //
  253. KeEnterCriticalRegionThread(&CurrentThread->Tcb);
  254. LpcpReleaseLpcpLock();
  255. KeReleaseSemaphore( QueuePort->MsgQueue.Semaphore,
  256. LPC_RELEASE_WAIT_INCREMENT,
  257. 1L,
  258. FALSE );
  259. //
  260. // If this is a waitable port then we'll need to set the event for
  261. // anyone that was waiting on the port
  262. //
  263. if ( QueuePort->Flags & PORT_WAITABLE ) {
  264. KeSetEvent( &QueuePort->WaitEvent,
  265. LPC_RELEASE_WAIT_INCREMENT,
  266. FALSE );
  267. }
  268. //
  269. // Exit the critical region and release the port object
  270. //
  271. KeLeaveCriticalRegionThread(&CurrentThread->Tcb);
  272. if (ConnectionPort) {
  273. ObDereferenceObject( ConnectionPort );
  274. }
  275. ObDereferenceObject( QueuePort );
  276. ObDereferenceObject( PortObject );
  277. //
  278. // And return to our caller. This is the only successful way out
  279. // of this routine
  280. //
  281. return Status;
  282. }
  283. }
  284. //
  285. // At this point we have a message but not a valid port to queue it off
  286. // to so we'll free up the port object and release the unused message.
  287. //
  288. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  289. ObDereferenceObject( PortObject );
  290. if (ConnectionPort) {
  291. ObDereferenceObject( ConnectionPort );
  292. }
  293. //
  294. // And return the error status to our caller
  295. //
  296. return STATUS_PORT_DISCONNECTED;
  297. }
  298. NTSTATUS
  299. NtRequestWaitReplyPort (
  300. IN HANDLE PortHandle,
  301. IN PPORT_MESSAGE RequestMessage,
  302. OUT PPORT_MESSAGE ReplyMessage
  303. )
  304. /*++
  305. Routine Description:
  306. A client and server process can send a request and wait for a reply using
  307. the NtRequestWaitReplyPort service.
  308. If the Type field of the RequestMessage structure is euqal to LPC_REQUEST,
  309. then this is identified as a callback request. The ClientId and MessageId
  310. fields are used to identify the thread that is waiting for a reply. This
  311. thread is unblocked and the current thread that called this service then
  312. blocks waiting for a reply.
  313. The Type field of the message is set to LPC_REQUEST by the service.
  314. Otherwise the Type field of the message must be zero and it will be set to
  315. LPC_REQUEST by the service. The message pointed to by the RequestMessage
  316. parameter is placed in the message queue of the port connected to the
  317. communication port specified by the PortHandle parameter. This service
  318. returns an error if PortHandle is invalid. The calling thread then blocks
  319. waiting for a reply.
  320. The reply message is stored in the location pointed to by the ReplyMessage
  321. parameter. The ClientId, MessageId and message type fields will be filled
  322. in by the service.
  323. Arguments:
  324. PortHandle - Specifies the handle of the communication port to send the
  325. request message to.
  326. RequestMessage - Specifies a pointer to a request message to send.
  327. ReplyMessage - Specifies the address of a variable that will receive the
  328. reply message. This parameter may point to the same buffer as the
  329. RequestMessage parameter.
  330. Return Value:
  331. NTSTATUS - A status code that indicates whether or not the operation was
  332. successful.
  333. --*/
  334. {
  335. PLPCP_PORT_OBJECT PortObject;
  336. PLPCP_PORT_OBJECT QueuePort;
  337. PLPCP_PORT_OBJECT RundownPort;
  338. PORT_MESSAGE CapturedRequestMessage;
  339. ULONG MsgType;
  340. PKSEMAPHORE ReleaseSemaphore;
  341. KPROCESSOR_MODE PreviousMode;
  342. NTSTATUS Status;
  343. PLPCP_MESSAGE Msg;
  344. PETHREAD CurrentThread;
  345. PETHREAD WakeupThread;
  346. BOOLEAN CallbackRequest;
  347. PORT_DATA_INFORMATION CapturedDataInfo;
  348. PLPCP_PORT_OBJECT ConnectionPort = NULL;
  349. PAGED_CODE();
  350. //
  351. // We cannot wait for a reply if the current thread is exiting
  352. //
  353. CurrentThread = PsGetCurrentThread();
  354. if (CurrentThread->LpcExitThreadCalled) {
  355. return STATUS_THREAD_IS_TERMINATING;
  356. }
  357. //
  358. // Get previous processor mode and probe output arguments if necessary.
  359. //
  360. PreviousMode = KeGetPreviousMode();
  361. if (PreviousMode != KernelMode) {
  362. try {
  363. ProbeForReadSmallStructure( RequestMessage,
  364. sizeof( *RequestMessage ),
  365. PROBE_ALIGNMENT (PORT_MESSAGE));
  366. CapturedRequestMessage = *RequestMessage;
  367. CapturedRequestMessage.u2.s2.Type &= ~LPC_KERNELMODE_MESSAGE;
  368. ProbeForWriteSmallStructure( ReplyMessage,
  369. sizeof( *ReplyMessage ),
  370. PROBE_ALIGNMENT (PORT_MESSAGE));
  371. //
  372. // Make sure that if this message has a data info offset that
  373. // the port data information actually fits in the message.
  374. //
  375. // We first check that the DataInfoOffset doesn't put us beyond
  376. // the end of the message.
  377. //
  378. // Then we capture the data info record and compute a pointer to
  379. // the first unused data entry based on the supplied count. If
  380. // the start of the message plus its total length doesn't come
  381. // up to the first unused data entry then the last valid data
  382. // entry doesn't fit in the message buffer. Also if the data
  383. // entry pointer that we compute is less than the data info
  384. // pointer then we must have wrapped.
  385. //
  386. if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
  387. PPORT_DATA_INFORMATION DataInfo;
  388. PPORT_DATA_ENTRY DataEntry;
  389. if (((ULONG)CapturedRequestMessage.u2.s2.DataInfoOffset) > (CapturedRequestMessage.u1.s1.TotalLength - sizeof(PORT_DATA_INFORMATION))) {
  390. return STATUS_INVALID_PARAMETER;
  391. }
  392. if ((ULONG)CapturedRequestMessage.u2.s2.DataInfoOffset < sizeof(PORT_MESSAGE)) {
  393. return STATUS_INVALID_PARAMETER;
  394. }
  395. DataInfo = (PPORT_DATA_INFORMATION)(((PUCHAR)RequestMessage) + CapturedRequestMessage.u2.s2.DataInfoOffset);
  396. ProbeForReadSmallStructure( DataInfo,
  397. sizeof( *DataInfo ),
  398. PROBE_ALIGNMENT (PORT_DATA_INFORMATION));
  399. CapturedDataInfo = *DataInfo;
  400. DataEntry = &(DataInfo->DataEntries[CapturedDataInfo.CountDataEntries]);
  401. //
  402. // The computation of above address, could overflow. So we have to return STATUS_INVALID_PARAMETER if :
  403. // CountDataEntries * sizeof(DataEntries) < CountDataEntries
  404. //
  405. // But CountDataEntries * sizeof(DataEntries) = DataEntry - DataInfo - sizeof(CountDataEntries)
  406. // and the final condition will be:
  407. //
  408. // DataEntry - DataInfo - sizeof(CountDataEntries) < CountDataEntries
  409. //
  410. // That is equivalent with:
  411. // DataEntry < DataInfo + sizeof(CountDataEntries) + CountDataEntries
  412. //
  413. // ( The condition (DataEntry < DataInfo) used in the previous versions
  414. // is always verified by this one )
  415. //
  416. if (((PUCHAR)DataEntry < (PUCHAR)DataInfo + sizeof(CapturedDataInfo.CountDataEntries) + CapturedDataInfo.CountDataEntries) ||
  417. ((((PUCHAR)RequestMessage) + CapturedRequestMessage.u1.s1.TotalLength) < (PUCHAR)DataEntry)) {
  418. return STATUS_INVALID_PARAMETER;
  419. }
  420. }
  421. } except( EXCEPTION_EXECUTE_HANDLER ) {
  422. return GetExceptionCode();
  423. }
  424. } else {
  425. CapturedRequestMessage = *RequestMessage;
  426. if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
  427. PPORT_DATA_INFORMATION DataInfo;
  428. DataInfo = (PPORT_DATA_INFORMATION)(((PUCHAR)RequestMessage) + CapturedRequestMessage.u2.s2.DataInfoOffset);
  429. CapturedDataInfo = *DataInfo;
  430. }
  431. }
  432. //
  433. // If the message type is an lpc request then say we need a callback.
  434. // Otherwise if it not an lpc request and it is not a kernel mode message
  435. // then it is an illegal parameter. A third case is if the type is
  436. // a kernel mode message in which case we make it look like an lpc request
  437. // but without the callback.
  438. //
  439. if ((CapturedRequestMessage.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) == LPC_REQUEST) {
  440. CallbackRequest = TRUE;
  441. } else if ((CapturedRequestMessage.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != 0) {
  442. return STATUS_INVALID_PARAMETER;
  443. } else {
  444. CapturedRequestMessage.u2.s2.Type |= LPC_REQUEST;
  445. CallbackRequest = FALSE;
  446. }
  447. //
  448. // Make sure DataLength is valid with respect to header size and total length
  449. //
  450. if ((((CLONG)CapturedRequestMessage.u1.s1.DataLength) + sizeof( PORT_MESSAGE )) >
  451. ((CLONG)CapturedRequestMessage.u1.s1.TotalLength)) {
  452. return STATUS_INVALID_PARAMETER;
  453. }
  454. //
  455. // Reference the communication port object by handle. Return status if
  456. // unsuccessful.
  457. //
  458. Status = LpcpReferencePortObject( PortHandle,
  459. 0,
  460. PreviousMode,
  461. &PortObject );
  462. if (!NT_SUCCESS( Status )) {
  463. return Status;
  464. }
  465. //
  466. // Validate the message length
  467. //
  468. if (((ULONG)CapturedRequestMessage.u1.s1.TotalLength > PortObject->MaxMessageLength) ||
  469. ((ULONG)CapturedRequestMessage.u1.s1.TotalLength <= (ULONG)CapturedRequestMessage.u1.s1.DataLength)) {
  470. ObDereferenceObject( PortObject );
  471. return STATUS_PORT_MESSAGE_TOO_LONG;
  472. }
  473. //
  474. // Determine which port to queue the message to and get client
  475. // port context if client sending to server. Also validate
  476. // length of message being sent.
  477. //
  478. //
  479. // Allocate and initialize the LPC message to send off
  480. //
  481. Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedRequestMessage.u1.s1.TotalLength );
  482. if (Msg == NULL) {
  483. ObDereferenceObject( PortObject );
  484. return STATUS_NO_MEMORY;
  485. }
  486. MsgType = CapturedRequestMessage.u2.s2.Type;
  487. //
  488. // Check if we need to do a callback
  489. //
  490. if (CallbackRequest) {
  491. //
  492. // Check for a valid request message id
  493. //
  494. if (CapturedRequestMessage.MessageId == 0) {
  495. LpcpFreeToPortZone( Msg, 0 );
  496. ObDereferenceObject( PortObject );
  497. return STATUS_INVALID_PARAMETER;
  498. }
  499. //
  500. // Translate the ClientId from the request into a
  501. // thread pointer. This is a referenced pointer to keep the thread
  502. // from evaporating out from under us.
  503. //
  504. Status = PsLookupProcessThreadByCid( &CapturedRequestMessage.ClientId,
  505. NULL,
  506. &WakeupThread );
  507. if (!NT_SUCCESS( Status )) {
  508. LpcpFreeToPortZone( Msg, 0 );
  509. ObDereferenceObject( PortObject );
  510. return Status;
  511. }
  512. //
  513. // Acquire the mutex that guards the LpcReplyMessage field of
  514. // the thread and get the pointer to the message that the thread
  515. // is waiting for a reply to.
  516. //
  517. LpcpAcquireLpcpLockByThread(CurrentThread);
  518. //
  519. // See if the thread is waiting for a reply to the message
  520. // specified on this call. If not then a bogus message has been
  521. // specified, so release the mutex, dereference the thread
  522. // and return failure.
  523. //
  524. if ((WakeupThread->LpcReplyMessageId != CapturedRequestMessage.MessageId)
  525. ||
  526. ((LpcpGetThreadMessage(WakeupThread) != NULL) &&
  527. (LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
  528. ||
  529. (!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY)) ) {
  530. LpcpPrint(( "%s Attempted CallBack Request to Thread %lx (%s)\n",
  531. PsGetCurrentProcess()->ImageFileName,
  532. WakeupThread,
  533. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  534. LpcpPrint(( "failed. MessageId == %u Client Id: %x.%x\n",
  535. CapturedRequestMessage.MessageId,
  536. CapturedRequestMessage.ClientId.UniqueProcess,
  537. CapturedRequestMessage.ClientId.UniqueThread ));
  538. LpcpPrint(( " Thread MessageId == %u Client Id: %x.%x\n",
  539. WakeupThread->LpcReplyMessageId,
  540. WakeupThread->Cid.UniqueProcess,
  541. WakeupThread->Cid.UniqueThread ));
  542. #if DBG
  543. if (LpcpStopOnReplyMismatch) {
  544. DbgBreakPoint();
  545. }
  546. #endif
  547. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  548. ObDereferenceObject( WakeupThread );
  549. ObDereferenceObject( PortObject );
  550. return STATUS_REPLY_MESSAGE_MISMATCH;
  551. }
  552. //
  553. // Copy over the text of the message
  554. //
  555. try {
  556. LpcpMoveMessage( &Msg->Request,
  557. &CapturedRequestMessage,
  558. (RequestMessage + 1),
  559. MsgType,
  560. &CurrentThread->Cid );
  561. if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
  562. PPORT_DATA_INFORMATION DataInfo;
  563. DataInfo = (PPORT_DATA_INFORMATION)(((PUCHAR)RequestMessage) + CapturedRequestMessage.u2.s2.DataInfoOffset);
  564. if ( DataInfo->CountDataEntries != CapturedDataInfo.CountDataEntries ) {
  565. Status = STATUS_INVALID_PARAMETER;
  566. }
  567. }
  568. } except( EXCEPTION_EXECUTE_HANDLER ) {
  569. Status = GetExceptionCode();
  570. }
  571. if (!NT_SUCCESS( Status )) {
  572. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  573. ObDereferenceObject( WakeupThread );
  574. ObDereferenceObject( PortObject );
  575. return Status;
  576. }
  577. //
  578. // Under the protect of the global lock we'll get everything
  579. // ready for the callback
  580. //
  581. QueuePort = NULL;
  582. Msg->PortContext = NULL;
  583. if ((PortObject->Flags & PORT_TYPE) == SERVER_CONNECTION_PORT) {
  584. RundownPort = PortObject;
  585. } else {
  586. RundownPort = PortObject->ConnectedPort;
  587. if (RundownPort == NULL) {
  588. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  589. ObDereferenceObject( WakeupThread );
  590. ObDereferenceObject( PortObject );
  591. return STATUS_PORT_DISCONNECTED;
  592. }
  593. if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
  594. Msg->PortContext = RundownPort->PortContext;
  595. }
  596. }
  597. Msg->Request.CallbackId = LpcpGenerateCallbackId();
  598. LpcpTrace(( "%s CallBack Request (%s) Msg %lx (%u.%u) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
  599. PsGetCurrentProcess()->ImageFileName,
  600. LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
  601. Msg,
  602. Msg->Request.MessageId,
  603. Msg->Request.CallbackId,
  604. *((PULONG)(Msg+1)+0),
  605. *((PULONG)(Msg+1)+1),
  606. *((PULONG)(Msg+1)+2),
  607. *((PULONG)(Msg+1)+3),
  608. WakeupThread,
  609. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  610. //
  611. // Add an extra reference so LpcExitThread does not evaporate
  612. // the pointer before we get to the wait below
  613. //
  614. ObReferenceObject( WakeupThread );
  615. Msg->RepliedToThread = WakeupThread;
  616. WakeupThread->LpcReplyMessageId = 0;
  617. WakeupThread->LpcReplyMessage = (PVOID)Msg;
  618. //
  619. // Remove the thread from the reply rundown list as we are sending a callback
  620. //
  621. if (!IsListEmpty( &WakeupThread->LpcReplyChain )) {
  622. RemoveEntryList( &WakeupThread->LpcReplyChain );
  623. InitializeListHead( &WakeupThread->LpcReplyChain );
  624. }
  625. CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
  626. CurrentThread->LpcReplyMessage = NULL;
  627. InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
  628. LpcpSetPortToThread( CurrentThread, PortObject );
  629. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  630. LpcpReleaseLpcpLock();
  631. //
  632. // Wake up the thread that is waiting for an answer to its request
  633. // inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort
  634. //
  635. ReleaseSemaphore = &WakeupThread->LpcReplySemaphore;
  636. } else {
  637. //
  638. // A callback is not required, so continue setting up the
  639. // lpc message
  640. //
  641. try {
  642. LpcpMoveMessage( &Msg->Request,
  643. &CapturedRequestMessage,
  644. (RequestMessage + 1),
  645. MsgType,
  646. &CurrentThread->Cid );
  647. if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
  648. PPORT_DATA_INFORMATION DataInfo;
  649. DataInfo = (PPORT_DATA_INFORMATION)(((PUCHAR)RequestMessage) + CapturedRequestMessage.u2.s2.DataInfoOffset);
  650. if ( DataInfo->CountDataEntries != CapturedDataInfo.CountDataEntries ) {
  651. LpcpFreeToPortZone( Msg, 0 );
  652. ObDereferenceObject( PortObject );
  653. return STATUS_INVALID_PARAMETER;
  654. }
  655. }
  656. } except( EXCEPTION_EXECUTE_HANDLER ) {
  657. LpcpFreeToPortZone( Msg, 0 );
  658. ObDereferenceObject( PortObject );
  659. return GetExceptionCode();
  660. }
  661. //
  662. // Acquire the global Lpc mutex that guards the LpcReplyMessage
  663. // field of the thread and the request message queue. Stamp the
  664. // request message with a serial number, insert the message at
  665. // the tail of the request message queue and remember the address
  666. // of the message in the LpcReplyMessage field for the current thread.
  667. //
  668. LpcpAcquireLpcpLockByThread(CurrentThread);
  669. Msg->PortContext = NULL;
  670. if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
  671. QueuePort = PortObject->ConnectedPort;
  672. if (QueuePort == NULL) {
  673. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  674. ObDereferenceObject( PortObject );
  675. return STATUS_PORT_DISCONNECTED;
  676. }
  677. RundownPort = QueuePort;
  678. if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
  679. Msg->PortContext = QueuePort->PortContext;
  680. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  681. if (ConnectionPort == NULL) {
  682. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  683. ObDereferenceObject( PortObject );
  684. return STATUS_PORT_DISCONNECTED;
  685. }
  686. } else if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
  687. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  688. if (ConnectionPort == NULL) {
  689. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  690. ObDereferenceObject( PortObject );
  691. return STATUS_PORT_DISCONNECTED;
  692. }
  693. }
  694. if (ConnectionPort) {
  695. ObReferenceObject( ConnectionPort );
  696. }
  697. } else {
  698. QueuePort = PortObject;
  699. RundownPort = PortObject;
  700. }
  701. //
  702. // Stamp the request message with a serial number, insert the message
  703. // at the tail of the request message queue
  704. //
  705. Msg->RepliedToThread = NULL;
  706. Msg->Request.MessageId = LpcpGenerateMessageId();
  707. Msg->Request.CallbackId = 0;
  708. Msg->SenderPort = PortObject;
  709. CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
  710. CurrentThread->LpcReplyMessage = NULL;
  711. InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
  712. InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
  713. LpcpSetPortToThread(CurrentThread, PortObject);
  714. LpcpTrace(( "%s Send Request (%s) Msg %lx (%u) [%08x %08x %08x %08x] to Port %lx (%s)\n",
  715. PsGetCurrentProcess()->ImageFileName,
  716. LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
  717. Msg,
  718. Msg->Request.MessageId,
  719. *((PULONG)(Msg+1)+0),
  720. *((PULONG)(Msg+1)+1),
  721. *((PULONG)(Msg+1)+2),
  722. *((PULONG)(Msg+1)+3),
  723. QueuePort,
  724. LpcpGetCreatorName( QueuePort )));
  725. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  726. LpcpReleaseLpcpLock();
  727. //
  728. // Increment the request message queue semaphore by one for
  729. // the newly inserted request message.
  730. //
  731. ReleaseSemaphore = QueuePort->MsgQueue.Semaphore;
  732. //
  733. // If port is waitable then set the event that someone could
  734. // be waiting on
  735. //
  736. if ( QueuePort->Flags & PORT_WAITABLE ) {
  737. KeSetEvent( &QueuePort->WaitEvent,
  738. LPC_RELEASE_WAIT_INCREMENT,
  739. FALSE );
  740. }
  741. }
  742. //
  743. // At this point we've enqueued our request and if necessary
  744. // set ourselves up for the callback or reply.
  745. //
  746. // So now wake up the other end
  747. //
  748. Status = KeReleaseSemaphore( ReleaseSemaphore,
  749. 1,
  750. 1,
  751. FALSE );
  752. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  753. if (CallbackRequest) {
  754. ObDereferenceObject( WakeupThread );
  755. }
  756. //
  757. // And wait for a reply
  758. //
  759. Status = KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  760. WrLpcReply,
  761. PreviousMode,
  762. FALSE,
  763. NULL );
  764. if (Status == STATUS_USER_APC) {
  765. //
  766. // if the semaphore is signaled, then clear it
  767. //
  768. if (KeReadStateSemaphore( &CurrentThread->LpcReplySemaphore )) {
  769. KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  770. WrExecutive,
  771. KernelMode,
  772. FALSE,
  773. NULL );
  774. Status = STATUS_SUCCESS;
  775. }
  776. }
  777. //
  778. // Acquire the LPC mutex. Remove the reply message from the current thread
  779. //
  780. LpcpAcquireLpcpLockByThread(CurrentThread);
  781. Msg = LpcpGetThreadMessage(CurrentThread);
  782. CurrentThread->LpcReplyMessage = NULL;
  783. CurrentThread->LpcReplyMessageId = 0;
  784. //
  785. // Remove the thread from the reply rundown list in case we did not wakeup due to
  786. // a reply
  787. //
  788. if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
  789. RemoveEntryList( &CurrentThread->LpcReplyChain );
  790. InitializeListHead( &CurrentThread->LpcReplyChain );
  791. }
  792. #if DBG
  793. if (Status == STATUS_SUCCESS && Msg != NULL) {
  794. LpcpTrace(( "%s Got Reply Msg %lx (%u) [%08x %08x %08x %08x] for Thread %lx (%s)\n",
  795. PsGetCurrentProcess()->ImageFileName,
  796. Msg,
  797. Msg->Request.MessageId,
  798. *((PULONG)(Msg+1)+0),
  799. *((PULONG)(Msg+1)+1),
  800. *((PULONG)(Msg+1)+2),
  801. *((PULONG)(Msg+1)+3),
  802. CurrentThread,
  803. THREAD_TO_PROCESS( CurrentThread )->ImageFileName ));
  804. if (!IsListEmpty( &Msg->Entry )) {
  805. LpcpTrace(( "Reply Msg %lx has non-empty list entry\n", Msg ));
  806. }
  807. }
  808. #endif
  809. LpcpReleaseLpcpLock();
  810. //
  811. // If the wait succeeded, copy the reply to the reply buffer.
  812. //
  813. if (Status == STATUS_SUCCESS) {
  814. if (Msg != NULL) {
  815. try {
  816. LpcpMoveMessage( ReplyMessage,
  817. &Msg->Request,
  818. (&Msg->Request) + 1,
  819. 0,
  820. NULL );
  821. } except( EXCEPTION_EXECUTE_HANDLER ) {
  822. Status = GetExceptionCode();
  823. }
  824. //
  825. // Acquire the LPC mutex and decrement the reference count for the
  826. // message. If the reference count goes to zero the message will be
  827. // deleted.
  828. //
  829. if (((Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) == LPC_REQUEST) &&
  830. (Msg->Request.u2.s2.DataInfoOffset != 0)) {
  831. LpcpSaveDataInfoMessage( PortObject, Msg, 0 );
  832. } else {
  833. LpcpFreeToPortZone( Msg, 0 );
  834. }
  835. } else {
  836. Status = STATUS_LPC_REPLY_LOST;
  837. }
  838. } else {
  839. //
  840. // Wait failed, free the message if any.
  841. //
  842. LpcpTrace(( "%s NtRequestWaitReply wait failed - Status == %lx\n",
  843. PsGetCurrentProcess()->ImageFileName,
  844. Status ));
  845. if (Msg != NULL) {
  846. LpcpFreeToPortZone( Msg, 0);
  847. }
  848. }
  849. ObDereferenceObject( PortObject );
  850. if (ConnectionPort) {
  851. ObDereferenceObject( ConnectionPort );
  852. }
  853. //
  854. // And return to our caller
  855. //
  856. return Status;
  857. }
  858. NTSTATUS
  859. LpcRequestPort (
  860. IN PVOID PortAddress,
  861. IN PPORT_MESSAGE RequestMessage
  862. )
  863. /*++
  864. Routine Description:
  865. This procedure is similar to NtRequestPort but without the Handle based
  866. interface.
  867. Arguments:
  868. PortAddress - Supplies a pointer to the communication port to send
  869. the request message to.
  870. RequestMessage - Specifies a pointer to the request message. The Type
  871. field of the message is set to LPC_DATAGRAM by the service.
  872. Return Value:
  873. NTSTATUS - A status code that indicates whether or not the operation was
  874. successful.
  875. --*/
  876. {
  877. PETHREAD CurrentThread;
  878. PLPCP_PORT_OBJECT PortObject = (PLPCP_PORT_OBJECT)PortAddress;
  879. PLPCP_PORT_OBJECT QueuePort;
  880. ULONG MsgType;
  881. PLPCP_MESSAGE Msg;
  882. KPROCESSOR_MODE PreviousMode;
  883. PLPCP_PORT_OBJECT ConnectionPort = NULL;
  884. PAGED_CODE();
  885. //
  886. // Get previous processor mode and validate parameters
  887. //
  888. PreviousMode = KeGetPreviousMode();
  889. if (RequestMessage->u2.s2.Type != 0) {
  890. MsgType = RequestMessage->u2.s2.Type & ~LPC_KERNELMODE_MESSAGE;
  891. if ((MsgType < LPC_DATAGRAM) ||
  892. (MsgType > LPC_CLIENT_DIED)) {
  893. return STATUS_INVALID_PARAMETER;
  894. }
  895. //
  896. // If previous mode is kernel, allow the LPC_KERNELMODE_MESSAGE
  897. // bit to be passed in message type field.
  898. //
  899. if ((PreviousMode == KernelMode) &&
  900. (RequestMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE)) {
  901. MsgType |= LPC_KERNELMODE_MESSAGE;
  902. }
  903. } else {
  904. MsgType = LPC_DATAGRAM;
  905. }
  906. if (RequestMessage->u2.s2.DataInfoOffset != 0) {
  907. return STATUS_INVALID_PARAMETER;
  908. }
  909. //
  910. // Validate the message length
  911. //
  912. if (((ULONG)RequestMessage->u1.s1.TotalLength > PortObject->MaxMessageLength) ||
  913. ((ULONG)RequestMessage->u1.s1.TotalLength <= (ULONG)RequestMessage->u1.s1.DataLength)) {
  914. return STATUS_PORT_MESSAGE_TOO_LONG;
  915. }
  916. //
  917. // Allocate a message block
  918. //
  919. Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( RequestMessage->u1.s1.TotalLength );
  920. if (Msg == NULL) {
  921. return STATUS_NO_MEMORY;
  922. }
  923. //
  924. // Fill in the message block.
  925. //
  926. Msg->RepliedToThread = NULL;
  927. Msg->PortContext = NULL;
  928. CurrentThread = PsGetCurrentThread();
  929. LpcpMoveMessage( &Msg->Request,
  930. RequestMessage,
  931. (RequestMessage + 1),
  932. MsgType,
  933. &CurrentThread->Cid );
  934. //
  935. // Acquire the global Lpc mutex that guards the LpcReplyMessage
  936. // field of the thread and the request message queue. Stamp the
  937. // request message with a serial number, insert the message at
  938. // the tail of the request message queue
  939. //
  940. LpcpAcquireLpcpLockByThread(CurrentThread);
  941. if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
  942. QueuePort = PortObject->ConnectedPort;
  943. if (QueuePort != NULL) {
  944. if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
  945. Msg->PortContext = QueuePort->PortContext;
  946. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  947. if (ConnectionPort == NULL) {
  948. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  949. return STATUS_PORT_DISCONNECTED;
  950. }
  951. //
  952. // In the case we don't have a CLIENT_COMMUNICATION_PORT nor
  953. // SERVER_COMMUNICATION_PORT we'll use the connection port
  954. // to queue messages.
  955. //
  956. } else if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
  957. QueuePort = PortObject->ConnectionPort;
  958. if (ConnectionPort == NULL) {
  959. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  960. return STATUS_PORT_DISCONNECTED;
  961. }
  962. }
  963. if (ConnectionPort) {
  964. ObReferenceObject( ConnectionPort );
  965. }
  966. }
  967. } else {
  968. QueuePort = PortObject;
  969. }
  970. //
  971. // At this point we have an LPC message ready to send and if queue port is
  972. // not null then we have a port to actually send the message off to
  973. //
  974. if (QueuePort != NULL) {
  975. Msg->Request.MessageId = LpcpGenerateMessageId();
  976. Msg->Request.CallbackId = 0;
  977. Msg->SenderPort = PortObject;
  978. CurrentThread->LpcReplyMessageId = 0;
  979. InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
  980. LpcpTrace(( "%s Send DataGram (%s) Msg %lx [%08x %08x %08x %08x] to Port %lx (%s)\n",
  981. PsGetCurrentProcess()->ImageFileName,
  982. LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
  983. Msg,
  984. *((PULONG)(Msg+1)+0),
  985. *((PULONG)(Msg+1)+1),
  986. *((PULONG)(Msg+1)+2),
  987. *((PULONG)(Msg+1)+3),
  988. QueuePort,
  989. LpcpGetCreatorName( QueuePort )));
  990. //
  991. // Release the mutex, increment the request message queue
  992. // semaphore by one for the newly inserted request message,
  993. // then exit the critical region.
  994. //
  995. // Disable APCs to prevent this thread from being suspended
  996. // before being able to release the semaphore.
  997. //
  998. KeEnterCriticalRegionThread(&CurrentThread->Tcb);
  999. LpcpReleaseLpcpLock();
  1000. KeReleaseSemaphore( QueuePort->MsgQueue.Semaphore,
  1001. LPC_RELEASE_WAIT_INCREMENT,
  1002. 1L,
  1003. FALSE );
  1004. if ( QueuePort->Flags & PORT_WAITABLE ) {
  1005. KeSetEvent( &QueuePort->WaitEvent,
  1006. LPC_RELEASE_WAIT_INCREMENT,
  1007. FALSE );
  1008. }
  1009. KeLeaveCriticalRegionThread(&CurrentThread->Tcb);
  1010. if (ConnectionPort) {
  1011. ObDereferenceObject( ConnectionPort );
  1012. }
  1013. return STATUS_SUCCESS;
  1014. }
  1015. //
  1016. // At this point we have a message but not a valid port to queue it off
  1017. // to so we'll release the unused message and release our mutex.
  1018. //
  1019. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1020. if (ConnectionPort) {
  1021. ObDereferenceObject( ConnectionPort );
  1022. }
  1023. //
  1024. // And return the error status to our caller
  1025. //
  1026. return STATUS_PORT_DISCONNECTED;
  1027. }
  1028. NTSTATUS
  1029. LpcpRequestWaitReplyPort (
  1030. IN PVOID PortAddress,
  1031. IN PPORT_MESSAGE RequestMessage,
  1032. OUT PPORT_MESSAGE ReplyMessage,
  1033. IN KPROCESSOR_MODE AccessMode
  1034. )
  1035. /*++
  1036. Routine Description:
  1037. This procedure is similar to NtRequestWaitReplyPort but without the
  1038. handle based interface
  1039. Arguments:
  1040. PortAddress - Supplies the communication port object to send the
  1041. request message to.
  1042. RequestMessage - Specifies a pointer to a request message to send.
  1043. ReplyMessage - Specifies the address of a variable that will receive the
  1044. reply message. This parameter may point to the same buffer as the
  1045. RequestMessage parameter.
  1046. Return Value:
  1047. NTSTATUS - A status code that indicates whether or not the operation was
  1048. successful.
  1049. --*/
  1050. {
  1051. PLPCP_PORT_OBJECT PortObject = (PLPCP_PORT_OBJECT)PortAddress;
  1052. PLPCP_PORT_OBJECT QueuePort;
  1053. PLPCP_PORT_OBJECT RundownPort;
  1054. PKSEMAPHORE ReleaseSemaphore;
  1055. NTSTATUS Status;
  1056. ULONG MsgType;
  1057. PLPCP_MESSAGE Msg;
  1058. PETHREAD CurrentThread;
  1059. PETHREAD WakeupThread;
  1060. BOOLEAN CallbackRequest;
  1061. KPROCESSOR_MODE PreviousMode;
  1062. PLPCP_PORT_OBJECT ConnectionPort = NULL;
  1063. PAGED_CODE();
  1064. CurrentThread = PsGetCurrentThread();
  1065. if (CurrentThread->LpcExitThreadCalled) {
  1066. return STATUS_THREAD_IS_TERMINATING;
  1067. }
  1068. //
  1069. // Get previous processor mode and validate parameters
  1070. //
  1071. PreviousMode = KeGetPreviousMode();
  1072. MsgType = RequestMessage->u2.s2.Type & ~LPC_KERNELMODE_MESSAGE;
  1073. CallbackRequest = FALSE;
  1074. switch (MsgType) {
  1075. case 0:
  1076. MsgType = LPC_REQUEST;
  1077. break;
  1078. case LPC_REQUEST:
  1079. CallbackRequest = TRUE;
  1080. break;
  1081. case LPC_CLIENT_DIED:
  1082. case LPC_PORT_CLOSED:
  1083. case LPC_EXCEPTION:
  1084. case LPC_DEBUG_EVENT:
  1085. case LPC_ERROR_EVENT:
  1086. break;
  1087. default :
  1088. return STATUS_INVALID_PARAMETER;
  1089. }
  1090. //
  1091. // Allow the LPC_KERNELMODE_MESSAGE
  1092. // bit to be passed in message type field. Don't check the previous mode !!!
  1093. //
  1094. if ( RequestMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE) {
  1095. MsgType |= LPC_KERNELMODE_MESSAGE;
  1096. }
  1097. RequestMessage->u2.s2.Type = (CSHORT)MsgType;
  1098. //
  1099. // Validate the message length
  1100. //
  1101. if (((ULONG)RequestMessage->u1.s1.TotalLength > PortObject->MaxMessageLength) ||
  1102. ((ULONG)RequestMessage->u1.s1.TotalLength <= (ULONG)RequestMessage->u1.s1.DataLength)) {
  1103. return STATUS_PORT_MESSAGE_TOO_LONG;
  1104. }
  1105. //
  1106. // Determine which port to queue the message to and get client
  1107. // port context if client sending to server. Also validate
  1108. // length of message being sent.
  1109. //
  1110. Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( RequestMessage->u1.s1.TotalLength );
  1111. if (Msg == NULL) {
  1112. return STATUS_NO_MEMORY;
  1113. }
  1114. if (CallbackRequest) {
  1115. //
  1116. // Check for a valid request message id
  1117. //
  1118. if (RequestMessage->MessageId == 0) {
  1119. LpcpFreeToPortZone( Msg, 0 );
  1120. return STATUS_INVALID_PARAMETER;
  1121. }
  1122. //
  1123. // Translate the ClientId from the request into a
  1124. // thread pointer. This is a referenced pointer to keep the thread
  1125. // from evaporating out from under us.
  1126. //
  1127. Status = PsLookupProcessThreadByCid( &RequestMessage->ClientId,
  1128. NULL,
  1129. &WakeupThread );
  1130. if (!NT_SUCCESS( Status )) {
  1131. LpcpFreeToPortZone( Msg, 0 );
  1132. return Status;
  1133. }
  1134. //
  1135. // Acquire the mutex that guards the LpcReplyMessage field of
  1136. // the thread and get the pointer to the message that the thread
  1137. // is waiting for a reply to.
  1138. //
  1139. LpcpAcquireLpcpLockByThread(CurrentThread);
  1140. //
  1141. // See if the thread is waiting for a reply to the message
  1142. // specified on this call. If not then a bogus message
  1143. // has been specified, so release the mutex, dereference the thread
  1144. // and return failure.
  1145. //
  1146. if ((WakeupThread->LpcReplyMessageId != RequestMessage->MessageId)
  1147. ||
  1148. ((LpcpGetThreadMessage(WakeupThread) != NULL) &&
  1149. (LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
  1150. ||
  1151. (!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY)) ) {
  1152. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1153. ObDereferenceObject( WakeupThread );
  1154. return STATUS_REPLY_MESSAGE_MISMATCH;
  1155. }
  1156. QueuePort = NULL;
  1157. Msg->PortContext = NULL;
  1158. if ((PortObject->Flags & PORT_TYPE) == SERVER_CONNECTION_PORT) {
  1159. RundownPort = PortObject;
  1160. } else {
  1161. RundownPort = PortObject->ConnectedPort;
  1162. if (RundownPort == NULL) {
  1163. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1164. ObDereferenceObject( WakeupThread );
  1165. return STATUS_PORT_DISCONNECTED;
  1166. }
  1167. if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
  1168. Msg->PortContext = RundownPort->PortContext;
  1169. }
  1170. }
  1171. //
  1172. // Allocate and initialize a request message
  1173. //
  1174. LpcpMoveMessage( &Msg->Request,
  1175. RequestMessage,
  1176. (RequestMessage + 1),
  1177. 0,
  1178. &CurrentThread->Cid );
  1179. Msg->Request.CallbackId = LpcpGenerateCallbackId();
  1180. LpcpTrace(( "%s CallBack Request (%s) Msg %lx (%u.%u) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
  1181. PsGetCurrentProcess()->ImageFileName,
  1182. LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
  1183. Msg,
  1184. Msg->Request.MessageId,
  1185. Msg->Request.CallbackId,
  1186. *((PULONG)(Msg+1)+0),
  1187. *((PULONG)(Msg+1)+1),
  1188. *((PULONG)(Msg+1)+2),
  1189. *((PULONG)(Msg+1)+3),
  1190. WakeupThread,
  1191. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  1192. //
  1193. // Add an extra reference so LpcExitThread does not evaporate
  1194. // the pointer before we get to the wait below
  1195. //
  1196. ObReferenceObject( WakeupThread );
  1197. Msg->RepliedToThread = WakeupThread;
  1198. WakeupThread->LpcReplyMessageId = 0;
  1199. WakeupThread->LpcReplyMessage = (PVOID)Msg;
  1200. //
  1201. // Remove the thread from the reply rundown list as we are sending a callback
  1202. //
  1203. if (!IsListEmpty( &WakeupThread->LpcReplyChain )) {
  1204. RemoveEntryList( &WakeupThread->LpcReplyChain );
  1205. InitializeListHead( &WakeupThread->LpcReplyChain );
  1206. }
  1207. CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
  1208. CurrentThread->LpcReplyMessage = NULL;
  1209. InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
  1210. LpcpSetPortToThread(CurrentThread, PortObject);
  1211. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  1212. LpcpReleaseLpcpLock();
  1213. //
  1214. // Wake up the thread that is waiting for an answer to its request
  1215. // inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort
  1216. //
  1217. ReleaseSemaphore = &WakeupThread->LpcReplySemaphore;
  1218. } else {
  1219. //
  1220. // There is no callback requested
  1221. //
  1222. LpcpMoveMessage( &Msg->Request,
  1223. RequestMessage,
  1224. (RequestMessage + 1),
  1225. 0,
  1226. &CurrentThread->Cid );
  1227. //
  1228. // Acquire the global Lpc mutex that guards the LpcReplyMessage
  1229. // field of the thread and the request message queue. Stamp the
  1230. // request message with a serial number, insert the message at
  1231. // the tail of the request message queue and remember the address
  1232. // of the message in the LpcReplyMessage field for the current thread.
  1233. //
  1234. LpcpAcquireLpcpLockByThread(CurrentThread);
  1235. Msg->PortContext = NULL;
  1236. if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
  1237. QueuePort = PortObject->ConnectedPort;
  1238. if (QueuePort == NULL) {
  1239. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1240. return STATUS_PORT_DISCONNECTED;
  1241. }
  1242. RundownPort = QueuePort;
  1243. if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
  1244. Msg->PortContext = QueuePort->PortContext;
  1245. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  1246. if (ConnectionPort == NULL) {
  1247. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1248. return STATUS_PORT_DISCONNECTED;
  1249. }
  1250. } else if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
  1251. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  1252. if (ConnectionPort == NULL) {
  1253. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1254. return STATUS_PORT_DISCONNECTED;
  1255. }
  1256. }
  1257. if (ConnectionPort) {
  1258. ObReferenceObject( ConnectionPort );
  1259. }
  1260. } else {
  1261. if ((PortObject->Flags & PORT_NAME_DELETED) != 0) {
  1262. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1263. return STATUS_PORT_DISCONNECTED;
  1264. }
  1265. QueuePort = PortObject;
  1266. RundownPort = PortObject;
  1267. }
  1268. Msg->RepliedToThread = NULL;
  1269. Msg->Request.MessageId = LpcpGenerateMessageId();
  1270. Msg->Request.CallbackId = 0;
  1271. Msg->SenderPort = PortObject;
  1272. CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
  1273. CurrentThread->LpcReplyMessage = NULL;
  1274. InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
  1275. InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
  1276. LpcpSetPortToThread(CurrentThread, PortObject);
  1277. LpcpTrace(( "%s Send Request (%s) Msg %lx (%u) [%08x %08x %08x %08x] to Port %lx (%s)\n",
  1278. PsGetCurrentProcess()->ImageFileName,
  1279. LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
  1280. Msg,
  1281. Msg->Request.MessageId,
  1282. *((PULONG)(Msg+1)+0),
  1283. *((PULONG)(Msg+1)+1),
  1284. *((PULONG)(Msg+1)+2),
  1285. *((PULONG)(Msg+1)+3),
  1286. QueuePort,
  1287. LpcpGetCreatorName( QueuePort )));
  1288. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  1289. LpcpReleaseLpcpLock();
  1290. //
  1291. // Increment the request message queue semaphore by one for
  1292. // the newly inserted request message. Release the spin
  1293. // lock, while remaining at the dispatcher IRQL. Then wait for the
  1294. // reply to this request by waiting on the LpcReplySemaphore
  1295. // for the current thread.
  1296. //
  1297. ReleaseSemaphore = QueuePort->MsgQueue.Semaphore;
  1298. if ( QueuePort->Flags & PORT_WAITABLE ) {
  1299. KeSetEvent( &QueuePort->WaitEvent,
  1300. LPC_RELEASE_WAIT_INCREMENT,
  1301. FALSE );
  1302. }
  1303. }
  1304. //
  1305. // At this point we've enqueued our request and if necessary
  1306. // set ourselves up for the callback or reply.
  1307. //
  1308. // So now wake up the other end
  1309. //
  1310. Status = KeReleaseSemaphore( ReleaseSemaphore,
  1311. 1,
  1312. 1,
  1313. FALSE );
  1314. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  1315. if (CallbackRequest) {
  1316. ObDereferenceObject( WakeupThread );
  1317. }
  1318. //
  1319. // And wait for a reply
  1320. //
  1321. Status = KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  1322. WrLpcReply,
  1323. AccessMode,
  1324. FALSE,
  1325. NULL );
  1326. if (Status == STATUS_USER_APC) {
  1327. //
  1328. // if the semaphore is signaled, then clear it
  1329. //
  1330. if (KeReadStateSemaphore( &CurrentThread->LpcReplySemaphore )) {
  1331. KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  1332. WrExecutive,
  1333. KernelMode,
  1334. FALSE,
  1335. NULL );
  1336. Status = STATUS_SUCCESS;
  1337. }
  1338. }
  1339. //
  1340. // Acquire the LPC mutex. Remove the reply message from the current thread
  1341. //
  1342. LpcpAcquireLpcpLockByThread(CurrentThread);
  1343. Msg = LpcpGetThreadMessage(CurrentThread);
  1344. CurrentThread->LpcReplyMessage = NULL;
  1345. CurrentThread->LpcReplyMessageId = 0;
  1346. //
  1347. // Remove the thread from the reply rundown list in case we did not wakeup due to
  1348. // a reply
  1349. //
  1350. if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
  1351. RemoveEntryList( &CurrentThread->LpcReplyChain );
  1352. InitializeListHead( &CurrentThread->LpcReplyChain );
  1353. }
  1354. #if DBG
  1355. if (Msg != NULL) {
  1356. LpcpTrace(( "%s Got Reply Msg %lx (%u) [%08x %08x %08x %08x] for Thread %lx (%s)\n",
  1357. PsGetCurrentProcess()->ImageFileName,
  1358. Msg,
  1359. Msg->Request.MessageId,
  1360. *((PULONG)(Msg+1)+0),
  1361. *((PULONG)(Msg+1)+1),
  1362. *((PULONG)(Msg+1)+2),
  1363. *((PULONG)(Msg+1)+3),
  1364. CurrentThread,
  1365. THREAD_TO_PROCESS( CurrentThread )->ImageFileName ));
  1366. }
  1367. #endif
  1368. LpcpReleaseLpcpLock();
  1369. //
  1370. // If the wait succeeded, copy the reply to the reply buffer.
  1371. //
  1372. if (Status == STATUS_SUCCESS) {
  1373. if (Msg != NULL) {
  1374. LpcpMoveMessage( ReplyMessage,
  1375. &Msg->Request,
  1376. (&Msg->Request) + 1,
  1377. 0,
  1378. NULL );
  1379. //
  1380. // Acquire the LPC mutex and decrement the reference count for the
  1381. // message. If the reference count goes to zero the message will be
  1382. // deleted.
  1383. //
  1384. LpcpAcquireLpcpLockByThread(CurrentThread);
  1385. if (Msg->RepliedToThread != NULL) {
  1386. ObDereferenceObject( Msg->RepliedToThread );
  1387. Msg->RepliedToThread = NULL;
  1388. }
  1389. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1390. } else {
  1391. Status = STATUS_LPC_REPLY_LOST;
  1392. }
  1393. } else {
  1394. //
  1395. // Wait failed, free the message if any.
  1396. //
  1397. if (Msg != NULL) {
  1398. LpcpFreeToPortZone( Msg, 0 );
  1399. }
  1400. }
  1401. if (ConnectionPort) {
  1402. ObDereferenceObject( ConnectionPort );
  1403. }
  1404. //
  1405. // And return to our caller
  1406. //
  1407. return Status;
  1408. }
  1409. NTSTATUS
  1410. LpcRequestWaitReplyPort (
  1411. IN PVOID PortAddress,
  1412. IN PPORT_MESSAGE RequestMessage,
  1413. OUT PPORT_MESSAGE ReplyMessage
  1414. )
  1415. {
  1416. return LpcpRequestWaitReplyPort( PortAddress,
  1417. RequestMessage,
  1418. ReplyMessage,
  1419. KernelMode
  1420. );
  1421. }
  1422. NTSTATUS
  1423. LpcRequestWaitReplyPortEx (
  1424. IN PVOID PortAddress,
  1425. IN PPORT_MESSAGE RequestMessage,
  1426. OUT PPORT_MESSAGE ReplyMessage
  1427. )
  1428. {
  1429. return LpcpRequestWaitReplyPort( PortAddress,
  1430. RequestMessage,
  1431. ReplyMessage,
  1432. KeGetPreviousMode()
  1433. );
  1434. }