|
|
/****************************************************************************
debug.c
winmm debug support module
Copyright (c) 1990-2001 Microsoft Corporation
History 10/1/92 Updated for NT by Robin Speed (RobinSp) ****************************************************************************/
#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include "winmmi.h"
#include <wchar.h>
#include <stdarg.h>
// no REAL logging for now ! - NT doesn't have Dr Watson !
#define LogParamError(a, b)
RTL_RESOURCE gHandleListResource;
/***************************************************************************
* @doc INTERNAL * * @func HANDLE | NewHandle | allocate a fixed handle in MMSYSTEM's local heap * * @parm UINT | uType | unique id describing handle type * @parm UINT | uSize | size in bytes to be allocated * * @rdesc Returns pointer/handle to memory object * * @comm a standard handle header (HNDL) will be added to the object, * and it will be linked into the list of MMSYSTEM handles. * ***************************************************************************/ HANDLE NewHandle(UINT uType, PCWSTR cookie, UINT uSize) { PHNDL pHandle; pHandle = (PHNDL)HeapAlloc(hHeap, 0, sizeof(HNDL) + uSize);
if (pHandle == NULL) { return pHandle; } else { ZeroMemory(pHandle, sizeof(HNDL) + uSize); // zero the whole bludy lot
if (!mmInitializeCriticalSection(&pHandle->CritSec)) { HeapFree(hHeap, 0, (LPSTR)pHandle); return NULL; }
pHandle->hThread = GetCurrentTask(); // For WOW validation
pHandle->uType = uType; pHandle->cookie = cookie;
RtlAcquireResourceExclusive(&gHandleListResource, TRUE); EnterCriticalSection(&HandleListCritSec); pHandle->pNext = pHandleList; pHandleList = pHandle; LeaveCriticalSection(&HandleListCritSec); } return PHtoH(pHandle); }
void AcquireHandleListResourceShared() { RtlAcquireResourceShared(&gHandleListResource, TRUE); }
void AcquireHandleListResourceExclusive() { RtlAcquireResourceExclusive(&gHandleListResource, TRUE); }
void ReleaseHandleListResource() { RtlReleaseResource(&gHandleListResource); }
/***************************************************************************
* @doc INTERNAL * * @func HANDLE | FreeHandle | free handle allocated with NewHandle * * @parm HANDLE | hUser | handle returned from NewHandle * * @comm handle will be unlinked from list, and memory will be freed * * ***************************************************************************/
void FreeHandle(HANDLE hUser) { /* Find handle and free from list */
PHNDL pHandle; PHNDL *pSearch;
if (hUser == NULL) { return; }
//
// Point to our handle data
//
pHandle = HtoPH(hUser);
AcquireHandleListResourceExclusive(); EnterCriticalSection(&HandleListCritSec);
pSearch = &pHandleList;
while (*pSearch != NULL) { if (*pSearch == pHandle) { //
// Found it
// Remove it from the list
//
*pSearch = pHandle->pNext; LeaveCriticalSection(&HandleListCritSec); // Making sure no one is using the handle while we mark it as invalid.
EnterCriticalSection(&pHandle->CritSec); pHandle->uType = 0; pHandle->fdwHandle = 0L; pHandle->hThread = NULL; pHandle->pNext = NULL; LeaveCriticalSection(&pHandle->CritSec);
DeleteCriticalSection(&pHandle->CritSec); HeapFree(hHeap, 0, (LPSTR)pHandle); ReleaseHandleListResource(); return; } else { pSearch = &(*pSearch)->pNext; } }
dprintf1(("Freeing handle which is not in the list !")); WinAssert(FALSE); LeaveCriticalSection(&HandleListCritSec); ReleaseHandleListResource(); }
/***************************************************************************
* @doc INTERNAL * * @func HANDLE | InvalidateHandle | invalidate handle allocated with * NewHandle for parameter validation * * @parm HANDLE | hUser | handle returned from NewHandle * * @comm handle will be marked as TYPE_UNKNOWN, causing handle based API's to * fail. * * ***************************************************************************/
void InvalidateHandle(HANDLE hUser) { /* Find handle and free from list */
PHNDL pHandle;
if (hUser == NULL) { return; }
//
// Point to our handle data
//
pHandle = HtoPH(hUser);
pHandle->uType = TYPE_UNKNOWN; }
/**************************************************************************
@doc INTERNAL
@api void | winmmSetDebugLevel | Set the current debug level
@parm int | iLevel | The new level to set
@rdesc There is no return value
**************************************************************************/
void winmmSetDebugLevel(int level) { #if DBG
winmmDebugLevel = level; dprintf(("debug level set to %d", winmmDebugLevel)); #endif
}
STATICDT UINT inited=0;
#if DBG
extern int mciDebugLevel; #endif
#if DBG
void InitDebugLevel(void) { if (!inited) {
INT level;
level = GetProfileInt("MMDEBUG", "WINMM", 99); if (level != 99) { winmmDebugLevel = level; }
level = GetProfileInt("MMDEBUG", "MCI", 99); if (level != 99) { mciDebugLevel = level; }
inited = 1; } dprintf2(("Starting, debug level=%d", winmmDebugLevel)); } #endif
#ifdef DEBUG_RETAIL
/***************************************************************************
* @doc INTERNAL WAVE MIDI * * @func BOOL | ValidateHeader | validates a wave or midi date header * * @parm LPVOID | lpHeader| pointer to wave/midi header * @parm UINT | wSize | size of header passed by app * @parm UINT | wType | unique id describing header/handle type * * @rdesc Returns TRUE if <p> is non NULL and <wSize> is the correct size * Returns FALSE otherwise * * @comm if the header is invalid an error will be generated. * ***************************************************************************/
BOOL ValidateHeader(PVOID pHdr, UINT uSize, UINT uType) { // Detect bad header
if (!ValidateWritePointer(pHdr, uSize)) { DebugErr(DBF_ERROR, "Invalid header pointer"); return FALSE; }
// Check type
switch (uType) { case TYPE_WAVEOUT: case TYPE_WAVEIN: { PWAVEHDR pHeader = pHdr;
// Check header
if (uSize < sizeof(WAVEHDR)) { DebugErr(DBF_ERROR, "Invalid header size"); LogParamError(ERR_BAD_VALUE, uSize); return FALSE; }
if (pHeader->dwFlags & ~WHDR_VALID) { LogParamError(ERR_BAD_FLAGS, ((PWAVEHDR)pHeader)->dwFlags); return FALSE; }
// Check buffer
if (!(uType == TYPE_WAVEOUT ? ValidateReadPointer(pHeader->lpData, pHeader->dwBufferLength) : ValidateWritePointer(pHeader->lpData, pHeader->dwBufferLength)) ) { DebugErr(DBF_ERROR, "Invalid buffer pointer"); return FALSE; } } break;
case TYPE_MIDIIN: case TYPE_MIDIOUT: case TYPE_MIDISTRM: { PMIDIHDR pHeader = pHdr;
if ((TYPE_MIDISTRM == uType) && uSize < sizeof(MIDIHDR)) { DebugErr(DBF_ERROR, "Invalid header size"); LogParamError(ERR_BAD_VALUE, uSize); return FALSE; } else if (uSize < sizeof(MIDIHDR31)) { DebugErr(DBF_ERROR, "Invalid header size"); LogParamError(ERR_BAD_VALUE, uSize); return FALSE; }
if (pHeader->dwFlags & ~MHDR_VALID) { LogParamError(ERR_BAD_FLAGS, ((PMIDIHDR)pHeader)->dwFlags); return FALSE; }
// Check buffer
if (!(uType == TYPE_MIDIOUT ? ValidateReadPointer(pHeader->lpData, pHeader->dwBufferLength) : ValidateWritePointer(pHeader->lpData, pHeader->dwBufferLength)) ) { DebugErr(DBF_ERROR, "Invalid buffer pointer"); return FALSE; } } break;
default: WinAssert(FALSE); break; }
return TRUE; }
#ifndef USE_KERNEL_VALIDATION
/***************************************************************************
* @doc INTERNAL * * @func BOOL | ValidateReadPointer | validates that a pointer is valid to * read from. * * @parm LPVOID | lpPoint| pointer to validate * @parm DWORD | dLen | supposed length of said pointer * * @rdesc Returns TRUE if <p> is a valid pointer * Returns FALSE if <p> is not a valid pointer * * @comm will generate error if the pointer is invalid * ***************************************************************************/
BOOL ValidateReadPointer(PVOID pPoint, ULONG Len) { // For now just check access to first and last byte
// Only validate if Len non zero. Midi APIs pass data in the
// pointer and pass a length of zero. Otherwise on 64 bit machines
// we look 4Gigs out from the pointer when we check Len-1 and Len==0.
// That doesn't work very well.
if (Len) { try { volatile BYTE b; b = ((PBYTE)pPoint)[0]; b = ((PBYTE)pPoint)[Len - 1]; } except(EXCEPTION_EXECUTE_HANDLER) { LogParamError(ERR_BAD_PTR, pPoint); return FALSE; } } return TRUE; }
/***************************************************************************
* @doc INTERNAL * * @func BOOL | ValidateWritePointer | validates that a pointer is valid to * write to. * * @parm LPVOID | lpPoint| pointer to validate * @parm DWORD | dLen | supposed length of said pointer * * @rdesc Returns TRUE if <p> is a valid pointer * Returns FALSE if <p> is not a valid pointer * * @comm will generate error if the pointer is invalid * ***************************************************************************/ BOOL ValidateWritePointer(PVOID pPoint, ULONG Len) { // For now just check read and write access to first and last byte
// Only validate if Len non zero. Midi APIs pass data in the
// pointer and pass a length of zero. Otherwise on 64 bit machines
// we look 4Gigs out from the pointer when we check Len-1 and Len==0.
// That doesn't work very well.
if (Len) { try { volatile BYTE b; b = ((PBYTE)pPoint)[0]; ((PBYTE)pPoint)[0] = b; b = ((PBYTE)pPoint)[Len - 1]; ((PBYTE)pPoint)[Len - 1] = b;
} except(EXCEPTION_EXECUTE_HANDLER) { LogParamError(ERR_BAD_PTR, pPoint); return FALSE; } } return TRUE; } #endif // USE_KERNEL_VALIDATION
/***************************************************************************
* @doc INTERNAL * * @func BOOL | ValidDriverCallback | * * validates that a driver callback is valid, to be valid a driver * callback must be a valid window, task, or a function in a FIXED DLL * code segment. * * @parm DWORD | dwCallback | callback to validate * @parm DWORD | wFlags | driver callback flags * * @rdesc Returns 0 if <dwCallback> is a valid callback * Returns error condition if <dwCallback> is not a valid callback ***************************************************************************/
BOOL ValidDriverCallback(HANDLE hCallback, DWORD dwFlags) { switch (dwFlags & DCB_TYPEMASK) { case DCB_WINDOW: if (!IsWindow(hCallback)) { LogParamError(ERR_BAD_HWND, hCallback); return FALSE; } break;
case DCB_EVENT: //if (hCallback is not an event)
// LogParamError(ERR_BAD_CALLBACK, hCallback);
// return FALSE;
//}
break;
case DCB_TASK: //if (IsBadCodePtr((FARPROC)hCallback)) {
// LogParamError(ERR_BAD_CALLBACK, hCallback);
// return FALSE;
//}
break;
case DCB_FUNCTION: if (IsBadCodePtr((FARPROC)hCallback)) { LogParamError(ERR_BAD_CALLBACK, hCallback); return FALSE; } break; }
return TRUE; }
#ifndef USE_KERNEL_VALIDATION
/**************************************************************************
* @doc INTERNAL * * @func BOOL | ValidateString | * **************************************************************************/ BOOL ValidateString(LPCSTR pPoint, DWORD Len) { // For now just check access - do a 'strnlen'
try { volatile BYTE b; LPCSTR p = pPoint;
while (Len--) { b = *p; if (!b) { break; } p++; }
} except(EXCEPTION_EXECUTE_HANDLER) { LogParamError(ERR_BAD_STRING_PTR, pPoint); return FALSE; } return TRUE; }
/**************************************************************************
* @doc INTERNAL * * @func BOOL | ValidateStringW | * **************************************************************************/ BOOL ValidateStringW(LPCWSTR pPoint, DWORD Len) { // For now just check access - do a 'strnlen'
try { volatile WCHAR b; LPCWSTR p = pPoint;
while (Len--) { b = *p; if (!b) { break; } p++; }
} except(EXCEPTION_EXECUTE_HANDLER) { LogParamError(ERR_BAD_STRING_PTR, pPoint); return FALSE; } return TRUE; } #endif //USE_KERNEL_VALIDATION
/**************************************************************************
* @doc INTERNAL * * @func BOOL | ValidateHandle | validates a handle created with NewHandle * * @parm PHNDL | hLocal | handle returned from NewHandle * @parm UINT | wType | unique id describing handle type * * @rdesc Returns TRUE if <h> is a valid handle of type <wType> * Returns FALSE if <h> is not a valid handle * * @comm if the handle is invalid an error will be generated. * **************************************************************************/ BOOL ValidateHandle(HANDLE hLocal, UINT uType)
{ BOOL OK;
//
// if the handle is less than 64k or a mapper id then
// don't bother with the overhead of the try-except.
//
// BUGBUG: MM needs to be audited for WIN64!
//
// This code is a mess. The mapper ids are defined as 32-bit
// unsigned values and then compared against a HANDLE? Of course
// an unsigned 32-bit -1 will never equal a 64-bit -1 so on WIN64,
// an exception is taken everytime an invalid handle is passed in.
// Someone hacked in enough coercions to mask valid warnings for WIN64.
// Even worse, there is at least one function that returns 0xffffffff
// explicity versus a define or const value.
//
// For now, change the const compare to a handle compare and add an
// invalid handle compare. This results in the same code on x86 as
// before (the compiler folds all of the redundant compares), and doesn't
// increase the codesize for IA64 (parallel compares are used) even though
// the extra compare for WIN64 is useless.
//
if (hLocal < (HANDLE)0x10000 || INVALID_HANDLE_VALUE == hLocal || WAVE_MAPPER == (UINT_PTR)hLocal || MIDI_MAPPER == (UINT_PTR)hLocal || AUX_MAPPER == (UINT_PTR)hLocal) { LogParamError(ERR_BAD_HANDLE, hLocal); return FALSE; }
try { OK = HtoPH(hLocal)->uType == uType;
} except(EXCEPTION_EXECUTE_HANDLER) { LogParamError(ERR_BAD_HANDLE, hLocal); return FALSE; }
return OK; }
#if DBG
char * Types[4] = {"Unknown callback type", "Window callback", "Task callback", "Function callback"}; #endif
/**************************************************************************
* @doc INTERNAL * * @func BOOL | ValidateCallbackType | validates a callback address, * window handle, or task handle * * @parm PHNDL | hLocal | handle returned from NewHandle * @parm UINT | wType | unique id describing handle type * * @rdesc Returns TRUE if <h> is a valid handle of type <wType> * Returns FALSE if <h> is not a valid handle * * @comm if the handle is invalid an error will be generated. * **************************************************************************/ BOOL ValidateCallbackType(DWORD_PTR dwCallback, UINT uType) {
#define DCALLBACK_WINDOW HIWORD(CALLBACK_WINDOW) // dwCallback is a HWND
#define DCALLBACK_TASK HIWORD(CALLBACK_TASK) // dwCallback is a HTASK
#define DCALLBACK_FUNCTION HIWORD(CALLBACK_FUNCTION) // dwCallback is a FARPROC
#define DCALLBACK_EVENT HIWORD(CALLBACK_EVENT) // dwCallback is an EVENT
UINT type = uType & HIWORD(CALLBACK_TYPEMASK);
#if DBG
if (type>5) { type = 0; } dprintf3(("Validating Callback, type=%d (%hs), handle=%8x", type, Types[type], dwCallback)); #endif
switch (type) { case DCALLBACK_WINDOW: return(IsWindow((HWND)dwCallback)); break;
case DCALLBACK_EVENT: { // ?? how to verify that this is an event handle??
//DWORD dwFlags;
//GetHandleInformation((HANDLE)dwCallback, &dwFlags);
return TRUE; } break;
case DCALLBACK_FUNCTION: return(!(IsBadCodePtr((FARPROC)dwCallback))); break;
case DCALLBACK_TASK: if (THREAD_PRIORITY_ERROR_RETURN == GetThreadPriority((HANDLE)dwCallback)) { dprintf1(("Invalid callback task handle")); // I suspect we do not have the correct thread handle, in
// which case we can only return TRUE.
//return(FALSE);
} return(TRUE); break;
} return TRUE; }
/**************************************************************************
@doc INTERNAL
@func void | dout | Output debug string if debug flag is set
@parm LPSTR | szString **************************************************************************/
#if DBG
int fDebug = 1; #else
int fDebug = 0; #endif
//void dout(LPSTR szString)
//{
// if (fDebug) {
// OutputDebugStringA(szString);
// }
//}
#ifdef LATER
This routine should probably be replaced in the headers by redefining to use OutputDebugString
#endif
#undef OutputDebugStr
// Make our function visible
/*****************************************************************************
* @doc EXTERNAL DDK * * @api void | OutputDebugStr | This function sends a debugging message * directly to the COM1 port or to a secondary monochrome display * adapter. Because it bypasses DOS, it can be called by low-level * callback functions and other code at interrupt time. * * @parm LPSTR | lpOutputString | Specifies a far pointer to a * null-terminated string. * * @comm This function is available only in the debugging version of * Windows. The DebugOutput keyname in the [mmsystem] * section of SYSTEM.INI controls where the debugging information is * sent. If fDebugOutput is 0, all debug output is disabled. ******************************************************************************/
/*****************************************************************************
* This function is basicly the same as OutputDebugString() in KERNEL. * * * DESCRIPTION: outputs a string to the debugger * * ENTRY: szString - string to output * * EXIT: * none * USES: * flags * *****************************************************************************/
VOID APIENTRY OutputDebugStr(LPCSTR szString) { OutputDebugStringA((LPSTR)szString); // Will always be an ASCII string
// When the MM WOW thunk is changed to call OutputDebugString directly
// we can remove this routine from our code
}
#endif // DEBUG_RETAIL
#if DBG
int winmmDebugLevel = 0;
/***************************************************************************
@doc INTERNAL
@api void | winmmDbgOut | This function sends output to the current debug output device.
@parm LPSTR | lpszFormat | Pointer to a printf style format string. @parm ??? | ... | Args.
@rdesc There is no return value.
****************************************************************************/ extern BOOL Quiet = FALSE;
void winmmDbgOut(LPSTR lpszFormat, ...)
{ char buf[512]; UINT n; va_list va;
if (Quiet) { return; }
n = wsprintf(buf, "WINMM(p%d:t%d): ", GetCurrentProcessId(), GetCurrentThreadId());
va_start(va, lpszFormat); n += vsprintf(buf+n, lpszFormat, va); va_end(va);
buf[n++] = '\n'; buf[n] = 0; OutputDebugString(buf); Sleep(0); // let terminal catch up
}
/***************************************************************************
@doc INTERNAL
@api void | dDbgAssert | This function prints an assertion message.
@parm LPSTR | exp | Pointer to the expression string. @parm LPSTR | file | Pointer to the file name. @parm int | line | The line number.
@rdesc There is no return value.
****************************************************************************/
void dDbgAssert(LPSTR exp, LPSTR file, int line) { dprintf(("Assertion failure:")); dprintf((" Exp: %s", exp)); dprintf((" File: %s, line: %d", file, line)); DebugBreak(); } #else // Still need to export this thing to help others
void winmmDbgOut(LPSTR lpszFormat, ...) { }
#endif // DBG
|