/****************************** Module Header ******************************\ * Module Name: validate.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * This module contains functions for validating windows, menus, cursors, etc. * * History: * 01-02-91 DarrinM Created. \***************************************************************************/ #include "precomp.h" #pragma hdrstop /* * These defines are used for using the validation macros * StartValidateHandleMacro and EndValidateHandleMacro. */ #define ClientSharedInfo() (&gSharedInfo) #define ServerInfo() (gpsi) #include "wow.h" #if DBG CRITSTACK gCritStack; #endif #ifdef USER_PERFORMANCE __int64 gCSTimeExclusiveWhenEntering; #endif /***************************************************************************\ * ValidateHwinsta * * Validate windowstation handle * * History: * 03-29-91 JimA Created. * 06-20-95 JimA Kernel-mode objects. \***************************************************************************/ NTSTATUS ValidateHwinsta( HWINSTA hwinsta, KPROCESSOR_MODE AccessMode, ACCESS_MASK amDesired, PWINDOWSTATION *ppwinsta) { NTSTATUS Status; Status = ObReferenceObjectByHandle(hwinsta, amDesired, *ExWindowStationObjectType, AccessMode, ppwinsta, NULL); if (!NT_SUCCESS(Status)) { RIPNTERR1(Status, RIP_WARNING, "ValidateHwinsta failed for 0x%p", hwinsta); } else if ((*ppwinsta)->dwSessionId != gSessionId) { RIPNTERR3(STATUS_INVALID_HANDLE, RIP_WARNING, "SessionId %d does not match id %d for pwinsta 0x%p", gSessionId, (*ppwinsta)->dwSessionId, *ppwinsta); ObDereferenceObject(*ppwinsta); return STATUS_INVALID_HANDLE; } return Status; } /***************************************************************************\ * ValidateHdesk * * Validate desktop handle * * History: * 03-29-91 JimA Created. * 06-20-95 JimA Kernel-mode objects. \***************************************************************************/ NTSTATUS ValidateHdesk( HDESK hdesk, KPROCESSOR_MODE AccessMode, ACCESS_MASK amDesired, PDESKTOP* ppdesk) { NTSTATUS Status; Status = ObReferenceObjectByHandle(hdesk, amDesired, *ExDesktopObjectType, AccessMode, ppdesk, NULL); if (NT_SUCCESS(Status)) { if ((*ppdesk)->dwSessionId != gSessionId) { RIPNTERR3(STATUS_INVALID_HANDLE, RIP_WARNING, "SessionId %d does not match id %d for pdesk 0x%p", gSessionId, (*ppdesk)->dwSessionId, *ppdesk); goto Error; } LogDesktop(*ppdesk, LDL_VALIDATE_HDESK, TRUE, (ULONG_PTR)PtiCurrent()); if ((*ppdesk)->dwDTFlags & (DF_DESTROYED | DF_DESKWNDDESTROYED | DF_DYING)) { RIPNTERR1(STATUS_INVALID_HANDLE, RIP_WARNING, "ValidateHdesk: destroyed desktop 0x%p", *ppdesk); Error: ObDereferenceObject(*ppdesk); return STATUS_INVALID_HANDLE; } } else { RIPNTERR1(Status, RIP_WARNING, "ValidateHdesk failed for 0x%p", hdesk); } return Status; } /***************************************************************************\ * UserValidateCopyRgn * * Validates a region-handle. This essentially tries to copy the region * in order to verify the region is valid. If hrgn isn't a valid region, * then the combine will fail. We return a copy of the region. * * History: * 24=Jan-1996 ChrisWil Created. \***************************************************************************/ HRGN UserValidateCopyRgn( HRGN hrgn) { HRGN hrgnCopy = NULL; if (hrgn && (GreValidateServerHandle(hrgn, RGN_TYPE))) { hrgnCopy = CreateEmptyRgn(); if (CopyRgn(hrgnCopy, hrgn) == ERROR) { GreDeleteObject(hrgnCopy); hrgnCopy = NULL; } } return hrgnCopy; } /***************************************************************************\ * ValidateHmenu * * Validate menu handle and open it. * * History: * 03-29-91 JimA Created. \***************************************************************************/ PMENU ValidateHmenu( HMENU hmenu) { PTHREADINFO pti = PtiCurrentShared(); PMENU pmenuRet; pmenuRet = (PMENU)HMValidateHandle(hmenu, TYPE_MENU); if (pmenuRet != NULL && pmenuRet->head.rpdesk != pti->rpdesk) { RIPERR1(ERROR_INVALID_MENU_HANDLE, RIP_WARNING, "Invalid menu handle 0x%p", hmenu); return NULL; } return pmenuRet; } /***************************************************************************\ * ValidateHmonitor * * Validate monitor handle and open it. * * History: * 03-29-91 JimA Created. \***************************************************************************/ PMONITOR ValidateHmonitor( HMONITOR hmonitor) { return (PMONITOR)HMValidateSharedHandle(hmonitor, TYPE_MONITOR); } /* * The handle validation routines should be optimized for time, not size, * since they get called so often. */ #pragma optimize("t", on) /***************************************************************************\ * IsHandleEntrySecure * * Validate a user handle for a restricted process bypassing the routine to * get the handle entry. * * History: * August 22, 97 CLupu Created. \***************************************************************************/ BOOL IsHandleEntrySecure( HANDLE h, PHE phe) { DWORD bCreateFlags; PPROCESSINFO ppiOwner; PPROCESSINFO ppiCurrent; PW32JOB pW32Job; DWORD ind; PULONG_PTR pgh; ppiCurrent = PpiCurrent(); if (ppiCurrent == NULL) { return TRUE; } UserAssert(ppiCurrent->pW32Job != NULL); UserAssert(ppiCurrent->W32PF_Flags & W32PF_RESTRICTED); /* * Get the process that owns the handle. */ bCreateFlags = gahti[phe->bType].bObjectCreateFlags; ppiOwner = NULL; if (bCreateFlags & OCF_PROCESSOWNED) { ppiOwner = (PPROCESSINFO)phe->pOwner; } else if (bCreateFlags & OCF_THREADOWNED) { PTHREADINFO pti = (PTHREADINFO)phe->pOwner; if (pti != NULL) { ppiOwner = pti->ppi; } } /* * If the owner is NULL then consider the handle secure. */ if (ppiOwner == NULL) { return FALSE; } /* * If the handle is owned by a process in the same job, then it's secure. */ if (ppiOwner->pW32Job == ppiCurrent->pW32Job) { return TRUE; } /* * The handle is not owned by the current process. */ pW32Job = ppiCurrent->pW32Job; if (pW32Job->pgh == NULL) { return FALSE; } pgh = pW32Job->pgh; UserAssert(pW32Job->ughCrt <= pW32Job->ughMax); for (ind = 0; ind < pW32Job->ughCrt; ind++) { if (*(pgh + ind) == (ULONG_PTR)h) { return TRUE; } } return FALSE; } /***************************************************************************\ * ValidateHandleSecure * * Validate a user handle for a restricted process. * * History: * July 29, 97 CLupu Created. \***************************************************************************/ BOOL ValidateHandleSecure( HANDLE h) { PVOID pobj; CheckCritInShared(); StartValidateHandleMacro(h) BeginTypeValidateHandleMacro(pobj, TYPE_GENERIC) if (IsHandleEntrySecure(h, phe)) { return TRUE; } EndTypeValidateHandleMacro EndValidateHandleMacro return FALSE; } /***************************************************************************\ * ValidateHwnd * * History: * 08-Feb-1991 mikeke \***************************************************************************/ PWND FASTCALL ValidateHwnd( HWND hwnd) { StartValidateHandleMacro(hwnd) /* * Now make sure the app is passing the right handle type for this * api. If the handle is TYPE_FREE, this'll catch it. */ if (phe->bType == TYPE_WINDOW) { PTHREADINFO pti = PtiCurrentShared(); PWND pwndRet = (PWND)phe->phead; /* * This test establishes that the window belongs to the current * 'desktop'.. The two exceptions are for the desktop-window of * the current desktop, which ends up belonging to another desktop, * and when pti->rpdesk is NULL. This last case happens for * initialization of TIF_SYSTEMTHREAD threads (ie. console windows). * IanJa doesn't know if we should be test TIF_CSRSSTHREAD here, but * JohnC thinks the whole test below is no longer required ??? LATER */ if (pwndRet != NULL) { if (phe->bFlags & HANDLEF_DESTROY) { RIPERR2(ERROR_INVALID_WINDOW_HANDLE, RIP_WARNING,"ValidateHwnd, hwnd %#p, pwnd %#p already destroyed\n", hwnd, pwndRet); return NULL; } if (GETPTI(pwndRet) == pti || ( (pwndRet->head.rpdesk == pti->rpdesk || (pti->TIF_flags & TIF_SYSTEMTHREAD) || // | TIF_CSRSSTHREAD I think GetDesktopView(pti->ppi, pwndRet->head.rpdesk) != NULL))) { if (IS_THREAD_RESTRICTED(pti, JOB_OBJECT_UILIMIT_HANDLES)) { /* * make sure this window belongs to this process */ if (!IsHandleEntrySecure(hwnd, phe)) { RIPERR1(ERROR_INVALID_WINDOW_HANDLE, RIP_WARNING, "ValidateHwnd: Invalid hwnd (%#p) for restricted process\n", hwnd); pwndRet = NULL; } } return pwndRet; } } } EndValidateHandleMacro RIPERR1(ERROR_INVALID_WINDOW_HANDLE, RIP_WARNING, "ValidateHwnd: Invalid hwnd (%#p)", hwnd); return NULL; } /* * Switch back to default optimization. */ #pragma optimize("", on) /******************************Public*Routine******************************\ * * UserCritSec routines * * Exposes an opaque interface to the user critical section for * the WNDOBJ code in GRE * * Exposed as functions because they aren't time critical and it * insulates GRE from rebuilding if the definitions of Enter/LeaveCrit change * * History: * Wed Sep 20 11:19:14 1995 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ #if DBG #define GetCallStack() \ { \ gCritStack.thread = PsGetCurrentThread(); \ gCritStack.nFrames = RtlWalkFrameChain(gCritStack.trace, \ MAX_STACK_CALLS, \ 0); \ } #define FlushCallStack() \ { \ gCritStack.thread = NULL; \ gCritStack.nFrames = 0; \ } #else #define GetCallStack() #define FlushCallStack() #endif // DBG VOID UserEnterUserCritSec( VOID) { EnterCrit(); } VOID UserLeaveUserCritSec( VOID) { LeaveCrit(); } #if DBG VOID UserAssertUserCritSecIn( VOID) { _AssertCritInShared(); } VOID UserAssertUserCritSecOut( VOID) { _AssertCritOut(); } #endif // DBG BOOL UserGetCurrentDesktopId( DWORD* pdwDesktopId) { PDESKTOP pdesktop; CheckCritIn(); /* * PtiCurrent()->rpdesk can be NULL (in the case of thread shutdown). */ pdesktop = PtiCurrent()->rpdesk; if (pdesktop != grpdeskRitInput) { RIPMSG0(RIP_WARNING, "UserGetCurrentDesktopId on wrong desktop pdesk\n"); return FALSE; } *pdwDesktopId = pdesktop->dwDesktopId; return TRUE; } #if 0 // // Temporary arrays used to track critsec frees // #define ARRAY_SIZE 20 #define LEAVE_TYPE 0xf00d0000 #define ENTER_TYPE 0x0000dead typedef struct _DEBUG_STASHCS { RTL_CRITICAL_SECTION Lock; DWORD Type; } DEBUG_STASHCS, *PDEBUG_STASHCS; DEBUG_STASHCS UserSrvArray[ARRAY_SIZE]; ULONG UserSrvIndex; VOID DumpArray( HANDLE hCurrentProcess, HANDLE hCurrentThread, DWORD dwCurrentPc, PNTSD_EXTENSION_APIS lpExtensionApis, LPSTR lpArgumentString, LPDWORD IndexAddress, LPDWORD ArrayAddress ) { PNTSD_OUTPUT_ROUTINE Print; PNTSD_GET_EXPRESSION EvalExpression; PNTSD_GET_SYMBOL GetSymbol; DWORD History; int InitialIndex; PDEBUG_STASHCS Array; BOOL b; PRTL_CRITICAL_SECTION CriticalSection; CHAR Symbol[64], Symbol2[64]; DWORD Displacement, Displacement2; int Position; LPSTR p; DBG_UNREFERENCED_PARAMETER(hCurrentThread); DBG_UNREFERENCED_PARAMETER(dwCurrentPc); Print = lpExtensionApis->lpOutputRoutine; EvalExpression = lpExtensionApis->lpGetExpressionRoutine; GetSymbol = lpExtensionApis->lpGetSymbolRoutine; p = lpArgumentString; History = 0; if (*p) { History = EvalExpression(p); } if (History == 0 || History >= ARRAY_SIZE) { History = 10; } // // Get the Current Index and the array. // b = ReadProcessMemory( hCurrentProcess, (LPVOID)IndexAddress, &InitialIndex, sizeof(InitialIndex), NULL ); if (!b) { return; } Array = RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(UserSrvArray)); if (!Array) { return; } b = ReadProcessMemory( hCurrentProcess, (LPVOID)ArrayAddress, Array, sizeof(UserSrvArray), NULL ); if (!b) { RtlFreeHeap(RtlProcessHeap(), 0, Array); return; } Position = 0; while (History) { InitialIndex--; if (InitialIndex < 0) { InitialIndex = ARRAY_SIZE - 1; } if (Array[InitialIndex].Type == LEAVE_TYPE) { (Print)("\n(%d) LEAVING Critical Section \n", Position); } else { (Print)("\n(%d) ENTERING Critical Section \n", Position); } CriticalSection = &Array[InitialIndex].Lock; if (CriticalSection->LockCount == -1) { (Print)("\tLockCount NOT LOCKED\n"); } else { (Print)("\tLockCount %ld\n", CriticalSection->LockCount); } (Print)("\tRecursionCount %ld\n", CriticalSection->RecursionCount); (Print)("\tOwningThread %lx\n", CriticalSection->OwningThread ); #if DBG (GetSymbol)(CriticalSection->OwnerBackTrace[0], Symbol, &Displacement); (GetSymbol)(CriticalSection->OwnerBackTrace[1], Symbol2, &Displacement2); (Print)("\tCalling Address %s+%lx\n", Symbol, Displacement); (Print)("\tCallers Caller %s+%lx\n", Symbol2, Displacement2); #endif // DBG Position--; History--; } RtlFreeHeap(RtlProcessHeap(), 0, Array); } VOID dsrv( HANDLE hCurrentProcess, HANDLE hCurrentThread, DWORD dwCurrentPc, PNTSD_EXTENSION_APIS lpExtensionApis, LPSTR lpArgumentString ) { DumpArray( hCurrentProcess, hCurrentThread, dwCurrentPc, lpExtensionApis, lpArgumentString, &UserSrvIndex, (LPDWORD)&UserSrvArray[0] ); } #endif // if 0 #if DBG /***************************************************************************\ * _EnterCrit * _LeaveCrit * * These are temporary routines that are used by USER.DLL until the critsect, * validation, mapping code is moved to the server-side stubs generated by * SMeans' Thank compiler. * * History: * 01-02-91 DarrinM Created. \***************************************************************************/ VOID _AssertCritIn( VOID) { UserAssert(gpresUser != NULL); UserAssert(ExIsResourceAcquiredExclusiveLite(gpresUser) == TRUE); } VOID _AssertDeviceInfoListCritIn( VOID) { UserAssert(gpresDeviceInfoList != NULL); UserAssert(ExIsResourceAcquiredExclusiveLite(gpresDeviceInfoList) == TRUE); } VOID _AssertCritInShared( VOID) { UserAssert(gpresUser != NULL); UserAssert( (ExIsResourceAcquiredExclusiveLite(gpresUser) == TRUE) || (ExIsResourceAcquiredSharedLite(gpresUser) == TRUE)); } VOID _AssertCritOut( VOID) { UserAssert(gpresUser != NULL); UserAssert(ExIsResourceAcquiredExclusiveLite(gpresUser) == FALSE); } VOID _AssertDeviceInfoListCritOut( VOID) { UserAssert(gpresDeviceInfoList != NULL); UserAssert(ExIsResourceAcquiredExclusiveLite(gpresDeviceInfoList) == FALSE); } /***************************************************************************\ * BeginAtomicCheck() * EndAtomicCheck() * * Routine that verify we never leave the critical section and that an * operation is truely atomic with the possiblity of other code being run * because we left the critical section * \***************************************************************************/ VOID BeginAtomicCheck( VOID) { gdwInAtomicOperation++; } VOID EndAtomicCheck( VOID) { UserAssert(gdwInAtomicOperation > 0); gdwInAtomicOperation--; } VOID BeginAtomicDeviceInfoListCheck( VOID) { gdwInAtomicDeviceInfoListOperation++; } VOID EndAtomicDeviceInfoListCheck( VOID) { UserAssert(gdwInAtomicDeviceInfoListOperation > 0); gdwInAtomicDeviceInfoListOperation--; } #define INCCRITSECCOUNT (gdwCritSecUseCount++) #define INCDEVICEINFOLISTCRITSECCOUNT (gdwDeviceInfoListCritSecUseCount++) #else // else DBG #define INCCRITSECCOUNT #define INCDEVICEINFOLISTCRITSECCOUNT #endif // endif DBG BOOL UserIsUserCritSecIn( VOID) { UserAssert(gpresUser != NULL); return ((ExIsResourceAcquiredExclusiveLite(gpresUser) == TRUE) || (ExIsResourceAcquiredSharedLite(gpresUser) == TRUE)); } #if DBG VOID CheckDevLockOut( VOID) { /* * gpDispInfo can be NULL if Win32UserInitialize fails before allocating * it. hDev is initialized later in InitVideo, after the critical * section has been released at least once, so we better check it too. */ if (gpDispInfo != NULL && gpDispInfo->hDev != NULL) { UserAssert(!GreIsDisplayLocked(gpDispInfo->hDev)); } } #else #define CheckDevLockOut() #endif VOID EnterCrit( VOID) { CheckCritOut(); CheckDeviceInfoListCritOut(); KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite(gpresUser, TRUE); CheckDevLockOut(); UserAssert(!ISATOMICCHECK()); UserAssert(gptiCurrent == NULL); gptiCurrent = ((PTHREADINFO)(W32GetCurrentThread())); INCCRITSECCOUNT; #if defined (USER_PERFORMANCE) { __int64 i64Frecv; *(LARGE_INTEGER*)(&gCSTimeExclusiveWhenEntering) = KeQueryPerformanceCounter((LARGE_INTEGER*)&i64Frecv); InterlockedIncrement(&gCSStatistics.cExclusive); } #endif // (USER_PERFORMANCE) GetCallStack(); } #if DBG VOID EnterDeviceInfoListCrit( VOID) { CheckDeviceInfoListCritOut(); KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite(gpresDeviceInfoList, TRUE); UserAssert(!ISATOMICDEVICEINFOLISTCHECK()); INCDEVICEINFOLISTCRITSECCOUNT; } #endif // DBG VOID EnterSharedCrit( VOID) { KeEnterCriticalRegion(); ExAcquireResourceSharedLite(gpresUser, TRUE); CheckDevLockOut(); UserAssert(!ISATOMICCHECK()); #if defined (USER_PERFORMANCE) InterlockedIncrement(&gCSStatistics.cShared); #endif // (USER_PERFORMANCE) INCCRITSECCOUNT; } VOID LeaveCrit( VOID) { INCCRITSECCOUNT; #if DBG UserAssert(!ISATOMICCHECK()); UserAssert(IsWinEventNotifyDeferredOK()); CheckDevLockOut(); FlushCallStack(); gptiCurrent = NULL; #endif // DBG #ifdef USER_PERFORMANCE /* * A non null gCSTimeExclusiveWhenEntering means the * critical section is owned exclusive */ if (gCSTimeExclusiveWhenEntering) { __int64 i64Temp, i64Frecv; *(LARGE_INTEGER*)(&i64Temp) = KeQueryPerformanceCounter((LARGE_INTEGER*)&i64Frecv); gCSStatistics.i64TimeExclusive += i64Temp - gCSTimeExclusiveWhenEntering; gCSTimeExclusiveWhenEntering = 0; } #endif // USER_PERFORMANCE ExReleaseResourceLite(gpresUser); KeLeaveCriticalRegion(); CheckCritOut(); } #if DBG VOID _LeaveDeviceInfoListCrit( VOID) { INCDEVICEINFOLISTCRITSECCOUNT; UserAssert(!ISATOMICDEVICEINFOLISTCHECK()); ExReleaseResourceLite(gpresDeviceInfoList); KeLeaveCriticalRegion(); CheckDeviceInfoListCritOut(); } #endif // DBG VOID ChangeAcquireResourceType( VOID) { #if DBG FlushCallStack(); CheckDevLockOut(); UserAssert(!ISATOMICCHECK()); #endif // DBG ExReleaseResourceLite(gpresUser); ExAcquireResourceExclusiveLite(gpresUser, TRUE); gptiCurrent = ((PTHREADINFO)(W32GetCurrentThread())); GetCallStack(); } #if DBG PTHREADINFO _ptiCrit( VOID) { UserAssert(gpresUser); UserAssert(ExIsResourceAcquiredExclusiveLite(gpresUser) == TRUE); UserAssert(gptiCurrent); UserAssert(gptiCurrent == ((PTHREADINFO)(W32GetCurrentThread()))); UserAssert(gptiCurrent); return gptiCurrent; } PTHREADINFO _ptiCritShared( VOID) { UserAssert(W32GetCurrentThread()); return ((PTHREADINFO)(W32GetCurrentThread())); } #undef KeUserModeCallback NTSTATUS _KeUserModeCallback ( IN ULONG ApiNumber, IN PVOID InputBuffer, IN ULONG InputLength, OUT PVOID *OutputBuffer, OUT PULONG OutputLength) { UserAssert(ExIsResourceAcquiredExclusiveLite(gpresUser) == FALSE); /* * Added this so we can detect an erroneous user mode callback * with a checked win32k on top of a free system. */ UserAssert(PsGetCurrentThreadPreviousMode() == UserMode); return KeUserModeCallback(ApiNumber, InputBuffer, InputLength, OutputBuffer, OutputLength); } #endif // DBG