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.

656 lines
23 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997.
  5. //
  6. // File: analyze.cxx
  7. //
  8. // Contents: program to analyze the lock order table
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // Coupling:
  15. //
  16. // Notes:
  17. //
  18. // History: 3-16-2000 benl Created
  19. //
  20. //----------------------------------------------------------------------------
  21. #include <nt.h>
  22. #include <ntrtl.h>
  23. #include <nturtl.h>
  24. #include <windows.h>
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27. #define _NTFS_NTFSDBG_DEFINITIONS_
  28. #include "lockorder.h"
  29. typedef struct {
  30. LIST_ENTRY Links;
  31. NTFS_RESOURCE_NAME Resource;
  32. } RESOURCE_LIST, *PRESOURCE_LIST;
  33. typedef struct {
  34. NTFS_RESOURCE_NAME Resource;
  35. LIST_ENTRY Links;
  36. NTFS_OWNERSHIP_STATE EndState;
  37. } RESOURCE_STATE_LIST, *PRESOURCE_STATE_LIST;
  38. //
  39. // structure used to track the order a state is reached by
  40. //
  41. typedef struct {
  42. NTFS_OWNERSHIP_STATE State;
  43. BOOLEAN ReachedByRelease;
  44. LIST_ENTRY Links; // RESOURCE_LIST
  45. } RESOURCE_STATE_ORDER, *PRESOURCE_STATE_ORDER;
  46. //+---------------------------------------------------------------------------
  47. //
  48. // Function: CompareRoutine
  49. //
  50. // Synopsis:
  51. //
  52. // Arguments: [Table] --
  53. // [First] --
  54. // [Second] --
  55. //
  56. // Returns:
  57. //
  58. // History: 5-08-2000 benl Created
  59. //
  60. // Notes:
  61. //
  62. //----------------------------------------------------------------------------
  63. RTL_GENERIC_COMPARE_RESULTS CompareRoutine( struct _RTL_GENERIC_TABLE *Table, PVOID First, PVOID Second )
  64. {
  65. PULONG FirstUlong = (PULONG)First;
  66. PULONG SecondUlong = (PULONG)Second;
  67. if (*FirstUlong == *SecondUlong) {
  68. return GenericEqual;
  69. } else if (*FirstUlong < *SecondUlong) {
  70. return GenericLessThan;
  71. } else {
  72. return GenericGreaterThan;
  73. }
  74. } // CompareRoutine
  75. //+---------------------------------------------------------------------------
  76. //
  77. // Function: AllocateRoutine
  78. //
  79. // Synopsis:
  80. //
  81. // Arguments: [Table] --
  82. // [ByteSize] --
  83. //
  84. // Returns:
  85. //
  86. // History: 5-08-2000 benl Created
  87. //
  88. // Notes:
  89. //
  90. //----------------------------------------------------------------------------
  91. PVOID AllocateRoutine( struct _RTL_GENERIC_TABLE *Table, CLONG ByteSize )
  92. {
  93. return malloc( ByteSize );
  94. } // AllocateRoutine
  95. //+---------------------------------------------------------------------------
  96. //
  97. // Function: FreeRoutine
  98. //
  99. // Synopsis:
  100. //
  101. // Arguments: [Table] --
  102. // [Buffer] --
  103. //
  104. // Returns:
  105. //
  106. // History: 5-08-2000 benl Created
  107. //
  108. // Notes:
  109. //
  110. //----------------------------------------------------------------------------
  111. VOID FreeRoutine( struct _RTL_GENERIC_TABLE *Table, PVOID Buffer )
  112. {
  113. free( Buffer );
  114. } // FreeRoutine
  115. //+---------------------------------------------------------------------------
  116. //
  117. // Function: FindOrInsertStateOrder
  118. //
  119. // Synopsis:
  120. //
  121. // Arguments: [Table] --
  122. // [Element] --
  123. //
  124. // Returns:
  125. //
  126. // History: 5-08-2000 benl Created
  127. //
  128. // Notes:
  129. //
  130. //----------------------------------------------------------------------------
  131. void FindOrInsertStateOrder( PRTL_GENERIC_TABLE StateTable, NTFS_OWNERSHIP_STATE State )
  132. {
  133. RESOURCE_STATE_ORDER TemplateState;
  134. PRESOURCE_STATE_ORDER NewState;
  135. TemplateState.State = State;
  136. if (!(RtlLookupElementGenericTable( StateTable, &TemplateState ))) {
  137. NewState = (PRESOURCE_STATE_ORDER)RtlInsertElementGenericTable( StateTable, &TemplateState, sizeof( TemplateState ), NULL );
  138. InitializeListHead( &NewState->Links );
  139. NewState->ReachedByRelease = FALSE;
  140. }
  141. } // FindOrInsertStateOrder
  142. //+---------------------------------------------------------------------------
  143. //
  144. // Function: FindOrInsertStateList
  145. //
  146. // Synopsis:
  147. //
  148. // Arguments: [StateTable] --
  149. // [Resource] --
  150. //
  151. // Returns:
  152. //
  153. // History: 5-08-2000 benl Created
  154. //
  155. // Notes:
  156. //
  157. //----------------------------------------------------------------------------
  158. void FindOrInsertStateList( PRTL_GENERIC_TABLE StateTable, NTFS_RESOURCE_NAME Resource )
  159. {
  160. RESOURCE_STATE_LIST TemplateState;
  161. PRESOURCE_STATE_LIST NewState;
  162. TemplateState.Resource = Resource;
  163. if (!(RtlLookupElementGenericTable( StateTable, &TemplateState ))) {
  164. NewState = (PRESOURCE_STATE_LIST)RtlInsertElementGenericTable( StateTable, &TemplateState, sizeof( TemplateState ), NULL );
  165. InitializeListHead( &NewState->Links );
  166. }
  167. } // FindOrInsertStateList
  168. //+---------------------------------------------------------------------------
  169. //
  170. // Function: main
  171. //
  172. // Synopsis:
  173. //
  174. // Arguments: [argc] --
  175. // [argv] --
  176. //
  177. // Returns:
  178. //
  179. // History: 3-16-2000 benl Created
  180. //
  181. // Notes:
  182. //
  183. //----------------------------------------------------------------------------
  184. void __cdecl main (int argc, char *argv[])
  185. {
  186. int NumTransitions;
  187. int Index;
  188. int Index2;
  189. PRESOURCE_LIST Entry;
  190. PRESOURCE_LIST Entry2;
  191. PRESOURCE_STATE_LIST EntryState;
  192. PRESOURCE_STATE_LIST EntryState2;
  193. BOOLEAN Skipped;
  194. BOOLEAN FoundState;
  195. BOOLEAN Verbose = FALSE;
  196. PRESOURCE_STATE_ORDER State;
  197. PRESOURCE_STATE_ORDER EndState;
  198. PRESOURCE_STATE_ORDER EndState2;
  199. RTL_GENERIC_TABLE StateTable;
  200. RTL_GENERIC_TABLE ResourceTable;
  201. RESOURCE_STATE_ORDER StateOrder;
  202. RESOURCE_STATE_LIST StateListOrderTemplate;
  203. PRESOURCE_STATE_LIST StateListOrder;
  204. PVOID Key;
  205. int StateCheck;
  206. if (argc > 1) {
  207. if (argv[1][0] == '-') {
  208. switch (argv[1][1]) {
  209. case 'v':
  210. Verbose = TRUE;
  211. break;
  212. default:
  213. printf( "Usage: analyze [-v]\nThis program analyzes ntfs's lock order state table\n");
  214. break;
  215. }
  216. }
  217. }
  218. //
  219. // Discover transition states
  220. //
  221. RtlInitializeGenericTable( &StateTable, CompareRoutine, AllocateRoutine, FreeRoutine, NULL );
  222. RtlInitializeGenericTable( &ResourceTable, CompareRoutine, AllocateRoutine, FreeRoutine, NULL );
  223. StateOrder.ReachedByRelease = FALSE;
  224. NumTransitions = sizeof( OwnershipTransitionTable ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
  225. for (Index=0; Index < NumTransitions; Index++) {
  226. FindOrInsertStateList( &ResourceTable, OwnershipTransitionTable[Index].Acquired );
  227. FindOrInsertStateOrder( &StateTable, OwnershipTransitionTable[Index].Begin );
  228. FindOrInsertStateOrder( &StateTable, OwnershipTransitionTable[Index].End );
  229. }
  230. NumTransitions = sizeof( OwnershipTransitionTableRelease ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
  231. for (Index=0; Index < NumTransitions; Index++) {
  232. FindOrInsertStateList( &ResourceTable, OwnershipTransitionTableRelease[Index].Acquired );
  233. FindOrInsertStateOrder( &StateTable, OwnershipTransitionTableRelease[Index].Begin );
  234. FindOrInsertStateOrder( &StateTable, OwnershipTransitionTableRelease[Index].End );
  235. }
  236. NumTransitions = sizeof( OwnershipTransitionTableAcquire ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
  237. for (Index=0; Index < NumTransitions; Index++) {
  238. FindOrInsertStateList( &ResourceTable, OwnershipTransitionTableAcquire[Index].Acquired );
  239. FindOrInsertStateOrder( &StateTable, OwnershipTransitionTableAcquire[Index].Begin );
  240. FindOrInsertStateOrder( &StateTable, OwnershipTransitionTableAcquire[Index].End );
  241. }
  242. NumTransitions = sizeof( OwnershipTransitionTableUnsafe ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
  243. for (Index=0; Index < NumTransitions; Index++) {
  244. FindOrInsertStateList( &ResourceTable, OwnershipTransitionTableUnsafe[Index].Acquired );
  245. FindOrInsertStateOrder( &StateTable, OwnershipTransitionTableUnsafe[Index].Begin );
  246. FindOrInsertStateOrder( &StateTable, OwnershipTransitionTableUnsafe[Index].End );
  247. }
  248. printf( "%d states and %d resources referenced in transition tables\n\n",
  249. StateTable.NumberGenericTableElements,
  250. ResourceTable.NumberGenericTableElements );
  251. /*
  252. for (State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTable( &StateTable, TRUE );
  253. State != 0;
  254. State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTable( &StateTable, FALSE )) {
  255. printf("0x%x\n", State->State );
  256. }
  257. */
  258. //
  259. // Visit each transition state until we know the full paths
  260. //
  261. do {
  262. Skipped = FALSE;
  263. FoundState = FALSE;
  264. Key = NULL;
  265. for (State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTableWithoutSplaying( &StateTable, &Key );
  266. State != 0;
  267. State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTableWithoutSplaying( &StateTable, &Key )) {
  268. // printf( "State: 0x%x\n", State->State );
  269. //
  270. // We don't know this state yet
  271. //
  272. if ((State->State != None) && IsListEmpty( &State->Links)) {
  273. if (Verbose) {
  274. printf( "skipping state 0x%x\n", State->State );
  275. }
  276. Skipped = TRUE;
  277. continue;
  278. }
  279. //
  280. // Look in the release only paths 1st
  281. //
  282. NumTransitions = sizeof( OwnershipTransitionTableRelease ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
  283. for (Index=0; Index < NumTransitions; Index++) {
  284. if (OwnershipTransitionTableRelease[Index].Begin == State->State &&
  285. OwnershipTransitionTableRelease[Index].End != None) {
  286. StateOrder.State = OwnershipTransitionTableRelease[Index].End;
  287. EndState = (PRESOURCE_STATE_ORDER)RtlLookupElementGenericTable( &StateTable, &StateOrder );
  288. //
  289. // Is this a new state?
  290. //
  291. if (EndState && IsListEmpty( &EndState->Links )) {
  292. FoundState = TRUE;
  293. EndState->ReachedByRelease = TRUE;
  294. if (Verbose) {
  295. printf( "removing resource: 0x%x to state: 0x%x from 0x%x during release\n",
  296. OwnershipTransitionTableRelease[Index].Acquired,
  297. OwnershipTransitionTableRelease[Index].End,
  298. State->State );
  299. }
  300. //
  301. // Add the old state's resource except for the resource being released
  302. //
  303. if (State->State != None) {
  304. Entry = (PRESOURCE_LIST)&State->Links;
  305. do {
  306. Entry = (PRESOURCE_LIST)Entry->Links.Flink;
  307. if (Entry->Resource != OwnershipTransitionTableRelease[Index].Acquired) {
  308. Entry2 = new RESOURCE_LIST;
  309. Entry2->Resource = Entry->Resource;
  310. InsertTailList( &EndState->Links, &Entry2->Links );
  311. }
  312. } while ( Entry->Links.Flink != &State->Links );
  313. }
  314. } // endif the new state is unknown
  315. } // endif rule beginning matches the state
  316. } // endfor over rules
  317. //
  318. // Then look in the acquire paths
  319. //
  320. NumTransitions = sizeof( OwnershipTransitionTable ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
  321. for (Index=0; Index < NumTransitions; Index++) {
  322. if (OwnershipTransitionTable[Index].Begin == State->State) {
  323. StateOrder.State = OwnershipTransitionTable[Index].End;
  324. EndState = (PRESOURCE_STATE_ORDER)RtlLookupElementGenericTable( &StateTable, &StateOrder );
  325. EndState->ReachedByRelease = FALSE;
  326. //
  327. // If we already know this state - then doublecheck this is an identical path
  328. //
  329. if (EndState && !IsListEmpty( &EndState->Links )) {
  330. Entry = (PRESOURCE_LIST)EndState->Links.Flink;
  331. if (Entry->Resource != NtfsResourceExVcb) {
  332. Entry = (PRESOURCE_LIST)EndState->Links.Blink;
  333. if (OwnershipTransitionTable[Index].Acquired != Entry->Resource ) {
  334. printf( "2 paths to state: 0x%x 0x%x 0x%x\n", OwnershipTransitionTable[Index].End, Entry->Resource, OwnershipTransitionTable[Index].Acquired );
  335. }
  336. }
  337. } else {
  338. FoundState = TRUE;
  339. if (Verbose) {
  340. printf( "adding resource: 0x%x to state: 0x%x from 0x%x\n",
  341. OwnershipTransitionTable[Index].Acquired,
  342. OwnershipTransitionTable[Index].End,
  343. State->State );
  344. }
  345. //
  346. // Add the old state's resource
  347. //
  348. if (State->State != None) {
  349. Entry = (PRESOURCE_LIST)&State->Links;
  350. do {
  351. Entry = (PRESOURCE_LIST)Entry->Links.Flink;
  352. Entry2 = new RESOURCE_LIST;
  353. Entry2->Resource = Entry->Resource;
  354. InsertTailList( &EndState->Links, &Entry2->Links );
  355. } while ( Entry->Links.Flink != &State->Links );
  356. }
  357. //
  358. // Finally add the transition resource into this state
  359. //
  360. Entry = new RESOURCE_LIST;
  361. Entry->Resource = OwnershipTransitionTable[Index].Acquired;
  362. InsertTailList( &EndState->Links, &Entry->Links );
  363. }
  364. }
  365. }
  366. }
  367. if (Verbose) {
  368. printf( "pass done\n" );
  369. }
  370. } while (FoundState && Skipped);
  371. //
  372. // Printout state maps
  373. //
  374. printf( "State Map\n" );
  375. for (State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTable( &StateTable, TRUE );
  376. State != 0;
  377. State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTable( &StateTable, FALSE )) {
  378. StateCheck = 0;
  379. if (!IsListEmpty( &State->Links )) {
  380. Entry = (PRESOURCE_LIST)&State->Links;
  381. do {
  382. Entry = (PRESOURCE_LIST)Entry->Links.Flink;
  383. StateCheck |= Entry->Resource;
  384. printf( "%x -> ", Entry->Resource );
  385. } while ( Entry->Links.Flink != &State->Links );
  386. printf( "state %x\n", State->State );
  387. if ((int)State->State != StateCheck) {
  388. printf( "State transistions do not make sense, check the state definition\n" );
  389. }
  390. } else {
  391. printf( "unreachable state: 0x%x\n", State->State );
  392. }
  393. }
  394. //
  395. // Now build up individual transitions
  396. //
  397. printf( "Subtransitions\n" );
  398. Key = NULL;
  399. for (State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTableWithoutSplaying( &StateTable, &Key );
  400. State != 0;
  401. State = (PRESOURCE_STATE_ORDER) RtlEnumerateGenericTableWithoutSplaying( &StateTable, &Key )) {
  402. if (!IsListEmpty( &State->Links )) {
  403. // printf( "State: 0x%x\n", State->State );
  404. Entry = (PRESOURCE_LIST)&State->Links;
  405. do {
  406. Entry = (PRESOURCE_LIST)Entry->Links.Flink;
  407. Entry2 = Entry;
  408. while (Entry2->Links.Flink != &State->Links) {
  409. BOOLEAN Found;
  410. Entry2 = (PRESOURCE_LIST)Entry2->Links.Flink;
  411. //
  412. // First search if transition exists already from Entry->Resource to Entry2->Resource
  413. //
  414. Found = FALSE;
  415. StateListOrderTemplate.Resource = Entry->Resource;
  416. StateListOrder = (PRESOURCE_STATE_LIST)RtlLookupElementGenericTable( &ResourceTable, &StateListOrderTemplate );
  417. if (!IsListEmpty( &StateListOrder->Links )) {
  418. // printf( "FirstLink: 0x%x\n", StateListOrder->Links.Flink );
  419. EntryState2 = StateListOrder;
  420. do {
  421. EntryState2 = CONTAINING_RECORD( EntryState2->Links.Flink, RESOURCE_STATE_LIST, Links );
  422. if (EntryState2->Resource == Entry2->Resource) {
  423. PRESOURCE_LIST OldEntry;
  424. //
  425. // Always choose state that started without vcb
  426. //
  427. StateOrder.State = EntryState2->EndState;
  428. EndState = (PRESOURCE_STATE_ORDER)RtlLookupElementGenericTable( &StateTable, &StateOrder );
  429. OldEntry = (PRESOURCE_LIST)EndState->Links.Flink;
  430. if ((OldEntry->Resource == NtfsResourceSharedVcb) ||
  431. (OldEntry->Resource == NtfsResourceExVcb)) {
  432. EntryState2->EndState = State->State;
  433. }
  434. Found = TRUE;
  435. break;
  436. }
  437. } while ( EntryState2->Links.Flink != &StateListOrder->Links );
  438. }
  439. if (!Found) {
  440. //
  441. // Look for conflicts since its new
  442. //
  443. StateListOrderTemplate.Resource = Entry2->Resource;
  444. StateListOrder = (PRESOURCE_STATE_LIST)RtlLookupElementGenericTable( &ResourceTable, &StateListOrderTemplate );
  445. // printf( "Resource: 0x%x, empty: %d\n", StateListOrder->Resource, IsListEmpty( &StateListOrder->Links ) );
  446. if (StateListOrder && !IsListEmpty( &StateListOrder->Links )) {
  447. EntryState2 = StateListOrder;
  448. do {
  449. EntryState2 = CONTAINING_RECORD( EntryState2->Links.Flink, RESOURCE_STATE_LIST, Links );
  450. if ((EntryState2->Resource == Entry->Resource)) {
  451. printf( "possible conflict from 0x%x to 0x%x in state 0x%x and state 0x%x\n", Entry->Resource, Entry2->Resource, State, EntryState2->EndState );
  452. }
  453. // printf( "check from 0x%x to 0x%x in state 0x%x and state 0x%x\n", Entry->Resource, Entry2->Resource, State, EntryState2->EndState );
  454. StateOrder.State = EntryState2->EndState;
  455. EndState = (PRESOURCE_STATE_ORDER)RtlLookupElementGenericTable( &StateTable, &StateOrder );
  456. StateOrder.State = State->State;
  457. EndState2 = (PRESOURCE_STATE_ORDER)RtlLookupElementGenericTable( &StateTable, &StateOrder );
  458. if ((EntryState2->Resource == Entry->Resource) &&
  459. !EndState->ReachedByRelease &&
  460. !EndState2->ReachedByRelease) {
  461. //
  462. // Now check if vcb locks it out
  463. //
  464. if (!(((((PRESOURCE_LIST)(EndState2->Links.Flink))->Resource == NtfsResourceSharedVcb) ||
  465. ((PRESOURCE_LIST)(EndState2->Links.Flink))->Resource == NtfsResourceExVcb) &&
  466. ((((PRESOURCE_LIST)(EndState->Links.Flink))->Resource == NtfsResourceSharedVcb) ||
  467. ((PRESOURCE_LIST)(EndState->Links.Flink))->Resource == NtfsResourceExVcb))) {
  468. printf( "Suborder conflict from 0x%x to 0x%x in state 0x%x and state 0x%x\n", Entry->Resource, Entry2->Resource, State->State, EntryState2->EndState );
  469. break;
  470. } else {
  471. // printf( "NonSuborder conflict from 0x%x to 0x%x in state 0x%x and state 0x%x\n", Entry->Resource, Entry2->Resource, State, EntryState2->EndState );
  472. }
  473. }
  474. } while ( EntryState2->Links.Flink != &StateListOrder->Links );
  475. } else {
  476. // printf ("Unfound resource: 0x%x 0x%x\n", Entry2->Resource, StateListOrder );
  477. }
  478. StateListOrderTemplate.Resource = Entry->Resource;
  479. StateListOrder = (PRESOURCE_STATE_LIST)RtlLookupElementGenericTable( &ResourceTable, &StateListOrderTemplate );
  480. EntryState = new RESOURCE_STATE_LIST;
  481. EntryState->Resource = Entry2->Resource;
  482. EntryState->EndState = State->State;
  483. // printf( "Adding 0x%x to list for resource 0x%x\n", EntryState, Entry->Resource );
  484. InsertTailList( &StateListOrder->Links, &EntryState->Links );
  485. }
  486. }
  487. } while ( Entry->Links.Flink != &State->Links );
  488. }
  489. }
  490. //
  491. // Dump the inidivual transitions
  492. //
  493. Key = NULL;
  494. for (StateListOrder = (PRESOURCE_STATE_LIST) RtlEnumerateGenericTableWithoutSplaying( &ResourceTable, &Key );
  495. StateListOrder != 0;
  496. StateListOrder = (PRESOURCE_STATE_LIST) RtlEnumerateGenericTableWithoutSplaying( &ResourceTable, &Key )) {
  497. if (!IsListEmpty( &StateListOrder->Links )) {
  498. EntryState = StateListOrder;
  499. do {
  500. EntryState = CONTAINING_RECORD( EntryState->Links.Flink, RESOURCE_STATE_LIST, Links );
  501. printf( "0x%x -> 0x%x endstate:0x%x\n ", StateListOrder->Resource, EntryState->Resource, EntryState->EndState );
  502. } while ( EntryState->Links.Flink != &StateListOrder->Links );
  503. }
  504. }
  505. } // main