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.

751 lines
23 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. critsect.c
  5. Abstract:
  6. This module implements verification functions for
  7. critical section interfaces.
  8. Author:
  9. Daniel Mihai (DMihai) 27-Mar-2001
  10. Revision History:
  11. --*/
  12. #include "pch.h"
  13. #include "verifier.h"
  14. VOID
  15. RtlpWaitForCriticalSection (
  16. IN PRTL_CRITICAL_SECTION CriticalSection
  17. );
  18. //NTSYSAPI
  19. BOOL
  20. NTAPI
  21. AVrfpRtlTryEnterCriticalSection(
  22. PRTL_CRITICAL_SECTION CriticalSection
  23. )
  24. {
  25. BOOL Result;
  26. HANDLE CurrentThread;
  27. LONG LockCount;
  28. if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_LOCK_CHECKS) != 0 &&
  29. RtlDllShutdownInProgress() == FALSE ) {
  30. //
  31. // Sanity test for DebugInfo.
  32. //
  33. if (CriticalSection->DebugInfo == NULL) {
  34. //
  35. // This critical section is not initialized.
  36. //
  37. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_NOT_INITIALIZED,
  38. "critical section not initialized",
  39. CriticalSection, "Critical section address",
  40. CriticalSection->DebugInfo, "Critical section debug info address",
  41. NULL, "",
  42. NULL, "");
  43. }
  44. CurrentThread = NtCurrentTeb()->ClientId.UniqueThread;
  45. LockCount = InterlockedCompareExchange( &CriticalSection->LockCount,
  46. 0,
  47. -1 );
  48. if (LockCount == -1) {
  49. //
  50. // The critical section was unowned and we just acquired it
  51. //
  52. //
  53. // Sanity test for the OwningThread.
  54. //
  55. if (CriticalSection->OwningThread != 0) {
  56. //
  57. // The loader lock gets handled differently, so don't assert on it.
  58. //
  59. if (CriticalSection != NtCurrentPeb()->LoaderLock ||
  60. CriticalSection->OwningThread != CurrentThread) {
  61. //
  62. // OwningThread should have been 0.
  63. //
  64. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_OWNER,
  65. "invalid critical section owner thread",
  66. CriticalSection, "Critical section address",
  67. CriticalSection->OwningThread, "Owning thread",
  68. 0, "Expected owning thread",
  69. CriticalSection->DebugInfo, "Critical section debug info address");
  70. }
  71. }
  72. //
  73. // Sanity test for the RecursionCount.
  74. //
  75. if (CriticalSection->RecursionCount != 0) {
  76. //
  77. // The loader lock gets handled differently, so don't assert on it.
  78. //
  79. if (CriticalSection != NtCurrentPeb()->LoaderLock) {
  80. //
  81. // RecursionCount should have been 0.
  82. //
  83. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_RECURSION_COUNT,
  84. "invalid critical section recursion count",
  85. CriticalSection, "Critical section address",
  86. CriticalSection->RecursionCount, "Recursion count",
  87. 0, "Expected recursion count",
  88. CriticalSection->DebugInfo, "Critical section debug info address");
  89. }
  90. }
  91. //
  92. // Set the critical section owner
  93. //
  94. CriticalSection->OwningThread = CurrentThread;
  95. //
  96. // Set the recursion count
  97. //
  98. // ntdll\ia64\critsect.s is using RecursionCount = 0 first time
  99. // the current thread is acquiring the critical section.
  100. //
  101. // ntdll\ia64\critsect.asm is using RecursionCount = 1 first time
  102. // the current thread is acquiring the critical section.
  103. //
  104. #if defined(_IA64_)
  105. CriticalSection->RecursionCount = 0;
  106. #else //#if defined(_IA64_)
  107. CriticalSection->RecursionCount = 1;
  108. #endif
  109. #if DBG
  110. //
  111. // In a chk build we are updating this additional counter,
  112. // just like the original function in ntdll does.
  113. //
  114. NtCurrentTeb()->CountOfOwnedCriticalSections += 1;
  115. #endif
  116. //
  117. // All done, CriticalSection is owned by the current thread.
  118. //
  119. Result = TRUE;
  120. }
  121. else {
  122. //
  123. // The critical section is currently owned by the current or another thread.
  124. //
  125. if (CriticalSection->OwningThread == CurrentThread) {
  126. //
  127. // The current thread is already the owner.
  128. //
  129. //
  130. // Inrelock increment the LockCount, and increment the RecursionCount.
  131. //
  132. InterlockedIncrement (&CriticalSection->LockCount);
  133. CriticalSection->RecursionCount += 1;
  134. //
  135. // All done, CriticalSection was already owned by
  136. // the current thread and we have just incremented the RecursionCount.
  137. //
  138. Result = TRUE;
  139. }
  140. else {
  141. //
  142. // Another thread is the owner of this critical section.
  143. //
  144. Result = FALSE;
  145. }
  146. }
  147. }
  148. else {
  149. //
  150. // The critical section verifier is not enabled
  151. //
  152. Result = RtlTryEnterCriticalSection (CriticalSection);
  153. }
  154. return Result;
  155. }
  156. //NTSYSAPI
  157. NTSTATUS
  158. NTAPI
  159. AVrfpRtlEnterCriticalSection(
  160. volatile RTL_CRITICAL_SECTION *CriticalSection
  161. )
  162. {
  163. NTSTATUS Status;
  164. HANDLE CurrentThread;
  165. LONG LockCount;
  166. ULONG_PTR SpinCount;
  167. if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_LOCK_CHECKS) != 0 &&
  168. RtlDllShutdownInProgress() == FALSE ) {
  169. //
  170. // Sanity test for DebugInfo.
  171. //
  172. if (CriticalSection->DebugInfo == NULL) {
  173. //
  174. // This critical section is not initialized.
  175. //
  176. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_NOT_INITIALIZED,
  177. "critical section not initialized",
  178. CriticalSection, "Critical section address",
  179. CriticalSection->DebugInfo, "Critical section debug info address",
  180. NULL, "",
  181. NULL, "");
  182. }
  183. Status = STATUS_SUCCESS;
  184. CurrentThread = NtCurrentTeb()->ClientId.UniqueThread;
  185. SpinCount = CriticalSection->SpinCount;
  186. if (SpinCount == 0) {
  187. //
  188. // Zero spincount for this critical section.
  189. //
  190. EnterZeroSpinCount:
  191. LockCount = InterlockedIncrement (&CriticalSection->LockCount);
  192. if (LockCount == 0) {
  193. EnterSetOwnerAndRecursion:
  194. //
  195. // The current thread is the new owner of the critical section.
  196. //
  197. //
  198. // Sanity test for the OwningThread.
  199. //
  200. if (CriticalSection->OwningThread != 0) {
  201. //
  202. // The loader lock gets handled differently, so don't assert on it.
  203. //
  204. if (CriticalSection != NtCurrentPeb()->LoaderLock ||
  205. CriticalSection->OwningThread != CurrentThread) {
  206. //
  207. // OwningThread should have been 0.
  208. //
  209. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_OWNER,
  210. "invalid critical section owner thread",
  211. CriticalSection, "Critical section address",
  212. CriticalSection->OwningThread, "Owning thread",
  213. 0, "Expected owning thread",
  214. CriticalSection->DebugInfo, "Critical section debug info address");
  215. }
  216. }
  217. //
  218. // Sanity test for the RecursionCount.
  219. //
  220. if (CriticalSection->RecursionCount != 0) {
  221. //
  222. // The loader lock gets handled differently, so don't assert on it.
  223. //
  224. if (CriticalSection != NtCurrentPeb()->LoaderLock) {
  225. //
  226. // RecursionCount should have been 0.
  227. //
  228. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_RECURSION_COUNT,
  229. "invalid critical section recursion count",
  230. CriticalSection, "Critical section address",
  231. CriticalSection->RecursionCount, "Recursion count",
  232. 0, "Expected recursion count",
  233. CriticalSection->DebugInfo, "Critical section debug info address");
  234. }
  235. }
  236. //
  237. // Set the critical section owner
  238. //
  239. CriticalSection->OwningThread = CurrentThread;
  240. //
  241. // Set the recursion count
  242. //
  243. // ntdll\ia64\critsect.s is using RecursionCount = 0 first time
  244. // the current thread is acquiring the critical section.
  245. //
  246. // ntdll\ia64\critsect.asm is using RecursionCount = 1 first time
  247. // the current thread is acquiring the critical section.
  248. //
  249. #if defined(_IA64_)
  250. CriticalSection->RecursionCount = 0;
  251. #else //#if defined(_IA64_)
  252. CriticalSection->RecursionCount = 1;
  253. #endif
  254. #if DBG
  255. //
  256. // In a chk build we are updating these additional counters,
  257. // just like the original function in ntdll does.
  258. //
  259. NtCurrentTeb()->CountOfOwnedCriticalSections += 1;
  260. CriticalSection->DebugInfo->EntryCount += 1;
  261. #endif
  262. //
  263. // All done, CriticalSection is owned by the current thread.
  264. //
  265. }
  266. else if (LockCount >= 0) {
  267. //
  268. // The critical section is currently owned by the current or another thread.
  269. //
  270. if (CriticalSection->OwningThread == CurrentThread) {
  271. //
  272. // The current thread is already the owner.
  273. //
  274. CriticalSection->RecursionCount += 1;
  275. #if DBG
  276. //
  277. // In a chk build we are updating this additional counter,
  278. // just like the original function in ntdll does.
  279. //
  280. CriticalSection->DebugInfo->EntryCount += 1;
  281. #endif
  282. //
  283. // All done, CriticalSection was already owned by
  284. // the current thread and we have just incremented the RecursionCount.
  285. //
  286. }
  287. else {
  288. //
  289. // The current thread is not the owner. Wait for ownership
  290. //
  291. RtlpWaitForCriticalSection ((PRTL_CRITICAL_SECTION)CriticalSection);
  292. //
  293. // We have just aquired the critical section.
  294. //
  295. goto EnterSetOwnerAndRecursion;
  296. }
  297. }
  298. else {
  299. //
  300. // The original LockCount was < -1 so the critical section was
  301. // over-released or corrupted.
  302. //
  303. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_OVER_RELEASED,
  304. "critical section over-released or corrupted",
  305. CriticalSection, "Critical section address",
  306. CriticalSection->LockCount, "Lock count",
  307. 0, "Expected minimum lock count",
  308. CriticalSection->DebugInfo, "Critical section debug info address");
  309. }
  310. }
  311. else {
  312. //
  313. // SpinCount > 0 for this critical section
  314. //
  315. if( CriticalSection->OwningThread == CurrentThread ) {
  316. //
  317. // The current thread is already the owner.
  318. //
  319. InterlockedIncrement( &CriticalSection->LockCount );
  320. CriticalSection->RecursionCount += 1;
  321. #if DBG
  322. //
  323. // In a chk build we are updating this additional counter,
  324. // just like the original function in ntdll does.
  325. //
  326. CriticalSection->DebugInfo->EntryCount += 1;
  327. #endif
  328. //
  329. // All done, CriticalSection was already owned by the current thread
  330. // and we have just incremented the LockCount and RecursionCount.
  331. //
  332. }
  333. else {
  334. //
  335. // The current thread is not the owner. Attempt to acquire.
  336. //
  337. EnterTryAcquire:
  338. LockCount = InterlockedCompareExchange( &CriticalSection->LockCount,
  339. 0,
  340. -1 );
  341. if (LockCount == -1) {
  342. //
  343. // We have just aquired the critical section.
  344. //
  345. goto EnterSetOwnerAndRecursion;
  346. }
  347. else {
  348. //
  349. // Look if there are already other threads spinning while
  350. // waiting for this critical section.
  351. //
  352. if (CriticalSection->LockCount >= 1) {
  353. //
  354. // There are other waiters for this critical section.
  355. // Do not spin, just wait for the critical section to be
  356. // released as if we had 0 spin count from the beginning.
  357. //
  358. goto EnterZeroSpinCount;
  359. }
  360. else {
  361. //
  362. // No other threads are waiting for this critical section.
  363. //
  364. EnterSpinOnLockCount:
  365. if (CriticalSection->LockCount == -1) {
  366. //
  367. // We have a chance for aquiring it now
  368. //
  369. goto EnterTryAcquire;
  370. }
  371. else {
  372. //
  373. // The critical section is still owned.
  374. // Decrement the spin count and decide if we should continue
  375. // to spin or simply wait for the critical section's event.
  376. //
  377. SpinCount -= 0;
  378. if (SpinCount > 0) {
  379. //
  380. // Spin
  381. //
  382. goto EnterSpinOnLockCount;
  383. }
  384. else {
  385. //
  386. // Spun enough, just wait for the critical section to be
  387. // released as if we had 0 spin count from the beginning.
  388. //
  389. goto EnterZeroSpinCount;
  390. }
  391. }
  392. }
  393. }
  394. }
  395. }
  396. }
  397. else {
  398. //
  399. // The critical section verifier is not enabled
  400. //
  401. Status = RtlEnterCriticalSection ((PRTL_CRITICAL_SECTION)CriticalSection);
  402. }
  403. return Status;
  404. }
  405. //NTSYSAPI
  406. NTSTATUS
  407. NTAPI
  408. AVrfpRtlLeaveCriticalSection(
  409. volatile RTL_CRITICAL_SECTION *CriticalSection
  410. )
  411. {
  412. NTSTATUS Status;
  413. HANDLE CurrentThread;
  414. if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_LOCK_CHECKS) != 0 &&
  415. RtlDllShutdownInProgress() == FALSE) {
  416. //
  417. // Sanity test for DebugInfo.
  418. //
  419. if (CriticalSection->DebugInfo == NULL) {
  420. //
  421. // This critical section is not initialized.
  422. //
  423. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_NOT_INITIALIZED,
  424. "critical section not initialized",
  425. CriticalSection, "Critical section address",
  426. CriticalSection->DebugInfo, "Critical section debug info address",
  427. NULL, "",
  428. NULL, "");
  429. }
  430. //
  431. // Verify that the critical section is locked before releasing.
  432. //
  433. if (CriticalSection->LockCount < 0) {
  434. //
  435. // The critical section is not locked
  436. //
  437. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_OVER_RELEASED,
  438. "critical section over-released or corrupted",
  439. CriticalSection, "Critical section address",
  440. CriticalSection->LockCount, "Lock count",
  441. 0, "Expected minimum lock count",
  442. CriticalSection->DebugInfo, "Critical section debug info address");
  443. }
  444. //
  445. // Verify that the current thread owns the critical section.
  446. //
  447. CurrentThread = NtCurrentTeb()->ClientId.UniqueThread;
  448. if (CriticalSection->OwningThread != CurrentThread) {
  449. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_OWNER,
  450. "invalid critical section owner thread",
  451. CriticalSection, "Critical section address",
  452. CriticalSection->OwningThread, "Owning thread",
  453. CurrentThread, "Expected owning thread",
  454. CriticalSection->DebugInfo, "Critical section debug info address");
  455. }
  456. //
  457. // Verify the recursion count.
  458. //
  459. // ntdll\ia64\critsect.s is using RecursionCount = 0 first time
  460. // the current thread is acquiring the critical section.
  461. //
  462. // ntdll\ia64\critsect.asm is using RecursionCount = 1 first time
  463. // the current thread is acquiring the critical section.
  464. //
  465. #if defined(_IA64_)
  466. if (CriticalSection->RecursionCount < 0) {
  467. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_RECURSION_COUNT,
  468. "invalid critical section recursion count",
  469. CriticalSection, "Critical section address",
  470. CriticalSection->RecursionCount, "Recursion count",
  471. 0, "Expected minimum recursion count",
  472. CriticalSection->DebugInfo, "Critical section debug info address");
  473. }
  474. #else //#if defined(_IA64_)
  475. if (CriticalSection->RecursionCount < 1) {
  476. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_RECURSION_COUNT,
  477. "invalid critical section recursion count",
  478. CriticalSection, "Critical section address",
  479. CriticalSection->RecursionCount, "Recursion count",
  480. 1, "Expected minimum recursion count",
  481. CriticalSection->DebugInfo, "Critical section debug info address");
  482. }
  483. #endif //#if defined(_IA64_)
  484. }
  485. Status = RtlLeaveCriticalSection ((PRTL_CRITICAL_SECTION)CriticalSection);
  486. return Status;
  487. }
  488. //NTSYSAPI
  489. NTSTATUS
  490. NTAPI
  491. AVrfpRtlInitializeCriticalSection(
  492. PRTL_CRITICAL_SECTION CriticalSection
  493. )
  494. {
  495. NTSTATUS Status;
  496. //
  497. // If we could have pointers to ntdll!RtlCriticalSectionLock and
  498. // RtlCriticalSectionList we could check for double-initialized
  499. // critical sections here.
  500. //
  501. Status = RtlInitializeCriticalSection (CriticalSection);
  502. return Status;
  503. }
  504. //NTSYSAPI
  505. NTSTATUS
  506. NTAPI
  507. AVrfpRtlInitializeCriticalSectionAndSpinCount(
  508. PRTL_CRITICAL_SECTION CriticalSection,
  509. ULONG SpinCount
  510. )
  511. {
  512. NTSTATUS Status;
  513. //
  514. // If we could have pointers to ntdll!RtlCriticalSectionLock and
  515. // RtlCriticalSectionList we could check for double-initialized
  516. // critical sections here.
  517. //
  518. Status = RtlInitializeCriticalSectionAndSpinCount (CriticalSection,
  519. SpinCount);
  520. return Status;
  521. }
  522. //NTSYSAPI
  523. NTSTATUS
  524. NTAPI
  525. AVrfpRtlDeleteCriticalSection(
  526. PRTL_CRITICAL_SECTION CriticalSection
  527. )
  528. {
  529. NTSTATUS Status;
  530. if ((AVrfpProvider.VerifierFlags & RTL_VRF_FLG_LOCK_CHECKS) != 0 &&
  531. RtlDllShutdownInProgress() == FALSE ) {
  532. //
  533. // Sanity test for DebugInfo.
  534. //
  535. if (CriticalSection->DebugInfo == NULL) {
  536. //
  537. // This critical section is not initialized.
  538. //
  539. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_NOT_INITIALIZED,
  540. "critical section not initialized",
  541. CriticalSection, "Critical section address",
  542. CriticalSection->DebugInfo, "Critical section debug info address",
  543. NULL, "",
  544. NULL, "");
  545. }
  546. //
  547. // Verify that no thread owns or waits for this critical section or
  548. // the owner is the current thread.
  549. //
  550. // ntdll\ia64\critsect.s is using RecursionCount = 0 first time
  551. // the current thread is acquiring the critical section.
  552. //
  553. // ntdll\ia64\critsect.asm is using RecursionCount = 1 first time
  554. // the current thread is acquiring the critical section.
  555. //
  556. if (CriticalSection->LockCount != -1 &&
  557. (CriticalSection->OwningThread != NtCurrentTeb()->ClientId.UniqueThread ||
  558. #if defined(_IA64_)
  559. CriticalSection->RecursionCount < 0) ) {
  560. #else
  561. CriticalSection->RecursionCount < 1) ) {
  562. #endif //#if defined(_IA64_)
  563. VERIFIER_STOP (APPLICATION_VERIFIER_LOCK_INVALID_LOCK_COUNT,
  564. "deleting critical section with invalid lock count",
  565. CriticalSection, "Critical section address",
  566. CriticalSection->LockCount, "Lock count",
  567. -1, "Expected lock count",
  568. CriticalSection->OwningThread, "Owning thread");
  569. }
  570. }
  571. Status = RtlDeleteCriticalSection (CriticalSection);
  572. return Status;
  573. }