Leaked source code of windows server 2003
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.

2095 lines
61 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. LOGICAL NoImpersonate;
  350. PAGED_CODE();
  351. //
  352. // We cannot wait for a reply if the current thread is exiting
  353. //
  354. CurrentThread = PsGetCurrentThread();
  355. if (CurrentThread->LpcExitThreadCalled) {
  356. return STATUS_THREAD_IS_TERMINATING;
  357. }
  358. //
  359. // Get previous processor mode and probe output arguments if necessary.
  360. //
  361. PreviousMode = KeGetPreviousMode();
  362. if (PreviousMode != KernelMode) {
  363. try {
  364. ProbeForReadSmallStructure( RequestMessage,
  365. sizeof( *RequestMessage ),
  366. PROBE_ALIGNMENT (PORT_MESSAGE));
  367. CapturedRequestMessage = *RequestMessage;
  368. CapturedRequestMessage.u2.s2.Type &= ~LPC_KERNELMODE_MESSAGE;
  369. ProbeForWriteSmallStructure( ReplyMessage,
  370. sizeof( *ReplyMessage ),
  371. PROBE_ALIGNMENT (PORT_MESSAGE));
  372. //
  373. // Make sure that if this message has a data info offset that
  374. // the port data information actually fits in the message.
  375. //
  376. // We first check that the DataInfoOffset doesn't put us beyond
  377. // the end of the message.
  378. //
  379. // Then we capture the data info record and compute a pointer to
  380. // the first unused data entry based on the supplied count. If
  381. // the start of the message plus its total length doesn't come
  382. // up to the first unused data entry then the last valid data
  383. // entry doesn't fit in the message buffer. Also if the data
  384. // entry pointer that we compute is less than the data info
  385. // pointer then we must have wrapped.
  386. //
  387. if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
  388. PPORT_DATA_INFORMATION DataInfo;
  389. PPORT_DATA_ENTRY DataEntry;
  390. if (((ULONG)CapturedRequestMessage.u2.s2.DataInfoOffset) > (CapturedRequestMessage.u1.s1.TotalLength - sizeof(PORT_DATA_INFORMATION))) {
  391. return STATUS_INVALID_PARAMETER;
  392. }
  393. if ((ULONG)CapturedRequestMessage.u2.s2.DataInfoOffset < sizeof(PORT_MESSAGE)) {
  394. return STATUS_INVALID_PARAMETER;
  395. }
  396. DataInfo = (PPORT_DATA_INFORMATION)(((PUCHAR)RequestMessage) + CapturedRequestMessage.u2.s2.DataInfoOffset);
  397. ProbeForReadSmallStructure( DataInfo,
  398. sizeof( *DataInfo ),
  399. PROBE_ALIGNMENT (PORT_DATA_INFORMATION));
  400. CapturedDataInfo = *DataInfo;
  401. if (CapturedDataInfo.CountDataEntries > ((CapturedRequestMessage.u1.s1.TotalLength - CapturedRequestMessage.u2.s2.DataInfoOffset) / sizeof(PORT_DATA_ENTRY))) {
  402. return STATUS_INVALID_PARAMETER;
  403. }
  404. DataEntry = &(DataInfo->DataEntries[CapturedDataInfo.CountDataEntries]);
  405. if ( ((PUCHAR)DataEntry < (PUCHAR)DataInfo)
  406. ||
  407. ((((PUCHAR)RequestMessage) + CapturedRequestMessage.u1.s1.TotalLength) < (PUCHAR)DataEntry)) {
  408. return STATUS_INVALID_PARAMETER;
  409. }
  410. }
  411. } except( EXCEPTION_EXECUTE_HANDLER ) {
  412. return GetExceptionCode();
  413. }
  414. } else {
  415. CapturedRequestMessage = *RequestMessage;
  416. if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
  417. PPORT_DATA_INFORMATION DataInfo;
  418. DataInfo = (PPORT_DATA_INFORMATION)(((PUCHAR)RequestMessage) + CapturedRequestMessage.u2.s2.DataInfoOffset);
  419. CapturedDataInfo = *DataInfo;
  420. }
  421. }
  422. //
  423. // Capture the NoImpersonateFlag and clear the bit if necessary
  424. //
  425. if (CapturedRequestMessage.u2.s2.Type & LPC_NO_IMPERSONATE) {
  426. NoImpersonate = TRUE;
  427. CapturedRequestMessage.u2.s2.Type &= ~LPC_NO_IMPERSONATE;
  428. } else {
  429. NoImpersonate = FALSE;
  430. }
  431. //
  432. // If the message type is an lpc request then say we need a callback.
  433. // Otherwise if it not an lpc request and it is not a kernel mode message
  434. // then it is an illegal parameter. A third case is if the type is
  435. // a kernel mode message in which case we make it look like an lpc request
  436. // but without the callback.
  437. //
  438. if ((CapturedRequestMessage.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) == LPC_REQUEST) {
  439. CallbackRequest = TRUE;
  440. } else if ((CapturedRequestMessage.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != 0) {
  441. return STATUS_INVALID_PARAMETER;
  442. } else {
  443. CapturedRequestMessage.u2.s2.Type |= LPC_REQUEST;
  444. CallbackRequest = FALSE;
  445. }
  446. //
  447. // Make sure DataLength is valid with respect to header size and total length
  448. //
  449. if ((((CLONG)CapturedRequestMessage.u1.s1.DataLength) + sizeof( PORT_MESSAGE )) >
  450. ((CLONG)CapturedRequestMessage.u1.s1.TotalLength)) {
  451. return STATUS_INVALID_PARAMETER;
  452. }
  453. //
  454. // Reference the communication port object by handle. Return status if
  455. // unsuccessful.
  456. //
  457. Status = LpcpReferencePortObject( PortHandle,
  458. 0,
  459. PreviousMode,
  460. &PortObject );
  461. if (!NT_SUCCESS( Status )) {
  462. return Status;
  463. }
  464. //
  465. // Validate the message length
  466. //
  467. if (((ULONG)CapturedRequestMessage.u1.s1.TotalLength > PortObject->MaxMessageLength) ||
  468. ((ULONG)CapturedRequestMessage.u1.s1.TotalLength <= (ULONG)CapturedRequestMessage.u1.s1.DataLength)) {
  469. ObDereferenceObject( PortObject );
  470. return STATUS_PORT_MESSAGE_TOO_LONG;
  471. }
  472. //
  473. // Determine which port to queue the message to and get client
  474. // port context if client sending to server. Also validate
  475. // length of message being sent.
  476. //
  477. //
  478. // Allocate and initialize the LPC message to send off
  479. //
  480. Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedRequestMessage.u1.s1.TotalLength );
  481. if (Msg == NULL) {
  482. ObDereferenceObject( PortObject );
  483. return STATUS_NO_MEMORY;
  484. }
  485. MsgType = CapturedRequestMessage.u2.s2.Type;
  486. //
  487. // Check if we need to do a callback
  488. //
  489. if (CallbackRequest) {
  490. //
  491. // Check for a valid request message id
  492. //
  493. if (CapturedRequestMessage.MessageId == 0) {
  494. LpcpFreeToPortZone( Msg, 0 );
  495. ObDereferenceObject( PortObject );
  496. return STATUS_INVALID_PARAMETER;
  497. }
  498. //
  499. // Translate the ClientId from the request into a
  500. // thread pointer. This is a referenced pointer to keep the thread
  501. // from evaporating out from under us.
  502. //
  503. Status = PsLookupProcessThreadByCid( &CapturedRequestMessage.ClientId,
  504. NULL,
  505. &WakeupThread );
  506. if (!NT_SUCCESS( Status )) {
  507. LpcpFreeToPortZone( Msg, 0 );
  508. ObDereferenceObject( PortObject );
  509. return Status;
  510. }
  511. //
  512. // Acquire the mutex that guards the LpcReplyMessage field of
  513. // the thread and get the pointer to the message that the thread
  514. // is waiting for a reply to.
  515. //
  516. LpcpAcquireLpcpLockByThread(CurrentThread);
  517. //
  518. // See if the thread is waiting for a reply to the message
  519. // specified on this call. If not then a bogus message has been
  520. // specified, so release the mutex, dereference the thread
  521. // and return failure.
  522. //
  523. if ((WakeupThread->LpcReplyMessageId != CapturedRequestMessage.MessageId)
  524. ||
  525. ((LpcpGetThreadMessage(WakeupThread) != NULL) &&
  526. (LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
  527. ||
  528. (!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY)) ) {
  529. LpcpPrint(( "%s Attempted CallBack Request to Thread %lx (%s)\n",
  530. PsGetCurrentProcess()->ImageFileName,
  531. WakeupThread,
  532. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  533. LpcpPrint(( "failed. MessageId == %u Client Id: %x.%x\n",
  534. CapturedRequestMessage.MessageId,
  535. CapturedRequestMessage.ClientId.UniqueProcess,
  536. CapturedRequestMessage.ClientId.UniqueThread ));
  537. LpcpPrint(( " Thread MessageId == %u Client Id: %x.%x\n",
  538. WakeupThread->LpcReplyMessageId,
  539. WakeupThread->Cid.UniqueProcess,
  540. WakeupThread->Cid.UniqueThread ));
  541. #if DBG
  542. if (LpcpStopOnReplyMismatch) {
  543. DbgBreakPoint();
  544. }
  545. #endif
  546. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  547. ObDereferenceObject( WakeupThread );
  548. ObDereferenceObject( PortObject );
  549. return STATUS_REPLY_MESSAGE_MISMATCH;
  550. }
  551. //
  552. // Copy over the text of the message
  553. //
  554. try {
  555. LpcpMoveMessage( &Msg->Request,
  556. &CapturedRequestMessage,
  557. (RequestMessage + 1),
  558. MsgType,
  559. &CurrentThread->Cid );
  560. if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
  561. PPORT_DATA_INFORMATION DataInfo;
  562. DataInfo = (PPORT_DATA_INFORMATION)(((PUCHAR)&Msg->Request) + CapturedRequestMessage.u2.s2.DataInfoOffset);
  563. if ( DataInfo->CountDataEntries != CapturedDataInfo.CountDataEntries ) {
  564. Status = STATUS_INVALID_PARAMETER;
  565. }
  566. }
  567. } except( EXCEPTION_EXECUTE_HANDLER ) {
  568. Status = GetExceptionCode();
  569. }
  570. if (!NT_SUCCESS( Status )) {
  571. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  572. ObDereferenceObject( WakeupThread );
  573. ObDereferenceObject( PortObject );
  574. return Status;
  575. }
  576. //
  577. // Under the protect of the global lock we'll get everything
  578. // ready for the callback
  579. //
  580. QueuePort = NULL;
  581. Msg->PortContext = NULL;
  582. if ((PortObject->Flags & PORT_TYPE) == SERVER_CONNECTION_PORT) {
  583. RundownPort = PortObject;
  584. } else {
  585. RundownPort = PortObject->ConnectedPort;
  586. if (RundownPort == NULL) {
  587. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  588. ObDereferenceObject( WakeupThread );
  589. ObDereferenceObject( PortObject );
  590. return STATUS_PORT_DISCONNECTED;
  591. }
  592. if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
  593. Msg->PortContext = RundownPort->PortContext;
  594. }
  595. }
  596. Msg->Request.CallbackId = LpcpGenerateCallbackId();
  597. LpcpTrace(( "%s CallBack Request (%s) Msg %lx (%u.%u) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
  598. PsGetCurrentProcess()->ImageFileName,
  599. LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
  600. Msg,
  601. Msg->Request.MessageId,
  602. Msg->Request.CallbackId,
  603. *((PULONG)(Msg+1)+0),
  604. *((PULONG)(Msg+1)+1),
  605. *((PULONG)(Msg+1)+2),
  606. *((PULONG)(Msg+1)+3),
  607. WakeupThread,
  608. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  609. //
  610. // Add an extra reference so LpcExitThread does not evaporate
  611. // the pointer before we get to the wait below
  612. //
  613. ObReferenceObject( WakeupThread );
  614. Msg->RepliedToThread = WakeupThread;
  615. WakeupThread->LpcReplyMessageId = 0;
  616. WakeupThread->LpcReplyMessage = (PVOID)Msg;
  617. //
  618. // Remove the thread from the reply rundown list as we are sending a callback
  619. //
  620. if (!IsListEmpty( &WakeupThread->LpcReplyChain )) {
  621. RemoveEntryList( &WakeupThread->LpcReplyChain );
  622. InitializeListHead( &WakeupThread->LpcReplyChain );
  623. }
  624. CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
  625. CurrentThread->LpcReplyMessage = NULL;
  626. InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
  627. LpcpSetPortToThread( CurrentThread, PortObject );
  628. if (NoImpersonate) {
  629. LpcpSetThreadAttributes(CurrentThread, LPCP_NO_IMPERSONATION);
  630. }
  631. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  632. LpcpReleaseLpcpLock();
  633. //
  634. // Wake up the thread that is waiting for an answer to its request
  635. // inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort
  636. //
  637. ReleaseSemaphore = &WakeupThread->LpcReplySemaphore;
  638. } else {
  639. //
  640. // A callback is not required, so continue setting up the
  641. // lpc message
  642. //
  643. try {
  644. LpcpMoveMessage( &Msg->Request,
  645. &CapturedRequestMessage,
  646. (RequestMessage + 1),
  647. MsgType,
  648. &CurrentThread->Cid );
  649. if (CapturedRequestMessage.u2.s2.DataInfoOffset != 0) {
  650. PPORT_DATA_INFORMATION DataInfo;
  651. DataInfo = (PPORT_DATA_INFORMATION)(((PUCHAR)&Msg->Request) + CapturedRequestMessage.u2.s2.DataInfoOffset);
  652. if ( DataInfo->CountDataEntries != CapturedDataInfo.CountDataEntries ) {
  653. LpcpFreeToPortZone( Msg, 0 );
  654. ObDereferenceObject( PortObject );
  655. return STATUS_INVALID_PARAMETER;
  656. }
  657. }
  658. } except( EXCEPTION_EXECUTE_HANDLER ) {
  659. LpcpFreeToPortZone( Msg, 0 );
  660. ObDereferenceObject( PortObject );
  661. return GetExceptionCode();
  662. }
  663. //
  664. // Acquire the global Lpc mutex that guards the LpcReplyMessage
  665. // field of the thread and the request message queue. Stamp the
  666. // request message with a serial number, insert the message at
  667. // the tail of the request message queue and remember the address
  668. // of the message in the LpcReplyMessage field for the current thread.
  669. //
  670. LpcpAcquireLpcpLockByThread(CurrentThread);
  671. Msg->PortContext = NULL;
  672. if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
  673. QueuePort = PortObject->ConnectedPort;
  674. if (QueuePort == NULL) {
  675. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  676. ObDereferenceObject( PortObject );
  677. return STATUS_PORT_DISCONNECTED;
  678. }
  679. RundownPort = QueuePort;
  680. if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
  681. Msg->PortContext = QueuePort->PortContext;
  682. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  683. if (ConnectionPort == NULL) {
  684. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  685. ObDereferenceObject( PortObject );
  686. return STATUS_PORT_DISCONNECTED;
  687. }
  688. } else if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
  689. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  690. if (ConnectionPort == NULL) {
  691. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  692. ObDereferenceObject( PortObject );
  693. return STATUS_PORT_DISCONNECTED;
  694. }
  695. }
  696. if (ConnectionPort) {
  697. ObReferenceObject( ConnectionPort );
  698. }
  699. } else {
  700. QueuePort = PortObject;
  701. RundownPort = PortObject;
  702. }
  703. //
  704. // Stamp the request message with a serial number, insert the message
  705. // at the tail of the request message queue
  706. //
  707. Msg->RepliedToThread = NULL;
  708. Msg->Request.MessageId = LpcpGenerateMessageId();
  709. Msg->Request.CallbackId = 0;
  710. Msg->SenderPort = PortObject;
  711. CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
  712. CurrentThread->LpcReplyMessage = NULL;
  713. InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
  714. InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
  715. LpcpSetPortToThread(CurrentThread, PortObject);
  716. if (NoImpersonate) {
  717. LpcpSetThreadAttributes(CurrentThread, LPCP_NO_IMPERSONATION);
  718. }
  719. LpcpTrace(( "%s Send Request (%s) Msg %lx (%u) [%08x %08x %08x %08x] to Port %lx (%s)\n",
  720. PsGetCurrentProcess()->ImageFileName,
  721. LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
  722. Msg,
  723. Msg->Request.MessageId,
  724. *((PULONG)(Msg+1)+0),
  725. *((PULONG)(Msg+1)+1),
  726. *((PULONG)(Msg+1)+2),
  727. *((PULONG)(Msg+1)+3),
  728. QueuePort,
  729. LpcpGetCreatorName( QueuePort )));
  730. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  731. LpcpReleaseLpcpLock();
  732. //
  733. // Increment the request message queue semaphore by one for
  734. // the newly inserted request message.
  735. //
  736. ReleaseSemaphore = QueuePort->MsgQueue.Semaphore;
  737. //
  738. // If port is waitable then set the event that someone could
  739. // be waiting on
  740. //
  741. if ( QueuePort->Flags & PORT_WAITABLE ) {
  742. KeSetEvent( &QueuePort->WaitEvent,
  743. LPC_RELEASE_WAIT_INCREMENT,
  744. FALSE );
  745. }
  746. }
  747. //
  748. // At this point we've enqueued our request and if necessary
  749. // set ourselves up for the callback or reply.
  750. //
  751. // So now wake up the other end
  752. //
  753. Status = KeReleaseSemaphore( ReleaseSemaphore,
  754. 1,
  755. 1,
  756. FALSE );
  757. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  758. if (CallbackRequest) {
  759. ObDereferenceObject( WakeupThread );
  760. }
  761. //
  762. // And wait for a reply
  763. //
  764. Status = KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  765. WrLpcReply,
  766. PreviousMode,
  767. FALSE,
  768. NULL );
  769. if (Status == STATUS_USER_APC) {
  770. //
  771. // if the semaphore is signaled, then clear it
  772. //
  773. if (KeReadStateSemaphore( &CurrentThread->LpcReplySemaphore )) {
  774. KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  775. WrExecutive,
  776. KernelMode,
  777. FALSE,
  778. NULL );
  779. Status = STATUS_SUCCESS;
  780. }
  781. }
  782. //
  783. // Acquire the LPC mutex. Remove the reply message from the current thread
  784. //
  785. LpcpAcquireLpcpLockByThread(CurrentThread);
  786. Msg = LpcpGetThreadMessage(CurrentThread);
  787. CurrentThread->LpcReplyMessage = NULL;
  788. CurrentThread->LpcReplyMessageId = 0;
  789. //
  790. // Remove the thread from the reply rundown list in case we did not wakeup due to
  791. // a reply
  792. //
  793. if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
  794. RemoveEntryList( &CurrentThread->LpcReplyChain );
  795. InitializeListHead( &CurrentThread->LpcReplyChain );
  796. }
  797. #if DBG
  798. if (Status == STATUS_SUCCESS && Msg != NULL) {
  799. LpcpTrace(( "%s Got Reply Msg %lx (%u) [%08x %08x %08x %08x] for Thread %lx (%s)\n",
  800. PsGetCurrentProcess()->ImageFileName,
  801. Msg,
  802. Msg->Request.MessageId,
  803. *((PULONG)(Msg+1)+0),
  804. *((PULONG)(Msg+1)+1),
  805. *((PULONG)(Msg+1)+2),
  806. *((PULONG)(Msg+1)+3),
  807. CurrentThread,
  808. THREAD_TO_PROCESS( CurrentThread )->ImageFileName ));
  809. if (!IsListEmpty( &Msg->Entry )) {
  810. LpcpTrace(( "Reply Msg %lx has non-empty list entry\n", Msg ));
  811. }
  812. }
  813. #endif
  814. LpcpReleaseLpcpLock();
  815. //
  816. // If the wait succeeded, copy the reply to the reply buffer.
  817. //
  818. if (Status == STATUS_SUCCESS) {
  819. if (Msg != NULL) {
  820. try {
  821. LpcpMoveMessage( ReplyMessage,
  822. &Msg->Request,
  823. (&Msg->Request) + 1,
  824. 0,
  825. NULL );
  826. } except( EXCEPTION_EXECUTE_HANDLER ) {
  827. Status = GetExceptionCode();
  828. }
  829. //
  830. // Acquire the LPC mutex and decrement the reference count for the
  831. // message. If the reference count goes to zero the message will be
  832. // deleted.
  833. //
  834. if (((Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) == LPC_REQUEST) &&
  835. (Msg->Request.u2.s2.DataInfoOffset != 0)) {
  836. LpcpSaveDataInfoMessage( PortObject, Msg, 0 );
  837. } else {
  838. LpcpFreeToPortZone( Msg, 0 );
  839. }
  840. } else {
  841. Status = STATUS_LPC_REPLY_LOST;
  842. }
  843. } else {
  844. //
  845. // Wait failed, free the message if any.
  846. //
  847. LpcpTrace(( "%s NtRequestWaitReply wait failed - Status == %lx\n",
  848. PsGetCurrentProcess()->ImageFileName,
  849. Status ));
  850. if (Msg != NULL) {
  851. LpcpFreeToPortZone( Msg, 0);
  852. }
  853. }
  854. ObDereferenceObject( PortObject );
  855. if (ConnectionPort) {
  856. ObDereferenceObject( ConnectionPort );
  857. }
  858. //
  859. // And return to our caller
  860. //
  861. return Status;
  862. }
  863. NTSTATUS
  864. LpcRequestPort (
  865. IN PVOID PortAddress,
  866. IN PPORT_MESSAGE RequestMessage
  867. )
  868. /*++
  869. Routine Description:
  870. This procedure is similar to NtRequestPort but without the Handle based
  871. interface.
  872. Arguments:
  873. PortAddress - Supplies a pointer to the communication port to send
  874. the request message to.
  875. RequestMessage - Specifies a pointer to the request message. The Type
  876. field of the message is set to LPC_DATAGRAM by the service.
  877. Return Value:
  878. NTSTATUS - A status code that indicates whether or not the operation was
  879. successful.
  880. --*/
  881. {
  882. PETHREAD CurrentThread;
  883. PLPCP_PORT_OBJECT PortObject = (PLPCP_PORT_OBJECT)PortAddress;
  884. PLPCP_PORT_OBJECT QueuePort;
  885. ULONG MsgType;
  886. PLPCP_MESSAGE Msg;
  887. KPROCESSOR_MODE PreviousMode;
  888. PLPCP_PORT_OBJECT ConnectionPort = NULL;
  889. PAGED_CODE();
  890. //
  891. // Get previous processor mode and validate parameters
  892. //
  893. PreviousMode = KeGetPreviousMode();
  894. if (RequestMessage->u2.s2.Type != 0) {
  895. MsgType = RequestMessage->u2.s2.Type & ~LPC_KERNELMODE_MESSAGE;
  896. if ((MsgType < LPC_DATAGRAM) ||
  897. (MsgType > LPC_CLIENT_DIED)) {
  898. return STATUS_INVALID_PARAMETER;
  899. }
  900. //
  901. // If previous mode is kernel, allow the LPC_KERNELMODE_MESSAGE
  902. // bit to be passed in message type field.
  903. //
  904. if ((PreviousMode == KernelMode) &&
  905. (RequestMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE)) {
  906. MsgType |= LPC_KERNELMODE_MESSAGE;
  907. }
  908. } else {
  909. MsgType = LPC_DATAGRAM;
  910. }
  911. if (RequestMessage->u2.s2.DataInfoOffset != 0) {
  912. return STATUS_INVALID_PARAMETER;
  913. }
  914. //
  915. // Validate the message length
  916. //
  917. if (((ULONG)RequestMessage->u1.s1.TotalLength > PortObject->MaxMessageLength) ||
  918. ((ULONG)RequestMessage->u1.s1.TotalLength <= (ULONG)RequestMessage->u1.s1.DataLength)) {
  919. return STATUS_PORT_MESSAGE_TOO_LONG;
  920. }
  921. //
  922. // Allocate a message block
  923. //
  924. Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( RequestMessage->u1.s1.TotalLength );
  925. if (Msg == NULL) {
  926. return STATUS_NO_MEMORY;
  927. }
  928. //
  929. // Fill in the message block.
  930. //
  931. Msg->RepliedToThread = NULL;
  932. Msg->PortContext = NULL;
  933. CurrentThread = PsGetCurrentThread();
  934. LpcpMoveMessage( &Msg->Request,
  935. RequestMessage,
  936. (RequestMessage + 1),
  937. MsgType,
  938. &CurrentThread->Cid );
  939. //
  940. // Acquire the global Lpc mutex that guards the LpcReplyMessage
  941. // field of the thread and the request message queue. Stamp the
  942. // request message with a serial number, insert the message at
  943. // the tail of the request message queue
  944. //
  945. LpcpAcquireLpcpLockByThread(CurrentThread);
  946. if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
  947. QueuePort = PortObject->ConnectedPort;
  948. if (QueuePort != NULL) {
  949. if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
  950. Msg->PortContext = QueuePort->PortContext;
  951. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  952. if (ConnectionPort == NULL) {
  953. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  954. return STATUS_PORT_DISCONNECTED;
  955. }
  956. //
  957. // In the case we don't have a CLIENT_COMMUNICATION_PORT nor
  958. // SERVER_COMMUNICATION_PORT we'll use the connection port
  959. // to queue messages.
  960. //
  961. } else if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
  962. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  963. if (ConnectionPort == NULL) {
  964. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  965. return STATUS_PORT_DISCONNECTED;
  966. }
  967. }
  968. if (ConnectionPort) {
  969. ObReferenceObject( ConnectionPort );
  970. }
  971. }
  972. } else {
  973. QueuePort = PortObject;
  974. }
  975. //
  976. // At this point we have an LPC message ready to send and if queue port is
  977. // not null then we have a port to actually send the message off to
  978. //
  979. if (QueuePort != NULL) {
  980. Msg->Request.MessageId = LpcpGenerateMessageId();
  981. Msg->Request.CallbackId = 0;
  982. Msg->SenderPort = PortObject;
  983. CurrentThread->LpcReplyMessageId = 0;
  984. InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
  985. LpcpTrace(( "%s Send DataGram (%s) Msg %lx [%08x %08x %08x %08x] to Port %lx (%s)\n",
  986. PsGetCurrentProcess()->ImageFileName,
  987. LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
  988. Msg,
  989. *((PULONG)(Msg+1)+0),
  990. *((PULONG)(Msg+1)+1),
  991. *((PULONG)(Msg+1)+2),
  992. *((PULONG)(Msg+1)+3),
  993. QueuePort,
  994. LpcpGetCreatorName( QueuePort )));
  995. //
  996. // Release the mutex, increment the request message queue
  997. // semaphore by one for the newly inserted request message,
  998. // then exit the critical region.
  999. //
  1000. // Disable APCs to prevent this thread from being suspended
  1001. // before being able to release the semaphore.
  1002. //
  1003. KeEnterCriticalRegionThread(&CurrentThread->Tcb);
  1004. LpcpReleaseLpcpLock();
  1005. KeReleaseSemaphore( QueuePort->MsgQueue.Semaphore,
  1006. LPC_RELEASE_WAIT_INCREMENT,
  1007. 1L,
  1008. FALSE );
  1009. if ( QueuePort->Flags & PORT_WAITABLE ) {
  1010. KeSetEvent( &QueuePort->WaitEvent,
  1011. LPC_RELEASE_WAIT_INCREMENT,
  1012. FALSE );
  1013. }
  1014. KeLeaveCriticalRegionThread(&CurrentThread->Tcb);
  1015. if (ConnectionPort) {
  1016. ObDereferenceObject( ConnectionPort );
  1017. }
  1018. return STATUS_SUCCESS;
  1019. }
  1020. //
  1021. // At this point we have a message but not a valid port to queue it off
  1022. // to so we'll release the unused message and release our mutex.
  1023. //
  1024. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1025. if (ConnectionPort) {
  1026. ObDereferenceObject( ConnectionPort );
  1027. }
  1028. //
  1029. // And return the error status to our caller
  1030. //
  1031. return STATUS_PORT_DISCONNECTED;
  1032. }
  1033. NTSTATUS
  1034. LpcpRequestWaitReplyPort (
  1035. IN PVOID PortAddress,
  1036. IN PPORT_MESSAGE RequestMessage,
  1037. OUT PPORT_MESSAGE ReplyMessage,
  1038. IN KPROCESSOR_MODE AccessMode
  1039. )
  1040. /*++
  1041. Routine Description:
  1042. This procedure is similar to NtRequestWaitReplyPort but without the
  1043. handle based interface
  1044. Arguments:
  1045. PortAddress - Supplies the communication port object to send the
  1046. request message to.
  1047. RequestMessage - Specifies a pointer to a request message to send.
  1048. ReplyMessage - Specifies the address of a variable that will receive the
  1049. reply message. This parameter may point to the same buffer as the
  1050. RequestMessage parameter.
  1051. Return Value:
  1052. NTSTATUS - A status code that indicates whether or not the operation was
  1053. successful.
  1054. --*/
  1055. {
  1056. PLPCP_PORT_OBJECT PortObject = (PLPCP_PORT_OBJECT)PortAddress;
  1057. PLPCP_PORT_OBJECT QueuePort;
  1058. PLPCP_PORT_OBJECT RundownPort;
  1059. PKSEMAPHORE ReleaseSemaphore;
  1060. NTSTATUS Status;
  1061. ULONG MsgType;
  1062. PLPCP_MESSAGE Msg;
  1063. PETHREAD CurrentThread;
  1064. PETHREAD WakeupThread;
  1065. BOOLEAN CallbackRequest;
  1066. KPROCESSOR_MODE PreviousMode;
  1067. PLPCP_PORT_OBJECT ConnectionPort = NULL;
  1068. PAGED_CODE();
  1069. CurrentThread = PsGetCurrentThread();
  1070. if (CurrentThread->LpcExitThreadCalled) {
  1071. return STATUS_THREAD_IS_TERMINATING;
  1072. }
  1073. //
  1074. // Get previous processor mode and validate parameters
  1075. //
  1076. PreviousMode = KeGetPreviousMode();
  1077. MsgType = RequestMessage->u2.s2.Type & ~LPC_KERNELMODE_MESSAGE;
  1078. CallbackRequest = FALSE;
  1079. switch (MsgType) {
  1080. case 0:
  1081. MsgType = LPC_REQUEST;
  1082. break;
  1083. case LPC_REQUEST:
  1084. CallbackRequest = TRUE;
  1085. break;
  1086. case LPC_CLIENT_DIED:
  1087. case LPC_PORT_CLOSED:
  1088. case LPC_EXCEPTION:
  1089. case LPC_DEBUG_EVENT:
  1090. case LPC_ERROR_EVENT:
  1091. break;
  1092. default :
  1093. return STATUS_INVALID_PARAMETER;
  1094. }
  1095. //
  1096. // Allow the LPC_KERNELMODE_MESSAGE
  1097. // bit to be passed in message type field. Don't check the previous mode !!!
  1098. //
  1099. if ( RequestMessage->u2.s2.Type & LPC_KERNELMODE_MESSAGE) {
  1100. MsgType |= LPC_KERNELMODE_MESSAGE;
  1101. }
  1102. RequestMessage->u2.s2.Type = (CSHORT)MsgType;
  1103. //
  1104. // Validate the message length
  1105. //
  1106. if (((ULONG)RequestMessage->u1.s1.TotalLength > PortObject->MaxMessageLength) ||
  1107. ((ULONG)RequestMessage->u1.s1.TotalLength <= (ULONG)RequestMessage->u1.s1.DataLength)) {
  1108. return STATUS_PORT_MESSAGE_TOO_LONG;
  1109. }
  1110. //
  1111. // Determine which port to queue the message to and get client
  1112. // port context if client sending to server. Also validate
  1113. // length of message being sent.
  1114. //
  1115. Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( RequestMessage->u1.s1.TotalLength );
  1116. if (Msg == NULL) {
  1117. return STATUS_NO_MEMORY;
  1118. }
  1119. if (CallbackRequest) {
  1120. //
  1121. // Check for a valid request message id
  1122. //
  1123. if (RequestMessage->MessageId == 0) {
  1124. LpcpFreeToPortZone( Msg, 0 );
  1125. return STATUS_INVALID_PARAMETER;
  1126. }
  1127. //
  1128. // Translate the ClientId from the request into a
  1129. // thread pointer. This is a referenced pointer to keep the thread
  1130. // from evaporating out from under us.
  1131. //
  1132. Status = PsLookupProcessThreadByCid( &RequestMessage->ClientId,
  1133. NULL,
  1134. &WakeupThread );
  1135. if (!NT_SUCCESS( Status )) {
  1136. LpcpFreeToPortZone( Msg, 0 );
  1137. return Status;
  1138. }
  1139. //
  1140. // Acquire the mutex that guards the LpcReplyMessage field of
  1141. // the thread and get the pointer to the message that the thread
  1142. // is waiting for a reply to.
  1143. //
  1144. LpcpAcquireLpcpLockByThread(CurrentThread);
  1145. //
  1146. // See if the thread is waiting for a reply to the message
  1147. // specified on this call. If not then a bogus message
  1148. // has been specified, so release the mutex, dereference the thread
  1149. // and return failure.
  1150. //
  1151. if ((WakeupThread->LpcReplyMessageId != RequestMessage->MessageId)
  1152. ||
  1153. ((LpcpGetThreadMessage(WakeupThread) != NULL) &&
  1154. (LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
  1155. ||
  1156. (!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY)) ) {
  1157. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1158. ObDereferenceObject( WakeupThread );
  1159. return STATUS_REPLY_MESSAGE_MISMATCH;
  1160. }
  1161. QueuePort = NULL;
  1162. Msg->PortContext = NULL;
  1163. if ((PortObject->Flags & PORT_TYPE) == SERVER_CONNECTION_PORT) {
  1164. RundownPort = PortObject;
  1165. } else {
  1166. RundownPort = PortObject->ConnectedPort;
  1167. if (RundownPort == NULL) {
  1168. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1169. ObDereferenceObject( WakeupThread );
  1170. return STATUS_PORT_DISCONNECTED;
  1171. }
  1172. if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
  1173. Msg->PortContext = RundownPort->PortContext;
  1174. }
  1175. }
  1176. //
  1177. // Allocate and initialize a request message
  1178. //
  1179. LpcpMoveMessage( &Msg->Request,
  1180. RequestMessage,
  1181. (RequestMessage + 1),
  1182. 0,
  1183. &CurrentThread->Cid );
  1184. Msg->Request.CallbackId = LpcpGenerateCallbackId();
  1185. LpcpTrace(( "%s CallBack Request (%s) Msg %lx (%u.%u) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
  1186. PsGetCurrentProcess()->ImageFileName,
  1187. LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
  1188. Msg,
  1189. Msg->Request.MessageId,
  1190. Msg->Request.CallbackId,
  1191. *((PULONG)(Msg+1)+0),
  1192. *((PULONG)(Msg+1)+1),
  1193. *((PULONG)(Msg+1)+2),
  1194. *((PULONG)(Msg+1)+3),
  1195. WakeupThread,
  1196. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  1197. //
  1198. // Add an extra reference so LpcExitThread does not evaporate
  1199. // the pointer before we get to the wait below
  1200. //
  1201. ObReferenceObject( WakeupThread );
  1202. Msg->RepliedToThread = WakeupThread;
  1203. WakeupThread->LpcReplyMessageId = 0;
  1204. WakeupThread->LpcReplyMessage = (PVOID)Msg;
  1205. //
  1206. // Remove the thread from the reply rundown list as we are sending a callback
  1207. //
  1208. if (!IsListEmpty( &WakeupThread->LpcReplyChain )) {
  1209. RemoveEntryList( &WakeupThread->LpcReplyChain );
  1210. InitializeListHead( &WakeupThread->LpcReplyChain );
  1211. }
  1212. CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
  1213. CurrentThread->LpcReplyMessage = NULL;
  1214. InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
  1215. LpcpSetPortToThread(CurrentThread, PortObject);
  1216. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  1217. LpcpReleaseLpcpLock();
  1218. //
  1219. // Wake up the thread that is waiting for an answer to its request
  1220. // inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort
  1221. //
  1222. ReleaseSemaphore = &WakeupThread->LpcReplySemaphore;
  1223. } else {
  1224. //
  1225. // There is no callback requested
  1226. //
  1227. LpcpMoveMessage( &Msg->Request,
  1228. RequestMessage,
  1229. (RequestMessage + 1),
  1230. 0,
  1231. &CurrentThread->Cid );
  1232. //
  1233. // Acquire the global Lpc mutex that guards the LpcReplyMessage
  1234. // field of the thread and the request message queue. Stamp the
  1235. // request message with a serial number, insert the message at
  1236. // the tail of the request message queue and remember the address
  1237. // of the message in the LpcReplyMessage field for the current thread.
  1238. //
  1239. LpcpAcquireLpcpLockByThread(CurrentThread);
  1240. if ((CurrentThread->LpcReplyMessage != NULL)
  1241. ||
  1242. (CurrentThread->LpcReplyMessageId != 0)
  1243. ||
  1244. (CurrentThread->KeyedEventInUse)) {
  1245. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1246. return STATUS_UNSUCCESSFUL;
  1247. }
  1248. Msg->PortContext = NULL;
  1249. if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
  1250. QueuePort = PortObject->ConnectedPort;
  1251. if (QueuePort == NULL) {
  1252. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1253. return STATUS_PORT_DISCONNECTED;
  1254. }
  1255. RundownPort = QueuePort;
  1256. if ((PortObject->Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT) {
  1257. Msg->PortContext = QueuePort->PortContext;
  1258. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  1259. if (ConnectionPort == NULL) {
  1260. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1261. return STATUS_PORT_DISCONNECTED;
  1262. }
  1263. } else if ((PortObject->Flags & PORT_TYPE) != SERVER_COMMUNICATION_PORT) {
  1264. ConnectionPort = QueuePort = PortObject->ConnectionPort;
  1265. if (ConnectionPort == NULL) {
  1266. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1267. return STATUS_PORT_DISCONNECTED;
  1268. }
  1269. }
  1270. if (ConnectionPort) {
  1271. ObReferenceObject( ConnectionPort );
  1272. }
  1273. } else {
  1274. if ((PortObject->Flags & PORT_NAME_DELETED) != 0) {
  1275. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1276. return STATUS_PORT_DISCONNECTED;
  1277. }
  1278. QueuePort = PortObject;
  1279. RundownPort = PortObject;
  1280. }
  1281. Msg->RepliedToThread = NULL;
  1282. Msg->Request.MessageId = LpcpGenerateMessageId();
  1283. Msg->Request.CallbackId = 0;
  1284. Msg->SenderPort = PortObject;
  1285. CurrentThread->LpcReplyMessageId = Msg->Request.MessageId;
  1286. CurrentThread->LpcReplyMessage = NULL;
  1287. InsertTailList( &QueuePort->MsgQueue.ReceiveHead, &Msg->Entry );
  1288. InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
  1289. LpcpSetPortToThread(CurrentThread, PortObject);
  1290. LpcpTrace(( "%s Send Request (%s) Msg %lx (%u) [%08x %08x %08x %08x] to Port %lx (%s)\n",
  1291. PsGetCurrentProcess()->ImageFileName,
  1292. LpcpMessageTypeName[ Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE ],
  1293. Msg,
  1294. Msg->Request.MessageId,
  1295. *((PULONG)(Msg+1)+0),
  1296. *((PULONG)(Msg+1)+1),
  1297. *((PULONG)(Msg+1)+2),
  1298. *((PULONG)(Msg+1)+3),
  1299. QueuePort,
  1300. LpcpGetCreatorName( QueuePort )));
  1301. KeEnterCriticalRegionThread (&CurrentThread->Tcb);
  1302. LpcpReleaseLpcpLock();
  1303. //
  1304. // Increment the request message queue semaphore by one for
  1305. // the newly inserted request message. Release the spin
  1306. // lock, while remaining at the dispatcher IRQL. Then wait for the
  1307. // reply to this request by waiting on the LpcReplySemaphore
  1308. // for the current thread.
  1309. //
  1310. ReleaseSemaphore = QueuePort->MsgQueue.Semaphore;
  1311. if ( QueuePort->Flags & PORT_WAITABLE ) {
  1312. KeSetEvent( &QueuePort->WaitEvent,
  1313. LPC_RELEASE_WAIT_INCREMENT,
  1314. FALSE );
  1315. }
  1316. }
  1317. //
  1318. // At this point we've enqueued our request and if necessary
  1319. // set ourselves up for the callback or reply.
  1320. //
  1321. // So now wake up the other end
  1322. //
  1323. Status = KeReleaseSemaphore( ReleaseSemaphore,
  1324. 1,
  1325. 1,
  1326. FALSE );
  1327. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  1328. if (CallbackRequest) {
  1329. ObDereferenceObject( WakeupThread );
  1330. }
  1331. //
  1332. // And wait for a reply
  1333. //
  1334. Status = KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  1335. WrLpcReply,
  1336. AccessMode,
  1337. FALSE,
  1338. NULL );
  1339. if (Status == STATUS_USER_APC) {
  1340. //
  1341. // if the semaphore is signaled, then clear it
  1342. //
  1343. if (KeReadStateSemaphore( &CurrentThread->LpcReplySemaphore )) {
  1344. KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  1345. WrExecutive,
  1346. KernelMode,
  1347. FALSE,
  1348. NULL );
  1349. Status = STATUS_SUCCESS;
  1350. }
  1351. }
  1352. //
  1353. // Acquire the LPC mutex. Remove the reply message from the current thread
  1354. //
  1355. LpcpAcquireLpcpLockByThread(CurrentThread);
  1356. Msg = LpcpGetThreadMessage(CurrentThread);
  1357. CurrentThread->LpcReplyMessage = NULL;
  1358. CurrentThread->LpcReplyMessageId = 0;
  1359. //
  1360. // Remove the thread from the reply rundown list in case we did not wakeup due to
  1361. // a reply
  1362. //
  1363. if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
  1364. RemoveEntryList( &CurrentThread->LpcReplyChain );
  1365. InitializeListHead( &CurrentThread->LpcReplyChain );
  1366. }
  1367. #if DBG
  1368. if (Msg != NULL) {
  1369. LpcpTrace(( "%s Got Reply Msg %lx (%u) [%08x %08x %08x %08x] for Thread %lx (%s)\n",
  1370. PsGetCurrentProcess()->ImageFileName,
  1371. Msg,
  1372. Msg->Request.MessageId,
  1373. *((PULONG)(Msg+1)+0),
  1374. *((PULONG)(Msg+1)+1),
  1375. *((PULONG)(Msg+1)+2),
  1376. *((PULONG)(Msg+1)+3),
  1377. CurrentThread,
  1378. THREAD_TO_PROCESS( CurrentThread )->ImageFileName ));
  1379. }
  1380. #endif
  1381. LpcpReleaseLpcpLock();
  1382. //
  1383. // If the wait succeeded, copy the reply to the reply buffer.
  1384. //
  1385. if (Status == STATUS_SUCCESS) {
  1386. if (Msg != NULL) {
  1387. LpcpMoveMessage( ReplyMessage,
  1388. &Msg->Request,
  1389. (&Msg->Request) + 1,
  1390. 0,
  1391. NULL );
  1392. //
  1393. // Acquire the LPC mutex and decrement the reference count for the
  1394. // message. If the reference count goes to zero the message will be
  1395. // deleted.
  1396. //
  1397. LpcpAcquireLpcpLockByThread(CurrentThread);
  1398. if (Msg->RepliedToThread != NULL) {
  1399. ObDereferenceObject( Msg->RepliedToThread );
  1400. Msg->RepliedToThread = NULL;
  1401. }
  1402. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  1403. } else {
  1404. Status = STATUS_LPC_REPLY_LOST;
  1405. }
  1406. } else {
  1407. //
  1408. // Wait failed, free the message if any.
  1409. //
  1410. if (Msg != NULL) {
  1411. LpcpFreeToPortZone( Msg, 0 );
  1412. }
  1413. }
  1414. if (ConnectionPort) {
  1415. ObDereferenceObject( ConnectionPort );
  1416. }
  1417. //
  1418. // And return to our caller
  1419. //
  1420. return Status;
  1421. }
  1422. NTSTATUS
  1423. LpcRequestWaitReplyPort (
  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. KernelMode
  1433. );
  1434. }
  1435. NTSTATUS
  1436. LpcRequestWaitReplyPortEx (
  1437. IN PVOID PortAddress,
  1438. IN PPORT_MESSAGE RequestMessage,
  1439. OUT PPORT_MESSAGE ReplyMessage
  1440. )
  1441. {
  1442. return LpcpRequestWaitReplyPort( PortAddress,
  1443. RequestMessage,
  1444. ReplyMessage,
  1445. KeGetPreviousMode()
  1446. );
  1447. }