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.

721 lines
21 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. lpcrecv.c
  5. Abstract:
  6. Local Inter-Process Communication (LPC) receive system services.
  7. Author:
  8. Steve Wood (stevewo) 15-May-1989
  9. Revision History:
  10. --*/
  11. #include "lpcp.h"
  12. #ifdef ALLOC_PRAGMA
  13. #pragma alloc_text(PAGE,NtReplyWaitReceivePort)
  14. #pragma alloc_text(PAGE,NtReplyWaitReceivePortEx)
  15. #endif
  16. NTSTATUS
  17. NtReplyWaitReceivePort (
  18. IN HANDLE PortHandle,
  19. OUT PVOID *PortContext OPTIONAL,
  20. IN PPORT_MESSAGE ReplyMessage OPTIONAL,
  21. OUT PPORT_MESSAGE ReceiveMessage
  22. )
  23. /*++
  24. Routine Description:
  25. This procedure is used by the server process to wait for a message from a
  26. client process
  27. A client and server process can receive messages using the
  28. NtReplyWaitReceivePort service:
  29. If the ReplyMessage parameter is specified, then the reply will be sent
  30. using NtReplyPort.
  31. If the PortHandle parameter specifies a connection port, then the receive
  32. will return whenever a message is sent to a server communication port that
  33. does not have its own receive queue and the message is therefore queued to
  34. the receive queue of the connection port.
  35. If the PortHandle parameter specifies a server communication port that
  36. does not have a receive queue, then behaves as if the associated
  37. connection port handle was specified. Otherwise the receive will return
  38. whenever message is placed in the receive queue associated with the
  39. server communication port.
  40. The received message will be returned in the variable specified by the
  41. ReceiveMessage parameter. If the MapInfoOffset field of the reply message
  42. is non-zero, then the PORT_MAP_INFORMATION structure it points to will be
  43. processed and the relevant pages will be mapped into the caller's address
  44. space. The service returns an error if there is not enough room in the
  45. caller's address space to accomodate the mappings.
  46. Arguments:
  47. PortHandle - Specifies the handle of the connection or communication port
  48. to do the receive from.
  49. PortContext - Specifies an optional pointer to a variable that is to
  50. receive the context value associated with the communication port that
  51. the message is being received from. This context variable was
  52. specified on the call to the NtAcceptConnectPort service.
  53. ReplyMessage - This optional parameter specifies the address of a reply
  54. message to be sent. The ClientId and MessageId fields determine which
  55. thread will get the reply. See description of NtReplyPort for how the
  56. reply is sent. The reply is sent before blocking for the receive.
  57. ReceiveMessage - Specifies the address of a variable to receive the
  58. message.
  59. Return Value:
  60. Status code that indicates whether or not the operation was successful.
  61. --*/
  62. {
  63. PAGED_CODE();
  64. return NtReplyWaitReceivePortEx (PortHandle,
  65. PortContext,
  66. ReplyMessage,
  67. ReceiveMessage,
  68. NULL);
  69. }
  70. NTSTATUS
  71. NtReplyWaitReceivePortEx(
  72. IN HANDLE PortHandle,
  73. OUT PVOID *PortContext OPTIONAL,
  74. IN PPORT_MESSAGE ReplyMessage OPTIONAL,
  75. OUT PPORT_MESSAGE ReceiveMessage,
  76. IN PLARGE_INTEGER Timeout OPTIONAL
  77. )
  78. /*++
  79. Routine Description:
  80. See NtReplyWaitReceivePort.
  81. Arguments:
  82. See NtReplyWaitReceivePort.
  83. Timeout - Supplies an optional timeout value to use when waiting for a
  84. receive.
  85. Return Value:
  86. See NtReplyWaitReceivePort.
  87. --*/
  88. {
  89. PLPCP_PORT_OBJECT PortObject;
  90. PLPCP_PORT_OBJECT ReceivePort;
  91. PORT_MESSAGE CapturedReplyMessage;
  92. KPROCESSOR_MODE PreviousMode;
  93. KPROCESSOR_MODE WaitMode;
  94. NTSTATUS Status;
  95. PLPCP_MESSAGE Msg;
  96. PETHREAD CurrentThread;
  97. PETHREAD WakeupThread;
  98. LARGE_INTEGER TimeoutValue ;
  99. PLPCP_PORT_OBJECT ConnectionPort = NULL;
  100. PAGED_CODE();
  101. CurrentThread = PsGetCurrentThread();
  102. TimeoutValue.QuadPart = 0 ;
  103. //
  104. // Get previous processor mode
  105. //
  106. PreviousMode = KeGetPreviousMode();
  107. WaitMode = PreviousMode;
  108. if (PreviousMode != KernelMode) {
  109. try {
  110. if (ARGUMENT_PRESENT( PortContext )) {
  111. ProbeForWriteUlong( (PULONG)PortContext );
  112. }
  113. if (ARGUMENT_PRESENT( ReplyMessage)) {
  114. ProbeForReadSmallStructure( ReplyMessage,
  115. sizeof( *ReplyMessage ),
  116. sizeof( ULONG ));
  117. CapturedReplyMessage = *ReplyMessage;
  118. }
  119. if (ARGUMENT_PRESENT( Timeout )) {
  120. TimeoutValue = ProbeAndReadLargeInteger( Timeout );
  121. Timeout = &TimeoutValue ;
  122. }
  123. ProbeForWriteSmallStructure( ReceiveMessage,
  124. sizeof( *ReceiveMessage ),
  125. sizeof( ULONG ));
  126. } except( EXCEPTION_EXECUTE_HANDLER ) {
  127. return GetExceptionCode();
  128. }
  129. } else {
  130. //
  131. // Kernel mode threads call with wait mode of user so that their
  132. // kernel // stacks are swappable. Main consumer of this is
  133. // SepRmCommandThread
  134. //
  135. if ( IS_SYSTEM_THREAD(CurrentThread) ) {
  136. WaitMode = UserMode;
  137. }
  138. if (ARGUMENT_PRESENT( ReplyMessage)) {
  139. CapturedReplyMessage = *ReplyMessage;
  140. }
  141. }
  142. if (ARGUMENT_PRESENT( ReplyMessage)) {
  143. //
  144. // Make sure DataLength is valid with respect to header size and total
  145. // length
  146. //
  147. if ((((CLONG)CapturedReplyMessage.u1.s1.DataLength) + sizeof( PORT_MESSAGE )) >
  148. ((CLONG)CapturedReplyMessage.u1.s1.TotalLength)) {
  149. return STATUS_INVALID_PARAMETER;
  150. }
  151. //
  152. // Make sure the user didn't give us a bogus reply message id
  153. //
  154. if (CapturedReplyMessage.MessageId == 0) {
  155. return STATUS_INVALID_PARAMETER;
  156. }
  157. }
  158. //
  159. // Reference the port object by handle and if that doesn't work try
  160. // a waitable port object type
  161. //
  162. Status = LpcpReferencePortObject( PortHandle,
  163. 0,
  164. PreviousMode,
  165. &PortObject );
  166. if (!NT_SUCCESS( Status )) {
  167. Status = ObReferenceObjectByHandle( PortHandle,
  168. 0,
  169. LpcWaitablePortObjectType,
  170. PreviousMode,
  171. &PortObject,
  172. NULL );
  173. if ( !NT_SUCCESS( Status ) ) {
  174. return Status;
  175. }
  176. }
  177. //
  178. // Validate the message length
  179. //
  180. if (ARGUMENT_PRESENT( ReplyMessage )) {
  181. if (((ULONG)CapturedReplyMessage.u1.s1.TotalLength > PortObject->MaxMessageLength) ||
  182. ((ULONG)CapturedReplyMessage.u1.s1.TotalLength <= (ULONG)CapturedReplyMessage.u1.s1.DataLength)) {
  183. ObDereferenceObject( PortObject );
  184. return STATUS_PORT_MESSAGE_TOO_LONG;
  185. }
  186. }
  187. //
  188. // The receive port we use is either the connection port for the port
  189. // object we were given if we were given a client communication port then
  190. // we expect to receive the reply on the communication port itself.
  191. //
  192. if ((PortObject->Flags & PORT_TYPE) != CLIENT_COMMUNICATION_PORT) {
  193. LpcpAcquireLpcpLockByThread(CurrentThread);
  194. ConnectionPort = ReceivePort = PortObject->ConnectionPort;
  195. if (ConnectionPort == NULL) {
  196. LpcpReleaseLpcpLock();
  197. ObDereferenceObject( PortObject );
  198. return STATUS_PORT_DISCONNECTED;
  199. }
  200. ObReferenceObject( ConnectionPort );
  201. LpcpReleaseLpcpLock();
  202. } else {
  203. ReceivePort = PortObject;
  204. }
  205. //
  206. // If ReplyMessage argument present, then send reply
  207. //
  208. if (ARGUMENT_PRESENT( ReplyMessage )) {
  209. //
  210. // Translate the ClientId from the connection request into a
  211. // thread pointer. This is a referenced pointer to keep the thread
  212. // from evaporating out from under us.
  213. //
  214. Status = PsLookupProcessThreadByCid( &CapturedReplyMessage.ClientId,
  215. NULL,
  216. &WakeupThread );
  217. if (!NT_SUCCESS( Status )) {
  218. ObDereferenceObject( PortObject );
  219. if (ConnectionPort) {
  220. ObDereferenceObject(ConnectionPort);
  221. }
  222. return Status;
  223. }
  224. //
  225. // Acquire the global Lpc mutex that guards the LpcReplyMessage
  226. // field of the thread and get the pointer to the message that
  227. // the thread is waiting for a reply to.
  228. //
  229. Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedReplyMessage.u1.s1.TotalLength );
  230. if (Msg == NULL) {
  231. if (ConnectionPort) {
  232. ObDereferenceObject(ConnectionPort);
  233. }
  234. ObDereferenceObject( WakeupThread );
  235. ObDereferenceObject( PortObject );
  236. return STATUS_NO_MEMORY;
  237. }
  238. LpcpAcquireLpcpLockByThread(CurrentThread);
  239. //
  240. // See if the thread is waiting for a reply to the message
  241. // specified on this call. If not then a bogus message
  242. // has been specified, so release the mutex, dereference the thread
  243. // and return failure.
  244. //
  245. // We also fail this request if the caller isn't replying to a request
  246. // message. For example, if the caller is replying to a connection
  247. // request
  248. //
  249. if ((WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId)
  250. ||
  251. ((LpcpGetThreadMessage(WakeupThread) != NULL) &&
  252. (LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
  253. ||
  254. (!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY))) {
  255. LpcpPrint(( "%s Attempted ReplyWaitReceive to Thread %lx (%s)\n",
  256. PsGetCurrentProcess()->ImageFileName,
  257. WakeupThread,
  258. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  259. LpcpPrint(( "failed. MessageId == %u Client Id: %x.%x\n",
  260. CapturedReplyMessage.MessageId,
  261. CapturedReplyMessage.ClientId.UniqueProcess,
  262. CapturedReplyMessage.ClientId.UniqueThread ));
  263. LpcpPrint(( " Thread MessageId == %u Client Id: %x.%x\n",
  264. WakeupThread->LpcReplyMessageId,
  265. WakeupThread->Cid.UniqueProcess,
  266. WakeupThread->Cid.UniqueThread ));
  267. #if DBG
  268. if (LpcpStopOnReplyMismatch) {
  269. DbgBreakPoint();
  270. }
  271. #endif
  272. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  273. if (ConnectionPort) {
  274. ObDereferenceObject(ConnectionPort);
  275. }
  276. ObDereferenceObject( WakeupThread );
  277. ObDereferenceObject( PortObject );
  278. return STATUS_REPLY_MESSAGE_MISMATCH;
  279. }
  280. //
  281. // Copy the reply message to the request message buffer. Do this before
  282. // we actually fiddle with the wakeup threads fields. Otherwise we
  283. // could mess up its state
  284. //
  285. try {
  286. LpcpMoveMessage( &Msg->Request,
  287. &CapturedReplyMessage,
  288. (ReplyMessage + 1),
  289. LPC_REPLY,
  290. NULL );
  291. } except( EXCEPTION_EXECUTE_HANDLER ) {
  292. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  293. if (ConnectionPort) {
  294. ObDereferenceObject(ConnectionPort);
  295. }
  296. ObDereferenceObject( WakeupThread );
  297. ObDereferenceObject( PortObject );
  298. return (Status = GetExceptionCode());
  299. }
  300. LpcpTrace(( "%s Sending Reply Msg %lx (%u.%u, %x) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
  301. PsGetCurrentProcess()->ImageFileName,
  302. Msg,
  303. CapturedReplyMessage.MessageId,
  304. CapturedReplyMessage.CallbackId,
  305. CapturedReplyMessage.u2.s2.DataInfoOffset,
  306. *((PULONG)(Msg+1)+0),
  307. *((PULONG)(Msg+1)+1),
  308. *((PULONG)(Msg+1)+2),
  309. *((PULONG)(Msg+1)+3),
  310. WakeupThread,
  311. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  312. //
  313. // Locate and free the messsage from the port. This call use to
  314. // test for (CapturedReplyMessage.u2.s2.DataInfoOffset != 0) as a
  315. // prerequisite for doing the call.
  316. //
  317. LpcpFreeDataInfoMessage( PortObject,
  318. CapturedReplyMessage.MessageId,
  319. CapturedReplyMessage.CallbackId );
  320. //
  321. // Add an extra reference so LpcExitThread does not evaporate
  322. // the pointer before we get to the wait below
  323. //
  324. ObReferenceObject( WakeupThread );
  325. //
  326. // Release the mutex that guards the LpcReplyMessage field
  327. // after marking message as being replied to.
  328. //
  329. Msg->RepliedToThread = WakeupThread;
  330. WakeupThread->LpcReplyMessageId = 0;
  331. WakeupThread->LpcReplyMessage = (PVOID)Msg;
  332. //
  333. // Remove the thread from the reply rundown list as we are sending the reply.
  334. //
  335. if ((!WakeupThread->LpcExitThreadCalled) && (!IsListEmpty( &WakeupThread->LpcReplyChain ))) {
  336. RemoveEntryList( &WakeupThread->LpcReplyChain );
  337. InitializeListHead( &WakeupThread->LpcReplyChain );
  338. }
  339. if ((CurrentThread->LpcReceivedMsgIdValid) &&
  340. (CurrentThread->LpcReceivedMessageId == CapturedReplyMessage.MessageId)) {
  341. CurrentThread->LpcReceivedMessageId = 0;
  342. CurrentThread->LpcReceivedMsgIdValid = FALSE;
  343. }
  344. LpcpReleaseLpcpLock();
  345. //
  346. // Wake up the thread that is waiting for an answer to its request
  347. // inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort
  348. //
  349. KeReleaseSemaphore( &WakeupThread->LpcReplySemaphore,
  350. 1,
  351. 1,
  352. FALSE );
  353. ObDereferenceObject( WakeupThread );
  354. }
  355. LpcpTrace(( "%s Waiting for message to Port %x (%s)\n",
  356. PsGetCurrentProcess()->ImageFileName,
  357. ReceivePort,
  358. LpcpGetCreatorName( ReceivePort )));
  359. //
  360. // The timeout on this wait and the next wait appear to be the
  361. // only substantial difference between NtReplyWaitReceivePort
  362. // and NtReplyWaitReceivePortEx
  363. Status = KeWaitForSingleObject( ReceivePort->MsgQueue.Semaphore,
  364. WrLpcReceive,
  365. WaitMode,
  366. FALSE,
  367. Timeout );
  368. //
  369. // Fall into receive code. Client thread reference will be
  370. // returned by the client when it wakes up.
  371. //
  372. //
  373. // At this point we've awoke from our wait for a receive
  374. //
  375. if (Status == STATUS_SUCCESS) {
  376. LpcpAcquireLpcpLockByThread(CurrentThread);
  377. //
  378. // See if we awoke without a message in our receive port
  379. //
  380. if (IsListEmpty( &ReceivePort->MsgQueue.ReceiveHead )) {
  381. if ( ReceivePort->Flags & PORT_WAITABLE ) {
  382. KeResetEvent( &ReceivePort->WaitEvent );
  383. }
  384. LpcpReleaseLpcpLock();
  385. if (ConnectionPort) {
  386. ObDereferenceObject(ConnectionPort);
  387. }
  388. ObDereferenceObject( PortObject );
  389. return STATUS_UNSUCCESSFUL;
  390. }
  391. //
  392. // We have a message in our receive port. So let's pull it out
  393. //
  394. Msg = (PLPCP_MESSAGE)RemoveHeadList( &ReceivePort->MsgQueue.ReceiveHead );
  395. if ( IsListEmpty( &ReceivePort->MsgQueue.ReceiveHead ) ) {
  396. if ( ReceivePort->Flags & PORT_WAITABLE ) {
  397. KeResetEvent( &ReceivePort->WaitEvent );
  398. }
  399. }
  400. InitializeListHead( &Msg->Entry );
  401. LpcpTrace(( "%s Receive Msg %lx (%u) from Port %lx (%s)\n",
  402. PsGetCurrentProcess()->ImageFileName,
  403. Msg,
  404. Msg->Request.MessageId,
  405. ReceivePort,
  406. LpcpGetCreatorName( ReceivePort )));
  407. //
  408. // Now make the thread state to be the message we're currently
  409. // working on
  410. //
  411. // NOTICE-2002/03/19-ScottMa -- The valid flag is set to TRUE before
  412. // filling in the actual Id, so that the Process/Thread rundown code
  413. // will not accidentally read the Id field as a Win32StartAddress.
  414. // This if safe, since the only code that reads this value is in one
  415. // of three places:
  416. // 1) LPC files -- All are protected by LpcpLock.
  417. // 2) ExpCopyThreadInfo -- This fix makes the refernce safe.
  418. // 3) Debugger extensions -- Both !lpc and !process can still
  419. // experience a bug displaying the
  420. // correct value of this union.
  421. // Note that checking the ownership of the LpcpLock would allow
  422. // both the debugger extensions and ExpCopyThreadInfo to determine
  423. // if the values are in a state of flux.
  424. CurrentThread->LpcReceivedMsgIdValid = TRUE;
  425. CurrentThread->LpcReceivedMessageId = Msg->Request.MessageId;
  426. try {
  427. //
  428. // Check if the message is a connection request
  429. //
  430. if ((Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) == LPC_CONNECTION_REQUEST) {
  431. PLPCP_CONNECTION_MESSAGE ConnectMsg;
  432. ULONG ConnectionInfoLength;
  433. PLPCP_MESSAGE TempMsg;
  434. ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(Msg + 1);
  435. ConnectionInfoLength = Msg->Request.u1.s1.DataLength - sizeof( *ConnectMsg );
  436. //
  437. // Don't free message until NtAcceptConnectPort called, and if it's never called
  438. // then we'll keep the message until the client exits.
  439. //
  440. TempMsg = Msg;
  441. Msg = NULL;
  442. *ReceiveMessage = TempMsg->Request;
  443. ReceiveMessage->u1.s1.TotalLength = (CSHORT)(sizeof( *ReceiveMessage ) + ConnectionInfoLength);
  444. ReceiveMessage->u1.s1.DataLength = (CSHORT)ConnectionInfoLength;
  445. RtlCopyMemory( ReceiveMessage+1,
  446. ConnectMsg + 1,
  447. ConnectionInfoLength );
  448. if (ARGUMENT_PRESENT( PortContext )) {
  449. *PortContext = NULL;
  450. }
  451. //
  452. // Check if the message is not a reply
  453. //
  454. } else if ((Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REPLY) {
  455. LpcpMoveMessage( ReceiveMessage,
  456. &Msg->Request,
  457. (&Msg->Request) + 1,
  458. 0,
  459. NULL );
  460. if (ARGUMENT_PRESENT( PortContext )) {
  461. *PortContext = Msg->PortContext;
  462. }
  463. //
  464. // If message contains DataInfo for access via NtRead/WriteRequestData
  465. // then put the message on a list in the communication port and don't
  466. // free it. It will be freed when the server replies to the message.
  467. //
  468. if (Msg->Request.u2.s2.DataInfoOffset != 0) {
  469. LpcpSaveDataInfoMessage( PortObject, Msg, LPCP_MUTEX_OWNED );
  470. Msg = NULL;
  471. }
  472. //
  473. // Otherwise this is a reply message we just received
  474. //
  475. } else {
  476. LpcpPrint(( "LPC: Bogus reply message (%08x) in receive queue of connection port %08x\n",
  477. Msg, ReceivePort ));
  478. KdBreakPoint();
  479. }
  480. } except( EXCEPTION_EXECUTE_HANDLER ) {
  481. Status = GetExceptionCode();
  482. }
  483. //
  484. // Acquire the LPC mutex and decrement the reference count for the
  485. // message. If the reference count goes to zero the message will be
  486. // deleted.
  487. //
  488. if (Msg != NULL) {
  489. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  490. }
  491. else {
  492. LpcpReleaseLpcpLock();
  493. }
  494. }
  495. if (ConnectionPort) {
  496. ObDereferenceObject(ConnectionPort);
  497. }
  498. ObDereferenceObject( PortObject );
  499. //
  500. // And return to our caller
  501. //
  502. return Status;
  503. }