|
|
/****************************** Module Header ******************************\
* Module Name: handtabl.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Implements the USER handle table. * * 01-13-92 ScottLu Created. \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
#pragma alloc_text(INIT, HMInitHandleTable)
#if DBG
#define HTIENTRY(szObjectType, structName, fnDestroy, dwAllocTag, bObjectCreateFlags) \
{szObjectType, sizeof(structName), (FnDestroyUserObject)fnDestroy, (CONST DWORD)dwAllocTag, (CONST BYTE)(bObjectCreateFlags)}
#define HTIENTRY_VARIABLESIZE(szObjectType, dwSize, fnDestroy, dwAllocTag, bObjectCreateFlags) \
{szObjectType, dwSize, (FnDestroyUserObject)fnDestroy, (CONST DWORD)dwAllocTag, (CONST BYTE)(bObjectCreateFlags)}
#else // DBG
#define HTIENTRY(szObjectType, structName, fnDestroy, dwAllocTag, bObjectCreateFlags) \
{(FnDestroyUserObject)fnDestroy, (CONST DWORD)dwAllocTag, (CONST BYTE)(bObjectCreateFlags)}
#define HTIENTRY_VARIABLESIZE(szObjectType, dwSize, fnDestroy, dwAllocTag, bObjectCreateFlags) \
{(FnDestroyUserObject)fnDestroy, (CONST DWORD)dwAllocTag, (CONST BYTE)(bObjectCreateFlags)}
#endif // DBG
VOID HMNullFnDestroy( PVOID pobj) { RIPMSG1(RIP_WARNING, "HM: No clean up function for 0x%p", pobj); HMDestroyObject(pobj); }
/***************************************************************************\
* * Table of user objects statistics. Used by userkdx.dumhmgr debugger extension * \***************************************************************************/
#if DBG
PERFHANDLEINFO gaPerfhti[TYPE_CTYPES]; /* stores current counts */ PERFHANDLEINFO gaPrevhti[TYPE_CTYPES]; /* stores previous counts */ #endif // DBG
#if DBG || FRE_LOCK_RECORD
DWORD gdwLockRecordFlags;
BOOL RecordLockThisType( PVOID pobj) { BOOL bRecord; PHE phe = HMPheFromObject(pobj);
bRecord = (gdwLockRecordFlags & (1 << (phe->bType)));
return bRecord; } #endif
/***************************************************************************\
* * Table of handle type information. * * Desktop and Shared Heap objects can't be tagged as yet * (TAG_WINDOW is bogus for heap windows, but not for desktop and other * windows allocated in pool). * * WARNING: Keep it in sync with aszTypeNames table from ntuser\kdexts\userexts.c * * All HM objects must start with a HEAD structure. In addition: * (If you find these comments to be wrong, please fix them) * * OCF_PROCESSOWNED: Object must start with a PROC*HEAD structure * A ptiOwner must be provided * The object affects the handle quota (ppi->UserHandleCount) * The object will be destroyed if the process goes away. * * OCF_MARKPROCESS: Object must start with a PROCMARKHEAD structure * A ptiOwner must be provided * It must not use OCF_DESKTOPHEAP (implementation limitation) * * OCF_THREADOWNED: Object must start with a THR*HEAD structure * The object affects the handle quota (ppi->UserHandleCount) * The object will be destroyed if the thread goes away. * * OCF_DESKTOPHEAP: Object must start with a *DESKHEAD structure * A pdeskSrc must be provided at allocation time * It must not use OCF_MARKPROCESS (implementation limitation) * \***************************************************************************/
#if (TYPE_FREE != 0)
#error TYPE_FREE must be zero.
#endif
CONST HANDLETYPEINFO gahti[TYPE_CTYPES] = { /* TYPE_FREE - HEAD */ HTIENTRY("Free", HEAD, NULL, 0, 0),
/* TYPE_WINDOW - WND(THRDESKHEAD) */ HTIENTRY("Window", WND, xxxDestroyWindow, TAG_WINDOW, OCF_THREADOWNED | OCF_USEPOOLQUOTA | OCF_DESKTOPHEAP | OCF_USEPOOLIFNODESKTOP | OCF_VARIABLESIZE),
/* TYPE_MENU - MENU(PROCDESKHEAD) */ HTIENTRY("Menu", MENU, _DestroyMenu, 0, OCF_PROCESSOWNED | OCF_DESKTOPHEAP),
/* TYPE_CURSOR - CURSOR(PROCMARKHEAD) or ACON(PROCMARKHEAD) */ HTIENTRY("Icon/Cursor", CURSOR, DestroyUnlockedCursor, TAG_CURSOR, OCF_PROCESSOWNED | OCF_MARKPROCESS | OCF_USEPOOLQUOTA),
/* TYPE_SETWINDOWPOS - SMWP(HEAD) */ HTIENTRY("WPI(SWP) structure", SMWP, DestroySMWP, TAG_SWP, OCF_THREADOWNED | OCF_USEPOOLQUOTA),
/* TYPE_HOOK - HOOK(THRDESKHEAD) */ HTIENTRY("Hook", HOOK, FreeHook, 0, OCF_THREADOWNED | OCF_DESKTOPHEAP),
/* TYPE_CLIPDATA - CLIPDATA(HEAD) */ HTIENTRY("Clipboard Data", CLIPDATA, HMNullFnDestroy, TAG_CLIPBOARD, OCF_VARIABLESIZE),
/* TYPE_CALLPROC - CALLPROCDATA(THRDESKHEAD) */ HTIENTRY("CallProcData", CALLPROCDATA, HMDestroyObject, 0, OCF_PROCESSOWNED | OCF_DESKTOPHEAP),
/* TYPE_ACCELTABLE - ACCELTABLE(PROCOBJHEAD) */ HTIENTRY("Accelerator", ACCELTABLE, HMDestroyObject, TAG_ACCEL, OCF_PROCESSOWNED | OCF_USEPOOLQUOTA | OCF_VARIABLESIZE),
/* TYPE_DDEACCESS - SVR_INSTANCE_INFO(THROBJHEAD) */ HTIENTRY("DDE access", SVR_INSTANCE_INFO, HMNullFnDestroy, TAG_DDE9, OCF_THREADOWNED | OCF_USEPOOLQUOTA),
/* TYPE_DDECONV - DDECONV(THROBJHEAD) */ HTIENTRY("DDE conv", DDECONV, FreeDdeConv, TAG_DDEa, OCF_THREADOWNED | OCF_USEPOOLQUOTA),
/* TYPE_DDEXACT - XSTATE(THROBJHEAD) */ HTIENTRY("DDE Transaction", XSTATE, FreeDdeXact, TAG_DDEb, OCF_THREADOWNED | OCF_USEPOOLQUOTA),
/* TYPE_MONITOR - MONITOR(HEAD) */ HTIENTRY("Monitor", MONITOR, DestroyMonitor, TAG_DISPLAYINFO, OCF_SHAREDHEAP),
/* TYPE_KBDLAYOUT - KL(HEAD) */ HTIENTRY("Keyboard Layout", KL, DestroyKL, TAG_KBDLAYOUT, 0),
/* TYPE_KBDFILE - KBDFILE(HEAD) */ HTIENTRY("Keyboard File", KBDFILE, DestroyKF, TAG_KBDFILE, 0),
/* TYPE_WINEVENTHOOK - EVENTHOOK(THROBJHEAD) */ HTIENTRY("WinEvent hook", EVENTHOOK, DestroyEventHook, TAG_WINEVENT, OCF_THREADOWNED),
/* TYPE_TIMER - TIMER(HEAD) */ HTIENTRY("Timer", TIMER, FreeTimer, TAG_TIMER, 0),
/* TYPE_INPUTCONTEXT - IMC(THRDESKHEAD) */ HTIENTRY("Input Context", IMC, FreeInputContext, TAG_IME, OCF_THREADOWNED | OCF_DESKTOPHEAP),
#ifdef GENERIC_INPUT
/* TYPE_HIDDATA - HIDDATA(THROBJHEAD) */ HTIENTRY_VARIABLESIZE("HID Raw Data", FIELD_OFFSET(HIDDATA, rid.data.hid.bRawData), FreeHidData, TAG_HIDDATA, OCF_THREADOWNED | OCF_VARIABLESIZE),
/* TYPE_DEVICEINFO - DEVICEINFO(HEAD) */ HTIENTRY("Device Info", GENERIC_DEVICE_INFO, FreeDeviceInfo, TAG_DEVICEINFO, OCF_VARIABLESIZE),
#endif // GENERIC_INPUT
};
/*
* Handle table allocation globals. The purpose of keeping per-page free * lists is to keep the table as small as is practical and to minimize * the number of pages touched while performing handle table operations. */ #define CPAGEENTRIESINIT 4
DWORD gcHandlePages; PHANDLEPAGE gpHandlePages;
#if DBG || FRE_LOCK_RECORD
PPAGED_LOOKASIDE_LIST LockRecordLookaside;
NTSTATUS InitLockRecordLookaside(VOID); VOID FreeLockRecord(PLR plr); VOID InitGlobalThreadLockArray(DWORD dwIndex); VOID ShowLocks(PHE); #endif
VOID HMDestroyUnlockedObject(PHE phe); VOID HMRecordLock(PVOID ppobj, PVOID pobj, DWORD cLockObj); BOOL HMUnrecordLock(PVOID ppobj, PVOID pobj);
/***************************************************************************\
* DBGValidateHandleQuota * * 11-19-97 GerardoB Created. \***************************************************************************/ #ifdef VALIDATEHANDLEQUOTA
VOID DBGValidateHandleQuota( VOID) { BYTE bCreateFlags; DWORD dw; HANDLEENTRY * phe;
PPROCESSINFO ppiT = gppiList;
while (ppiT != NULL) { ppiT->lHandles = 0; ppiT = ppiT->ppiNextRunning; }
phe = gSharedInfo.aheList; for (dw = 0; dw <= giheLast; dw++, phe++) { if (phe->bType == TYPE_FREE) { UserAssert(phe->pOwner == NULL); continue; } bCreateFlags = gahti[phe->bType].bObjectCreateFlags; if (bCreateFlags & OCF_PROCESSOWNED) { ((PPROCESSINFO)phe->pOwner)->lHandles++; continue; } if (bCreateFlags & OCF_THREADOWNED) { ((PTHREADINFO)phe->pOwner)->ppi->lHandles++; continue; } UserAssert(phe->pOwner == NULL); }
ppiT = gppiList; while (ppiT != NULL) { UserAssert(ppiT->lHandles == ppiT->UserHandleCount); ppiT = ppiT->ppiNextRunning; } } #else
#define DBGValidateHandleQuota()
#endif
/***************************************************************************\
* DBGHMPheFromObject * * Validates and returns the HANDLEENTRY corresponding to a given object * * 09-23-97 GerardoB Created. \***************************************************************************/ #if DBG
PHE DBGHMPheFromObject( PVOID p) { PHE phe = _HMPheFromObject(p);
UserAssert(phe->phead == p); UserAssert(_HMObjectFromHandle(phe->phead->h) == p); UserAssert(phe->wUniq == HMUniqFromHandle(phe->phead->h)); UserAssert(phe->bType < TYPE_CTYPES); UserAssert((phe->pOwner != NULL) || !(gahti[phe->bType].bObjectCreateFlags & (OCF_PROCESSOWNED | OCF_THREADOWNED))); UserAssert(!(phe->bFlags & ~HANDLEF_VALID));
return phe; } #endif
/***************************************************************************\
* DBGHMPheFromObject * * Validates and returns the object corresponding to a given handle. * * 09-23-97 GerardoB Created. \***************************************************************************/ #if DBG
PVOID DBGHMObjectFromHandle( HANDLE h) { PVOID p = _HMObjectFromHandle(h);
UserAssert((h != NULL) ^ (p == NULL)); if (p != NULL) { UserAssert(HMIndexFromHandle(((PHEAD)p)->h) == HMIndexFromHandle(h)); UserAssert(p == HMRevalidateCatHandle(h));
/*
* This routine, unlike Validation, should return a real pointer if * the object exists, even if it is destroyed. But we should still * generate a warning. */ if (HMPheFromObject(p)->bFlags & HANDLEF_DESTROY) { RIPMSGF1(RIP_WARNING, "Object p 0x%p is destroyed", p); } }
return p; }
PVOID DBGHMCatObjectFromHandle( HANDLE h) { /*
* Note -- at this point, _HMObjectFromHandle does not check to see if * an object is destroyed. */ PVOID p = _HMObjectFromHandle(h);
UserAssert((h != NULL) ^ (p == NULL)); if (p != NULL) { UserAssert(HMIndexFromHandle(((PHEAD)p)->h) == HMIndexFromHandle(h)); UserAssert(p == HMRevalidateCatHandle(h)); }
return p; } #endif
/***************************************************************************\
* DBGPtoH and DBGPtoHq * * Validates and returns the handle corresponding to a given object * * 09-23-97 GerardoB Created. \***************************************************************************/ #if DBG
VOID DBGValidatePtoH( PVOID p, HANDLE h) { UserAssert((h != NULL) ^ (p == NULL)); if (h != NULL) { UserAssert(p == HMRevalidateCatHandle(h)); } }
HANDLE DBGPtoH (PVOID p) { HANDLE h = _PtoH(p);
DBGValidatePtoH(p, h);
return h; }
HANDLE DBGPtoHq (PVOID p) { HANDLE h;
UserAssert(p != NULL);
h = _PtoHq(p); DBGValidatePtoH(p, h);
return h; } #endif
/***************************************************************************\
* DBGHW and DBGHWq * * Validates and returns the hwnd corresponding to a given pwnd. * * 09-23-97 GerardoB Created. \***************************************************************************/ #if DBG
VOID DBGValidateHW( PWND pwnd, HWND hwnd) { UserAssert((hwnd != NULL) ^ (pwnd == NULL));
if (hwnd != NULL) { UserAssert(pwnd == HMValidateCatHandleNoSecure(hwnd, TYPE_WINDOW)); } }
PVOID DBGValidateHWCCX( PWND ccxPwnd, HWND hwnd, PTHREADINFO pti) { PVOID pobj = NULL;
UserAssert((hwnd != NULL) ^ (ccxPwnd == NULL)); if (hwnd != NULL) { pobj = HMValidateCatHandleNoSecureCCX(hwnd, TYPE_WINDOW, pti); UserAssert(ccxPwnd == pobj); }
return pobj; }
HWND DBGHW( PWND pwnd) { HWND hwnd = _HW(pwnd);
DBGValidateHW(pwnd, hwnd);
return hwnd; }
HWND DBGHWCCX( PWND ccxPwnd) { HWND hwnd = _HWCCX(ccxPwnd); PWND pwndK = RevalidateHwnd(hwnd); PTHREADINFO pti = _GETPTI(pwndK); PWND pwnd = NULL;
CheckCritIn();
if (pwndK) { pwnd = (PWND) DBGValidateHWCCX(ccxPwnd, hwnd, pti); }
if (pwnd == ccxPwnd) { if (!KeIsAttachedProcess()) { UserAssert(PpiCurrent() == _GETPTI(pwndK)->ppi); } }
return hwnd; }
HWND DBGHWq( PWND pwnd) { HWND hwnd;
UserAssert(pwnd != NULL); hwnd = _HWq(pwnd);
DBGValidateHW(pwnd, hwnd);
return hwnd; } #endif
/***************************************************************************\
* DBGHMValidateFreeLists * * Walks all handle free lists to make sure all links are fine. * * 10/08/97 GerardoB Created \***************************************************************************/ #if DBG
VOID DBGHMValidateFreeList( ULONG_PTR iheFreeNext, BOOL fEven) { PHE phe;
do { UserAssert(fEven ^ !!(iheFreeNext & 0x1)); UserAssert(iheFreeNext < gpsi->cHandleEntries); phe = &gSharedInfo.aheList[iheFreeNext]; UserAssert(phe->bType == TYPE_FREE); UserAssert(phe->pOwner == NULL); UserAssert(phe->bFlags == 0); iheFreeNext = (ULONG_PTR)phe->phead; } while (iheFreeNext != 0); }
VOID DBGHMValidateFreeLists( VOID) { DWORD dw; PHANDLEPAGE php = gpHandlePages;
for (dw = 0; dw < gcHandlePages; ++dw, ++php) { if (php->iheFreeEven != 0) { DBGHMValidateFreeList(php->iheFreeEven, TRUE); }
if (php->iheFreeOdd != 0) { DBGHMValidateFreeList(php->iheFreeOdd, FALSE); } } } #else
#define DBGHMValidateFreeLists()
#endif
#if DBG || FRE_LOCK_RECORD
/***************************************************************************\
* DbgDumpHandleTable * \***************************************************************************/ DWORD DbgDumpHandleTable( VOID) { DWORD dw; PHE phe; DWORD dwHandles = 0;
phe = gSharedInfo.aheList;
if (phe == NULL) { KdPrint(("\nTERMSRV\nEmpty handle table\n")); return 0; }
KdPrint(("\nTERMSRV\nDump the handle table\n")); KdPrint(("---------------------------------------------------\n")); KdPrint((" phead handle lock pOwner type flags\n")); KdPrint(("---------------------------------------------------\n"));
for (dw = 0; dw <= giheLast; dw++, phe++) { if (phe->bType == TYPE_FREE) { UserAssert(phe->pOwner == NULL); continue; }
KdPrint(("%04d %08x %08x %08d %08x %04x %05x\n", dwHandles++, phe->phead, phe->phead->h, phe->phead->cLockObj, phe->pOwner, phe->bType, phe->bFlags)); }
KdPrint(("----------------------------------------------\n")); KdPrint(("Number of handles left: %d\n", dwHandles)); KdPrint(("End of handle table\n"));
UserAssert(dwHandles == 0);
return dwHandles; }
/***************************************************************************\
* HMCleanUpHandleTable * \***************************************************************************/ VOID HMCleanUpHandleTable( VOID) { #if DBG
DbgDumpHandleTable(); #endif // DBG
if (LockRecordLookaside != NULL) { ExDeletePagedLookasideList(LockRecordLookaside); UserFreePool(LockRecordLookaside); } } #endif // DBG
/***************************************************************************\
* HMInitHandleEntries * * 10/10/97 GerardoB Extracted from HMInitHandleTable and HMGrowHandleTable \***************************************************************************/ VOID HMInitHandleEntries( ULONG_PTR iheFirstFree) { ULONG_PTR ihe; PHE pheT;
/*
* Zero out all the new entries */ RtlZeroMemory (&gSharedInfo.aheList[iheFirstFree], (gpsi->cHandleEntries - iheFirstFree) * sizeof(HANDLEENTRY)); /*
* Link them together. * Each free odd/even entry points to the next odd/even free entry. */ ihe = iheFirstFree; for (pheT = &gSharedInfo.aheList[ihe]; ihe < gpsi->cHandleEntries; ihe++, pheT++) { pheT->phead = (PHEAD)(ihe + 2); pheT->wUniq = 1; }
/*
* Terminate the lists. */ if (gpsi->cHandleEntries > iheFirstFree) { UserAssert(pheT - 1 >= &gSharedInfo.aheList[iheFirstFree]); (pheT - 1)->phead = NULL; } if (gpsi->cHandleEntries > iheFirstFree + 1) { UserAssert(pheT - 2 >= &gSharedInfo.aheList[iheFirstFree]); (pheT - 2)->phead = NULL; }
/*
* Let's check that we got it right */ DBGHMValidateFreeLists(); }
/***************************************************************************\
* HMInitHandleTable * * Initialize the handle table. Unused entries are linked together. * * 01-13-92 ScottLu Created. \***************************************************************************/ BOOL HMInitHandleTable( PVOID pReadOnlySharedSectionBase) { NTSTATUS Status; SIZE_T ulCommit;
/*
* Allocate the handle page array. Make it big enough for 4 pages, which * should be sufficient for nearly all instances. */ gpHandlePages = UserAllocPool(CPAGEENTRIESINIT * sizeof(HANDLEPAGE), TAG_SYSTEM); if (gpHandlePages == NULL) { return FALSE; }
#if DBG || FRE_LOCK_RECORD
if (!NT_SUCCESS(InitLockRecordLookaside())) return FALSE; #endif
/*
* Allocate the array. We have the space from * NtCurrentPeb()->ReadOnlySharedMemoryBase to * NtCurrentPeb()->ReadOnlySharedMemoryHeap reserved for * the handle table. All we need to do is commit the pages. * * Compute the minimum size of the table. The allocation will * round this up to the next page size. */ ulCommit = gpsi->cbHandleTable = PAGE_SIZE; Status = CommitReadOnlyMemory(ghSectionShared, &ulCommit, 0, NULL); if (!NT_SUCCESS(Status)) { return FALSE; }
gSharedInfo.aheList = pReadOnlySharedSectionBase; gpsi->cHandleEntries = gpsi->cbHandleTable / sizeof(HANDLEENTRY); gcHandlePages = 1;
/*
* Initialize the handlepage info. Handle 0 is reserved so even free * list starts at 2. */ gpHandlePages[0].iheFreeOdd = 1; gpHandlePages[0].iheFreeEven = 2; gpHandlePages[0].iheLimit = gpsi->cHandleEntries;
/*
* Initialize the handle entries. */ HMInitHandleEntries(0);
/*
* PW(NULL) (ie, handle 0) must map to a NULL pointer. */ gSharedInfo.aheList[0].phead = NULL; UserAssert(gSharedInfo.aheList[0].bType == TYPE_FREE); UserAssert(gSharedInfo.aheList[0].wUniq == 1);
#if DBG
/*
* Make sure we don't need to add the special case to handle HMINDEXBITS * in this function. */ UserAssert(gpsi->cHandleEntries <= HMINDEXBITS);
/*
* PDESKOBJHEAD won't do the right casting unless these structs have the * same size. */ UserAssert(sizeof(THROBJHEAD) == sizeof(PROCOBJHEAD)); UserAssert(sizeof(THRDESKHEAD) == sizeof(PROCDESKHEAD)); UserAssert(sizeof(THRDESKHEAD) == sizeof(DESKOBJHEAD));
/*
* Validate type flags to make sure that assumptions made throughout HM * code are OK. */ { PHANDLETYPEINFO pahti = (PHANDLETYPEINFO)gahti; UINT uTypes = TYPE_CTYPES; BYTE bObjectCreateFlags; while (uTypes-- != 0) { bObjectCreateFlags = pahti->bObjectCreateFlags; /*
* Illegal flag combinations. */ UserAssert(!((bObjectCreateFlags & OCF_DESKTOPHEAP) && (bObjectCreateFlags & OCF_MARKPROCESS)));
/*
* Pointless (and probably illegal) flag combinations. */ UserAssert(!((bObjectCreateFlags & OCF_DESKTOPHEAP) && (bObjectCreateFlags & OCF_SHAREDHEAP))); UserAssert(!((bObjectCreateFlags & OCF_USEPOOLQUOTA) && (bObjectCreateFlags & OCF_SHAREDHEAP))); UserAssert(!((bObjectCreateFlags & OCF_THREADOWNED) && (bObjectCreateFlags & OCF_PROCESSOWNED))); UserAssert(!(bObjectCreateFlags & OCF_USEPOOLQUOTA) || !(bObjectCreateFlags & OCF_DESKTOPHEAP) || (bObjectCreateFlags & OCF_USEPOOLIFNODESKTOP));
/*
* Required flag combinations. */ UserAssert(!(bObjectCreateFlags & OCF_DESKTOPHEAP) || (bObjectCreateFlags & (OCF_PROCESSOWNED | OCF_THREADOWNED)));
UserAssert(!(bObjectCreateFlags & OCF_MARKPROCESS) || (bObjectCreateFlags & OCF_PROCESSOWNED));
UserAssert(!(bObjectCreateFlags & OCF_USEPOOLIFNODESKTOP) || (bObjectCreateFlags & OCF_DESKTOPHEAP));
pahti++; } } #endif
return TRUE; }
/***************************************************************************\
* HMGrowHandleTable * * Grows the handle table. Assumes the handle table already exists. * * 01-13-92 ScottLu Created. \***************************************************************************/ BOOL HMGrowHandleTable( VOID) { ULONG_PTR i, iheFirstFree; PHE pheT; PVOID p; PHANDLEPAGE phpNew; DWORD dwCommitOffset; SIZE_T ulCommit; NTSTATUS Status;
/*
* If we've run out of handle space, fail. */ i = gpsi->cHandleEntries; if (i & ~HMINDEXBITS) { return FALSE; }
/*
* Grow the page table if need be. */ i = gcHandlePages + 1; if (i > CPAGEENTRIESINIT) { DWORD dwSize = gcHandlePages * sizeof(HANDLEPAGE);
phpNew = UserReAllocPool(gpHandlePages, dwSize, dwSize + sizeof(HANDLEPAGE), TAG_SYSTEM); if (phpNew == NULL) { return FALSE; }
gpHandlePages = phpNew; }
/*
* Commit some more pages to the table. First find the * address where the commitment needs to be. */ p = (PBYTE)gSharedInfo.aheList + gpsi->cbHandleTable; if (p >= Win32HeapGetHandle(gpvSharedAlloc)) { return FALSE; }
dwCommitOffset = (ULONG)((PBYTE)p - (PBYTE)gpvSharedBase);
ulCommit = PAGE_SIZE;
Status = CommitReadOnlyMemory(ghSectionShared, &ulCommit, dwCommitOffset, NULL); if (!NT_SUCCESS(Status)) { return FALSE; }
phpNew = &gpHandlePages[gcHandlePages++];
/*
* Update the global information to include the new * page. */ iheFirstFree = gpsi->cHandleEntries; if (gpsi->cHandleEntries & 0x1) { phpNew->iheFreeOdd = gpsi->cHandleEntries; phpNew->iheFreeEven = gpsi->cHandleEntries + 1; } else { phpNew->iheFreeEven = gpsi->cHandleEntries; phpNew->iheFreeOdd = gpsi->cHandleEntries + 1; } gpsi->cbHandleTable += PAGE_SIZE;
/*
* Check for handle overflow. */ gpsi->cHandleEntries = gpsi->cbHandleTable / sizeof(HANDLEENTRY); if (gpsi->cHandleEntries & ~HMINDEXBITS) { gpsi->cHandleEntries = (HMINDEXBITS + 1); }
phpNew->iheLimit = gpsi->cHandleEntries; if (phpNew->iheFreeEven >= phpNew->iheLimit) { phpNew->iheFreeEven = 0; } if (phpNew->iheFreeOdd >= phpNew->iheLimit) { phpNew->iheFreeOdd = 0; }
HMInitHandleEntries(iheFirstFree);
/*
* HMINDEXBITS has a special meaning. We used to handle this in HMAllocObject. * Now we handle it here right after adding that handle to the table. * Old Comment: * Reserve this table entry so that PW(HMINDEXBITS) maps to a * NULL pointer. Set it to TYPE_FREE so the cleanup code doesn't think * it is allocated. Set wUniq to 1 so that RevalidateHandles on HMINDEXBITS * will fail. */ if ((gpsi->cHandleEntries > HMINDEXBITS) && (phpNew->iheFreeOdd != 0) && (phpNew->iheFreeOdd <= HMINDEXBITS)) {
pheT = &gSharedInfo.aheList[HMINDEXBITS]; if (phpNew->iheFreeOdd == HMINDEXBITS) { phpNew->iheFreeOdd = (ULONG_PTR)pheT->phead; } else { UserAssert(pheT - 2 >= &gSharedInfo.aheList[iheFirstFree]); UserAssert((pheT - 2)->phead == (PVOID)HMINDEXBITS); (pheT - 2)->phead = pheT->phead; } pheT->phead = NULL; UserAssert(pheT->bType == TYPE_FREE); UserAssert(pheT->wUniq == 1); }
return TRUE; }
/***************************************************************************\
* HMAllocObject * * Allocs a non-secure object by allocating a handle and memory for * the object. * * 01-13-92 ScottLu Created. \***************************************************************************/
PVOID HMAllocObject( PTHREADINFO ptiOwner, PDESKTOP pdeskSrc, BYTE bType, DWORD size) { DWORD i; PHEAD phead; PHE pheT; ULONG_PTR iheFree, *piheFreeHead; PHANDLEPAGE php; BYTE bCreateFlags; PPROCESSINFO ppiQuotaCharge = NULL; BOOL fUsePoolIfNoDesktop; BOOL fEven; #if DBG
SIZE_T dwAllocSize; #endif
CheckCritIn(); bCreateFlags = gahti[bType].bObjectCreateFlags;
#if DBG
/*
* Validate size */ if (bCreateFlags & OCF_VARIABLESIZE) { UserAssert(gahti[bType].uSize <= size); } else { UserAssert(gahti[bType].uSize == size); } #endif
/*
* Check for process handle quota */ if (bCreateFlags & (OCF_PROCESSOWNED | OCF_THREADOWNED)) { UserAssert(ptiOwner != NULL); ppiQuotaCharge = ptiOwner->ppi; if (ppiQuotaCharge->UserHandleCount >= gUserProcessHandleQuota) { RIPERR0(ERROR_NO_MORE_USER_HANDLES, RIP_WARNING, "USER: HMAllocObject: out of handle quota"); return NULL; } }
/*
* Find the next free handle * Window handles must be even; hence we try first to use odd handles * for all other objects. * Old comment: * Some wow apps, like WinProj, require even Window handles so we'll * accomodate them; build a list of the odd handles so they won't get lost * 10/13/97: WinProj never fixed this; even the 32 bit version has the problem. */ fEven = (bType == TYPE_WINDOW); piheFreeHead = NULL; do { php = gpHandlePages; for (i = 0; i < gcHandlePages; ++i, ++php) { if (fEven) { if (php->iheFreeEven != 0) { piheFreeHead = &php->iheFreeEven; break; } } else { if (php->iheFreeOdd != 0) { piheFreeHead = &php->iheFreeOdd; break; } } } /* for */ /*
* If we couldn't find an odd handle, then search for an even one */ fEven = ((piheFreeHead == NULL) && !fEven); } while (fEven); /*
* If there are no free handles we can use, grow the table */ if (piheFreeHead == NULL) { HMGrowHandleTable(); /*
* If the table didn't grow, get out. */ if (i == gcHandlePages) { RIPMSG0(RIP_WARNING, "HMAllocObject: could not grow handle space"); return NULL; } /*
* Because the handle page table may have moved, * recalc the page entry pointer. */ php = &gpHandlePages[i]; piheFreeHead = (bType == TYPE_WINDOW ? &php->iheFreeEven : &php->iheFreeOdd); if (*piheFreeHead == 0) { UserAssert(gpsi->cHandleEntries == (HMINDEXBITS + 1)); RIPMSG0(RIP_WARNING, "HMAllocObject: handle table is full"); return NULL; } }
/*
* HMINDEXBITS is a reserved value that should never be in the free lists * (see HMGrowHandleTable()). */ UserAssert(HMIndexFromHandle(*piheFreeHead) != HMINDEXBITS);
/*
* Try to allocate the object. If this fails, bail out. */ if ((bCreateFlags & OCF_DESKTOPHEAP) && pdeskSrc) { phead = (PHEAD)DesktopAlloc(pdeskSrc, size, MAKELONG(DTAG_HANDTABL, bType)); if (phead) { LockDesktop(&((PDESKOBJHEAD)phead)->rpdesk, pdeskSrc, LDL_OBJ_DESK, (ULONG_PTR)phead); ((PDESKOBJHEAD)phead)->pSelf = (PBYTE)phead; #if DBG
dwAllocSize = Win32HeapSize(pdeskSrc->pheapDesktop, phead); #endif
} } else if (bCreateFlags & OCF_SHAREDHEAP) { UserAssert(!pdeskSrc); phead = (PHEAD)SharedAlloc(size); #if DBG
if (phead) { dwAllocSize = Win32HeapSize(gpvSharedAlloc, phead); } #endif
} else { fUsePoolIfNoDesktop = !pdeskSrc && (bCreateFlags & OCF_USEPOOLIFNODESKTOP); UserAssert(!(bCreateFlags & OCF_DESKTOPHEAP) || fUsePoolIfNoDesktop);
if ((bCreateFlags & OCF_USEPOOLQUOTA) && !fUsePoolIfNoDesktop) { phead = (PHEAD)UserAllocPoolWithQuotaZInit(size, gahti[bType].dwAllocTag); } else { phead = (PHEAD)UserAllocPoolZInit(size, gahti[bType].dwAllocTag); }
#if DBG
if (phead) { dwAllocSize = Win32QueryPoolSize(phead); } #endif
}
if (phead == NULL) { RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_WARNING, "USER: HMAllocObject: out of memory"); return NULL; }
/*
* We're going to use this handle so get it off its free list. * The free handle phead points to the next free handle. */ iheFree = *piheFreeHead; pheT = &gSharedInfo.aheList[iheFree]; *piheFreeHead = (ULONG_PTR)pheT->phead; DBGHMValidateFreeLists();
/*
* Track high water mark for handle allocation. */ if ((DWORD)iheFree > giheLast) { giheLast = (DWORD)iheFree; }
/*
* Setup the handle contents, plus initialize the object header. */ pheT->bType = bType; pheT->phead = phead; UserAssert(pheT->bFlags == 0); if (bCreateFlags & OCF_PROCESSOWNED) { if ((ptiOwner->TIF_flags & TIF_16BIT) && (ptiOwner->ptdb)) { ((PPROCOBJHEAD)phead)->hTaskWow = ptiOwner->ptdb->hTaskWow; } else { ((PPROCOBJHEAD)phead)->hTaskWow = 0; } pheT->pOwner = ptiOwner->ppi; if (bCreateFlags & OCF_MARKPROCESS) { ((PPROCMARKHEAD)phead)->ppi = ptiOwner->ppi; } } else if (bCreateFlags & OCF_THREADOWNED) { ((PTHROBJHEAD)phead)->pti = pheT->pOwner = ptiOwner; } else { /*
* The caller is wasting time if ptiOwner != NULL * The handle entry must already have pOwner == NULL. */ UserAssert(ptiOwner == NULL); UserAssert(pheT->pOwner == NULL); }
phead->h = HMHandleFromIndex(iheFree);
if (ppiQuotaCharge) { ppiQuotaCharge->UserHandleCount++; DBGValidateHandleQuota(); }
#if DBG
/*
* Performance counters. */
gaPerfhti[bType].lTotalCount++; gaPerfhti[bType].lCount++; if (gaPerfhti[bType].lCount > gaPerfhti[bType].lMaxCount) { gaPerfhti[bType].lMaxCount = gaPerfhti[bType].lCount; }
gaPerfhti[bType].lSize += dwAllocSize;
#endif // DBG
/*
* Return a handle entry pointer. */ return pheT->phead; }
/***************************************************************************\
* HMFreeObject * * Frees an object - the handle and the referenced memory. * * 01-13-92 ScottLu Created. \***************************************************************************/ BOOL HMFreeObject( PVOID pobj) { PHE pheT; WORD wUniqT; PHANDLEPAGE php; DWORD i; ULONG_PTR iheCurrent, *piheCurrentHead; BYTE bCreateFlags; PDESKTOP pdesk; PPROCESSINFO ppiQuotaCharge = NULL; #if DBG || FRE_LOCK_RECORD
PLR plrT, plrNextT; #endif
UserAssert(((PHEAD)pobj)->cLockObj == 0); UserAssert(pobj == HtoPqCat(PtoHq(pobj))); /*
* Free the object first. */ pheT = HMPheFromObject(pobj); bCreateFlags = gahti[pheT->bType].bObjectCreateFlags;
UserAssertMsg1(pheT->bType != TYPE_FREE, "Object already marked as freed! %#p", pobj);
/*
* Adjust the process handle usage. */ if (bCreateFlags & OCF_PROCESSOWNED) { ppiQuotaCharge = (PPROCESSINFO)pheT->pOwner; UserAssert(ppiQuotaCharge != NULL); } else if (bCreateFlags & OCF_THREADOWNED) { ppiQuotaCharge = (PPROCESSINFO)(((PTHREADINFO)(pheT->pOwner))->ppi); UserAssert(ppiQuotaCharge != NULL); } else { ppiQuotaCharge = NULL; }
if (ppiQuotaCharge != NULL) { ppiQuotaCharge->UserHandleCount--; }
if (pheT->bFlags & HANDLEF_GRANTED) { HMCleanupGrantedHandle(pheT->phead->h); pheT->bFlags &= ~HANDLEF_GRANTED; }
#if DBG
/*
* Performance counters. */ gaPerfhti[pheT->bType].lCount--;
if ((pheT->bFlags & HANDLEF_POOL) == 0 && (bCreateFlags & OCF_DESKTOPHEAP) && ((PDESKOBJHEAD)pobj)->rpdesk) { pdesk = ((PDESKOBJHEAD)pobj)->rpdesk; gaPerfhti[pheT->bType].lSize -= Win32HeapSize(pdesk->pheapDesktop, pobj); } else if ((pheT->bFlags & HANDLEF_POOL) == 0 && bCreateFlags & OCF_SHAREDHEAP) { gaPerfhti[pheT->bType].lSize -= Win32HeapSize(gpvSharedAlloc, pobj); } else { gaPerfhti[pheT->bType].lSize -= Win32QueryPoolSize(pobj); }
#endif // DBG
if ((bCreateFlags & OCF_DESKTOPHEAP)) { #if DBG
BOOL bSuccess; #endif
if (!(pheT->bFlags & HANDLEF_POOL)) { UserAssert(((PDESKOBJHEAD)pobj)->rpdesk != NULL); }
/*
* pobj->rpdesk is cached and the object is freed after which the * reference count on the desktop is decremented. This is done in * this order such that if this is the last reference on the desktop * the desktop heap is not destroyed before we free the object. */ pdesk = ((PDESKOBJHEAD)pobj)->rpdesk; ((PDESKOBJHEAD)pobj)->rpdesk = NULL;
if (pheT->bFlags & HANDLEF_POOL) { UserFreePool(pobj); } else {
#if DBG
bSuccess = #endif
DesktopFree(pdesk, pobj); #if DBG
if (!bSuccess) { /*
* We would hit this assert in HYDRA trying to free the * mother desktop window which was allocated out of pool */ RIPMSG1(RIP_ERROR, "Object already freed from desktop heap! %#p", pobj); } #endif
}
/*
* NOTE: Using pobj after freeing the object is not a problem because * UnlockDesktop uses the value for tracking and doesn't dereference * the pointer. If this ever changes we'll get a BC. */ UnlockDesktop(&pdesk, LDU_OBJ_DESK, (ULONG_PTR)pobj); } else if (bCreateFlags & OCF_SHAREDHEAP) { SharedFree(pobj); } else { UserFreePool(pobj); }
#if DBG || FRE_LOCK_RECORD
/*
* Go through and delete the lock records, if they exist. */ for (plrT = pheT->plr; plrT != NULL; plrT = plrNextT) {
/*
* Remember the next one before freeing this one. */ plrNextT = plrT->plrNext; FreeLockRecord((HANDLE)plrT); } #endif
/*
* Clear the handle contents. Need to remember the uniqueness across * the clear. Also, advance uniqueness on free so that uniqueness checking * against old handles also fails. */ wUniqT = (WORD)((pheT->wUniq + 1) & HMUNIQBITS);
/*
* Be sure that wUniqT will never be 0 nor HMUNIQBITS. * Then if we hit the max (i.e. HMUNIQBITS) then reset it to 1. */ if (wUniqT == HMUNIQBITS) { wUniqT = 1; } RtlZeroMemory(pheT, sizeof(HANDLEENTRY)); pheT->wUniq = wUniqT;
UserAssert(pheT->bType == TYPE_FREE);
/*
* Put the handle on the free list of the appropriate page. */ php = gpHandlePages; iheCurrent = pheT - gSharedInfo.aheList; for (i = 0; i < gcHandlePages; ++i, ++php) { if (iheCurrent < php->iheLimit) { piheCurrentHead = (iheCurrent & 0x1 ? &php->iheFreeOdd : &php->iheFreeEven); pheT->phead = (PHEAD)*piheCurrentHead; *piheCurrentHead = iheCurrent; DBGHMValidateFreeLists(); break; } }
/*
* We must have found it. */ UserAssert(i < gcHandlePages); UserAssert(pheT->pOwner == NULL);
DBGValidateHandleQuota();
return TRUE; }
/***************************************************************************\
* HMMarkObjectDestroy * * Marks an object for destruction. * * Returns TRUE if the object can be destroyed; that is, if it's * lock count is 0. * * 02-10-92 ScottLu Created. \***************************************************************************/ BOOL HMMarkObjectDestroy( PVOID pobj) { PHE phe = HMPheFromObject(pobj);
#if DBG || FRE_LOCK_RECORD
/*
* Record where the object was marked for destruction. */ if (RecordLockThisType(pobj)) { if (!(phe->bFlags & HANDLEF_DESTROY)) { HMRecordLock(LOCKRECORD_MARKDESTROY, pobj, ((PHEAD)pobj)->cLockObj); } } #endif
/*
* Set the destroy flag so our unlock code will know we're trying to * destroy this object. */ phe->bFlags |= HANDLEF_DESTROY;
/*
* If this object can't be destroyed, then CLEAR the HANDLEF_INDESTROY * flag - because this object won't be currently "in destruction"! * (if we didn't clear it, when it was unlocked it wouldn't get destroyed). */ if (((PHEAD)pobj)->cLockObj != 0) { phe->bFlags &= ~HANDLEF_INDESTROY;
/*
* Return FALSE because we can't destroy this object. */ return FALSE; }
#if DBG
/*
* Ensure that this function only returns TRUE once. */ UserAssert(!(phe->bFlags & HANDLEF_MARKED_OK)); phe->bFlags |= HANDLEF_MARKED_OK; #endif
/*
* Return TRUE because Lock count is zero - ok to destroy this object. */ return TRUE; }
/***************************************************************************\
* HMDestroyObject * * This routine marks an object for destruction, and frees it if * it is unlocked. * * 10-13-94 JimA Created. \***************************************************************************/
BOOL HMDestroyObject( PVOID pobj) { /*
* First mark the object for destruction. This tells the locking code * that we want to destroy this object when the lock count goes to 0. * If this returns FALSE, we can't destroy the object yet (and can't get * rid of security yet either.) */
if (!HMMarkObjectDestroy(pobj)) return FALSE;
/*
* Ok to destroy... Free the handle (which will free the object * and the handle). */ HMFreeObject(pobj); return TRUE; }
#if DBG || FRE_LOCK_RECORD
NTSTATUS InitLockRecordLookaside() { LockRecordLookaside = Win32AllocPoolNonPagedNS(sizeof(PAGED_LOOKASIDE_LIST), TAG_LOOKASIDE); if (LockRecordLookaside == NULL) { return STATUS_NO_MEMORY; }
ExInitializePagedLookasideList(LockRecordLookaside, NULL, NULL, SESSION_POOL_MASK, sizeof(LOCKRECORD), TAG_LOCKRECORD, 1000); return STATUS_SUCCESS; }
PLR AllocLockRecord() { PLR plr;
/*
* Allocate a LOCKRECORD structure. */ if ((plr = ExAllocateFromPagedLookasideList(LockRecordLookaside)) == NULL) { return NULL; }
RtlZeroMemory(plr, sizeof(*plr));
return plr; }
VOID FreeLockRecord( PLR plr) { ExFreeToPagedLookasideList(LockRecordLookaside, plr); }
/***************************************************************************\
* HMRecordLock * * This routine records a lock on a "lock list", so that locks and unlocks * can be tracked in the debugger. Only called if DBGTAG_TrackLocks is enabled. * * 02-27-92 ScottLu Created. \***************************************************************************/ VOID HMRecordLock( PVOID ppobj, PVOID pobj, DWORD cLockObj) { PHE phe; PLR plr; int i; phe = HMPheFromObject(pobj);
if ((plr = AllocLockRecord()) == NULL) { RIPMSG0(RIP_WARNING, "HMRecordLock failed to allocate memory"); return; }
/*
* Link it in front of the list */ plr->plrNext = phe->plr; phe->plr = plr;
/*
* This propably happens only for unmatched locks */ if (((PHEAD)pobj)->cLockObj > cLockObj) {
RIPMSG3(RIP_WARNING, "Unmatched lock. ppobj %#p pobj %#p cLockObj %d", ppobj, pobj, cLockObj);
i = (int)cLockObj; i = -i; cLockObj = (DWORD)i; }
plr->ppobj = ppobj; plr->cLockObj = cLockObj;
RtlWalkFrameChain(plr->trace, LOCKRECORD_STACK, 0); } #endif
#if DBG
/***************************************************************************\
* HMLockObject * * This routine locks an object. This is a macro in retail systems. * * 02-24-92 ScottLu Created. \***************************************************************************/ VOID HMLockObject( PVOID pobj) { HANDLE h; PVOID pobjValidate;
/*
* Validate by going through the handle entry so that we make sure pobj * is not just pointing off into space. This may GP fault, but that's * ok: this case should not ever happen if we're bug free. */
h = HMPheFromObject(pobj)->phead->h; pobjValidate = HMRevalidateCatHandle(h); if (!pobj || pobj != pobjValidate) { RIPMSG2(RIP_ERROR, "HMLockObject invalid object %#p, handle %#p", pobj, h); return; }
/*
* Inc the reference count. */ ((PHEAD)pobj)->cLockObj++;
if (((PHEAD)pobj)->cLockObj == 0) { RIPMSG1(RIP_ERROR, "Object lock count has overflowed: %#p", pobj); } } #endif // DBG
/***************************************************************************\
* HMUnlockObjectInternal * * This routine is called from the macro HMUnlockObject when an object's * reference count drops to zero. This routine will destroy an object * if is has been marked for destruction. * * 01-21-92 ScottLu Created. \***************************************************************************/
PVOID HMUnlockObjectInternal( PVOID pobj) { PHE phe;
/*
* The object is not reference counted. If the object is not a zombie, * return success because the object is still around. */ phe = HMPheFromObject(pobj); if (!(phe->bFlags & HANDLEF_DESTROY)) return pobj;
/*
* We're destroying the object based on an unlock... Make sure it isn't * currently being destroyed! (It is valid to have lock counts go from * 0 to != 0 to 0 during destruction... don't want recursion into * the destroy routine. */ if (phe->bFlags & HANDLEF_INDESTROY) return pobj;
HMDestroyUnlockedObject(phe); return NULL; }
/***************************************************************************\
* HMAssignmentLock * * This api is used for structure and global variable assignment. * Returns pobjOld if the object was *not* destroyed. Means the object is * still valid. * * 02-24-92 ScottLu Created. \***************************************************************************/
PVOID FASTCALL HMAssignmentLock( PVOID *ppobj, PVOID pobj) { PVOID pobjOld;
pobjOld = *ppobj; *ppobj = pobj;
/*
* Unlocks the old, locks the new. */ if (pobjOld != NULL) {
/*
* if we are locking in the same object that is there then * it is a no-op but we don't want to do the Unlock and the Lock * because the unlock could free object and the lock would lock * in a freed pointer; 6410. */ if (pobjOld == pobj) { return pobjOld; }
#if DBG || FRE_LOCK_RECORD
/*
* Track assignment locks. */ if (RecordLockThisType(pobjOld)) { if (!HMUnrecordLock(ppobj, pobjOld)) { HMRecordLock(ppobj, pobjOld, ((PHEAD)pobjOld)->cLockObj - 1); } } #endif
}
if (pobj != NULL) { UserAssert(pobj == HMValidateCatHandleNoSecure(((PHEAD)pobj)->h, TYPE_GENERIC)); if (HMIsMarkDestroy(pobj)) { RIPERR2(ERROR_INVALID_PARAMETER, RIP_WARNING, "HMAssignmentLock, locking object %#p marked for destruction at %#p", pobj, ppobj); }
#if DBG || FRE_LOCK_RECORD
/*
* Track assignment locks. */ if (RecordLockThisType(pobj)) { HMRecordLock(ppobj, pobj, ((PHEAD)pobj)->cLockObj + 1); if (HMIsMarkDestroy(pobj)) {
RIPMSG2(RIP_WARNING, "Locking object %#p marked for destruction at %#p", pobj, ppobj); } } #endif
HMLockObject(pobj); }
/*
* This unlock has been moved from up above, so that we implement a * "lock before unlock" strategy. Just in case pobjOld was the * only object referencing pobj, pobj won't go away when we unlock * pobjNew -- it will have been locked above. */
if (pobjOld) { pobjOld = HMUnlockObject(pobjOld); }
return pobjOld; }
/***************************************************************************\
* HMAssignmentUnLock * * This api is used for structure and global variable assignment. * Returns pobjOld if the object was *not* destroyed. Means the object is * still valid. * * 02-24-92 ScottLu Created. \***************************************************************************/ PVOID FASTCALL HMAssignmentUnlock( PVOID *ppobj) { PVOID pobjOld;
pobjOld = *ppobj; *ppobj = NULL;
/*
* Unlocks the old, locks the new. */ if (pobjOld != NULL) { #if DBG || FRE_LOCK_RECORD
/*
* Track assignment locks. */ if (RecordLockThisType(pobjOld)) { if (!HMUnrecordLock(ppobj, pobjOld)) { HMRecordLock(ppobj, pobjOld, ((PHEAD)pobjOld)->cLockObj - 1); } } #endif
pobjOld = HMUnlockObject(pobjOld); }
return pobjOld; }
/***************************************************************************\
* IsValidThreadLock * * This routine checks to make sure that the thread lock structures passed * in are valid. * * 03-17-92 ScottLu Created. * 02-22-99 MCostea Also validate the shadow of the stack TL * from gThreadLocksArray \***************************************************************************/ #if DBG
VOID IsValidThreadLock( PTHREADINFO pti, PTL ptl, ULONG_PTR dwLimit, BOOLEAN fHM) { /*
* Check that ptl is a valid stack address. Allow ptl == dwLimit so we * can call ValidateThreadLocks passing the address of the last thing we * locked. */ UserAssert((ULONG_PTR)ptl >= dwLimit); UserAssert((ULONG_PTR)ptl < (ULONG_PTR)PsGetCurrentThreadStackBase());
/*
* Check ptl owner. */ UserAssert(ptl->pW32Thread == (PW32THREAD)pti);
/*
* If this is an HM object, verify handle and lock count (guess max value) */ if (fHM && (ptl->pobj != NULL)) { /*
* The locked object could be a destroyed object. */ UserAssert(ptl->pobj == HtoPqCat(PtoHq(ptl->pobj))); if (((PHEAD)ptl->pobj)->cLockObj >= 32000) { RIPMSG2(RIP_WARNING, "IsValidThreadLock: Object %#p has %d locks", ptl->pobj, ((PHEAD)ptl->pobj)->cLockObj); } }
/*
* Make sure the shadow in gThreadLocksArray is doing fine. */ UserAssert(ptl->ptl->ptl == ptl); } #endif
#if DBG
/***************************************************************************\
* ValidateThreadLocks * * This routine validates the thread lock list of a thread. * * 03-10-92 ScottLu Created. \***************************************************************************/ ULONG ValidateThreadLocks( PTL NewLock, PTL OldLock, ULONG_PTR dwLimit, BOOLEAN fHM) { UINT uTLCount = 0; PTL ptlTopLock = OldLock; PTHREADINFO ptiCurrent;
BEGIN_REENTERCRIT();
ptiCurrent = PtiCurrent();
/*
* Validate the new thread lock. */ if (NewLock != NULL) { UserAssert(NewLock->next == OldLock); IsValidThreadLock(ptiCurrent, NewLock, dwLimit, fHM); uTLCount++; }
/*
* Loop through the list of thread locks and check to make sure the * new lock is not in the list and that list is valid. */ while (OldLock != NULL) { /*
* The new lock must not be the same as the old lock. */ UserAssert(NewLock != OldLock); /*
* Validate the old thread lock. */ IsValidThreadLock(ptiCurrent, OldLock, dwLimit, fHM); uTLCount++; OldLock = OldLock->next; } /*
* If this is thread lock, set uTLCount, else verify it */ if (NewLock != NULL) { NewLock->uTLCount = uTLCount; } else { if (ptlTopLock == NULL) { RIPMSG0(RIP_WARNING, "ptlTopLock is NULL, the system will AV now"); } UserAssert(uTLCount == ptlTopLock->uTLCount); }
END_REENTERCRIT();
return uTLCount; } #endif // DBG
#if DBG
/***************************************************************************\
* CreateShadowTL * * This function creates a shaddow for the stack allocated ptl parameter * in the global thread locks arrays * * 08-04-99 MCostea Created. \***************************************************************************/ VOID CreateShadowTL( PTL ptl) { PTL pTLNextFree; if (gFreeTLList->next == NULL) { UserAssert(gcThreadLocksArraysAllocated < MAX_THREAD_LOCKS_ARRAYS && "No more room in gpaThreadLocksArrays! The system will bugcheck."); gFreeTLList->next = gpaThreadLocksArrays[gcThreadLocksArraysAllocated] = UserAllocPoolZInit(sizeof(TL)*MAX_THREAD_LOCKS, TAG_GLOBALTHREADLOCK); if (gFreeTLList->next == NULL) { UserAssert("Can't allocate memory for gpaThreadLocksArrays: the system will bugcheck soon!"); } InitGlobalThreadLockArray(gcThreadLocksArraysAllocated); gcThreadLocksArraysAllocated++; } pTLNextFree = gFreeTLList->next; RtlCopyMemory(gFreeTLList, ptl, sizeof(TL)); gFreeTLList->ptl = ptl; ptl->ptl = gFreeTLList; gFreeTLList = pTLNextFree; } #endif // DBG
/***************************************************************************\
* ThreadLock * * This api is used for locking objects across callbacks, so they are still * there when the callback returns. * * 03-04-92 ScottLu Created. \***************************************************************************/
#if DBG
VOID ThreadLock( PVOID pobj, PTL ptl)
{ PTHREADINFO ptiCurrent; PVOID pfnT;
/*
* This is a handy place, because it is called so often, to see if we're * eating up too much stack. */ ASSERT_STACK();
/*
* Store the address of the object in the thread lock structure and * link the structure into the thread lock list. * * N.B. The lock structure is always linked into the thread lock list * regardless of whether the object address is NULL. The reason * this is done is so the lock address does not need to be passed * to the unlock function since the first entry in the lock list * is always the entry to be unlocked. */
UserAssert(!(PpiCurrent()->W32PF_Flags & W32PF_TERMINATED)); ptiCurrent = PtiCurrent(); UserAssert(ptiCurrent);
/*
* Get the callers address and validate the thread lock list. */ RtlGetCallersAddress(&ptl->pfnCaller, &pfnT); ptl->pW32Thread = (PW32THREAD)ptiCurrent;
ptl->next = ptiCurrent->ptl; ptiCurrent->ptl = ptl; ptl->pobj = pobj; if (pobj != NULL) { HMLockObject(pobj); }
CreateShadowTL(ptl); ValidateThreadLocks(ptl, ptl->next, (ULONG_PTR)&pobj, TRUE); } #endif
/***************************************************************************\
* ThreadLockExchange * * Reuses a TL structure by locking the new object and unlocking * the old one. This is used where you enumerate a list of * structure locked objects, e.g. the window list. * * History: * 05-Mar-1997 adams Created. \***************************************************************************/
#if DBG
PVOID ThreadLockExchange(PVOID pobj, PTL ptl) { PTHREADINFO ptiCurrent; PVOID pobjOld; PVOID pfnT;
/*
* This is a handy place, because it is called so often, to see if User is * eating up too much stack. */ ASSERT_STACK();
/*
* Store the address of the object in the thread lock structure and * link the structure into the thread lock list. * * N.B. The lock structure is always linked into the thread lock list * regardless of whether the object address is NULL. The reason * this is done is so the lock address does not need to be passed * to the unlock function since the first entry in the lock list * is always the entry to be unlocked. */
UserAssert(!(PpiCurrent()->W32PF_Flags & W32PF_TERMINATED)); ptiCurrent = PtiCurrent(); UserAssert(ptiCurrent);
/*
* Get the callers address. */ RtlGetCallersAddress(&ptl->pfnCaller, &pfnT); UserAssert(ptl->pW32Thread == (PW32THREAD)ptiCurrent);
/*
* Remember the old object. */ UserAssert(ptl->pobj == ptl->ptl->pobj); pobjOld = ptl->pobj;
/*
* Store and lock the new object. It is important to do this step * before unlocking the old object, since the new object might be * structure locked by the old object. */ ptl->pobj = pobj; if (pobj != NULL) { HMLockObject(pobj); }
/*
* Unlock the old object. */ if (pobjOld) { pobjOld = HMUnlockObject((PHEAD)pobjOld); }
/*
* Validate the entire thread lock list. */ ValidateThreadLocks(NULL, ptiCurrent->ptl, (ULONG_PTR)&pobj, TRUE);
/*
* Maintain gFreeTLList */ UserAssert(ptl->ptl->ptl == ptl); ptl->ptl->pobj = pobj; ptl->ptl->pfnCaller = ptl->pfnCaller;
return pobjOld; } #endif
/*
* The thread locking routines should be optimized for time, not size, * since they get called so often. */ #pragma optimize("t", on)
/***************************************************************************\
* ThreadUnlock1 * * This api unlocks a thread locked object. Returns pobj if the object * was *not* destroyed (meaning the pointer is still valid). * * N.B. In a free build the first entry in the thread lock list is unlocked. * * 03-04-92 ScottLu Created. \***************************************************************************/
#if DBG
PVOID ThreadUnlock1( PTL ptlIn) #else
PVOID ThreadUnlock1( VOID) #endif
{ PHEAD phead; PTHREADINFO ptiCurrent; PTL ptl;
ptiCurrent = PtiCurrent(); ptl = ptiCurrent->ptl; UserAssert(ptl != NULL); /*
* Validate the thread lock list. */ ValidateThreadLocks(NULL, ptl, (ULONG_PTR)&ptlIn, TRUE); /*
* Make sure the caller wants to unlock the top lock. */ UserAssert(ptlIn == ptl); ptiCurrent->ptl = ptl->next; /*
* If the object address is not NULL, then unlock the object. */ phead = (PHEAD)(ptl->pobj); if (phead != NULL) {
/*
* Unlock the object. */
phead = (PHEAD)HMUnlockObject(phead); } #if DBG
{ /*
* Remove the corresponding element from gFreeTLList */ ptl->ptl->next = gFreeTLList; ptl->ptl->uTLCount += TL_FREED_PATTERN; gFreeTLList = ptl->ptl; } #endif
return (PVOID)phead; }
/*
* Switch back to default optimization. */ #pragma optimize("", on)
#if DBG
/***************************************************************************\
* CheckLock * * This routine only exists in DBG builds - it checks to make sure objects * are thread locked. * * 03-09-92 ScottLu Created. \***************************************************************************/ VOID CheckLock( PVOID pobj) { PTHREADINFO ptiCurrent = PtiCurrentShared(); PTL ptl;
if (pobj == NULL) { return; }
/*
* Validate all locks first */ UserAssert(ptiCurrent != NULL); ValidateThreadLocks(NULL, ptiCurrent->ptl, (ULONG_PTR)&pobj, TRUE);
for (ptl = ptiCurrent->ptl; ptl != NULL; ptl=ptl->next) { if (ptl->pobj == pobj) return; }
/*
* WM_FINALDESTROY messages get sent without thread locking, so if * marked for destruction, don't print the message. */ if (HMPheFromObject(pobj)->bFlags & HANDLEF_DESTROY) return;
RIPMSG1(RIP_ERROR, "Object not thread locked! %#p", pobj); } #endif
/***************************************************************************\
* HMDestroyUnlockedObject * * Destroy an object based on an unlock or cleanup from thread or * process termination. * * The functions called to destroy a particular object can be called * directly from code as well as the result of an unlock. Destroy * functions have the following 4 sections. * * (1) Remove the object from a list or other global * context. If the destroy function has to leave the * critical section (e.g. make an xxx call), it must * do so in this step. * * (2) Call HMMarkDestroy, and return if HMMarkDestroy * returns FALSE. This is required. * * (3) Destroy resources held by the objects - locks to * other objects, alloc'd memory, etc. This is required. * * (4) Free the memory of the object and its handle by calling * HMFreeObject. This is required. * * Note that if the object is locked when it's destroy function * is called directly, step (1) will be repeated when the object is * unlocked. We should probably check for this in the destroy functions, * which we currently do not do. * * Note that we could be destroying this object in a context different * than the one that created it. This is very important to understand * since in lots of code the "current thread" is referenced and assumed * as the creator. * * 02-10-92 ScottLu Created. \***************************************************************************/ VOID HMDestroyUnlockedObject( PHE phe) { BEGINATOMICCHECK();
/*
* Remember that we're destroying this object so we don't try to destroy * it again when the lock count goes from != 0 to 0 (especially true * for thread locks). */ phe->bFlags |= HANDLEF_INDESTROY;
/*
* This'll call the destroy handler for this object type. */ (*gahti[phe->bType].fnDestroy)(phe->phead);
/*
* HANDLEF_INDESTROY is supposed to be cleared either by HMMarkObjectDestroy * or by HMFreeObject; the destroy handler was supposed to call at least * the former. */ UserAssert(!(phe->bFlags & HANDLEF_INDESTROY));
/*
* If the object wasn't freed, it must be marked as destroyed * and must have a lock count. */ UserAssert((phe->bType == TYPE_FREE) || ((phe->bFlags & HANDLEF_DESTROY) && (phe->phead->cLockObj > 0)));
ENDATOMICCHECK(); }
/***************************************************************************\
* HMChangeOwnerThread * * Changes the owning thread of an object. * * 09-13-93 JimA Created. \***************************************************************************/ VOID HMChangeOwnerThread( PVOID pobj, PTHREADINFO pti) { PHE phe = HMPheFromObject(pobj); PTHREADINFO ptiOld = ((PTHROBJHEAD)pobj)->pti; PWND pwnd; PPCLS ppcls; PPROCESSINFO ppi;
CheckCritIn();
UserAssert(HMObjectFlags(pobj) & OCF_THREADOWNED); UserAssert(pti != NULL);
((PTHREADINFO)phe->pOwner)->ppi->UserHandleCount--;
((PTHROBJHEAD)pobj)->pti = phe->pOwner = pti;
((PTHREADINFO)phe->pOwner)->ppi->UserHandleCount++;
DBGValidateHandleQuota();
/*
* If this is a window, update the window counts. */ switch (phe->bType) { case TYPE_WINDOW: /*
* Desktop thread used to hit this assert in HYDRA because * pti == ptiOld. */ UserAssert(ptiOld->cWindows > 0 || ptiOld == pti); pti->cWindows++; ptiOld->cWindows--;
pwnd = (PWND)pobj;
/*
* Make sure thread visible window count is properly updated. */ if (TestWF(pwnd, WFVISIBLE) && FVisCountable(pwnd)) { pti->cVisWindows++; ptiOld->cVisWindows--; }
/*
* If the owning process is changing, fix up the window class. */ if (pti->ppi != ptiOld->ppi) {
ppcls = GetClassPtr(pwnd->pcls->atomClassName, pti->ppi, hModuleWin);
if (ppcls == NULL) { if (pwnd->head.rpdesk) { ppi = pwnd->head.rpdesk->rpwinstaParent->pTerm->ptiDesktop->ppi; } else { ppi = PpiCurrent(); } ppcls = GetClassPtr(gpsi->atomSysClass[ICLS_ICONTITLE], ppi, hModuleWin); } UserAssert(ppcls); #if DBG
if (!TestWF(pwnd, WFDESTROYED)) { if ((*ppcls)->rpdeskParent == NULL) { /*
* If rpdeskParent NULL then it has to be a system thread. */ UserAssert(pti->TIF_flags & TIF_SYSTEMTHREAD); } else { /*
* The desktop of the class has to be the same as the window's desktop */ UserAssert((*ppcls)->rpdeskParent == pwnd->head.rpdesk); } } #endif
{ DereferenceClass(pwnd); pwnd->pcls = *ppcls; /*
* We might fail to clone the class for a zombie window in * ReferenceClass since we ran out of desktop heap (see bug * #375171). In this case, we just increment the class window * reference since there will be no client-side reference to * the class. Need to assert that the window is destroyed or * we will be in trouble. A better fix would be to clone the * icon title class beforehand during desktop creation. * [msadek, 06/21/2001] */ if (!ReferenceClass(pwnd->pcls, pwnd)) { pwnd->pcls->cWndReferenceCount++; if (!TestWF(pwnd, WFDESTROYED)) { FRE_RIPMSG2(RIP_ERROR, "Non destroyed window using a non cloned class. cls 0x%p, pwnd 0x%p", pwnd->pcls, pwnd); } } } } break;
case TYPE_HOOK: /*
* If this is a global hook, remember this hook's desktop so we'll be * able to unlink it later (gptiRit might switch to a different desktop * at any time). */ UserAssert(!!(((PHOOK)pobj)->flags & HF_GLOBAL) ^ (((PHOOK)pobj)->ptiHooked != NULL)); if (((PHOOK)pobj)->flags & HF_GLOBAL) { UserAssert(pti == gptiRit); LockDesktop(&((PHOOK)pobj)->rpdesk, ptiOld->rpdesk, LDL_HOOK_DESK, 0); } else { /*
* This must be a hook on another thread or it was supposed to be * gone by now. */ UserAssert(((PHOOK)pobj)->ptiHooked != ptiOld); } break;
default: break; } }
/***************************************************************************\
* HMChangeOwnerProcess * * Changes the owning process of an object. * * 04-15-97 JerrySh Created. * 09-23-97 GerardoB Changed parameters (and name) so HMDestroyUnlockedObject * could use this function (instead of duplicating the code there) \***************************************************************************/ VOID HMChangeOwnerPheProcess( PHE phe, PTHREADINFO pti) { PPROCESSINFO ppiOwner = (PPROCESSINFO)(phe->pOwner); PVOID pobj = phe->phead;
UserAssert(HMObjectFlags(pobj) & OCF_PROCESSOWNED); UserAssert(pti != NULL); /*
* Dec current owner handle count */ ppiOwner->UserHandleCount--; /*
* hTaskWow */ if ((pti->TIF_flags & TIF_16BIT) && (pti->ptdb)) { ((PPROCOBJHEAD)pobj)->hTaskWow = pti->ptdb->hTaskWow; } else { ((PPROCOBJHEAD)pobj)->hTaskWow = 0; } /*
* ppi */ if (gahti[phe->bType].bObjectCreateFlags & OCF_MARKPROCESS) { ((PPROCMARKHEAD)pobj)->ppi = pti->ppi; } /*
* Set new owner in handle entry */ phe->pOwner = pti->ppi; /*
* Inc new owner handle count */ ((PPROCESSINFO)(phe->pOwner))->UserHandleCount++; /*
* If the handle is a cursor, adjust GDI cursor handle count */ if (phe->bType == TYPE_CURSOR) { GreDecQuotaCount((PW32PROCESS)ppiOwner); GreIncQuotaCount((PW32PROCESS)phe->pOwner);
if (((PCURSOR)pobj)->hbmColor) { GreDecQuotaCount((PW32PROCESS)ppiOwner); GreIncQuotaCount((PW32PROCESS)phe->pOwner); } if (((PCURSOR)pobj)->hbmUserAlpha) { GreDecQuotaCount((PW32PROCESS)ppiOwner); GreIncQuotaCount((PW32PROCESS)phe->pOwner); } }
DBGValidateHandleQuota(); }
/***************************************************************************\
* DestroyThreadsObjects * * Goes through the handle table list and destroy all objects owned by this * thread, because the thread is going away (either nicely, it faulted, or * was terminated). It is ok to destroy the objects in any order, because * object locking will ensure that they get destroyed in the right order. * * This routine gets called in the context of the thread that is exiting. * * 02-08-92 ScottLu Created. \***************************************************************************/ VOID DestroyThreadsObjects( VOID) { PTHREADINFO ptiCurrent; HANDLEENTRY volatile * (*pphe); PHE pheT; DWORD i;
ptiCurrent = PtiCurrent(); DBGValidateHandleQuota();
/*
* Before any window destruction occurs, we need to destroy any dcs * in use in the dc cache. When a dc is checked out, it is marked owned, * which makes gdi's process cleanup code delete it when a process * goes away. We need to similarly destroy the cache entry of any dcs * in use by the exiting process. */ DestroyCacheDCEntries(ptiCurrent);
/*
* Remove any thread locks that may exist for this thread. */ while (ptiCurrent->ptl != NULL) { UserAssert((ULONG_PTR)ptiCurrent->ptl > (ULONG_PTR)&i); UserAssert((ULONG_PTR)ptiCurrent->ptl < (ULONG_PTR)PsGetCurrentThreadStackBase()); ThreadUnlock(ptiCurrent->ptl); }
/*
* CleanupPool stuff must happen before handle table clean up (as it * always has been). This is because SMWPs can be HM objects and still * be locked in ptlPool. If the handle is destroyed first (and it's not * locked) we would end up with a bogus pointer in ptlPool. If ptlPool * is cleaned up first, the handle will be freed or properly preserved * if locked. */ CleanupW32ThreadLocks((PW32THREAD)ptiCurrent);
/*
* Even though HMDestroyUnlockedObject might call xxxDestroyWindow, the * following loop is not supposed to leave the critical section. We must * have called PatchThreadWindows before coming here. */ BEGINATOMICCHECK();
/*
* Loop through the table destroying all objects created by the current * thread. All objects will get destroyed in their proper order simply * because of the object locking. */ pphe = &gSharedInfo.aheList; for (i = 0; i <= giheLast; i++) { /*
* This pointer is done this way because it can change when we leave * the critical section below. The above volatile ensures that we * always use the most current value */ pheT = (PHE)((*pphe) + i);
/*
* Check against free before we look at pti... because pq is stored * in the object itself, which won't be there if TYPE_FREE. */ if (pheT->bType == TYPE_FREE) { continue; }
/*
* If a menu refererences a window owned by this thread, unlock * the window. This is done to prevent calling xxxDestroyWindow * during process cleanup. */ if (gahti[pheT->bType].bObjectCreateFlags & OCF_PROCESSOWNED) { if (pheT->bType == TYPE_MENU) { PWND pwnd = ((PMENU)pheT->phead)->spwndNotify;
if (pwnd != NULL && GETPTI(pwnd) == ptiCurrent) { Unlock(&((PMENU)pheT->phead)->spwndNotify); } }
continue; }
/*
* Destroy those objects created by this queue. */ if ((PTHREADINFO)pheT->pOwner != ptiCurrent) { continue; }
UserAssert(gahti[pheT->bType].bObjectCreateFlags & OCF_THREADOWNED);
/*
* Make sure this object isn't already marked to be destroyed - we'll * do no good if we try to destroy it now since it is locked. */ if (pheT->bFlags & HANDLEF_DESTROY) { continue; }
/*
* Destroy this object. */ HMDestroyUnlockedObject(pheT); }
ENDATOMICCHECK(); DBGValidateHandleQuota(); }
#if DBG || FRE_LOCK_RECORD
VOID ShowLocks( PHE phe) { PLR plr = phe->plr; INT c;
RIPMSG2(RIP_WARNING | RIP_THERESMORE, "Lock records for %s %#p:", gahti[phe->bType].szObjectType, phe->phead->h); /*
* We have the handle entry: 'head' and 'he' are both filled in. Dump * the lock records. Remember the first record is the last transaction!! */ c = 0; while (plr != NULL) { char achPrint[80];
if (plr->ppobj == LOCKRECORD_MARKDESTROY) { strcpy(achPrint, "Destroyed with"); } else if ((int)plr->cLockObj <= 0) { strcpy(achPrint, " Unlock"); } else { /*
* Find corresponding unlock; */ { PLR plrUnlock; DWORD cT; DWORD cUnlock;
plrUnlock = phe->plr; cT = 0; cUnlock = (DWORD)-1;
while (plrUnlock != plr) { if (plrUnlock->ppobj == plr->ppobj) { if ((int)plrUnlock->cLockObj <= 0) { // a matching unlock found
cUnlock = cT; } else { // the unlock #cUnlock matches this lock #cT, thus
// #cUnlock is not the unlock we were looking for.
cUnlock = (DWORD)-1; } } plrUnlock = plrUnlock->plrNext; cT++; } if (cUnlock == (DWORD)-1) { /*
* Corresponding unlock not found! * This may not mean something is wrong: the structure * containing the pointer to the object may have moved * during a reallocation. This can cause ppobj at Unlock * time to differ from that recorded at Lock time. * (Warning: moving structures like this may cause a Lock * and an Unlock to be misidentified as a pair, if by a * stroke of incredibly bad luck, the new location of a * pointer to an object is now where an old pointer to the * same object used to be) */ sprintf(achPrint, "Unmatched Lock"); } else { sprintf(achPrint, "lock #%ld", cUnlock); } } }
RIPMSG4(RIP_WARNING | RIP_NONAME | RIP_THERESMORE, " %s cLock=%d, pobj at %#p, code at %#p", achPrint, abs((int)plr->cLockObj), plr->ppobj, plr->trace[0]);
plr = plr->plrNext; c++; }
RIPMSG1(RIP_WARNING | RIP_NONAME, " 0x%lx records", c); } #endif
/***************************************************************************\
* FixupGlobalCursor * * Spins through a global cursor (a cursor who's owner is NULL), and * reassigns ownership to the specified process. * * Note: This changes the owner process field inside the object itself. It * does not change the owner field of the handle referencing it. * \***************************************************************************/ VOID FixupGlobalCursor( PCURSOR pcur, PPROCESSINFO ppi) { int i; PACON pacon = (PACON)pcur;
if (pcur->head.ppi == NULL) { pcur->head.ppi = ppi; }
if (pacon->CURSORF_flags & CURSORF_ACON) { for (i = 0; i < pacon->cpcur; i++) {
UserAssert(pacon->aspcur[i]->CURSORF_flags & CURSORF_ACONFRAME);
if (pacon->aspcur[i]->head.ppi == NULL) { pacon->aspcur[i]->head.ppi = ppi; } } } }
/***************************************************************************\
* DestroyProcessesObjects * * Goes through the handle table list and destroy all objects owned by this * process, because the process is going away (either nicely, it faulted, or * was terminated). It is ok to destroy the objects in any order, because * object locking will ensure that they get destroyed in the right order. * * This routine gets called in the context of the last thread in the process. * * 08-17-92 JimA Created. \***************************************************************************/ VOID DestroyProcessesObjects( PPROCESSINFO ppi) { PHE pheT, pheMax; BOOL fCSRSS = (ppi->Process == gpepCSRSS);
#if DBG
BOOL fOrphaned = FALSE; #endif // DBG
DBGValidateHandleQuota();
/*
* Loop through the table destroying all objects owned by the current * process. All objects will get destroyed in their proper order simply * because of the object locking. */ pheMax = &gSharedInfo.aheList[giheLast]; for (pheT = gSharedInfo.aheList; pheT <= pheMax; pheT++) {
/*
* If this handle entry is free, skip it. */ if (pheT->bType == TYPE_FREE) { continue; }
/*
* Don't destroy objects that are either not owned by a process at all, or * are owned by a process - but a different process than us! */ if (!(gahti[pheT->bType].bObjectCreateFlags & OCF_PROCESSOWNED) || (PPROCESSINFO)pheT->pOwner != ppi) { continue; }
/*
* If this is CSRSS being destroyed, then we need to clean up all * "global" cursors. Note that the owner process stored in the * handle is CSRSS, but the owner process stored in the object is * NULL. We assign ownership of the cursor (and all associated * frames) to CSRSS, so that it will be cleaned up during the * HMDestroyUnlockedObject call. */ if (fCSRSS && pheT->bType == TYPE_CURSOR) { FixupGlobalCursor((PCURSOR)pheT->phead, ppi); }
/*
* Destroy this object - but only if it hasn't already been destroyed! */ if (!(pheT->bFlags & HANDLEF_DESTROY)) { HMDestroyUnlockedObject(pheT); } else { //
// If the handle was marked as having already been destroyed, it
// should have a non-zero lock count. When the final Unlock is
// called, the object will be freed.
//
UserAssert(pheT->phead->cLockObj != 0); }
/*
* Check to see if the object was destroyed, but not freed. */ if (pheT->bType != TYPE_FREE) { /*
* This object has already been destroyed. Is is just waiting for its * lock count to reach 0 before it can be actually freed. However, * since this object is owned by the process that is going away, it * is now an "orphaned" object. Pass ownership to the RIT if possible. * Once the other objects that are holding locks on this object release * their locks, this object will evaporate. If the locks are never * released then we have a leak, and we will catch it later. * * Note that this might be uneccessary, as the owners of the locks * may all belong to this process, and as such will all be destroyed * during this function - and therefore the reparenting was not needed. * However, doing so now allows us to complete in a single pass * through the handle table. */ if (gptiRit != NULL) { if (pheT->bType == TYPE_CURSOR) { ZombieCursor((PCURSOR)pheT->phead); } else { HMChangeOwnerPheProcess(pheT, gptiRit); } }
#if DBG
fOrphaned = TRUE; #endif // DBG
} }
#if DBG
/*
* Check to see if we have any orphans left in the handle table that * used to belong to this process. This only poses a serious problem * when the RIT is not available (for instance, if we are shutting down) * because we have no one to adopt these objects. This would indicate * a serious resource leak and should be fixed. */ if (fOrphaned && gptiRit == NULL) { pheMax = &gSharedInfo.aheList[giheLast]; for (pheT = gSharedInfo.aheList; pheT <= pheMax; pheT++) { if (pheT->bType != TYPE_FREE && (gahti[pheT->bType].bObjectCreateFlags & OCF_PROCESSOWNED) && (PPROCESSINFO)pheT->pOwner == ppi) { RIPMSGF1(RIP_ERROR, "Object leak detected! phe= 0x%p", pheT); } } } #endif
DBGValidateHandleQuota(); }
/***************************************************************************\
* MarkThreadsObjects * * This is called for the *final* exiting condition when a thread * may have objects still around... in which case their owner must * be changed to something "safe" that won't be going away. * * 03-02-92 ScottLu Created. \***************************************************************************/ VOID MarkThreadsObjects( PTHREADINFO pti) { PHE pheT, pheMax;
pheMax = &gSharedInfo.aheList[giheLast]; for (pheT = gSharedInfo.aheList; pheT <= pheMax; pheT++) { /*
* Check against free before we look at pti... because pti is stored * in the object itself, which won't be there if TYPE_FREE. */ if (pheT->bType == TYPE_FREE) { continue; }
/*
* Change ownership! */ if (gahti[pheT->bType].bObjectCreateFlags & OCF_PROCESSOWNED || (PTHREADINFO)pheT->pOwner != pti) { continue; }
/*
* This is just to make sure that RIT or DT never get here. */ UserAssert(pti != gptiRit && pti != gTermIO.ptiDesktop);
HMChangeOwnerThread(pheT->phead, gptiRit);
#if DBG
if (IsDbgTagEnabled(DBGTAG_TrackLocks)) { /*
* Object still around: print warning message. */ if (pheT->bFlags & HANDLEF_DESTROY) { TAGMSG2(DBGTAG_TrackLocks, "Zombie %s 0x%p still locked", gahti[pheT->bType].szObjectType, pheT->phead->h); } else { TAGMSG1(DBGTAG_TrackLocks, "Thread object 0x%p not destroyed.", pheT->phead->h); }
ShowLocks(pheT); }
#endif
} }
/***************************************************************************\
* HMRelocateLockRecord * * If a pointer to a locked object has been relocated, then this routine will * adjust the lock record accordingly. Must be called after the relocation. * * The arguments are: * ppobjNew - the address of the new pointer * MUST already contain the pointer to the object!! * cbDelta - the amount by which this pointer was moved. * * Using this routine appropriately will prevent spurious "unmatched lock" * reports. See mnchange.c for an example. * * * 03-18-93 IanJa Created. \***************************************************************************/
#if DBG || FRE_LOCK_RECORD
BOOL HMRelocateLockRecord( PVOID ppobjNew, LONG_PTR cbDelta) { PHE phe; PVOID ppobjOld = (PBYTE)ppobjNew - cbDelta; PHEAD pobj; PLR plr;
if (ppobjNew == NULL) { return FALSE; }
pobj = *(PHEAD *)ppobjNew;
if (pobj == NULL) { return FALSE; }
phe = HMPheFromObject(pobj); if (phe->phead != pobj) { RIPMSG3(RIP_WARNING, "HmRelocateLockRecord(%#p, %lx) - %#p is bad pobj", ppobjNew, cbDelta, pobj);
return FALSE; }
plr = phe->plr;
while (plr != NULL) { if (plr->ppobj == ppobjOld) { (PBYTE)(plr->ppobj) += cbDelta; return TRUE; } plr = plr->plrNext; }
RIPMSG2(RIP_WARNING, "HmRelocateLockRecord(%#p, %lx) - couldn't find lock record", ppobjNew, cbDelta);
ShowLocks(phe); return FALSE; }
BOOL HMUnrecordLock( PVOID ppobj, PVOID pobj) { PHE phe; PLR plr; PLR *pplr;
phe = HMPheFromObject(pobj);
pplr = &(phe->plr); plr = *pplr;
/*
* Find corresponding lock; */ while (plr != NULL) { if (plr->ppobj == ppobj) { /*
* Remove the lock from the list... */ *pplr = plr->plrNext; // unlink it
plr->plrNext = NULL; // make the dead entry safe (?)
/*
* ...and free it. */ FreeLockRecord(plr); return TRUE; } pplr = &(plr->plrNext); plr = *pplr; }
RIPMSG2(RIP_WARNING, "Could not find lock for ppobj %#p pobj %#p", ppobj, pobj);
return FALSE; }
#endif // DBG
/***************************************************************************\
* _QueryUserHandles * * This function retrieves the USER handle counters for all processes * specified by their client ID in the paPids array * Specify QUC_PID_TOTAL to retrieve totals for all processes in the system * * Parameters: * paPids - pointer to an array of pids (DWORDS) that we're interested in * dwNumInstances - number of DWORDS in paPids * pdwResult - will receive TYPES_CTYPESxdwNumInstances counters * * returns: none * * 07-25-97 mcostea Created \***************************************************************************/ VOID _QueryUserHandles( LPDWORD paPids, DWORD dwNumInstances, DWORD dwResult[][TYPE_CTYPES]) { PHE pheCurPos; // Current position in the table
PHE pheMax; // address of last table entry
DWORD index; DWORD pid; DWORD dwTotalCounters[TYPE_CTYPES]; // system wide counters
RtlZeroMemory(dwTotalCounters, TYPE_CTYPES*sizeof(DWORD)); RtlZeroMemory(dwResult, dwNumInstances*TYPE_CTYPES*sizeof(DWORD)); /*
* Walk the handle table and update the counters */ pheMax = &gSharedInfo.aheList[giheLast]; for(pheCurPos = gSharedInfo.aheList; pheCurPos <= pheMax; pheCurPos++) {
UserAssert(pheCurPos->bType < TYPE_CTYPES);
if (pheCurPos->pOwner) {
if (gahti[pheCurPos->bType].bObjectCreateFlags & OCF_PROCESSOWNED) { pid = HandleToUlong(PsGetProcessId(((PPROCESSINFO)(pheCurPos->pOwner))->Process)); } else if (gahti[pheCurPos->bType].bObjectCreateFlags & OCF_THREADOWNED) { pid = HandleToUlong(PsGetThreadProcessId(((PTHREADINFO)pheCurPos->pOwner)->pEThread)); } else { pid = 0; } }
/*
* Search to see if we are interested in this process. Unowned * handles are reported for the "System" process whose pid is 0. */ for (index = 0; index < dwNumInstances; index++) { if (paPids[index] == pid) { dwResult[index][pheCurPos->bType]++; } }
/*
* Update the totals. */ dwTotalCounters[pheCurPos->bType]++; }
/*
* Search to see if we are interested in the totals. */ for (index = 0; index < dwNumInstances; index++) { if (paPids[index] == QUC_PID_TOTAL) { RtlMoveMemory(dwResult[index], dwTotalCounters, sizeof(dwTotalCounters)); } } }
/***************************************************************************\
* HMCleanupGrantedHandle * * This function is called to cleanup this handle from pW32Job->pgh arrays. * It walks the job list to find jobs that have the handle granted. * * HISTORY: * 22 Jul 97 CLupu Created \***************************************************************************/ VOID HMCleanupGrantedHandle( HANDLE h) { PW32JOB pW32Job;
pW32Job = gpJobsList;
while (pW32Job != NULL) { PULONG_PTR pgh; DWORD dw;
pgh = pW32Job->pgh;
/*
* search for the handle in the array. */ for (dw = 0; dw < pW32Job->ughCrt; dw++) { if (*(pgh + dw) == (ULONG_PTR)h) { /*
* Found the handle granted to this process. */ RtlMoveMemory(pgh + dw, pgh + dw + 1, (pW32Job->ughCrt - dw - 1) * sizeof(*pgh));
(pW32Job->ughCrt)--;
/*
* we should shrink the array also */
break; } }
pW32Job = pW32Job->pNext; } }
|