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.

1442 lines
38 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. lpcreply.c
  5. Abstract:
  6. Local Inter-Process Communication (LPC) reply system services.
  7. Author:
  8. Steve Wood (stevewo) 15-May-1989
  9. Revision History:
  10. --*/
  11. #include "lpcp.h"
  12. NTSTATUS
  13. LpcpCopyRequestData (
  14. IN BOOLEAN WriteToMessageData,
  15. IN HANDLE PortHandle,
  16. IN PPORT_MESSAGE Message,
  17. IN ULONG DataEntryIndex,
  18. IN PVOID Buffer,
  19. IN SIZE_T BufferSize,
  20. OUT PSIZE_T NumberOfBytesCopied OPTIONAL
  21. );
  22. #if 0
  23. VOID
  24. LpcpAuditInvalidUse (
  25. IN PVOID Context
  26. );
  27. #endif
  28. #ifdef ALLOC_PRAGMA
  29. #pragma alloc_text(PAGE,NtReplyPort)
  30. #pragma alloc_text(PAGE,NtReplyWaitReplyPort)
  31. #pragma alloc_text(PAGE,NtReadRequestData)
  32. #pragma alloc_text(PAGE,NtWriteRequestData)
  33. #pragma alloc_text(PAGE,LpcpCopyRequestData)
  34. #pragma alloc_text(PAGE,LpcpValidateClientPort)
  35. #if 0
  36. #pragma alloc_text(PAGE,LpcpAuditInvalidUse)
  37. #endif
  38. ULONG LpcMaxEventLogs = 10;
  39. #define LPCP_PORT_NAME_MAX 256
  40. typedef struct _LPC_WORK_CONTEXT {
  41. WORK_QUEUE_ITEM WorkItem;
  42. PVOID Buffer;
  43. } LPC_WORK_CONTEXT, *PLPC_WORK_CONTEXT;
  44. #endif
  45. //
  46. // The current number of events registered
  47. //
  48. ULONG LpcpEventCounts = 0;
  49. NTSTATUS
  50. NtReplyPort (
  51. IN HANDLE PortHandle,
  52. IN PPORT_MESSAGE ReplyMessage
  53. )
  54. /*++
  55. Routine Description:
  56. A client and server process can send a reply to a previous request
  57. message with the NtReplyPort service:
  58. The Type field of the message is set to LPC_REPLY by the service. If the
  59. MapInfoOffset field of the reply message is non-zero, then the
  60. PORT_MAP_INFORMATION structure it points to will be processed and the
  61. relevant pages in the caller's address space will be unmapped.
  62. The ClientId and MessageId fields of the ReplyMessage structure are used
  63. to identify the thread waiting for this reply. If the target thread is
  64. in fact waiting for this reply message, then the reply message is copied
  65. into the thread's message buffer and the thread's wait is satisfied.
  66. If the thread is not waiting for a reply or is waiting for a reply to
  67. some other MessageId, then the message is placed in the message queue of
  68. the port that is connected to the communication port specified by the
  69. PortHandle parameter and the Type field of the message is set to
  70. LPC_LOST_REPLY.
  71. Arguments:
  72. PortHandle - Specifies the handle of the communication port that the
  73. original message was received from.
  74. ReplyMessage - Specifies a pointer to the reply message to be sent.
  75. The ClientId and MessageId fields determine which thread will
  76. get the reply.
  77. Return Value:
  78. Status code that indicates whether or not the operation was
  79. successful.
  80. --*/
  81. {
  82. KPROCESSOR_MODE PreviousMode;
  83. PLPCP_PORT_OBJECT PortObject;
  84. PORT_MESSAGE CapturedReplyMessage;
  85. NTSTATUS Status;
  86. PLPCP_MESSAGE Msg;
  87. PETHREAD CurrentThread;
  88. PETHREAD WakeupThread;
  89. PAGED_CODE();
  90. CurrentThread = PsGetCurrentThread();
  91. //
  92. // Get previous processor mode and probe output arguments if necessary.
  93. //
  94. PreviousMode = KeGetPreviousMode();
  95. if (PreviousMode != KernelMode) {
  96. try {
  97. ProbeForReadSmallStructure( ReplyMessage,
  98. sizeof( *ReplyMessage ),
  99. sizeof( ULONG ));
  100. CapturedReplyMessage = *ReplyMessage;
  101. } except( EXCEPTION_EXECUTE_HANDLER ) {
  102. return GetExceptionCode();
  103. }
  104. } else {
  105. CapturedReplyMessage = *ReplyMessage;
  106. }
  107. //
  108. // Make sure DataLength is valid with respect to header size and total
  109. // length
  110. //
  111. if ((((CLONG)CapturedReplyMessage.u1.s1.DataLength) + sizeof( PORT_MESSAGE )) >
  112. ((CLONG)CapturedReplyMessage.u1.s1.TotalLength)) {
  113. return STATUS_INVALID_PARAMETER;
  114. }
  115. //
  116. // Make sure the user didn't give us a bogus reply message id
  117. //
  118. if (CapturedReplyMessage.MessageId == 0) {
  119. return STATUS_INVALID_PARAMETER;
  120. }
  121. //
  122. // Reference the port object by handle
  123. //
  124. Status = LpcpReferencePortObject( PortHandle,
  125. 0,
  126. PreviousMode,
  127. &PortObject );
  128. if (!NT_SUCCESS( Status )) {
  129. Status = ObReferenceObjectByHandle( PortHandle,
  130. 0,
  131. LpcWaitablePortObjectType,
  132. PreviousMode,
  133. &PortObject,
  134. NULL );
  135. if ( !NT_SUCCESS( Status ) ) {
  136. return Status;
  137. }
  138. }
  139. //
  140. // Validate the message length
  141. //
  142. if (((ULONG)CapturedReplyMessage.u1.s1.TotalLength > PortObject->MaxMessageLength) ||
  143. ((ULONG)CapturedReplyMessage.u1.s1.TotalLength <= (ULONG)CapturedReplyMessage.u1.s1.DataLength)) {
  144. ObDereferenceObject( PortObject );
  145. return STATUS_PORT_MESSAGE_TOO_LONG;
  146. }
  147. //
  148. // Translate the ClientId from the connection request into a thread
  149. // pointer. This is a referenced pointer to keep the thread from
  150. // evaporating out from under us.
  151. //
  152. Status = PsLookupProcessThreadByCid( &CapturedReplyMessage.ClientId,
  153. NULL,
  154. &WakeupThread );
  155. if (!NT_SUCCESS( Status )) {
  156. ObDereferenceObject( PortObject );
  157. return Status;
  158. }
  159. //
  160. // Acquire the mutex that guards the LpcReplyMessage field of the thread
  161. // and get the pointer to the message that the thread is waiting for a
  162. // reply to.
  163. //
  164. Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedReplyMessage.u1.s1.TotalLength );
  165. if (Msg == NULL) {
  166. ObDereferenceObject( WakeupThread );
  167. ObDereferenceObject( PortObject );
  168. return STATUS_NO_MEMORY;
  169. }
  170. LpcpAcquireLpcpLockByThread(CurrentThread);
  171. //
  172. // See if the thread is waiting for a reply to the message specified on
  173. // this call. If not then a bogus message has been specified, so
  174. // release the mutex, dereference the thread and return failure.
  175. //
  176. // We also fail this request if the caller isn't replying to a request
  177. // message. For example, if the caller is replying to a connection
  178. // request
  179. //
  180. if ((WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId)
  181. ||
  182. ((LpcpGetThreadMessage(WakeupThread) != NULL) &&
  183. (LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
  184. ||
  185. (!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY)) ) {
  186. LpcpPrint(( "%s Attempted reply to Thread %lx (%s)\n",
  187. PsGetCurrentProcess()->ImageFileName,
  188. WakeupThread,
  189. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  190. LpcpPrint(( "failed. MessageId == %u Client Id: %x.%x\n",
  191. CapturedReplyMessage.MessageId,
  192. CapturedReplyMessage.ClientId.UniqueProcess,
  193. CapturedReplyMessage.ClientId.UniqueThread ));
  194. LpcpPrint(( " Thread MessageId == %u Client Id: %x.%x\n",
  195. WakeupThread->LpcReplyMessageId,
  196. WakeupThread->Cid.UniqueProcess,
  197. WakeupThread->Cid.UniqueThread ));
  198. #if DBG
  199. if (LpcpStopOnReplyMismatch) {
  200. DbgBreakPoint();
  201. }
  202. #endif
  203. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  204. ObDereferenceObject( WakeupThread );
  205. ObDereferenceObject( PortObject );
  206. return STATUS_REPLY_MESSAGE_MISMATCH;
  207. }
  208. //
  209. // Copy the reply message to the request message buffer. Do this before
  210. // we actually fiddle with the wakeup threads fields. Otherwise we
  211. // could mess up its state
  212. //
  213. try {
  214. LpcpMoveMessage( &Msg->Request,
  215. &CapturedReplyMessage,
  216. (ReplyMessage + 1),
  217. LPC_REPLY,
  218. NULL );
  219. } except( EXCEPTION_EXECUTE_HANDLER ) {
  220. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  221. ObDereferenceObject( WakeupThread );
  222. ObDereferenceObject( PortObject );
  223. return GetExceptionCode();
  224. }
  225. //
  226. // At this point we know the thread is waiting for our reply
  227. //
  228. LpcpTrace(( "%s Sending Reply Msg %lx (%u, %x) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
  229. PsGetCurrentProcess()->ImageFileName,
  230. Msg,
  231. CapturedReplyMessage.MessageId,
  232. CapturedReplyMessage.u2.s2.DataInfoOffset,
  233. *((PULONG)(Msg+1)+0),
  234. *((PULONG)(Msg+1)+1),
  235. *((PULONG)(Msg+1)+2),
  236. *((PULONG)(Msg+1)+3),
  237. WakeupThread,
  238. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  239. //
  240. // Locate and free the message from the port. This call use to
  241. // test for (CapturedReplyMessage.u2.s2.DataInfoOffset != 0) as a
  242. // prerequisite for doing the call.
  243. //
  244. LpcpFreeDataInfoMessage( PortObject,
  245. CapturedReplyMessage.MessageId,
  246. CapturedReplyMessage.CallbackId );
  247. //
  248. // Add an extra reference so LpcExitThread does not evaporate the
  249. // pointer before we get to the wait below
  250. //
  251. ObReferenceObject( WakeupThread );
  252. //
  253. // Release the mutex that guards the LpcReplyMessage field after marking
  254. // message as being replied to.
  255. //
  256. Msg->RepliedToThread = WakeupThread;
  257. WakeupThread->LpcReplyMessageId = 0;
  258. WakeupThread->LpcReplyMessage = (PVOID)Msg;
  259. //
  260. // Remove the thread from the reply rundown list as we are sending the
  261. // reply.
  262. //
  263. if (!WakeupThread->LpcExitThreadCalled && !IsListEmpty( &WakeupThread->LpcReplyChain )) {
  264. RemoveEntryList( &WakeupThread->LpcReplyChain );
  265. InitializeListHead( &WakeupThread->LpcReplyChain );
  266. }
  267. if ((CurrentThread->LpcReceivedMsgIdValid) &&
  268. (CurrentThread->LpcReceivedMessageId == CapturedReplyMessage.MessageId)) {
  269. CurrentThread->LpcReceivedMessageId = 0;
  270. CurrentThread->LpcReceivedMsgIdValid = FALSE;
  271. }
  272. LpcpReleaseLpcpLock();
  273. //
  274. // Wake up the thread that is waiting for an answer to its request
  275. // inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort. That
  276. // will dereference itself when it wakes up.
  277. //
  278. KeReleaseSemaphore( &WakeupThread->LpcReplySemaphore,
  279. 0,
  280. 1L,
  281. FALSE );
  282. ObDereferenceObject( WakeupThread );
  283. //
  284. // Dereference port object and return the system service status.
  285. //
  286. ObDereferenceObject( PortObject );
  287. return Status;
  288. }
  289. NTSTATUS
  290. NtReplyWaitReplyPort (
  291. IN HANDLE PortHandle,
  292. IN OUT PPORT_MESSAGE ReplyMessage
  293. )
  294. /*++
  295. Routine Description:
  296. A client and server process can send a reply to a previous message and
  297. block waiting for a reply using the NtReplyWaitReplyPort service:
  298. This service works the same as NtReplyPort, except that after delivering
  299. the reply message, it blocks waiting for a reply to a previous message.
  300. When the reply is received, it will be placed in the location specified
  301. by the ReplyMessage parameter.
  302. Arguments:
  303. PortHandle - Specifies the handle of the communication port that the
  304. original message was received from.
  305. ReplyMessage - Specifies a pointer to the reply message to be sent.
  306. The ClientId and MessageId fields determine which thread will
  307. get the reply. This buffer also receives any reply that comes
  308. back from the wait.
  309. Return Value:
  310. Status code that indicates whether or not the operation was
  311. successful.
  312. --*/
  313. {
  314. KPROCESSOR_MODE PreviousMode;
  315. NTSTATUS Status;
  316. PLPCP_PORT_OBJECT PortObject;
  317. PORT_MESSAGE CapturedReplyMessage;
  318. PLPCP_MESSAGE Msg;
  319. PETHREAD CurrentThread;
  320. PETHREAD WakeupThread;
  321. PLPCP_PORT_OBJECT RundownPort;
  322. PAGED_CODE();
  323. CurrentThread = PsGetCurrentThread();
  324. //
  325. // Get previous processor mode and probe output arguments if necessary.
  326. //
  327. PreviousMode = KeGetPreviousMode();
  328. if (PreviousMode != KernelMode) {
  329. try {
  330. ProbeForWriteSmallStructure( ReplyMessage,
  331. sizeof( *ReplyMessage ),
  332. sizeof( ULONG ));
  333. CapturedReplyMessage = *ReplyMessage;
  334. } except( EXCEPTION_EXECUTE_HANDLER ) {
  335. return GetExceptionCode();
  336. }
  337. } else {
  338. CapturedReplyMessage = *ReplyMessage;
  339. }
  340. //
  341. // Make sure DataLength is valid with respect to header size and total length
  342. //
  343. if ((((CLONG)CapturedReplyMessage.u1.s1.DataLength) + sizeof( PORT_MESSAGE )) >
  344. ((CLONG)CapturedReplyMessage.u1.s1.TotalLength)) {
  345. return STATUS_INVALID_PARAMETER;
  346. }
  347. //
  348. // Make sure the user didn't give us a bogus reply message id
  349. //
  350. if (CapturedReplyMessage.MessageId == 0) {
  351. return STATUS_INVALID_PARAMETER;
  352. }
  353. //
  354. // Reference the communication port object by handle. Return status if
  355. // unsuccessful.
  356. //
  357. Status = LpcpReferencePortObject( PortHandle,
  358. 0,
  359. PreviousMode,
  360. &PortObject );
  361. if (!NT_SUCCESS( Status )) {
  362. return Status;
  363. }
  364. //
  365. // Validate the message length
  366. //
  367. if (((ULONG)CapturedReplyMessage.u1.s1.TotalLength > PortObject->MaxMessageLength) ||
  368. ((ULONG)CapturedReplyMessage.u1.s1.TotalLength <= (ULONG)CapturedReplyMessage.u1.s1.DataLength)) {
  369. ObDereferenceObject( PortObject );
  370. return STATUS_PORT_MESSAGE_TOO_LONG;
  371. }
  372. //
  373. // Translate the ClientId from the connection request into a
  374. // thread pointer. This is a referenced pointer to keep the thread
  375. // from evaporating out from under us.
  376. //
  377. Status = PsLookupProcessThreadByCid( &CapturedReplyMessage.ClientId,
  378. NULL,
  379. &WakeupThread );
  380. if (!NT_SUCCESS( Status )) {
  381. ObDereferenceObject( PortObject );
  382. return Status;
  383. }
  384. //
  385. // Acquire the mutex that guards the LpcReplyMessage field of
  386. // the thread and get the pointer to the message that the thread
  387. // is waiting for a reply to.
  388. //
  389. Msg = (PLPCP_MESSAGE)LpcpAllocateFromPortZone( CapturedReplyMessage.u1.s1.TotalLength );
  390. if (Msg == NULL) {
  391. ObDereferenceObject( WakeupThread );
  392. ObDereferenceObject( PortObject );
  393. return STATUS_NO_MEMORY;
  394. }
  395. LpcpAcquireLpcpLockByThread(CurrentThread);
  396. //
  397. // See if the thread is waiting for a reply to the message
  398. // specified on this call. If not then a bogus message
  399. // has been specified, so release the mutex, dereference the thread
  400. // and return failure.
  401. //
  402. // We also fail this request if the caller isn't replying to a request
  403. // message. For example, if the caller is replying to a connection
  404. // request
  405. //
  406. if ((WakeupThread->LpcReplyMessageId != CapturedReplyMessage.MessageId)
  407. ||
  408. ((LpcpGetThreadMessage(WakeupThread) != NULL) &&
  409. (LpcpGetThreadMessage(WakeupThread)->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) != LPC_REQUEST)
  410. ||
  411. (!LpcpValidateClientPort(WakeupThread, PortObject, LPCP_VALIDATE_REASON_REPLY)) ) {
  412. LpcpPrint(( "%s Attempted reply wait reply to Thread %lx (%s)\n",
  413. PsGetCurrentProcess()->ImageFileName,
  414. WakeupThread,
  415. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  416. LpcpPrint(( "failed. MessageId == %u Client Id: %x.%x\n",
  417. CapturedReplyMessage.MessageId,
  418. CapturedReplyMessage.ClientId.UniqueProcess,
  419. CapturedReplyMessage.ClientId.UniqueThread ));
  420. LpcpPrint(( " Thread MessageId == %u Client Id: %x.%x\n",
  421. WakeupThread->LpcReplyMessageId,
  422. WakeupThread->Cid.UniqueProcess,
  423. WakeupThread->Cid.UniqueThread ));
  424. #if DBG
  425. if (LpcpStopOnReplyMismatch) {
  426. DbgBreakPoint();
  427. }
  428. #endif
  429. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  430. ObDereferenceObject( WakeupThread );
  431. ObDereferenceObject( PortObject );
  432. return STATUS_REPLY_MESSAGE_MISMATCH;
  433. }
  434. //
  435. // Copy the reply message to the request message buffer. Do this before
  436. // we actually fiddle with the wakeup threads fields. Otherwise we
  437. // could mess up its state
  438. //
  439. try {
  440. LpcpMoveMessage( &Msg->Request,
  441. &CapturedReplyMessage,
  442. (ReplyMessage + 1),
  443. LPC_REPLY,
  444. NULL );
  445. } except( EXCEPTION_EXECUTE_HANDLER ) {
  446. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  447. ObDereferenceObject( WakeupThread );
  448. ObDereferenceObject( PortObject );
  449. return (Status = GetExceptionCode());
  450. }
  451. //
  452. // At this point we know the thread is waiting for our reply
  453. //
  454. LpcpTrace(( "%s Sending Reply Wait Reply Msg %lx (%u, %x) [%08x %08x %08x %08x] to Thread %lx (%s)\n",
  455. PsGetCurrentProcess()->ImageFileName,
  456. Msg,
  457. CapturedReplyMessage.MessageId,
  458. CapturedReplyMessage.u2.s2.DataInfoOffset,
  459. *((PULONG)(Msg+1)+0),
  460. *((PULONG)(Msg+1)+1),
  461. *((PULONG)(Msg+1)+2),
  462. *((PULONG)(Msg+1)+3),
  463. WakeupThread,
  464. THREAD_TO_PROCESS( WakeupThread )->ImageFileName ));
  465. //
  466. // Locate and free the message from the port. This call use to
  467. // test for (CapturedReplyMessage.u2.s2.DataInfoOffset != 0) as a
  468. // prerequisite for doing the call.
  469. //
  470. LpcpFreeDataInfoMessage( PortObject,
  471. CapturedReplyMessage.MessageId,
  472. CapturedReplyMessage.CallbackId );
  473. //
  474. // Add an extra reference so LpcExitThread does not evaporate
  475. // the pointer before we get to the wait below
  476. //
  477. ObReferenceObject( WakeupThread );
  478. //
  479. // Release the mutex that guards the LpcReplyMessage field
  480. // after marking message as being replied to.
  481. //
  482. Msg->RepliedToThread = WakeupThread;
  483. WakeupThread->LpcReplyMessageId = 0;
  484. WakeupThread->LpcReplyMessage = (PVOID)Msg;
  485. //
  486. // Remove the thread from the reply rundown list as we are sending the reply.
  487. //
  488. if (!WakeupThread->LpcExitThreadCalled && !IsListEmpty( &WakeupThread->LpcReplyChain )) {
  489. RemoveEntryList( &WakeupThread->LpcReplyChain );
  490. InitializeListHead( &WakeupThread->LpcReplyChain );
  491. }
  492. //
  493. // Set ourselves up to get the following reply
  494. //
  495. CurrentThread->LpcReplyMessageId = CapturedReplyMessage.MessageId;
  496. CurrentThread->LpcReplyMessage = NULL;
  497. if ((CurrentThread->LpcReceivedMsgIdValid) &&
  498. (CurrentThread->LpcReceivedMessageId == CapturedReplyMessage.MessageId)) {
  499. CurrentThread->LpcReceivedMessageId = 0;
  500. CurrentThread->LpcReceivedMsgIdValid = FALSE;
  501. }
  502. //
  503. // Insert the current thread into the rundown queue
  504. //
  505. if ((PortObject->Flags & PORT_TYPE) != SERVER_CONNECTION_PORT) {
  506. RundownPort = PortObject->ConnectedPort;
  507. } else {
  508. RundownPort = PortObject;
  509. }
  510. InsertTailList( &RundownPort->LpcReplyChainHead, &CurrentThread->LpcReplyChain );
  511. //
  512. // Save the port context in the current thread, because
  513. // it waits a reply from the same message
  514. //
  515. LpcpSetPortToThread(CurrentThread, PortObject);
  516. LpcpReleaseLpcpLock();
  517. //
  518. // Wake up the thread that is waiting for an answer to its request
  519. // inside of NtRequestWaitReplyPort or NtReplyWaitReplyPort. That
  520. // will dereference itself when it wakes up.
  521. //
  522. KeReleaseSemaphore( &WakeupThread->LpcReplySemaphore,
  523. 1,
  524. 1,
  525. FALSE );
  526. ObDereferenceObject( WakeupThread );
  527. //
  528. // And wait for a reply
  529. //
  530. Status = KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  531. Executive,
  532. PreviousMode,
  533. FALSE,
  534. NULL );
  535. if (Status == STATUS_USER_APC) {
  536. //
  537. // if the semaphore is signaled, then clear it
  538. //
  539. if (KeReadStateSemaphore( &CurrentThread->LpcReplySemaphore )) {
  540. KeWaitForSingleObject( &CurrentThread->LpcReplySemaphore,
  541. WrExecutive,
  542. KernelMode,
  543. FALSE,
  544. NULL );
  545. Status = STATUS_SUCCESS;
  546. }
  547. }
  548. //
  549. // Remove the thread from the reply rundown list in case we did not wakeup due to
  550. // a reply
  551. //
  552. LpcpAcquireLpcpLockByThread(CurrentThread);
  553. if (!IsListEmpty( &CurrentThread->LpcReplyChain )) {
  554. RemoveEntryList( &CurrentThread->LpcReplyChain );
  555. InitializeListHead( &CurrentThread->LpcReplyChain );
  556. }
  557. //
  558. // If the wait succeeded, copy the reply to the reply buffer.
  559. //
  560. if (Status == STATUS_SUCCESS) {
  561. //
  562. // Acquire the mutex that guards the request message queue. Remove
  563. // the request message from the list of messages being processed and
  564. // free the message back to the queue's zone. If the zone's free
  565. // list was zero before freeing this message then pulse the free
  566. // event after free the message so that threads waiting to allocate
  567. // a request message buffer will wake up. Finally, release the mutex
  568. // and return the system service status.
  569. //
  570. Msg = LpcpGetThreadMessage(CurrentThread);
  571. CurrentThread->LpcReplyMessage = NULL;
  572. #if DBG
  573. if (Msg != NULL) {
  574. LpcpTrace(( "%s Got Reply Msg %lx (%u) [%08x %08x %08x %08x] for Thread %lx (%s)\n",
  575. PsGetCurrentProcess()->ImageFileName,
  576. Msg,
  577. Msg->Request.MessageId,
  578. *((PULONG)(Msg+1)+0),
  579. *((PULONG)(Msg+1)+1),
  580. *((PULONG)(Msg+1)+2),
  581. *((PULONG)(Msg+1)+3),
  582. CurrentThread,
  583. THREAD_TO_PROCESS( CurrentThread )->ImageFileName ));
  584. if (!IsListEmpty( &Msg->Entry )) {
  585. LpcpTrace(( "Reply Msg %lx has non-empty list entry\n", Msg ));
  586. }
  587. }
  588. #endif
  589. LpcpReleaseLpcpLock();
  590. if (Msg != NULL) {
  591. try {
  592. LpcpMoveMessage( ReplyMessage,
  593. &Msg->Request,
  594. (&Msg->Request) + 1,
  595. 0,
  596. NULL );
  597. } except( EXCEPTION_EXECUTE_HANDLER ) {
  598. Status = GetExceptionCode();
  599. }
  600. //
  601. // Acquire the LPC mutex and decrement the reference count for the
  602. // message. If the reference count goes to zero the message will be
  603. // deleted.
  604. //
  605. LpcpAcquireLpcpLockByThread(CurrentThread);
  606. if (Msg->RepliedToThread != NULL) {
  607. ObDereferenceObject( Msg->RepliedToThread );
  608. Msg->RepliedToThread = NULL;
  609. }
  610. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED | LPCP_MUTEX_RELEASE_ON_RETURN );
  611. } else {
  612. Status = STATUS_LPC_REPLY_LOST;
  613. }
  614. }
  615. else {
  616. LpcpReleaseLpcpLock();
  617. }
  618. ObDereferenceObject( PortObject );
  619. return Status;
  620. }
  621. NTSTATUS
  622. NtReadRequestData (
  623. IN HANDLE PortHandle,
  624. IN PPORT_MESSAGE Message,
  625. IN ULONG DataEntryIndex,
  626. OUT PVOID Buffer,
  627. IN SIZE_T BufferSize,
  628. OUT PSIZE_T NumberOfBytesRead OPTIONAL
  629. )
  630. /*++
  631. Routine Description:
  632. This routine is used to copy data from a port message into the user
  633. supplied buffer.
  634. Arguments:
  635. PortHandle - Supplies the port from which the message is being read
  636. Message - Supplies the message that we are trying to read
  637. DataEntryIndex - Supplies the index of the port data entry in the
  638. preceeding message that we are reading
  639. Buffer - Supplies the location into which the data is to be read
  640. BufferSize - Supplies the size, in bytes, of the preceeding buffer
  641. NumberOfBytesRead - Optionally returns the number of bytes read into
  642. the buffer
  643. Return Value:
  644. NTSTATUS - An appropriate status value
  645. --*/
  646. {
  647. NTSTATUS status;
  648. PAGED_CODE();
  649. status = LpcpCopyRequestData( FALSE,
  650. PortHandle,
  651. Message,
  652. DataEntryIndex,
  653. Buffer,
  654. BufferSize,
  655. NumberOfBytesRead );
  656. return status;
  657. }
  658. NTSTATUS
  659. NtWriteRequestData (
  660. IN HANDLE PortHandle,
  661. IN PPORT_MESSAGE Message,
  662. IN ULONG DataEntryIndex,
  663. IN PVOID Buffer,
  664. IN SIZE_T BufferSize,
  665. OUT PSIZE_T NumberOfBytesWritten OPTIONAL
  666. )
  667. /*++
  668. Routine Description:
  669. This routine is used to copy data from the user supplied buffer into the
  670. port message
  671. Arguments:
  672. PortHandle - Supplies the port into which the message is being written
  673. Message - Supplies the message that we are trying to write
  674. DataEntryIndex - Supplies the index of the port data entry in the
  675. preceeding message that we are writing
  676. Buffer - Supplies the location into which the data is to be written
  677. BufferSize - Supplies the size, in bytes, of the preceeding buffer
  678. NumberOfBytesWritten - Optionally returns the number of bytes written from
  679. the buffer
  680. Return Value:
  681. NTSTATUS - An appropriate status value
  682. --*/
  683. {
  684. NTSTATUS status;
  685. PAGED_CODE();
  686. status = LpcpCopyRequestData( TRUE,
  687. PortHandle,
  688. Message,
  689. DataEntryIndex,
  690. Buffer,
  691. BufferSize,
  692. NumberOfBytesWritten );
  693. return status;
  694. }
  695. //
  696. // Local support routine
  697. //
  698. NTSTATUS
  699. LpcpCopyRequestData (
  700. IN BOOLEAN WriteToMessageData,
  701. IN HANDLE PortHandle,
  702. IN PPORT_MESSAGE Message,
  703. IN ULONG DataEntryIndex,
  704. IN PVOID Buffer,
  705. IN SIZE_T BufferSize,
  706. OUT PSIZE_T NumberOfBytesCopied OPTIONAL
  707. )
  708. /*++
  709. Routine Description:
  710. This routine will copy data to or from the user supplied buffer and the
  711. port message data information buffer
  712. Arguments:
  713. WriteToMessageData - TRUE if the data is to be copied from the user buffer
  714. to the message and FALSE otherwise
  715. PortHandle - Supplies the port into which the message is being manipulated
  716. Message - Supplies the message that we are trying to manipulate
  717. DataEntryIndex - Supplies the index of the port data entry in the
  718. preceeding message that we are transferring
  719. Buffer - Supplies the location into which the data is to be transfered
  720. BufferSize - Supplies the size, in bytes, of the preceeding buffer
  721. NumberOfBytesRead - Optionally returns the number of bytes transfered from
  722. the buffer
  723. Return Value:
  724. NTSTATUS - An appropriate status value
  725. --*/
  726. {
  727. KPROCESSOR_MODE PreviousMode;
  728. PLPCP_PORT_OBJECT PortObject;
  729. PLPCP_MESSAGE Msg;
  730. NTSTATUS Status;
  731. PETHREAD ClientThread;
  732. PPORT_DATA_INFORMATION DataInfo;
  733. PPORT_DATA_ENTRY DataEntry;
  734. PORT_MESSAGE CapturedMessage;
  735. PORT_DATA_ENTRY CapturedDataEntry;
  736. SIZE_T BytesCopied;
  737. PAGED_CODE();
  738. //
  739. // Get previous processor mode and probe output arguments if necessary.
  740. //
  741. PreviousMode = KeGetPreviousMode();
  742. if (PreviousMode != KernelMode) {
  743. try {
  744. //
  745. // We are either reading or writing the user buffer
  746. //
  747. if (WriteToMessageData) {
  748. ProbeForRead( Buffer,
  749. BufferSize,
  750. 1 );
  751. } else {
  752. ProbeForWrite( Buffer,
  753. BufferSize,
  754. 1 );
  755. }
  756. ProbeForReadSmallStructure( Message,
  757. sizeof( *Message ),
  758. sizeof( ULONG ));
  759. CapturedMessage = *Message;
  760. if (ARGUMENT_PRESENT( NumberOfBytesCopied )) {
  761. ProbeForWriteUlong_ptr( NumberOfBytesCopied );
  762. }
  763. } except( EXCEPTION_EXECUTE_HANDLER ) {
  764. return GetExceptionCode();
  765. }
  766. } else {
  767. CapturedMessage = *Message;
  768. }
  769. //
  770. // The message better have at least one data entry
  771. //
  772. if (CapturedMessage.u2.s2.DataInfoOffset == 0) {
  773. return STATUS_INVALID_PARAMETER;
  774. }
  775. //
  776. // Reference the port object by handle
  777. //
  778. Status = LpcpReferencePortObject( PortHandle,
  779. 0,
  780. PreviousMode,
  781. &PortObject );
  782. if (!NT_SUCCESS( Status )) {
  783. return Status;
  784. }
  785. //
  786. // Translate the ClientId from the connection request into a
  787. // thread pointer. This is a referenced pointer to keep the thread
  788. // from evaporating out from under us.
  789. //
  790. Status = PsLookupProcessThreadByCid( &CapturedMessage.ClientId,
  791. NULL,
  792. &ClientThread );
  793. if (!NT_SUCCESS( Status )) {
  794. ObDereferenceObject( PortObject );
  795. return Status;
  796. }
  797. //
  798. // Acquire the mutex that guards the LpcReplyMessage field of
  799. // the thread and get the pointer to the message that the thread
  800. // is waiting for a reply to.
  801. //
  802. LpcpAcquireLpcpLock();
  803. //
  804. // See if the thread is waiting for a reply to the message
  805. // specified on this call. If not then a bogus message
  806. // has been specified, so release the mutex, dereference the thread
  807. // and return failure.
  808. //
  809. if ( (ClientThread->LpcReplyMessageId != CapturedMessage.MessageId) ||
  810. !LpcpValidateClientPort(ClientThread, PortObject, LPCP_VALIDATE_REASON_WRONG_DATA) ) {
  811. Status = STATUS_REPLY_MESSAGE_MISMATCH;
  812. } else {
  813. Status = STATUS_INVALID_PARAMETER;
  814. Msg = LpcpFindDataInfoMessage( PortObject,
  815. CapturedMessage.MessageId,
  816. CapturedMessage.CallbackId );
  817. if (Msg != NULL) {
  818. DataInfo = (PPORT_DATA_INFORMATION)((PUCHAR)&Msg->Request +
  819. Msg->Request.u2.s2.DataInfoOffset);
  820. //
  821. // Make sure the caller isn't asking for an index beyond what's
  822. // in the message
  823. //
  824. if (DataInfo->CountDataEntries > DataEntryIndex) {
  825. DataEntry = &DataInfo->DataEntries[ DataEntryIndex ];
  826. CapturedDataEntry = *DataEntry;
  827. if (CapturedDataEntry.Size >= BufferSize) {
  828. Status = STATUS_SUCCESS;
  829. }
  830. }
  831. }
  832. }
  833. //
  834. // Release the mutex that guards the LpcReplyMessage field
  835. //
  836. LpcpReleaseLpcpLock();
  837. if (!NT_SUCCESS( Status )) {
  838. ObDereferenceObject( ClientThread );
  839. ObDereferenceObject( PortObject );
  840. return Status;
  841. }
  842. //
  843. // Copy the message data
  844. //
  845. if (WriteToMessageData) {
  846. Status = MmCopyVirtualMemory( PsGetCurrentProcess(),
  847. Buffer,
  848. THREAD_TO_PROCESS( ClientThread ),
  849. CapturedDataEntry.Base,
  850. BufferSize,
  851. PreviousMode,
  852. &BytesCopied );
  853. } else {
  854. Status = MmCopyVirtualMemory( THREAD_TO_PROCESS( ClientThread ),
  855. CapturedDataEntry.Base,
  856. PsGetCurrentProcess(),
  857. Buffer,
  858. BufferSize,
  859. PreviousMode,
  860. &BytesCopied );
  861. }
  862. if (ARGUMENT_PRESENT( NumberOfBytesCopied )) {
  863. try {
  864. *NumberOfBytesCopied = BytesCopied;
  865. } except( EXCEPTION_EXECUTE_HANDLER ) {
  866. NOTHING;
  867. }
  868. }
  869. //
  870. // Dereference client thread and return the system service status.
  871. //
  872. ObDereferenceObject( ClientThread );
  873. ObDereferenceObject( PortObject );
  874. return Status;
  875. }
  876. BOOLEAN
  877. FASTCALL
  878. LpcpValidateClientPort(
  879. IN PETHREAD Thread,
  880. IN PLPCP_PORT_OBJECT ReplyPort,
  881. IN ULONG Reason
  882. )
  883. /*++
  884. Routine Description:
  885. This routine validates whether the reply for a request come from
  886. an appropriate port
  887. Arguments:
  888. Thread - The thread waiting for a reply
  889. ReplyPort - The port object that is replying
  890. Return Value:
  891. BOOLEAN - TRUE if the reply come from a valid port
  892. Environment:
  893. This is called holding the global LPC mutex.
  894. --*/
  895. {
  896. PLPCP_PORT_OBJECT PortThread;
  897. PortThread = LpcpGetThreadPort(Thread);
  898. //
  899. // The thread must have a port set
  900. //
  901. if (PortThread == NULL) {
  902. return FALSE;
  903. }
  904. //
  905. // We only allow a port connected with the requestor.
  906. // Also csrss is giving the server connection port to clients
  907. // and we can have a client sending a request from a server connection port
  908. // and csrss can reply with a server communication port
  909. //
  910. if ( ( ReplyPort == PortThread->ConnectionPort )
  911. ||
  912. ( ReplyPort == PortThread->ConnectedPort )
  913. ||
  914. ( ReplyPort == PortThread )
  915. ||
  916. (
  917. ((ReplyPort->Flags & PORT_TYPE) == SERVER_COMMUNICATION_PORT)
  918. &&
  919. (ReplyPort->ConnectionPort == PortThread)
  920. )
  921. ) {
  922. return TRUE;
  923. }
  924. #if 0
  925. if (LpcpEventCounts < LpcMaxEventLogs) {
  926. PUNICODE_STRING StrReason;
  927. POBJECT_NAME_INFORMATION ObjectNameInfo;
  928. NTSTATUS Status;
  929. ULONG Length;
  930. PLPC_WORK_CONTEXT AuditItem;
  931. if (PortThread->ConnectionPort) {
  932. ObjectNameInfo = ExAllocatePoolWithTag(PagedPool, LPCP_PORT_NAME_MAX + sizeof (UNICODE_STRING), 'ScpL');
  933. if (ObjectNameInfo != NULL) {
  934. Status = ObQueryNameString( PortThread->ConnectionPort,
  935. ObjectNameInfo,
  936. LPCP_PORT_NAME_MAX,
  937. &Length
  938. );
  939. if (NT_SUCCESS(Status)) {
  940. //
  941. // Audit the event. Use a worker thread to avoid burning
  942. // up a bunch of cycles since the global mutex is held.
  943. //
  944. StrReason = (PUNICODE_STRING)((ULONG_PTR) ObjectNameInfo + LPCP_PORT_NAME_MAX);
  945. switch (Reason) {
  946. case LPCP_VALIDATE_REASON_IMPERSONATION:
  947. RtlInitUnicodeString( StrReason, L"impersonation" );
  948. break;
  949. case LPCP_VALIDATE_REASON_REPLY:
  950. RtlInitUnicodeString( StrReason, L"reply" );
  951. break;
  952. case LPCP_VALIDATE_REASON_WRONG_DATA:
  953. RtlInitUnicodeString( StrReason, L"data access" );
  954. break;
  955. }
  956. AuditItem = ExAllocatePoolWithTag (NonPagedPool,
  957. sizeof(LPC_WORK_CONTEXT),
  958. 'wcpL');
  959. if (AuditItem != NULL) {
  960. AuditItem->Buffer = (PVOID) ObjectNameInfo;
  961. ExInitializeWorkItem (&AuditItem->WorkItem,
  962. LpcpAuditInvalidUse,
  963. (PVOID) AuditItem);
  964. ExQueueWorkItem (&AuditItem->WorkItem, DelayedWorkQueue);
  965. LpcpEventCounts += 1;
  966. }
  967. else {
  968. ExFreePool (ObjectNameInfo);
  969. }
  970. }
  971. else {
  972. ExFreePool (ObjectNameInfo);
  973. }
  974. }
  975. }
  976. #if DBG
  977. if (LpcpStopOnReplyMismatch) {
  978. DbgBreakPoint();
  979. }
  980. #endif
  981. }
  982. #endif
  983. return FALSE;
  984. }
  985. #if 0
  986. VOID
  987. LpcpAuditInvalidUse (
  988. IN PVOID Context
  989. )
  990. /*++
  991. Routine Description:
  992. This routine is the worker routine which logs security items.
  993. Arguments:
  994. Context - Supplies a pointer to the LPC_WORK_CONTEXT for the audit event.
  995. Return Value:
  996. None.
  997. Environment:
  998. Kernel mode, PASSIVE_LEVEL.
  999. --*/
  1000. {
  1001. PUNICODE_STRING StrReason;
  1002. POBJECT_NAME_INFORMATION ObjectNameInfo;
  1003. PLPC_WORK_CONTEXT AuditItem;
  1004. PAGED_CODE();
  1005. AuditItem = (PLPC_WORK_CONTEXT) Context;
  1006. ObjectNameInfo = (POBJECT_NAME_INFORMATION) AuditItem->Buffer;
  1007. StrReason = (PUNICODE_STRING)((ULONG_PTR) ObjectNameInfo + LPCP_PORT_NAME_MAX);
  1008. SeAuditLPCInvalidUse (StrReason, &ObjectNameInfo->Name);
  1009. ExFreePool (ObjectNameInfo);
  1010. ExFreePool (AuditItem);
  1011. }
  1012. #endif