Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

902 lines
24 KiB

/****************************** 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