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.

718 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. if (PortObject->ConnectionPort == PortObject) {
  194. ConnectionPort = ReceivePort = PortObject;
  195. ObReferenceObject (ConnectionPort);
  196. } else {
  197. LpcpAcquireLpcpLockByThread(CurrentThread);
  198. ConnectionPort = ReceivePort = PortObject->ConnectionPort;
  199. if (ConnectionPort == NULL) {
  200. LpcpReleaseLpcpLock();
  201. ObDereferenceObject( PortObject );
  202. return STATUS_PORT_DISCONNECTED;
  203. }
  204. ObReferenceObject( ConnectionPort );
  205. LpcpReleaseLpcpLock();
  206. }
  207. } else {
  208. ReceivePort = PortObject;
  209. }
  210. //
  211. // If ReplyMessage argument present, then send reply
  212. //
  213. if (ARGUMENT_PRESENT( ReplyMessage )) {
  214. //
  215. // Translate the ClientId from the connection request into a
  216. // thread pointer. This is a referenced pointer to keep the thread
  217. // from evaporating out from under us.
  218. //
  219. Status = PsLookupProcessThreadByCid( &CapturedReplyMessage.ClientId,
  220. NULL,
  221. &WakeupThread );
  222. if (!NT_SUCCESS( Status )) {
  223. ObDereferenceObject( PortObject );
  224. if (ConnectionPort) {
  225. ObDereferenceObject(ConnectionPort);
  226. }
  227. return Status;
  228. }
  229. //
  230. // Acquire the global Lpc mutex that guards the LpcReplyMessage
  231. // field of the thread and get the pointer to the message that
  232. // the thread is waiting for a reply to.
  233. //
  234. Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedReplyMessage.u1.s1.TotalLength );
  235. if (Msg == NULL) {
  236. LpcpTraceError(STATUS_NO_MEMORY, CurrentThread->Cid, &CapturedReplyMessage);
  237. if (ConnectionPort) {
  238. ObDereferenceObject(ConnectionPort);
  239. }
  240. ObDereferenceObject( WakeupThread );
  241. ObDereferenceObject( PortObject );
  242. return STATUS_NO_MEMORY;
  243. }
  244. LpcpAcquireLpcpLockByThread(CurrentThread);
  245. //
  246. // See if the thread is waiting for a reply to the message
  247. // specified on this call. If not then a bogus message
  248. // has been specified, so release the mutex, dereference the thread
  249. // and return failure.
  250. //
  251. // We also fail this request if the caller isn't replying to a request
  252. // message. For example, if the caller is replying to a connection
  253. // request
  254. //
  255. if ((WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId)
  256. ||
  257. ((LpcpGetThreadMessage(WakeupThread) != NULL) &&
  258. (LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
  259. ||
  260. (!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY))) {
  261. LpcpPrint(( "%s Attempted ReplyWaitReceive to Thread %lx (%s)\n",
  262. PsGetCurrentProcess()->ImageFileName,
  263. WakeupThread,
  264. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  265. LpcpPrint(( "failed. MessageId == %u Client Id: %x.%x\n",
  266. CapturedReplyMessage.MessageId,
  267. CapturedReplyMessage.ClientId.UniqueProcess,
  268. CapturedReplyMessage.ClientId.UniqueThread ));
  269. LpcpPrint(( " Thread MessageId == %u Client Id: %x.%x\n",
  270. WakeupThread->LpcReplyMessageId,
  271. WakeupThread->Cid.UniqueProcess,
  272. WakeupThread->Cid.UniqueThread ));
  273. #if DBG
  274. if (LpcpStopOnReplyMismatch) {
  275. DbgBreakPoint();
  276. }
  277. #endif
  278. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  279. if (ConnectionPort) {
  280. ObDereferenceObject(ConnectionPort);
  281. }
  282. ObDereferenceObject( WakeupThread );
  283. ObDereferenceObject( PortObject );
  284. return STATUS_REPLY_MESSAGE_MISMATCH;
  285. }
  286. //
  287. // Copy the reply message to the request message buffer. Do this before
  288. // we actually fiddle with the wakeup threads fields. Otherwise we
  289. // could mess up its state
  290. //
  291. try {
  292. LpcpMoveMessage( &Msg->Request,
  293. &CapturedReplyMessage,
  294. (ReplyMessage + 1),
  295. LPC_REPLY,
  296. NULL );
  297. } except( EXCEPTION_EXECUTE_HANDLER ) {
  298. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  299. if (ConnectionPort) {
  300. ObDereferenceObject(ConnectionPort);
  301. }
  302. ObDereferenceObject( WakeupThread );
  303. ObDereferenceObject( PortObject );
  304. return (Status = GetExceptionCode());
  305. }
  306. LpcpTrace(( "%s Sending Reply Msg %lx (%u.%u, %x) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
  307. PsGetCurrentProcess()->ImageFileName,
  308. Msg,
  309. CapturedReplyMessage.MessageId,
  310. CapturedReplyMessage.CallbackId,
  311. CapturedReplyMessage.u2.s2.DataInfoOffset,
  312. *((PULONG)(Msg+1)+0),
  313. *((PULONG)(Msg+1)+1),
  314. *((PULONG)(Msg+1)+2),
  315. *((PULONG)(Msg+1)+3),
  316. WakeupThread,
  317. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  318. //
  319. // Add an extra reference so LpcExitThread does not evaporate
  320. // the pointer before we get to the wait below
  321. //
  322. ObReferenceObject( WakeupThread );
  323. //
  324. // Release the mutex that guards the LpcReplyMessage field
  325. // after marking message as being replied to.
  326. //
  327. Msg->RepliedToThread = WakeupThread;
  328. WakeupThread->LpcReplyMessageId = 0;
  329. WakeupThread->LpcReplyMessage = (PVOID)Msg;
  330. //
  331. // Remove the thread from the reply rundown list as we are sending the reply.
  332. //
  333. if ((!WakeupThread->LpcExitThreadCalled) && (!IsListEmpty( &WakeupThread->LpcReplyChain ))) {
  334. RemoveEntryList( &WakeupThread->LpcReplyChain );
  335. InitializeListHead( &WakeupThread->LpcReplyChain );
  336. }
  337. if ((CurrentThread->LpcReceivedMsgIdValid) &&
  338. (CurrentThread->LpcReceivedMessageId == CapturedReplyMessage.MessageId)) {
  339. CurrentThread->LpcReceivedMessageId = 0;
  340. CurrentThread->LpcReceivedMsgIdValid = FALSE;
  341. }
  342. //
  343. // Locate and free the message from the port. This call may
  344. // free an existing message and release the LPC lock. It was moved down
  345. // to keep checking for the message ID and set the reply message
  346. // atomically
  347. //
  348. LpcpFreeDataInfoMessage( PortObject,
  349. CapturedReplyMessage.MessageId,
  350. CapturedReplyMessage.CallbackId,
  351. CapturedReplyMessage.ClientId );
  352. LpcpReleaseLpcpLock();
  353. //
  354. // Wake up the thread that is waiting for an answer to its request
  355. // inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort
  356. //
  357. KeReleaseSemaphore( &WakeupThread->LpcReplySemaphore,
  358. 1,
  359. 1,
  360. FALSE );
  361. ObDereferenceObject( WakeupThread );
  362. }
  363. LpcpTrace(( "%s Waiting for message to Port %x (%s)\n",
  364. PsGetCurrentProcess()->ImageFileName,
  365. ReceivePort,
  366. LpcpGetCreatorName( ReceivePort )));
  367. //
  368. // The timeout on this wait and the next wait appear to be the
  369. // only substantial difference between NtReplyWaitReceivePort
  370. // and NtReplyWaitReceivePortEx
  371. Status = KeWaitForSingleObject( ReceivePort->MsgQueue.Semaphore,
  372. WrLpcReceive,
  373. WaitMode,
  374. FALSE,
  375. Timeout );
  376. //
  377. // Fall into receive code. Client thread reference will be
  378. // returned by the client when it wakes up.
  379. //
  380. //
  381. // At this point we've awoke from our wait for a receive
  382. //
  383. if (Status == STATUS_SUCCESS) {
  384. LpcpAcquireLpcpLockByThread(CurrentThread);
  385. //
  386. // See if we awoke without a message in our receive port
  387. //
  388. if (IsListEmpty( &ReceivePort->MsgQueue.ReceiveHead )) {
  389. if ( ReceivePort->Flags & PORT_WAITABLE ) {
  390. KeResetEvent( &ReceivePort->WaitEvent );
  391. }
  392. LpcpReleaseLpcpLock();
  393. if (ConnectionPort) {
  394. ObDereferenceObject(ConnectionPort);
  395. }
  396. ObDereferenceObject( PortObject );
  397. return STATUS_UNSUCCESSFUL;
  398. }
  399. //
  400. // We have a message in our receive port. So let's pull it out
  401. //
  402. Msg = (PLPCP_MESSAGE)RemoveHeadList( &ReceivePort->MsgQueue.ReceiveHead );
  403. if ( IsListEmpty( &ReceivePort->MsgQueue.ReceiveHead ) ) {
  404. if ( ReceivePort->Flags & PORT_WAITABLE ) {
  405. KeResetEvent( &ReceivePort->WaitEvent );
  406. }
  407. }
  408. InitializeListHead( &Msg->Entry );
  409. LpcpTrace(( "%s Receive Msg %lx (%u) from Port %lx (%s)\n",
  410. PsGetCurrentProcess()->ImageFileName,
  411. Msg,
  412. Msg->Request.MessageId,
  413. ReceivePort,
  414. LpcpGetCreatorName( ReceivePort )));
  415. //
  416. // Now make the thread state to be the message we're currently
  417. // working on
  418. //
  419. CurrentThread->LpcReceivedMessageId = Msg->Request.MessageId;
  420. CurrentThread->LpcReceivedMsgIdValid = TRUE;
  421. try {
  422. //
  423. // Check if the message is a connection request
  424. //
  425. if ((Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) == LPC_CONNECTION_REQUEST) {
  426. PLPCP_CONNECTION_MESSAGE ConnectMsg;
  427. ULONG ConnectionInfoLength;
  428. PLPCP_MESSAGE TempMsg;
  429. ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(Msg + 1);
  430. ConnectionInfoLength = Msg->Request.u1.s1.DataLength - sizeof( *ConnectMsg );
  431. //
  432. // Don't free message until NtAcceptConnectPort called, and if it's never called
  433. // then we'll keep the message until the client exits.
  434. //
  435. TempMsg = Msg;
  436. Msg = NULL;
  437. *ReceiveMessage = TempMsg->Request;
  438. ReceiveMessage->u1.s1.TotalLength = (CSHORT)(sizeof( *ReceiveMessage ) + ConnectionInfoLength);
  439. ReceiveMessage->u1.s1.DataLength = (CSHORT)ConnectionInfoLength;
  440. RtlCopyMemory( ReceiveMessage+1,
  441. ConnectMsg + 1,
  442. ConnectionInfoLength );
  443. if (ARGUMENT_PRESENT( PortContext )) {
  444. *PortContext = NULL;
  445. }
  446. //
  447. // Check if the message is not a reply
  448. //
  449. } else if ((Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REPLY) {
  450. LpcpMoveMessage( ReceiveMessage,
  451. &Msg->Request,
  452. (&Msg->Request) + 1,
  453. 0,
  454. NULL );
  455. if (ARGUMENT_PRESENT( PortContext )) {
  456. *PortContext = Msg->PortContext;
  457. }
  458. //
  459. // If message contains DataInfo for access via NtRead/WriteRequestData
  460. // then put the message on a list in the communication port and don't
  461. // free it. It will be freed when the server replies to the message.
  462. //
  463. if (Msg->Request.u2.s2.DataInfoOffset != 0) {
  464. LpcpSaveDataInfoMessage( PortObject, Msg, LPCP_MUTEX_OWNED );
  465. Msg = NULL;
  466. }
  467. //
  468. // Otherwise this is a reply message we just received
  469. //
  470. } else {
  471. LpcpPrint(( "LPC: Bogus reply message (%08x) in receive queue of connection port %08x\n",
  472. Msg, ReceivePort ));
  473. KdBreakPoint();
  474. }
  475. } except( EXCEPTION_EXECUTE_HANDLER ) {
  476. Status = GetExceptionCode();
  477. }
  478. //
  479. // Acquire the LPC mutex and decrement the reference count for the
  480. // message. If the reference count goes to zero the message will be
  481. // deleted.
  482. //
  483. if (Msg != NULL) {
  484. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  485. }
  486. else {
  487. LpcpReleaseLpcpLock();
  488. }
  489. }
  490. if (ConnectionPort) {
  491. ObDereferenceObject(ConnectionPort);
  492. }
  493. ObDereferenceObject( PortObject );
  494. //
  495. // And return to our caller
  496. //
  497. return Status;
  498. }