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.

697 lines
20 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. keyedevent.c
  5. Abstract:
  6. This module houses routines that do keyed event processing.
  7. Author:
  8. Neill Clift (NeillC) 25-Apr-2001
  9. Revision History:
  10. --*/
  11. #include "exp.h"
  12. #pragma hdrstop
  13. #ifdef ALLOC_PRAGMA
  14. #pragma alloc_text(INIT, ExpKeyedEventInitialization)
  15. #pragma alloc_text(PAGE, NtCreateKeyedEvent)
  16. #pragma alloc_text(PAGE, NtOpenKeyedEvent)
  17. #pragma alloc_text(PAGE, NtReleaseKeyedEvent)
  18. #pragma alloc_text(PAGE, NtWaitForKeyedEvent)
  19. #endif
  20. //
  21. // Define the keyed event object type
  22. //
  23. typedef struct _KEYED_EVENT_OBJECT {
  24. EX_PUSH_LOCK Lock;
  25. LIST_ENTRY WaitQueue;
  26. } KEYED_EVENT_OBJECT, *PKEYED_EVENT_OBJECT;
  27. POBJECT_TYPE ExpKeyedEventObjectType;
  28. //
  29. // The low bit of the keyvalue signifies that we are a release thread waiting
  30. // for the wait thread to enter the keyed event code.
  31. //
  32. #define KEYVALUE_RELEASE 1
  33. #define LOCK_KEYED_EVENT_EXCLUSIVE(xxxKeyedEventObject,xxxCurrentThread) { \
  34. KeEnterCriticalRegionThread (&(xxxCurrentThread)->Tcb); \
  35. ExAcquirePushLockExclusive (&(xxxKeyedEventObject)->Lock); \
  36. }
  37. #define UNLOCK_KEYED_EVENT_EXCLUSIVE(xxxKeyedEventObject,xxxCurrentThread) { \
  38. ExReleasePushLockExclusive (&(xxxKeyedEventObject)->Lock); \
  39. KeLeaveCriticalRegionThread (&(xxxCurrentThread)->Tcb); \
  40. }
  41. #define UNLOCK_KEYED_EVENT_EXCLUSIVE_UNSAFE(xxxKeyedEventObject) { \
  42. ExReleasePushLockExclusive (&(xxxKeyedEventObject)->Lock); \
  43. }
  44. NTSTATUS
  45. ExpKeyedEventInitialization (
  46. VOID
  47. )
  48. /*++
  49. Routine Description:
  50. Initialize the keyed event objects and globals.
  51. Arguments:
  52. None.
  53. Return Value:
  54. NTSTATUS - Status of call
  55. --*/
  56. {
  57. NTSTATUS Status;
  58. UNICODE_STRING Name;
  59. OBJECT_TYPE_INITIALIZER oti = {0};
  60. OBJECT_ATTRIBUTES oa;
  61. SECURITY_DESCRIPTOR SecurityDescriptor;
  62. PACL Dacl;
  63. ULONG DaclLength;
  64. HANDLE KeyedEventHandle;
  65. GENERIC_MAPPING GenericMapping = {STANDARD_RIGHTS_READ | KEYEDEVENT_WAIT,
  66. STANDARD_RIGHTS_WRITE | KEYEDEVENT_WAKE,
  67. STANDARD_RIGHTS_EXECUTE,
  68. KEYEDEVENT_ALL_ACCESS};
  69. PAGED_CODE ();
  70. RtlInitUnicodeString (&Name, L"KeyedEvent");
  71. oti.Length = sizeof (oti);
  72. oti.InvalidAttributes = 0;
  73. oti.PoolType = PagedPool;
  74. oti.ValidAccessMask = KEYEDEVENT_ALL_ACCESS;
  75. oti.GenericMapping = GenericMapping;
  76. oti.DefaultPagedPoolCharge = 0;
  77. oti.DefaultNonPagedPoolCharge = 0;
  78. oti.UseDefaultObject = TRUE;
  79. Status = ObCreateObjectType (&Name, &oti, NULL, &ExpKeyedEventObjectType);
  80. if (!NT_SUCCESS (Status)) {
  81. return Status;
  82. }
  83. //
  84. // Create a global object for processes that are out of memory
  85. //
  86. Status = RtlCreateSecurityDescriptor (&SecurityDescriptor,
  87. SECURITY_DESCRIPTOR_REVISION);
  88. if (!NT_SUCCESS (Status)) {
  89. return Status;
  90. }
  91. DaclLength = sizeof (ACL) + sizeof (ACCESS_ALLOWED_ACE) * 3 +
  92. RtlLengthSid (SeLocalSystemSid) +
  93. RtlLengthSid (SeAliasAdminsSid) +
  94. RtlLengthSid (SeWorldSid);
  95. Dacl = ExAllocatePoolWithTag (PagedPool, DaclLength, 'lcaD');
  96. if (Dacl == NULL) {
  97. return STATUS_INSUFFICIENT_RESOURCES;
  98. }
  99. Status = RtlCreateAcl (Dacl, DaclLength, ACL_REVISION);
  100. if (!NT_SUCCESS (Status)) {
  101. ExFreePool (Dacl);
  102. return Status;
  103. }
  104. Status = RtlAddAccessAllowedAce (Dacl,
  105. ACL_REVISION,
  106. KEYEDEVENT_ALL_ACCESS,
  107. SeAliasAdminsSid);
  108. if (!NT_SUCCESS (Status)) {
  109. ExFreePool (Dacl);
  110. return Status;
  111. }
  112. Status = RtlAddAccessAllowedAce (Dacl,
  113. ACL_REVISION,
  114. KEYEDEVENT_ALL_ACCESS,
  115. SeLocalSystemSid);
  116. if (!NT_SUCCESS (Status)) {
  117. ExFreePool (Dacl);
  118. return Status;
  119. }
  120. Status = RtlAddAccessAllowedAce (Dacl,
  121. ACL_REVISION,
  122. KEYEDEVENT_WAIT|KEYEDEVENT_WAKE|READ_CONTROL,
  123. SeWorldSid);
  124. if (!NT_SUCCESS (Status)) {
  125. ExFreePool (Dacl);
  126. return Status;
  127. }
  128. Status = RtlSetDaclSecurityDescriptor (&SecurityDescriptor,
  129. TRUE,
  130. Dacl,
  131. FALSE);
  132. if (!NT_SUCCESS (Status)) {
  133. ExFreePool (Dacl);
  134. return Status;
  135. }
  136. RtlInitUnicodeString (&Name, L"\\KernelObjects\\CritSecOutOfMemoryEvent");
  137. InitializeObjectAttributes (&oa, &Name, OBJ_PERMANENT, NULL, &SecurityDescriptor);
  138. Status = ZwCreateKeyedEvent (&KeyedEventHandle,
  139. KEYEDEVENT_ALL_ACCESS,
  140. &oa,
  141. 0);
  142. ExFreePool (Dacl);
  143. if (NT_SUCCESS (Status)) {
  144. Status = ZwClose (KeyedEventHandle);
  145. }
  146. return Status;
  147. }
  148. NTSTATUS
  149. NtCreateKeyedEvent (
  150. OUT PHANDLE KeyedEventHandle,
  151. IN ACCESS_MASK DesiredAccess,
  152. IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
  153. IN ULONG Flags
  154. )
  155. /*++
  156. Routine Description:
  157. Create a keyed event object and return its handle
  158. Arguments:
  159. KeyedEventHandle - Address to store returned handle in
  160. DesiredAccess - Access required to keyed event
  161. ObjectAttributes - Object attributes block to describe parent
  162. handle and name of event
  163. Return Value:
  164. NTSTATUS - Status of call
  165. --*/
  166. {
  167. NTSTATUS Status;
  168. PKEYED_EVENT_OBJECT KeyedEventObject;
  169. HANDLE Handle;
  170. KPROCESSOR_MODE PreviousMode;
  171. //
  172. // Get previous processor mode and probe output arguments if necessary.
  173. // Zero the handle for error paths.
  174. //
  175. PreviousMode = KeGetPreviousMode();
  176. try {
  177. if (PreviousMode != KernelMode) {
  178. ProbeForReadSmallStructure (KeyedEventHandle,
  179. sizeof (*KeyedEventHandle),
  180. sizeof (*KeyedEventHandle));
  181. }
  182. *KeyedEventHandle = NULL;
  183. } except (ExSystemExceptionFilter ()) {
  184. return GetExceptionCode ();
  185. }
  186. if (Flags != 0) {
  187. return STATUS_INVALID_PARAMETER_4;
  188. }
  189. //
  190. // Create a new keyed event object and initialize it.
  191. //
  192. Status = ObCreateObject (PreviousMode,
  193. ExpKeyedEventObjectType,
  194. ObjectAttributes,
  195. PreviousMode,
  196. NULL,
  197. sizeof (KEYED_EVENT_OBJECT),
  198. 0,
  199. 0,
  200. &KeyedEventObject);
  201. if (!NT_SUCCESS (Status)) {
  202. return Status;
  203. }
  204. //
  205. // Initialize the lock and wait queue
  206. //
  207. ExInitializePushLock (&KeyedEventObject->Lock);
  208. InitializeListHead (&KeyedEventObject->WaitQueue);
  209. //
  210. // Insert the object into the handle table
  211. //
  212. Status = ObInsertObject (KeyedEventObject,
  213. NULL,
  214. DesiredAccess,
  215. 0,
  216. NULL,
  217. &Handle);
  218. if (!NT_SUCCESS (Status)) {
  219. return Status;
  220. }
  221. try {
  222. *KeyedEventHandle = Handle;
  223. } except (ExSystemExceptionFilter ()) {
  224. //
  225. // The caller changed the page protection or deleted the momory for the handle.
  226. // No point closing the handle as process rundown will do that and we don't
  227. // know its still the same handle
  228. //
  229. Status = GetExceptionCode ();
  230. }
  231. return Status;
  232. }
  233. NTSTATUS
  234. NtOpenKeyedEvent (
  235. OUT PHANDLE KeyedEventHandle,
  236. IN ACCESS_MASK DesiredAccess,
  237. IN POBJECT_ATTRIBUTES ObjectAttributes
  238. )
  239. /*++
  240. Routine Description:
  241. Open a keyed event object and return its handle
  242. Arguments:
  243. KeyedEventHandle - Address to store returned handle in
  244. DesiredAccess - Access required to keyed event
  245. ObjectAttributes - Object attributes block to describe parent
  246. handle and name of event
  247. Return Value:
  248. NTSTATUS - Status of call
  249. --*/
  250. {
  251. HANDLE Handle;
  252. KPROCESSOR_MODE PreviousMode;
  253. NTSTATUS Status;
  254. //
  255. // Get previous processor mode and probe output handle address
  256. // if necessary.
  257. //
  258. PreviousMode = KeGetPreviousMode();
  259. try {
  260. if (PreviousMode != KernelMode) {
  261. ProbeForReadSmallStructure (KeyedEventHandle,
  262. sizeof (*KeyedEventHandle),
  263. sizeof (*KeyedEventHandle));
  264. }
  265. *KeyedEventHandle = NULL;
  266. } except (ExSystemExceptionFilter ()) {
  267. return GetExceptionCode ();
  268. }
  269. //
  270. // Open handle to the keyed event object with the specified desired access.
  271. //
  272. Status = ObOpenObjectByName (ObjectAttributes,
  273. ExpKeyedEventObjectType,
  274. PreviousMode,
  275. NULL,
  276. DesiredAccess,
  277. NULL,
  278. &Handle);
  279. if (NT_SUCCESS (Status)) {
  280. try {
  281. *KeyedEventHandle = Handle;
  282. } except (ExSystemExceptionFilter ()) {
  283. Status = GetExceptionCode ();
  284. }
  285. }
  286. return Status;
  287. }
  288. NTSTATUS
  289. NtReleaseKeyedEvent (
  290. IN HANDLE KeyedEventHandle,
  291. IN PVOID KeyValue,
  292. IN BOOLEAN Alertable,
  293. IN PLARGE_INTEGER Timeout OPTIONAL
  294. )
  295. /*++
  296. Routine Description:
  297. Release a previous or soon to be waiter with a matching key
  298. Arguments:
  299. KeyedEventHandle - Handle to a keyed event
  300. KeyValue - Value to be used to match the waiter against
  301. Alertable - Should the wait be alertable, we rarely should have to wait
  302. Timeout - Timout value for the wait, waits should be rare
  303. Return Value:
  304. NTSTATUS - Status of call
  305. --*/
  306. {
  307. NTSTATUS Status;
  308. KPROCESSOR_MODE PreviousMode;
  309. PKEYED_EVENT_OBJECT KeyedEventObject;
  310. PETHREAD CurrentThread, TargetThread;
  311. PEPROCESS CurrentProcess;
  312. PLIST_ENTRY ListHead, ListEntry;
  313. LARGE_INTEGER TimeoutValue;
  314. PVOID OldKeyValue = NULL;
  315. if ((((ULONG_PTR)KeyValue) & KEYVALUE_RELEASE) != 0) {
  316. return STATUS_INVALID_PARAMETER_1;
  317. }
  318. CurrentThread = PsGetCurrentThread ();
  319. PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
  320. if (Timeout != NULL) {
  321. try {
  322. if (PreviousMode != KernelMode) {
  323. ProbeForRead (Timeout, sizeof (*Timeout), sizeof (UCHAR));
  324. }
  325. TimeoutValue = *Timeout;
  326. Timeout = &TimeoutValue;
  327. } except(ExSystemExceptionFilter ()) {
  328. return GetExceptionCode ();
  329. }
  330. }
  331. Status = ObReferenceObjectByHandle (KeyedEventHandle,
  332. KEYEDEVENT_WAKE,
  333. ExpKeyedEventObjectType,
  334. PreviousMode,
  335. &KeyedEventObject,
  336. NULL);
  337. if (!NT_SUCCESS (Status)) {
  338. return Status;
  339. }
  340. CurrentProcess = PsGetCurrentProcessByThread (CurrentThread);
  341. ListHead = &KeyedEventObject->WaitQueue;
  342. LOCK_KEYED_EVENT_EXCLUSIVE (KeyedEventObject, CurrentThread);
  343. ListEntry = ListHead->Flink;
  344. while (1) {
  345. if (ListEntry == ListHead) {
  346. //
  347. // We could not find a key matching ours in the list.
  348. // Either somebody called us with wrong values or the waiter
  349. // has not managed to get queued yet. We wait ourselves
  350. // to be released by the waiter.
  351. //
  352. OldKeyValue = CurrentThread->KeyedWaitValue;
  353. CurrentThread->KeyedWaitValue = (PVOID) (((ULONG_PTR)KeyValue)|KEYVALUE_RELEASE);
  354. //
  355. // Insert the thread at the head of the list. We establish an invariant
  356. // were release waiters are always at the front of the queue to improve
  357. // the wait code since it only has to search as far as the first non-release
  358. // waiter.
  359. //
  360. InsertHeadList (ListHead, &CurrentThread->KeyedWaitChain);
  361. TargetThread = NULL;
  362. break;
  363. } else {
  364. TargetThread = CONTAINING_RECORD (ListEntry, ETHREAD, KeyedWaitChain);
  365. if (TargetThread->KeyedWaitValue == KeyValue &&
  366. THREAD_TO_PROCESS (TargetThread) == CurrentProcess) {
  367. RemoveEntryList (ListEntry);
  368. InitializeListHead (ListEntry);
  369. break;
  370. }
  371. }
  372. ListEntry = ListEntry->Flink;
  373. }
  374. //
  375. // Release the lock but leave APC's disabled.
  376. // This prevents us from being suspended and holding up the target.
  377. //
  378. UNLOCK_KEYED_EVENT_EXCLUSIVE_UNSAFE (KeyedEventObject);
  379. if (TargetThread != NULL) {
  380. KeReleaseSemaphore (&TargetThread->KeyedWaitSemaphore,
  381. SEMAPHORE_INCREMENT,
  382. 1,
  383. FALSE);
  384. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  385. } else {
  386. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  387. Status = KeWaitForSingleObject (&CurrentThread->KeyedWaitSemaphore,
  388. Executive,
  389. PreviousMode,
  390. Alertable,
  391. Timeout);
  392. //
  393. // If we were woken by termination then we must manualy remove
  394. // ourselves from the queue
  395. //
  396. if (Status != STATUS_SUCCESS) {
  397. BOOLEAN Wait = TRUE;
  398. LOCK_KEYED_EVENT_EXCLUSIVE (KeyedEventObject, CurrentThread);
  399. if (!IsListEmpty (&CurrentThread->KeyedWaitChain)) {
  400. RemoveEntryList (&CurrentThread->KeyedWaitChain);
  401. InitializeListHead (&CurrentThread->KeyedWaitChain);
  402. Wait = FALSE;
  403. }
  404. UNLOCK_KEYED_EVENT_EXCLUSIVE (KeyedEventObject, CurrentThread);
  405. //
  406. // If this thread was no longer in the queue then another thread
  407. // must be about to wake us up. Wait for that wake.
  408. //
  409. if (Wait) {
  410. KeWaitForSingleObject (&CurrentThread->KeyedWaitSemaphore,
  411. Executive,
  412. KernelMode,
  413. FALSE,
  414. NULL);
  415. }
  416. }
  417. CurrentThread->KeyedWaitValue = OldKeyValue;
  418. }
  419. ObDereferenceObject (KeyedEventObject);
  420. return Status;
  421. }
  422. NTSTATUS
  423. NtWaitForKeyedEvent (
  424. IN HANDLE KeyedEventHandle,
  425. IN PVOID KeyValue,
  426. IN BOOLEAN Alertable,
  427. IN PLARGE_INTEGER Timeout OPTIONAL
  428. )
  429. /*++
  430. Routine Description:
  431. Wait on the keyed event for a specific release
  432. Arguments:
  433. KeyedEventHandle - Handle to a keyed event
  434. KeyValue - Value to be used to match the release thread against
  435. Alertable - Makes the wait alertable or not
  436. Timeout - Timeout value for wait
  437. Return Value:
  438. NTSTATUS - Status of call
  439. --*/
  440. {
  441. NTSTATUS Status;
  442. KPROCESSOR_MODE PreviousMode;
  443. PKEYED_EVENT_OBJECT KeyedEventObject;
  444. PETHREAD CurrentThread, TargetThread;
  445. PEPROCESS CurrentProcess;
  446. PLIST_ENTRY ListHead, ListEntry;
  447. LARGE_INTEGER TimeoutValue;
  448. PVOID OldKeyValue=NULL;
  449. if ((((ULONG_PTR)KeyValue) & KEYVALUE_RELEASE) != 0) {
  450. return STATUS_INVALID_PARAMETER_1;
  451. }
  452. CurrentThread = PsGetCurrentThread ();
  453. PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
  454. if (Timeout != NULL) {
  455. try {
  456. if (PreviousMode != KernelMode) {
  457. ProbeForRead (Timeout, sizeof (*Timeout), sizeof (UCHAR));
  458. }
  459. TimeoutValue = *Timeout;
  460. Timeout = &TimeoutValue;
  461. } except(ExSystemExceptionFilter ()) {
  462. return GetExceptionCode ();
  463. }
  464. }
  465. Status = ObReferenceObjectByHandle (KeyedEventHandle,
  466. KEYEDEVENT_WAIT,
  467. ExpKeyedEventObjectType,
  468. PreviousMode,
  469. &KeyedEventObject,
  470. NULL);
  471. if (!NT_SUCCESS (Status)) {
  472. return Status;
  473. }
  474. CurrentProcess = PsGetCurrentProcessByThread (CurrentThread);
  475. ListHead = &KeyedEventObject->WaitQueue;
  476. LOCK_KEYED_EVENT_EXCLUSIVE (KeyedEventObject, CurrentThread);
  477. ListEntry = ListHead->Flink;
  478. while (1) {
  479. TargetThread = CONTAINING_RECORD (ListEntry, ETHREAD, KeyedWaitChain);
  480. if (ListEntry == ListHead ||
  481. (((ULONG_PTR)(TargetThread->KeyedWaitValue))&KEYVALUE_RELEASE) == 0) {
  482. //
  483. // We could not find a key matching ours in the list so we must wait
  484. //
  485. OldKeyValue = CurrentThread->KeyedWaitValue;
  486. CurrentThread->KeyedWaitValue = KeyValue;
  487. //
  488. // Insert the thread at the tail of the list. We establish an invariant
  489. // were waiters are always at the back of the queue behind releasers to improve
  490. // the wait code since it only has to search as far as the first non-release
  491. // waiter.
  492. //
  493. InsertTailList (ListHead, &CurrentThread->KeyedWaitChain);
  494. TargetThread = NULL;
  495. break;
  496. } else {
  497. if (TargetThread->KeyedWaitValue == (PVOID)(((ULONG_PTR)KeyValue)|KEYVALUE_RELEASE) &&
  498. THREAD_TO_PROCESS (TargetThread) == CurrentProcess) {
  499. RemoveEntryList (ListEntry);
  500. InitializeListHead (ListEntry);
  501. break;
  502. }
  503. }
  504. ListEntry = ListEntry->Flink;
  505. }
  506. //
  507. // Release the lock but leave APC's disabled.
  508. // This prevents us from being suspended and holding up the target.
  509. //
  510. UNLOCK_KEYED_EVENT_EXCLUSIVE_UNSAFE (KeyedEventObject);
  511. if (TargetThread == NULL) {
  512. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  513. Status = KeWaitForSingleObject (&CurrentThread->KeyedWaitSemaphore,
  514. Executive,
  515. PreviousMode,
  516. Alertable,
  517. Timeout);
  518. //
  519. // If we were woken by termination then we must manualy remove
  520. // ourselves from the queue
  521. //
  522. if (Status != STATUS_SUCCESS) {
  523. BOOLEAN Wait = TRUE;
  524. LOCK_KEYED_EVENT_EXCLUSIVE (KeyedEventObject, CurrentThread);
  525. if (!IsListEmpty (&CurrentThread->KeyedWaitChain)) {
  526. RemoveEntryList (&CurrentThread->KeyedWaitChain);
  527. InitializeListHead (&CurrentThread->KeyedWaitChain);
  528. Wait = FALSE;
  529. }
  530. UNLOCK_KEYED_EVENT_EXCLUSIVE (KeyedEventObject, CurrentThread);
  531. //
  532. // If this thread was no longer in the queue then another thread
  533. // must be about to wake us up. Wait for that wake.
  534. //
  535. if (Wait) {
  536. KeWaitForSingleObject (&CurrentThread->KeyedWaitSemaphore,
  537. Executive,
  538. KernelMode,
  539. FALSE,
  540. NULL);
  541. }
  542. }
  543. CurrentThread->KeyedWaitValue = OldKeyValue;
  544. } else {
  545. KeReleaseSemaphore (&TargetThread->KeyedWaitSemaphore,
  546. SEMAPHORE_INCREMENT,
  547. 1,
  548. FALSE);
  549. KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
  550. }
  551. ObDereferenceObject (KeyedEventObject);
  552. return Status;
  553. }