|
|
/*++ 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 <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntregapi.h>
#include <ntprfctr.h>
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <winperf.h>
#include <rpc.h>
#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, liDiff;
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 pNewBuffer;
HEAP_PROBE();
//
// Make sure that the caller's buffer is aligned properly
//
#ifdef _WIN64
if ((ULONG_PTR) *lppDataDefinition & (ULONG_PTR) 0x07) { DebugPrint((0, "QueryExtensibleData: lppDataDefinition not aligned %I64x\n", lppDataDefinition)); return ERROR_INVALID_USER_BUFFER; } /*
if ((ULONG_PTR) *lpcbData & (ULONG_PTR) 0x07) { DebugPrint((0, "QueryExtensibleData: lpcbData not aligned %I64x\n", *lpcbData)); return ERROR_INVALID_USER_BUFFER; } */ if ((ULONG_PTR) lpData & (ULONG_PTR) 0x07) { DebugPrint((0, "QueryExtensibleData: lpData not aligned %I64x\n", lpData)); return ERROR_INVALID_USER_BUFFER; } #endif
// see if perf data has been disabled
// this is to prevent crashing WINLOGON if the
// system has installed a bogus DLL
if (ghKeyPerflib == NULL || ghKeyPerflib == INVALID_HANDLE_VALUE) { // Ignore Status return. We only need ghKeyPerflib to query "DisablePerformanceCounters" DWORD value.
//
HKEY lhKeyPerflib = NULL; status = (DWORD) RegOpenKeyExW(HKEY_LOCAL_MACHINE, HKLMPerflibKey, 0L, KEY_READ, & lhKeyPerflib); if (status == ERROR_SUCCESS) { if (InterlockedCompareExchangePointer(& ghKeyPerflib, lhKeyPerflib, NULL) != NULL) { RegCloseKey(lhKeyPerflib); lhKeyPerflib = NULL; } } }
assert (ghKeyPerflib != NULL); dwSize = sizeof(dwValue); dwValue = dwType = 0; if (ghKeyPerflib != NULL && ghKeyPerflib != INVALID_HANDLE_VALUE) { 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
pNewBuffer = NULL; if (pQueryList == NULL) { // alloc a new buffer
pNewBuffer = ALLOCMEM ((sizeof(EXT_OBJ_LIST) * dwItemsInArray)); } else { // realloc a new buffer
pNewBuffer = REALLOCMEM(pQueryList, (sizeof(EXT_OBJ_LIST) * dwItemsInArray)); } if (pNewBuffer == NULL) { // unable to alloc memory so bail
if (pQueryList) FREEMEM(pQueryList); return ERROR_OUTOFMEMORY; } else { pQueryList = pNewBuffer; } }
// 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) { if ((GetCurrentThreadId() != pThisExtObj->ThreadId) && (pThisExtObj->dwOpenFail == 0)) { // make sure the library is open
Win32Error = OpenExtObjectLibrary(pThisExtObj); if (Win32Error != ERROR_SUCCESS) { #if DBG
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)); } #endif
ReleaseMutex (pThisExtObj->hMutex); continue; // to next entry
} } else { 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;
NtQueryPerformanceCounter (&liStartTime, NULL);
Win32Error = (*pThisExtObj->CollectProc) ( lpValueName, &lpCallBuffer, &BytesLeft, &NumObjectTypes);
NtQueryPerformanceCounter (&liEndTime, &liDiff);
if (liDiff.QuadPart > 0) { liDiff.QuadPart = (liEndTime.QuadPart - liStartTime.QuadPart) / (liDiff.QuadPart / 1000) ; if ((liDiff.QuadPart > 100) || (Win32Error != ERROR_SUCCESS)) { TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_QUERY_EXTDATA, ARG_DEF(ARG_TYPE_WSTR, 1) | ARG_DEF(ARG_TYPE_ULONG64, 2), Win32Error, pThisExtObj->szServiceName, WSTRSIZE(pThisExtObj->szServiceName), liDiff, sizeof(liDiff), NULL)); } }
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; #if DBG
if ( (((ULONG_PTR) lpCallBuffer) & 0x07) != 0) { DbgPrint("Perflib: Misaligned pointer %X returned from '%s'\n", lpCallBuffer, pThisExtObj->szLibraryName); // ASSERT( (((ULONG_PTR)lpCallBuffer) & 0x07) == 0);
} #endif
} else { if (pThisExtObj->CollectProc != NULL) { DebugPrint((0, "Unable to Lock object for %ws to Collect data\n", pThisExtObj->szServiceName)); TRACE((WINPERF_DBG_TRACE_ERROR), (&PerflibGuid, __LINE__, PERF_QUERY_EXTDATA, ARG_TYPE_WSTR, Win32Error, pThisExtObj->szServiceName, WSTRSIZE(pThisExtObj->szServiceName), NULL)); if (THROTTLE_PERFDLL(PERFLIB_COLLECTION_HUNG, pThisExtObj)) { dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = BytesLeft; dwRawDataDwords[dwDataIndex++] = (DWORD)((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(DWORD), // 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)); TRACE((WINPERF_DBG_TRACE_ERROR), (&PerflibGuid, __LINE__, PERF_QUERY_EXTDATA, ARG_TYPE_WSTR, Win32Error, pThisExtObj->szServiceName, WSTRSIZE(pThisExtObj->szServiceName), NULL)); }
if ((Win32Error == ERROR_SUCCESS) && (BytesLeft > 0)) { // increment perf counters
if (BytesLeft > InitialBytesLeft) { TRACE((WINPERF_DBG_TRACE_ERROR), (&PerflibGuid, __LINE__, PERF_QUERY_EXTDATA, ARG_TYPE_WSTR, Win32Error, pThisExtObj->szServiceName, WSTRSIZE(pThisExtObj->szServiceName), BytesLeft, sizeof(DWORD), InitialBytesLeft, sizeof(DWORD), NULL)); if (THROTTLE_PERFDLL(PERFLIB_INVALID_SIZE_RETURNED, pThisExtObj)) { // 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, PERFLIB_DISABLE_ALL); } // 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) && THROTTLE_PERFDLL(PERFLIB_BUFFER_ALIGNMENT_ERROR, pThisExtObj)) { 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; } #ifdef _WIN64
// try and fix up BytesLeft and lpCallBuffer
BytesLeft = ExtpAlignBuffer(lpBufferBefore, (PCHAR*) &lpCallBuffer, (InitialBytesLeft - BytesLeft)); lpBufferAfter = lpCallBuffer; #endif
}
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) { DWORD BytesAvailable; //
// check 1: bytes left should be the same as
// new data buffer ptr - orig data buffer ptr
//
BytesAvailable = (DWORD)((LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore); if (BytesLeft != BytesAvailable) { if (THROTTLE_PERFDLL(PERFLIB_BUFFER_POINTER_MISMATCH, pThisExtObj)) { // issue WARNING, that bytes left param is incorrect
// load data for eventlog message
// this error is correctable
dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = BytesLeft; dwRawDataDwords[dwDataIndex++] = BytesAvailable; 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(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
TRACE((WINPERF_DBG_TRACE_ERROR), (&PerflibGuid, __LINE__, PERF_QUERY_EXTDATA, ARG_TYPE_WSTR, Win32Error, pThisExtObj->szServiceName, WSTRSIZE(pThisExtObj->szServiceName), BytesLeft, sizeof(DWORD), BytesAvailable, sizeof(DWORD), NULL)); // toss this buffer
bBufferOK = FALSE; DisablePerfLibrary(pThisExtObj, PERFLIB_DISABLE_ALL); // <<old code>>
// 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 (THROTTLE_PERFDLL(PERFLIB_HEAP_ERROR, pThisExtObj)) { // 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 (THROTTLE_PERFDLL(PERFLIB_BUFFER_OVERFLOW, pThisExtObj)) { // 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, PERFLIB_DISABLE_ALL); // 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 (THROTTLE_PERFDLL(PERFLIB_GUARD_PAGE_VIOLATION, pThisExtObj)) { // 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, PERFLIB_DISABLE_ALL); } } //
// 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 (THROTTLE_PERFDLL(PERFLIB_GUARD_PAGE_VIOLATION, pThisExtObj)) { // 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, PERFLIB_DISABLE_ALL); } } //
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 (THROTTLE_PERFDLL( PERFLIB_INCORRECT_OBJECT_LENGTH, pThisExtObj)) { // 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, PERFLIB_DISABLE_ALL); } //
// 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 (THROTTLE_PERFDLL( PERFLIB_INVALID_DEFINITION_BLOCK, pThisExtObj)) { // 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, PERFLIB_DISABLE_ALL); }
} //
// 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 (THROTTLE_PERFDLL( PERFLIB_INCORRECT_INSTANCE_LENGTH, pThisExtObj)) { // 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, PERFLIB_DISABLE_ALL); } } } } //
// 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 (THROTTLE_PERFDLL(PERFLIB_COLLECT_PROC_EXCEPTION, pThisExtObj)) { // 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, PERFLIB_DISABLE_ALL); } } // 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; }
|