/*++ Copyright (c) 1991 Microsoft Corporation Module Name: hivechek.c Abstract: This module implements consistency checking for hives. Author: Bryan M. Willman (bryanwi) 09-Dec-91 Environment: Revision History: --*/ #include "cmp.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,HvCheckHive) #pragma alloc_text(PAGE,HvCheckBin) #endif // // debug structures // extern struct { PHHIVE Hive; ULONG Status; ULONG Space; HCELL_INDEX MapPoint; PHBIN BinPoint; } HvCheckHiveDebug; extern struct { PHBIN Bin; ULONG Status; PHCELL CellPoint; } HvCheckBinDebug; #if DBG ULONG HvHiveChecking=0; #endif ULONG HvCheckHive( PHHIVE Hive, PULONG Storage OPTIONAL ) /*++ Routine Description: Check the consistency of a hive. Apply CheckBin to bins, make sure all pointers in the cell map point to correct places. Arguments: Hive - supplies a pointer to the hive control structure for the hive of interest. Storage - supplies adddress of ULONG to receive size of allocated user data Return Value: 0 if Hive is OK. Error return indicator if not. Error value comes from one of the check procedures. RANGE: 2000 - 2999 --*/ { HCELL_INDEX p; ULONG Length; ULONG localstorage = 0; PHMAP_ENTRY t; PHBIN Bin; ULONG i; ULONG rc; PFREE_HBIN FreeBin; HvCheckHiveDebug.Hive = Hive; HvCheckHiveDebug.Status = 0; HvCheckHiveDebug.Space = (ULONG)-1; HvCheckHiveDebug.MapPoint = HCELL_NIL; HvCheckHiveDebug.BinPoint = 0; p = 0; #ifdef CM_MAP_NO_READ #ifndef _CM_LDR_ // // we need to make sure all the cell's data is faulted in inside a // try/except block, as the IO to fault the data in can throw exceptions // STATUS_INSUFFICIENT_RESOURCES, in particular // try { #endif //_CM_LDR_ #endif //CM_MAP_NO_READ // // one pass for Stable space, one pass for Volatile // for (i = 0; i <= Volatile; i++) { Length = Hive->Storage[i].Length; // // for each bin in the space // while (p < Length) { t = HvpGetCellMap(Hive, p); if (t == NULL) { CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"HvCheckHive:")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\tBin@:%p invalid\n", Bin)); HvCheckHiveDebug.Status = 2005; HvCheckHiveDebug.Space = i; HvCheckHiveDebug.MapPoint = p; return 2005; } if( (t->BinAddress & (HMAP_INPAGEDPOOL|HMAP_INVIEW)) == 0) { // // view is not mapped, neither in paged pool // try to map it. // // volatile info is always in paged pool ASSERT( i == Stable ); if( !NT_SUCCESS(CmpMapThisBin((PCMHIVE)Hive,p,FALSE)) ) { // // we cannot map this bin due to insufficient resources. // CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"HvCheckHive:")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\tinsufficient resources while mapping Bin@:%p\n", Bin)); HvCheckHiveDebug.Status = 2006; HvCheckHiveDebug.Space = i; HvCheckHiveDebug.MapPoint = p; return 2010; } } if ((t->BinAddress & HMAP_DISCARDABLE) == 0) { Bin = (PHBIN)HBIN_BASE(t->BinAddress); // // bin header valid? // if ( (Bin->Size > Length) || (Bin->Signature != HBIN_SIGNATURE) || (Bin->FileOffset != p) ) { CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"HvCheckHive:")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\tBin@:%p invalid\n", Bin)); HvCheckHiveDebug.Status = 2010; HvCheckHiveDebug.Space = i; HvCheckHiveDebug.MapPoint = p; HvCheckHiveDebug.BinPoint = Bin; return 2010; } // // structure inside the bin valid? // rc = HvCheckBin(Hive, Bin, &localstorage); if (rc != 0) { HvCheckHiveDebug.Status = rc; HvCheckHiveDebug.Space = i; HvCheckHiveDebug.MapPoint = p; HvCheckHiveDebug.BinPoint = Bin; return rc; } p = (ULONG)p + Bin->Size; } else { // // Bin is not present, skip it and advance to the next one. // FreeBin = (PFREE_HBIN)t->BlockAddress; p+=FreeBin->Size; } } p = 0x80000000; // Beginning of Volatile space } #ifdef CM_MAP_NO_READ #ifndef _CM_LDR_ } except (EXCEPTION_EXECUTE_HANDLER) { HvCheckHiveDebug.Status = 2015; HvCheckHiveDebug.Space = GetExceptionCode(); return HvCheckHiveDebug.Status; } #endif //_CM_LDR_ #endif //CM_MAP_NO_READ if (ARGUMENT_PRESENT(Storage)) { *Storage = localstorage; } return 0; } ULONG HvCheckBin( PHHIVE Hive, PHBIN Bin, PULONG Storage ) /*++ Routine Description: Step through all of the cells in the bin. Make sure that they are consistent with each other, and with the bin header. Arguments: Hive - pointer to the hive control structure Bin - pointer to bin to check Storage - pointer to a ulong to get allocated user data size Return Value: 0 if Bin is OK. Number of test in procedure that failed if not. RANGE: 1 - 1999 --*/ { PHCELL p; PHCELL np; PHCELL lp; ULONG freespace = 0L; ULONG allocated = 0L; ULONG userallocated = 0L; HvCheckBinDebug.Bin = Bin; HvCheckBinDebug.Status = 0; HvCheckBinDebug.CellPoint = 0; // // Scan all the cells in the bin, total free and allocated, check // for impossible pointers. // p = (PHCELL)((PUCHAR)Bin + sizeof(HBIN)); lp = p; // DRAGOS: // The way allocated and freespace are computed implies the following invariants: // 1. allocated + freespace = p + p->Size - (Bin + sizeof(HBIN)). This is because p->Size is added either to allocated or to freespace. // So, assuming that allocated > Bin->Size , then // ==> p + p->Size - (Bin + sizeof(HBIN)) > Bin->Size. // ==> p + p->Size > Bin + Bin->Size + sizeof(HBIN) // ==> p + p->Size > Bin + Bin->Size // This proves that the test "NeverFail 1" (see bellow) will never fail, because when something is wrong, the test above it (namely "Fail 1") will fail // and the function will exit. // // The same logic applies to the test "NeverFail 2", so it can be removed also. // // 2. The new value of p is always calculated as p = p + p->Size. By the time this is done, the new value of p (ie. p + p->Size) is already checked against // Bin + Bin->Size (see tests "Fail 1" and "Fail 2"). So, if p > Bin + Bin->Size, either "Fail 1" or "Fail 2" will fail before asigning the new bogus value // to p. Therefore, the only possible path to exit the while loop (except a return 20 or return 40), is when p == Bin + Bin->Size. // ==> test "NeverFail 3" can be removed as it will never fail ! // // 3. Considering 1 (where p + p->Size became p) // ==> allocated + freespace = p - (Bin + sizeof(HBIN)) // But, Considering 2 (above), when the while loop exits, p = Bin + Bin->Size // ==> allocated + freespace = Bin + Bin->Size - (Bin + sizeof(HBIN)) // ==> allocated + freespace + sizeof(HBIN) = Bin->Size // This proves that test "NeverFail 4" (see bellow) will never fail as the expresion tested is always true (if the flow of execution reaches the test point). // while (p < (PHCELL)((PUCHAR)Bin + Bin->Size)) { // // Check last pointer // if (USE_OLD_CELL(Hive)) { if (lp == p) { if (p->u.OldCell.Last != HBIN_NIL) { CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"HvCheckBin 20: First cell has wrong last pointer\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Bin = %p\n", Bin)); HvCheckBinDebug.Status = 20; HvCheckBinDebug.CellPoint = p; return 20; } } else { if ((PHCELL)(p->u.OldCell.Last + (PUCHAR)Bin) != lp) { CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"HvCheckBin 30: incorrect last pointer\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Bin = %p\n", Bin)); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"p = %p\n", (ULONG_PTR)p)); HvCheckBinDebug.Status = 30; HvCheckBinDebug.CellPoint = p; return 30; } } } // // Check size // if (p->Size < 0) { // // allocated cell // // DRAGOS: Fail 1 // This test will alway fail prior to the failure of the bellow test // if ( ((ULONG)(p->Size * -1) > Bin->Size) || ( (PHCELL)((p->Size * -1) + (PUCHAR)p) > (PHCELL)((PUCHAR)Bin + Bin->Size) ) ) { CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"HvCheckBin 40: impossible allocation\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Bin = %p\n", Bin)); HvCheckBinDebug.Status = 40; HvCheckBinDebug.CellPoint = p; return 40; } allocated += (p->Size * -1); if (USE_OLD_CELL(Hive)) { userallocated += (p->Size * -1) - FIELD_OFFSET(HCELL, u.OldCell.u.UserData); } else { userallocated += (p->Size * -1) - FIELD_OFFSET(HCELL, u.NewCell.u.UserData); } // // DRAGOS: NeverFail 1 // This test will never fail. If a size is wrong the above test (Fail 1)will fail. We can remove this test (it's useless). // if (allocated > Bin->Size) { CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"HvCheckBin 50: allocated exceeds available\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Bin = %p\n", Bin)); HvCheckBinDebug.Status = 50; HvCheckBinDebug.CellPoint = p; return 50; } np = (PHCELL)((PUCHAR)p + (p->Size * -1)); } else { // // free cell // // DRAGOS: Fail 2 // This test will alway fail prior to the failure of the bellow test // if ( ((ULONG)p->Size > Bin->Size) || ( (PHCELL)(p->Size + (PUCHAR)p) > (PHCELL)((PUCHAR)Bin + Bin->Size) ) || (p->Size == 0) ) { CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"HvCheckBin 60: impossible free block\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Bin = %p\n", Bin)); HvCheckBinDebug.Status = 60; HvCheckBinDebug.CellPoint = p; return 60; } freespace = freespace + p->Size; // // DRAGOS: NeverFail 2 // This test will never fail. If a size is wrong the above test (Fail 2) will fail. We can remove this test (it's useless). // if (freespace > Bin->Size) { CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"HvCheckBin 70: free exceeds available\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Bin = %p\n", Bin)); HvCheckBinDebug.Status = 70; HvCheckBinDebug.CellPoint = p; return 70; } np = (PHCELL)((PUCHAR)p + p->Size); } lp = p; p = np; } // DRAGOS: NeverFail 4 // This test never fails. If the while loop exits, the condition tested here is always true!!! // We can remove this test (it's useless) // if ((freespace + allocated + sizeof(HBIN)) != Bin->Size) { CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"HvCheckBin 995: sizes do not add up\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Bin = %p\n", Bin)); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"freespace = %08lx ", freespace)); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"allocated = %08lx ", allocated)); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"size = %08lx\n", Bin->Size)); HvCheckBinDebug.Status = 995; return 995; } // DRAGOS: NeverFail 3 // This test never fails. The only way out of the while loop is when p == Bin + Bin->Size !!!!!!! // We can remove this test (it's useless) // if (p != (PHCELL)((PUCHAR)Bin + Bin->Size)) { CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"HvCheckBin 1000: last cell points off the end\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Bin = %p\n", Bin)); HvCheckBinDebug.Status = 1000; return 1000; } if (ARGUMENT_PRESENT(Storage)) { *Storage += userallocated; } return 0; }