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.

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