/*++ BUILD Version: 0001 // Increment this if a change has global effects Copyright (c) 2000 Microsoft Corporation Module Name: extinit.c Abstract: This file implements all the initialization library routines operating on extensible performance libraries. Author: JeePang Revision History: 09/27/2000 - JeePang - Moved from perflib.c --*/ #define UNICODE // // Include files // #pragma warning(disable:4306) #include #include #include #include #include #include #include #include #include #include #include "regrpc.h" #include "ntconreg.h" #include "prflbmsg.h" // event log messages #include "perflib.h" #pragma warning(default:4306) // // static constant definitions // // constants used by guard page testing // #define GUARD_PAGE_SIZE 1024 #define GUARD_PAGE_CHAR 0xA5 #define GUARD_PAGE_DWORD 0xA5A5A5A5 typedef struct _EXT_OBJ_ITEM { DWORD dwObjId; DWORD dwFlags; } EXT_OBJ_LIST, *PEXT_OBJ_LIST; #define PERF_EOL_ITEM_FOUND ((DWORD)0x00000001) __inline DWORD RegisterExtObjListAccess () { LONG Status; LARGE_INTEGER liWaitTime; if (hGlobalDataMutex != NULL) { liWaitTime.QuadPart = MakeTimeOutValue(QUERY_WAIT_TIME); // wait for access to the list of ext objects Status = NtWaitForSingleObject ( hGlobalDataMutex, FALSE, &liWaitTime); if (Status != WAIT_TIMEOUT) { if (hExtObjListIsNotInUse != NULL) { // indicate that we are going to use the list InterlockedIncrement ((LONG *)&dwExtObjListRefCount); if (dwExtObjListRefCount > 0) { ResetEvent (hExtObjListIsNotInUse); // indicate list is busy } else { SetEvent (hExtObjListIsNotInUse); // indicate list is not busy } Status = ERROR_SUCCESS; } else { Status = ERROR_NOT_READY; } ReleaseMutex (hGlobalDataMutex); } // else return status; } else { Status = ERROR_LOCK_FAILED; } return Status; } __inline DWORD DeRegisterExtObjListAccess () { LONG Status; LARGE_INTEGER liWaitTime; if (hGlobalDataMutex != NULL) { liWaitTime.QuadPart = MakeTimeOutValue(QUERY_WAIT_TIME); // wait for access to the list of ext objects Status = NtWaitForSingleObject ( hGlobalDataMutex, FALSE, &liWaitTime); if (Status != WAIT_TIMEOUT) { if (hExtObjListIsNotInUse != NULL) { assert (dwExtObjListRefCount > 0); // indicate that we are going to use the list InterlockedDecrement ((LONG *)&dwExtObjListRefCount); if (dwExtObjListRefCount > 0) { ResetEvent (hExtObjListIsNotInUse); // indicate list is busy } else { SetEvent (hExtObjListIsNotInUse); // indicate list is not busy } Status = ERROR_SUCCESS; } else { Status = ERROR_NOT_READY; } ReleaseMutex (hGlobalDataMutex); } // else return status; } else { Status = ERROR_LOCK_FAILED; } return Status; } LONG QueryExtensibleData ( COLLECT_THREAD_DATA * pArgs ) /*++ QueryExtensibleData - Get data from extensible objects Inputs: dwQueryType - Query type (GLOBAL, COSTLY, item list, etc.) lpValueName - pointer to value string (unused) lpData - pointer to start of data block where data is being collected lpcbData - pointer to size of data buffer lppDataDefinition - pointer to pointer to where object definition for this object type should go Outputs: *lppDataDefinition - set to location for next Type Definition if successful Returns: 0 if successful, else Win 32 error code of failure --*/ { DWORD dwQueryType = pArgs->dwQueryType; LPWSTR lpValueName = pArgs->lpValueName; LPBYTE lpData = pArgs->lpData; LPDWORD lpcbData = pArgs->lpcbData; LPVOID *lppDataDefinition = pArgs->lppDataDefinition; DWORD Win32Error=ERROR_SUCCESS; // Failure code DWORD BytesLeft; DWORD InitialBytesLeft; DWORD NumObjectTypes; LPVOID lpExtDataBuffer = NULL; LPVOID lpCallBuffer = NULL; LPVOID lpLowGuardPage = NULL; LPVOID lpHiGuardPage = NULL; LPVOID lpEndPointer = NULL; LPVOID lpBufferBefore = NULL; LPVOID lpBufferAfter = NULL; PUCHAR lpCheckPointer; LARGE_INTEGER liStartTime, liEndTime, liWaitTime; PEXT_OBJECT pThisExtObj = NULL; DWORD dwLibEntry; BOOL bGuardPageOK; BOOL bBufferOK; BOOL bException; BOOL bUseSafeBuffer; BOOL bUnlockObjData = FALSE; LPTSTR szMessageArray[8]; ULONG_PTR dwRawDataDwords[8]; // raw data buffer DWORD dwDataIndex; WORD wStringIndex; LONG lReturnValue = ERROR_SUCCESS; LONG lDllTestLevel; LONG lInstIndex; DWORD lCtrIndex; PERF_OBJECT_TYPE *pObject, *pNextObject; PERF_INSTANCE_DEFINITION *pInstance; PERF_COUNTER_DEFINITION *pCounterDef; PERF_DATA_BLOCK *pPerfData; BOOL bForeignDataBuffer; DWORD dwItemsInArray = 0; DWORD dwItemsInList = 0; volatile PEXT_OBJ_LIST pQueryList = NULL; LPWSTR pwcThisChar; DWORD dwThisNumber; DWORD dwIndex, dwEntry; BOOL bFound; BOOL bDisabled = FALSE; BOOL bUseTimer; DWORD dwType = 0; DWORD dwValue = 0; DWORD dwSize = sizeof(DWORD); DWORD status = 0; DWORD dwObjectBufSize; OPEN_PROC_WAIT_INFO opwInfo; HANDLE hPerflibFuncTimer; PVOID pOldBuffer; HEAP_PROBE(); // see if perf data has been disabled // this is to prevent crashing WINLOGON if the // system has installed a bogus DLL assert (ghKeyPerflib != NULL); dwSize = sizeof(dwValue); dwValue = dwType = 0; status = PrivateRegQueryValueExW ( ghKeyPerflib, DisablePerformanceCounters, NULL, &dwType, (LPBYTE)&dwValue, &dwSize); if ((status == ERROR_SUCCESS) && (dwType == REG_DWORD) && (dwValue == 1)) { // then DON'T Load any libraries and unload any that have been // loaded bDisabled = TRUE; } // if data collection is disabled and there's a collection thread // then close it if (bDisabled && (hCollectThread != NULL)) { pArgs->dwActionFlags = CTD_AF_CLOSE_THREAD; } else if (!bDisabled && ((hCollectThread == NULL) && (dwCollectionFlags == COLL_FLAG_USE_SEPARATE_THREAD))) { // then data collection is enabled and they want a separate collection // thread, but there's no thread at the moment, so create it here pArgs->dwActionFlags = CTD_AF_OPEN_THREAD; } lReturnValue = RegisterExtObjListAccess(); if (lReturnValue == ERROR_SUCCESS) { liStartTime.QuadPart = 0; InitialBytesLeft = 0; liEndTime.QuadPart = 0; if ((dwQueryType == QUERY_ITEMS) && (!bDisabled)) { // alloc the call list pwcThisChar = lpValueName; dwThisNumber = 0; // read the value string and build an object ID list while (*pwcThisChar != 0) { dwThisNumber = GetNextNumberFromList ( pwcThisChar, &pwcThisChar); if (dwThisNumber != 0) { if (dwItemsInList >= dwItemsInArray) { dwItemsInArray += 16; // starting point for # of objects pOldBuffer = NULL; if (pQueryList == NULL) { // alloc a new buffer pQueryList = ALLOCMEM ((sizeof(EXT_OBJ_LIST) * dwItemsInArray)); } else { // realloc a new buffer pOldBuffer = pQueryList; pQueryList = REALLOCMEM(pQueryList, (sizeof(EXT_OBJ_LIST) * dwItemsInArray)); } if (pQueryList == NULL) { // unable to alloc memory so bail if (pOldBuffer) FREEMEM(pOldBuffer); return ERROR_OUTOFMEMORY; } } // then add to the list pQueryList[dwItemsInList].dwObjId = dwThisNumber; pQueryList[dwItemsInList].dwFlags = 0; dwItemsInList++; } } if (Win32Error == ERROR_SUCCESS) { // // Walk through list of ext. objects and tag the ones to call // as the query objects are found // for (pThisExtObj = ExtensibleObjects, dwLibEntry = 0; pThisExtObj != NULL; pThisExtObj = pThisExtObj->pNext, dwLibEntry++) { if (pThisExtObj->dwNumObjects > 0) { // then examine list for (dwIndex = 0; dwIndex < pThisExtObj->dwNumObjects; dwIndex++) { // look at each entry in the list for (dwEntry = 0; dwEntry < dwItemsInList; dwEntry++) { if (pQueryList[dwEntry].dwObjId == pThisExtObj->dwObjList[dwIndex]) { // tag this entry as found pQueryList[dwEntry].dwFlags |= PERF_EOL_ITEM_FOUND; // tag the object as needed pThisExtObj->dwFlags |= PERF_EO_OBJ_IN_QUERY; } } } } else { // this entry doesn't list it's supported objects } } assert (dwLibEntry == NumExtensibleObjects); // see if any in the query list do not have entries bFound = TRUE; for (dwEntry = 0; dwEntry < dwItemsInList; dwEntry++) { if (!(pQueryList[dwEntry].dwFlags & PERF_EOL_ITEM_FOUND)) { // no matching object found bFound = FALSE; break; } } if (!bFound) { // at least one of the object ID's in the query list was // not found in an object that supports an object list // then tag all entries that DO NOT support an object list // to be called and hope one of them supports it/them. for (pThisExtObj = ExtensibleObjects; pThisExtObj != NULL; pThisExtObj = pThisExtObj->pNext) { if (pThisExtObj->dwNumObjects == 0) { // tag this one so it will be called pThisExtObj->dwFlags |= PERF_EO_OBJ_IN_QUERY; } } } } // end if first scan was successful if (pQueryList != NULL) FREEMEM (pQueryList); } // end if QUERY_ITEMS if (lReturnValue == ERROR_SUCCESS) { for (pThisExtObj = ExtensibleObjects; pThisExtObj != NULL; pThisExtObj = pThisExtObj->pNext) { // set the current ext object pointer pArgs->pCurrentExtObject = pThisExtObj; // convert timeout value liWaitTime.QuadPart = MakeTimeOutValue (pThisExtObj->dwCollectTimeout); // close the unused Perf DLL's IF: // the perflib key is disabled or this is an item query // and this is an Item (as opposed to a global or foreign) query or // the requested objects are not it this library or this library is disabled // and this library has been opened // if (((dwQueryType == QUERY_ITEMS) || bDisabled) && (bDisabled || (!(pThisExtObj->dwFlags & PERF_EO_OBJ_IN_QUERY)) || (pThisExtObj->dwFlags & PERF_EO_DISABLED)) && (pThisExtObj->hLibrary != NULL)) { // then free this object if (pThisExtObj->hMutex != NULL) { NTSTATUS NtStatus = NtWaitForSingleObject ( pThisExtObj->hMutex, FALSE, &liWaitTime); Win32Error = PerfpDosError(NtStatus); if (NtStatus == STATUS_SUCCESS) { // then we got a lock CloseExtObjectLibrary (pThisExtObj, bDisabled); ReleaseMutex (pThisExtObj->hMutex); } else { pThisExtObj->dwLockoutCount++; DebugPrint((0, "Unable to Lock object for %ws to close in Query\n", pThisExtObj->szServiceName)); } } else { Win32Error = ERROR_LOCK_FAILED; DebugPrint((0, "No Lock found for %ws\n", pThisExtObj->szServiceName)); } if (hCollectThread != NULL) { // close the collection thread } } else if (((dwQueryType == QUERY_FOREIGN) || (dwQueryType == QUERY_GLOBAL) || (dwQueryType == QUERY_COSTLY) || ((dwQueryType == QUERY_ITEMS) && (pThisExtObj->dwFlags & PERF_EO_OBJ_IN_QUERY))) && (!(pThisExtObj->dwFlags & PERF_EO_DISABLED))) { // initialize values to pass to the extensible counter function NumObjectTypes = 0; BytesLeft = (DWORD) (*lpcbData - ((LPBYTE) *lppDataDefinition - lpData)); bException = FALSE; if ((pThisExtObj->hLibrary == NULL) || (dwQueryType == QUERY_GLOBAL) || (dwQueryType == QUERY_COSTLY)) { // lock library object if (pThisExtObj->hMutex != NULL) { NTSTATUS NtStatus = NtWaitForSingleObject ( pThisExtObj->hMutex, FALSE, &liWaitTime); Win32Error = ERROR_SUCCESS; if (NtStatus == STATUS_SUCCESS) { // if this is a global or costly query, then reset the "in query" // flag for this object. The next ITEMS query will restore it. if ((dwQueryType == QUERY_GLOBAL) || (dwQueryType == QUERY_COSTLY)) { pThisExtObj->dwFlags &= ~PERF_EO_OBJ_IN_QUERY; } // if necessary, open the library if (pThisExtObj->hLibrary == NULL) { // make sure the library is open Win32Error = OpenExtObjectLibrary(pThisExtObj); if (Win32Error != ERROR_SUCCESS) { if (Win32Error != ERROR_SERVICE_DISABLED) { // SERVICE_DISABLED is returned when the // service has been disabled via ExCtrLst. // so no point in complaining about it. // assume error has been posted DebugPrint((0, "Unable to open perf counter library for %ws, Error: 0x%8.8x\n", pThisExtObj->szServiceName, Win32Error)); } ReleaseMutex (pThisExtObj->hMutex); continue; // to next entry } } ReleaseMutex (pThisExtObj->hMutex); } else { Win32Error = PerfpDosError(NtStatus); pThisExtObj->dwLockoutCount++; DebugPrint((0, "Unable to Lock object for %ws to open for Query\n", pThisExtObj->szServiceName)); } } else { Win32Error = ERROR_LOCK_FAILED; DebugPrint((0, "No Lock found for %ws\n", pThisExtObj->szServiceName)); } } else { // library should be ready to use } // if this dll is trusted, then use the system // defined test level, otherwise, test it // thorourghly bUseTimer = TRUE; // default if (!(lPerflibConfigFlags & PLCF_NO_DLL_TESTING)) { if (pThisExtObj->dwFlags & PERF_EO_TRUSTED) { lDllTestLevel = lExtCounterTestLevel; bUseTimer = FALSE; // Trusted DLL's are not timed } else { // not trusted so use full test lDllTestLevel = EXT_TEST_ALL; } } else { // disable DLL testing lDllTestLevel = EXT_TEST_NOMEMALLOC; bUseTimer = FALSE; // Timing is disabled as well } if (lDllTestLevel < EXT_TEST_NOMEMALLOC) { bUseSafeBuffer = TRUE; } else { bUseSafeBuffer = FALSE; } // allocate a local block of memory to pass to the // extensible counter function. if (bUseSafeBuffer) { lpExtDataBuffer = ALLOCMEM (BytesLeft + (2*GUARD_PAGE_SIZE)); } else { lpExtDataBuffer = lpCallBuffer = *lppDataDefinition; } if (lpExtDataBuffer != NULL) { if (bUseSafeBuffer) { // set buffer pointers lpLowGuardPage = lpExtDataBuffer; lpCallBuffer = (LPBYTE)lpExtDataBuffer + GUARD_PAGE_SIZE; lpHiGuardPage = (LPBYTE)lpCallBuffer + BytesLeft; lpEndPointer = (LPBYTE)lpHiGuardPage + GUARD_PAGE_SIZE; // initialize GuardPage Data memset (lpLowGuardPage, GUARD_PAGE_CHAR, GUARD_PAGE_SIZE); memset (lpHiGuardPage, GUARD_PAGE_CHAR, GUARD_PAGE_SIZE); } lpBufferBefore = lpCallBuffer; lpBufferAfter = NULL; hPerflibFuncTimer = NULL; try { // // Collect data from extensible objects // if (pThisExtObj->hMutex != NULL) { NTSTATUS NtStatus = NtWaitForSingleObject ( pThisExtObj->hMutex, FALSE, &liWaitTime); Win32Error = PerfpDosError(NtStatus); if ((NtStatus == STATUS_SUCCESS) && (pThisExtObj->CollectProc != NULL)) { bUnlockObjData = TRUE; opwInfo.pNext = NULL; opwInfo.szLibraryName = pThisExtObj->szLibraryName; opwInfo.szServiceName = pThisExtObj->szServiceName; opwInfo.dwWaitTime = pThisExtObj->dwCollectTimeout; opwInfo.dwEventMsg = PERFLIB_COLLECTION_HUNG; opwInfo.pData = (LPVOID)pThisExtObj; if (bUseTimer) { hPerflibFuncTimer = StartPerflibFunctionTimer(&opwInfo); // if no timer, continue anyway, even though things may // hang, it's better than not loading the DLL since they // usually load OK // if (hPerflibFuncTimer == NULL) { // unable to get a timer entry DebugPrint((0, "Unable to acquire timer for Collect Proc\n")); } } else { hPerflibFuncTimer = NULL; } InitialBytesLeft = BytesLeft; QueryPerformanceCounter (&liStartTime); Win32Error = (*pThisExtObj->CollectProc) ( lpValueName, &lpCallBuffer, &BytesLeft, &NumObjectTypes); QueryPerformanceCounter (&liEndTime); if (hPerflibFuncTimer != NULL) { // kill timer KillPerflibFunctionTimer (hPerflibFuncTimer); hPerflibFuncTimer = NULL; } // update statistics pThisExtObj->dwLastBufferSize = BytesLeft; if (BytesLeft > pThisExtObj->dwMaxBufferSize) { pThisExtObj->dwMaxBufferSize = BytesLeft; } if ((Win32Error == ERROR_MORE_DATA) && (InitialBytesLeft > pThisExtObj->dwMaxBufferRejected)) { pThisExtObj->dwMaxBufferRejected = InitialBytesLeft; } lpBufferAfter = lpCallBuffer; pThisExtObj->llLastUsedTime = GetTimeAsLongLong(); ReleaseMutex (pThisExtObj->hMutex); bUnlockObjData = FALSE; } else { if ((pThisExtObj->CollectProc != NULL) && (lEventLogLevel >= LOG_USER)) { DebugPrint((0, "Unable to Lock object for %ws to Collect data\n", pThisExtObj->szServiceName)); dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = BytesLeft; dwRawDataDwords[dwDataIndex++] = (ULONG_PTR)((LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore); szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; ReportEvent (hEventLog, EVENTLOG_WARNING_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_COLLECTION_HUNG, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data pThisExtObj->dwLockoutCount++; } else { // else it's not open so ignore. BytesLeft = 0; NumObjectTypes = 0; } } } else { Win32Error = ERROR_LOCK_FAILED; DebugPrint((0, "No Lock found for %ws\n", pThisExtObj->szServiceName)); } if ((Win32Error == ERROR_SUCCESS) && (BytesLeft > 0)) { // increment perf counters if ((BytesLeft > InitialBytesLeft) && (lEventLogLevel >= LOG_USER)) { // memory error dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = (ULONG_PTR)InitialBytesLeft; dwRawDataDwords[dwDataIndex++] = (ULONG_PTR)BytesLeft; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_INVALID_SIZE_RETURNED, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data // disable the dll unless: // testing has been disabled. // or this is a trusted DLL (which are never disabled) // the event log message should be reported in any case since // this is a serious error // if ((!(lPerflibConfigFlags & PLCF_NO_DLL_TESTING)) && (!(pThisExtObj->dwFlags & PERF_EO_TRUSTED))) { DisablePerfLibrary (pThisExtObj); } // set error values to correct entries BytesLeft = 0; NumObjectTypes = 0; } else { // the buffer seems ok so far, so validate it InterlockedIncrement ((LONG *)&pThisExtObj->dwCollectCount); pThisExtObj->llElapsedTime += liEndTime.QuadPart - liStartTime.QuadPart; // test all returned buffers for correct alignment if ((((ULONG_PTR)BytesLeft & (ULONG_PTR)0x07)) && !(lPerflibConfigFlags & PLCF_NO_ALIGN_ERRORS)) { if (((pThisExtObj->dwFlags & PERF_EO_ALIGN_ERR_POSTED) == 0) && (lEventLogLevel >= LOG_USER)) { dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = (ULONG_PTR)lpCallBuffer; dwRawDataDwords[dwDataIndex++] = (ULONG_PTR)BytesLeft; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; ReportEvent (hEventLog, EVENTLOG_WARNING_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_BUFFER_ALIGNMENT_ERROR, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data pThisExtObj->dwFlags |= PERF_EO_ALIGN_ERR_POSTED; } } if (bUseSafeBuffer) { // a data buffer was returned and // the function returned OK so see how things // turned out... // // // check for buffer corruption here // bBufferOK = TRUE; // assume it's ok until a check fails // if (lDllTestLevel <= EXT_TEST_BASIC) { // // check 1: bytes left should be the same as // new data buffer ptr - orig data buffer ptr // if (BytesLeft != (DWORD)((LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore)) { if (lEventLogLevel >= LOG_USER) { // issue WARNING, that bytes left param is incorrect // load data for eventlog message // since this error is correctable (though with // some risk) this won't be reported at LOG_USER // level dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = BytesLeft; dwRawDataDwords[dwDataIndex++] = (ULONG_PTR)((LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore); szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; ReportEvent (hEventLog, EVENTLOG_WARNING_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_BUFFER_POINTER_MISMATCH, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } // toss this buffer bBufferOK = FALSE; DisablePerfLibrary (pThisExtObj); // <> // we'll keep the buffer, since the returned bytes left // value is ignored anyway, in order to make the // rest of this function work, we'll fix it here // BytesLeft = (DWORD)((LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore); // << end old code >> } // // check 2: buffer after ptr should be < hi Guard page ptr // if (((LPBYTE)lpBufferAfter > (LPBYTE)lpHiGuardPage) && bBufferOK) { // see if they exceeded the allocated memory if ((LPBYTE)lpBufferAfter >= (LPBYTE)lpEndPointer) { // this is very serious since they've probably trashed // the heap by overwriting the heap sig. block // issue ERROR, buffer overrun if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = (ULONG_PTR)((LPBYTE)lpBufferAfter - (LPBYTE)lpHiGuardPage); szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_HEAP_ERROR, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } } else { // issue ERROR, buffer overrun if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = (ULONG_PTR)((LPBYTE)lpBufferAfter - (LPBYTE)lpHiGuardPage); szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_BUFFER_OVERFLOW, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } } bBufferOK = FALSE; DisablePerfLibrary (pThisExtObj); // since the DLL overran the buffer, the buffer // must be too small (no comments about the DLL // will be made here) so the status will be // changed to ERROR_MORE_DATA and the function // will return. Win32Error = ERROR_MORE_DATA; } // // check 3: check lo guard page for corruption // if (bBufferOK) { bGuardPageOK = TRUE; for (lpCheckPointer = (PUCHAR)lpLowGuardPage; lpCheckPointer < (PUCHAR)lpBufferBefore; lpCheckPointer++) { if (*lpCheckPointer != GUARD_PAGE_CHAR) { bGuardPageOK = FALSE; break; } } if (!bGuardPageOK) { // issue ERROR, Lo Guard Page corrupted if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_GUARD_PAGE_VIOLATION, // event NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } bBufferOK = FALSE; DisablePerfLibrary (pThisExtObj); } } // // check 4: check hi guard page for corruption // if (bBufferOK) { bGuardPageOK = TRUE; for (lpCheckPointer = (PUCHAR)lpHiGuardPage; lpCheckPointer < (PUCHAR)lpEndPointer; lpCheckPointer++) { if (*lpCheckPointer != GUARD_PAGE_CHAR) { bGuardPageOK = FALSE; break; } } if (!bGuardPageOK) { // issue ERROR, Hi Guard Page corrupted if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_GUARD_PAGE_VIOLATION, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } bBufferOK = FALSE; DisablePerfLibrary (pThisExtObj); } } // if ((lDllTestLevel <= EXT_TEST_ALL) && bBufferOK) { // // Internal consistency checks // // // Check 5: Check object length field values // // first test to see if this is a foreign // computer data block or not // pPerfData = (PERF_DATA_BLOCK *)lpBufferBefore; if ((pPerfData->Signature[0] == (WCHAR)'P') && (pPerfData->Signature[1] == (WCHAR)'E') && (pPerfData->Signature[2] == (WCHAR)'R') && (pPerfData->Signature[3] == (WCHAR)'F')) { // if this is a foreign computer data block, then the // first object is after the header pObject = (PERF_OBJECT_TYPE *) ( (LPBYTE)pPerfData + pPerfData->HeaderLength); bForeignDataBuffer = TRUE; } else { // otherwise, if this is just a buffer from // an extensible counter, the object starts // at the beginning of the buffer pObject = (PERF_OBJECT_TYPE *)lpBufferBefore; bForeignDataBuffer = FALSE; } // go to where the pointers say the end of the // buffer is and then see if it's where it // should be dwObjectBufSize = 0; for (dwIndex = 0; dwIndex < NumObjectTypes; dwIndex++) { dwObjectBufSize += pObject->TotalByteLength; pObject = (PERF_OBJECT_TYPE *)((LPBYTE)pObject + pObject->TotalByteLength); } if (((LPBYTE)pObject != (LPBYTE)lpCallBuffer) || (dwObjectBufSize > BytesLeft)) { // then a length field is incorrect. This is FATAL // since it can corrupt the rest of the buffer // and render the buffer unusable. if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = NumObjectTypes; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_INCORRECT_OBJECT_LENGTH, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } bBufferOK = FALSE; DisablePerfLibrary (pThisExtObj); } // // Test 6: Test Object definitions fields // if (bBufferOK) { // set object pointer if (bForeignDataBuffer) { pObject = (PERF_OBJECT_TYPE *) ( (LPBYTE)pPerfData + pPerfData->HeaderLength); } else { // otherwise, if this is just a buffer from // an extensible counter, the object starts // at the beginning of the buffer pObject = (PERF_OBJECT_TYPE *)lpBufferBefore; } for (dwIndex = 0; dwIndex < NumObjectTypes; dwIndex++) { pNextObject = (PERF_OBJECT_TYPE *)((LPBYTE)pObject + pObject->DefinitionLength); if (pObject->NumCounters != 0) { pCounterDef = (PERF_COUNTER_DEFINITION *) ((LPBYTE)pObject + pObject->HeaderLength); lCtrIndex = 0; while (lCtrIndex < pObject->NumCounters) { if ((LPBYTE)pCounterDef < (LPBYTE)pNextObject) { // still ok so go to next counter pCounterDef = (PERF_COUNTER_DEFINITION *) ((LPBYTE)pCounterDef + pCounterDef->ByteLength); lCtrIndex++; } else { bBufferOK = FALSE; break; } } if ((LPBYTE)pCounterDef != (LPBYTE)pNextObject) { bBufferOK = FALSE; } } if (!bBufferOK) { break; } else { pObject = (PERF_OBJECT_TYPE *)((LPBYTE)pObject + pObject->TotalByteLength); } } if (!bBufferOK) { if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = pObject->ObjectNameTitleIndex; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_INVALID_DEFINITION_BLOCK, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } DisablePerfLibrary (pThisExtObj); } } // // Test 7: Test instance field size values // if (bBufferOK) { // set object pointer if (bForeignDataBuffer) { pObject = (PERF_OBJECT_TYPE *) ( (LPBYTE)pPerfData + pPerfData->HeaderLength); } else { // otherwise, if this is just a buffer from // an extensible counter, the object starts // at the beginning of the buffer pObject = (PERF_OBJECT_TYPE *)lpBufferBefore; } for (dwIndex = 0; dwIndex < NumObjectTypes; dwIndex++) { pNextObject = (PERF_OBJECT_TYPE *)((LPBYTE)pObject + pObject->TotalByteLength); if (pObject->NumInstances != PERF_NO_INSTANCES) { pInstance = (PERF_INSTANCE_DEFINITION *) ((LPBYTE)pObject + pObject->DefinitionLength); lInstIndex = 0; while (lInstIndex < pObject->NumInstances) { PERF_COUNTER_BLOCK *pCounterBlock; pCounterBlock = (PERF_COUNTER_BLOCK *) ((PCHAR) pInstance + pInstance->ByteLength); pInstance = (PERF_INSTANCE_DEFINITION *) ((PCHAR) pCounterBlock + pCounterBlock->ByteLength); lInstIndex++; } if ((LPBYTE)pInstance > (LPBYTE)pNextObject) { bBufferOK = FALSE; } } if (!bBufferOK) { break; } else { pObject = pNextObject; } } if (!bBufferOK) { if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = pObject->ObjectNameTitleIndex; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_INCORRECT_INSTANCE_LENGTH, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } DisablePerfLibrary (pThisExtObj); } } } } // // if all the tests pass,then copy the data to the // original buffer and update the pointers if (bBufferOK) { RtlMoveMemory (*lppDataDefinition, lpBufferBefore, BytesLeft); // returned buffer size } else { NumObjectTypes = 0; // since this buffer was tossed BytesLeft = 0; // reset the size value since the buffer wasn't used } } else { // function already copied data to caller's buffer // so no further action is necessary } *lppDataDefinition = (LPVOID)((LPBYTE)(*lppDataDefinition) + BytesLeft); // update data pointer } } else { if (Win32Error != ERROR_SUCCESS) { InterlockedIncrement ((LONG *)&pThisExtObj->dwErrorCount); } if (bUnlockObjData) { ReleaseMutex (pThisExtObj->hMutex); } NumObjectTypes = 0; // clear counter }// end if function returned successfully } except (EXCEPTION_EXECUTE_HANDLER) { Win32Error = GetExceptionCode(); InterlockedIncrement ((LONG *)&pThisExtObj->dwErrorCount); bException = TRUE; if (bUnlockObjData) { ReleaseMutex (pThisExtObj->hMutex); bUnlockObjData = FALSE; } if (hPerflibFuncTimer != NULL) { // kill timer KillPerflibFunctionTimer (hPerflibFuncTimer); hPerflibFuncTimer = NULL; } } if (bUseSafeBuffer) { FREEMEM (lpExtDataBuffer); } } else { // unable to allocate memory so set error value Win32Error = ERROR_OUTOFMEMORY; } // end if temp buffer allocated successfully // // Update the count of the number of object types // ((PPERF_DATA_BLOCK) lpData)->NumObjectTypes += NumObjectTypes; if ( Win32Error != ERROR_SUCCESS) { if (bException || !((Win32Error == ERROR_MORE_DATA) || (Win32Error == WAIT_TIMEOUT))) { // inform on exceptions & illegal error status only if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = Win32Error; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_COLLECT_PROC_EXCEPTION, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } else { if (bException) { DebugPrint((0, "Extensible Counter %d generated an exception code: 0x%8.8x (%dL)\n", NumObjectTypes, Win32Error, Win32Error)); } else { DebugPrint((0, "Extensible Counter %d returned error code: 0x%8.8x (%dL)\n", NumObjectTypes, Win32Error, Win32Error)); } } if (bException) { DisablePerfLibrary (pThisExtObj); } } // the ext. dll is only supposed to return: // ERROR_SUCCESS even if it encountered a problem, OR // ERROR_MODE_DATA if the buffer was too small. // if it's ERROR_MORE_DATA, then break and return the // error now, since it'll just be returned again and again. if (Win32Error == ERROR_MORE_DATA) { lReturnValue = Win32Error; break; } } // update perf data in global section if (pThisExtObj->pPerfSectionEntry != NULL) { pThisExtObj->pPerfSectionEntry->llElapsedTime = pThisExtObj->llElapsedTime; pThisExtObj->pPerfSectionEntry->dwCollectCount = pThisExtObj->dwCollectCount; pThisExtObj->pPerfSectionEntry->dwOpenCount = pThisExtObj->dwOpenCount; pThisExtObj->pPerfSectionEntry->dwCloseCount = pThisExtObj->dwCloseCount; pThisExtObj->pPerfSectionEntry->dwLockoutCount = pThisExtObj->dwLockoutCount; pThisExtObj->pPerfSectionEntry->dwErrorCount = pThisExtObj->dwErrorCount; pThisExtObj->pPerfSectionEntry->dwLastBufferSize = pThisExtObj->dwLastBufferSize; pThisExtObj->pPerfSectionEntry->dwMaxBufferSize = pThisExtObj->dwMaxBufferSize; pThisExtObj->pPerfSectionEntry->dwMaxBufferRejected = pThisExtObj->dwMaxBufferRejected; } else { // no data section was initialized so skip } } // end if this object is to be called } // end for each object } // else an error occurred so unable to call functions Win32Error = DeRegisterExtObjListAccess(); } // else unable to access ext object list HEAP_PROBE(); if (bDisabled) lReturnValue = ERROR_SERVICE_DISABLED; return lReturnValue; }