Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2157 lines
59 KiB

/****************************** Module Header ******************************\
* Module Name: handtabl.c
*
* Copyright (c) 1985-95, Microsoft Corporation
*
* Implements the USER handle table.
*
* 01-13-92 ScottLu Created.
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/*
* Turning this variable on results in lock tracking, for debugging
* purposes. This is FALSE by default.
*/
#ifdef DEBUG_LOCKS
BOOL gfTrackLocks = TRUE;
#else
BOOL gfTrackLocks = FALSE;
#endif
/*
* 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
typedef struct _HANDLEPAGE {
DWORD iheLimit; /* first handle index past the end of the page */
DWORD iheFree; /* first free handle in the page */
} HANDLEPAGE, *PHANDLEPAGE;
DWORD gcHandlePages;
PHANDLEPAGE gpHandlePages;
CONST BYTE gabObjectCreateFlags[TYPE_CTYPES] = {
0, /* free */
OCF_THREADOWNED | OCF_MARKTHREAD | OCF_USEQUOTA, /* window */
OCF_PROCESSOWNED, /* menu */
OCF_PROCESSOWNED | OCF_USEQUOTA, /* cursor/icon */
OCF_THREADOWNED | OCF_USEQUOTA, /* hswpi (SetWindowPos Information) */
OCF_THREADOWNED | OCF_MARKTHREAD, /* hook */
OCF_THREADOWNED | OCF_USEQUOTA, /* thread info object (internal) */
0, /* clipboard data (internal) */
OCF_THREADOWNED, /* CALLPROCDATA */
OCF_PROCESSOWNED | OCF_USEQUOTA, /* accel table */
OCF_THREADOWNED | OCF_USEQUOTA, /* dde access */
OCF_THREADOWNED | OCF_MARKTHREAD | OCF_USEQUOTA, /* dde conversation */
OCF_THREADOWNED | OCF_MARKTHREAD | OCF_USEQUOTA, /* ddex */
OCF_PROCESSOWNED, /* zombie */
OCF_PROCESSOWNED, /* keyboard layout */
OCF_PROCESSOWNED, /* keyboard file */
#ifdef FE_IME
OCF_THREADOWNED | OCF_MARKTHREAD, /* input context */
#endif
};
/*
* Tag array for objects allocated from pool
*/
CONST DWORD gdwAllocTag[TYPE_CTYPES] = {
0, /* free */
0, /* window */
0, /* menu */
TAG_CURSOR, /* cursor/icon */
TAG_SWP, /* hswpi (SetWindowPos Information) */
0, /* hook */
TAG_THREADINFO, /* thread info object (internal) */
TAG_CLIPBOARD, /* clipboard data (internal) */
0, /* CALLPROCDATA */
TAG_ACCEL, /* accel table */
TAG_DDE9, /* dde access */
TAG_DDEa, /* dde conversation */
TAG_DDEb, /* ddex */
0, /* zombie */
TAG_KBDLAYOUT, /* keyboard layout */
TAG_KBDFILE, /* keyboard file */
#ifdef FE_IME
0, /* input context */
#endif
};
#ifdef DEBUG
PVOID LockRecordLookasideBase;
PVOID LockRecordLookasideBounds;
ZONE_HEADER LockRecordLookasideZone;
ULONG AllocLockRecordHiWater;
ULONG AllocLockRecordCalls;
ULONG AllocLockRecordSlowCalls;
ULONG DelLockRecordCalls;
ULONG DelLockRecordSlowCalls;
NTSTATUS InitLockRecordLookaside();
void FreeLockRecord(PLR plr);
#endif
void HMDestroyUnlockedObject(PHE phe);
void HMRecordLock(PVOID ppobj, PVOID pobj, DWORD cLockObj, PVOID pfn);
BOOL HMUnrecordLock(PVOID ppobj, PVOID pobj);
VOID ShowLocks(PHE);
BOOL HMRelocateLockRecord(PVOID ppobjNew, int cbDelta);
/***************************************************************************\
* HMInitHandleTable
*
* Initialize the handle table. Unused entries are linked together.
*
* 01-13-92 ScottLu Created.
\***************************************************************************/
#define CHANDLEENTRIESINIT 200
#define CLOCKENTRIESINIT 100
BOOL HMInitHandleTable(
PVOID pReadOnlySharedSectionBase)
{
int i;
PHE pheT;
NTSTATUS Status;
/*
* 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;
#ifdef DEBUG
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.
*/
gpsi->cbHandleTable = PAGE_SIZE;
Status = CommitReadOnlyMemory(ghReadOnlySharedSection,
gpsi->cbHandleTable, 0);
gSharedInfo.aheList = pReadOnlySharedSectionBase;
gpsi->cHandleEntries = gpsi->cbHandleTable / sizeof(HANDLEENTRY);
gcHandlePages = 1;
/*
* Put these free handles on the free list. The last free handle points
* to NULL. Use indexes; the handle table may move around in memory when
* growing.
*/
RtlZeroMemory(gSharedInfo.aheList, gpsi->cHandleEntries * sizeof(HANDLEENTRY));
for (pheT = gSharedInfo.aheList, i = 0; i < (int)gpsi->cHandleEntries; i++, pheT++) {
pheT->phead = ((PHEAD)(((PBYTE)i) + 1));
pheT->bType = TYPE_FREE;
pheT->wUniq = 1;
}
(pheT - 1)->phead = NULL;
/*
* Reserve the first handle table entry so that PW(NULL) 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 NULL
* will fail.
*/
gpHandlePages[0].iheFree = 1;
gpHandlePages[0].iheLimit = gpsi->cHandleEntries;
RtlZeroMemory(&gSharedInfo.aheList[0], sizeof(HANDLEENTRY));
gSharedInfo.aheList[0].bType = TYPE_FREE;
gSharedInfo.aheList[0].wUniq = 1;
return TRUE;
}
/***************************************************************************\
* HMGrowHandleTable
*
* Grows the handle table. Assumes the handle table already exists.
*
* 01-13-92 ScottLu Created.
\***************************************************************************/
BOOL HMGrowHandleTable()
{
DWORD i;
PHE pheT;
PVOID p;
PHANDLEPAGE phpNew;
DWORD dwCommitOffset;
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 >= ghheapSharedRO) {
return FALSE;
}
dwCommitOffset = (ULONG)((PBYTE)p - (PBYTE)gpReadOnlySharedSectionBase);
Status = CommitReadOnlyMemory(ghReadOnlySharedSection,
PAGE_SIZE, dwCommitOffset);
if (!NT_SUCCESS(Status))
return FALSE;
phpNew = &gpHandlePages[gcHandlePages++];
/*
* Update the global information to include the new
* page.
*/
phpNew->iheFree = gpsi->cHandleEntries;
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;
/*
* Link all the new handle entries together.
*/
i = phpNew->iheFree;
RtlZeroMemory(&gSharedInfo.aheList[i],
(gpsi->cHandleEntries - i) * sizeof(HANDLEENTRY));
for (pheT = &gSharedInfo.aheList[i]; i < gpsi->cHandleEntries; i++, pheT++) {
pheT->phead = ((PHEAD)(((PBYTE)i) + 1));
pheT->bType = TYPE_FREE;
pheT->wUniq = 1;
}
/*
* There are no old free entries (since we're growing the table), so the
* last new free handle points to 0.
*/
(pheT - 1)->phead = 0;
return TRUE;
}
/***************************************************************************\
* HMAllocObject
*
* Allocs a handle by removing it from the free list.
*
* 01-13-92 ScottLu Created.
\***************************************************************************/
#define TRACE_OBJECT_ALLOCS 0
#define TYPE_MAXTYPES 20
#if (DBG || TRACE_OBJECT_ALLOCS)
DWORD acurObjectCount[TYPE_MAXTYPES] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
DWORD amaxObjectCount[TYPE_MAXTYPES] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
DWORD atotObjectCount[TYPE_MAXTYPES] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
DWORD abasObjectCount[TYPE_MAXTYPES] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
DWORD asizObjectCount[TYPE_MAXTYPES] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
#endif // TRACE_OBJECT_ALLOCS
/***************************************************************************\
* 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;
DWORD iheFreeOdd = 0;
DWORD iheFree;
PHANDLEPAGE php;
BOOL fPoolAlloc = TRUE;
BYTE bCreateFlags;
/*
* If there are no more free handles, grow the table.
*/
TryFreeHandle:
iheFree = 0;
php = gpHandlePages;
for (i = 0; i < gcHandlePages; ++i, ++php)
if (php->iheFree != 0) {
iheFree = php->iheFree;
break;
}
if (iheFree == 0) {
HMGrowHandleTable();
/*
* If the table didn't grow, get out.
*/
if (i == gcHandlePages) {
RIPMSG0(RIP_WARNING, "USER: HMAllocObject: could not grow handle space\n");
return NULL;
}
/*
* Because the handle page table may have moved,
* recalc the page entry pointer.
*/
php = &gpHandlePages[i];
iheFree = php->iheFree;
UserAssert(iheFree);
}
/*
* NOTE: the next two tests will nicely fail if iheFree == 0
*
* If the next handle is 0xFFFF, we need to treat it specially because
* internally 0xFFFF is a constant.
*/
if (LOWORD(iheFree) == 0xFFFF) {
/*
* Reserve this table entry so that PW(FFFF) 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 FFFF
* will fail.
*/
pheT = &gSharedInfo.aheList[iheFree];
php->iheFree = (DWORD)pheT->phead;
RtlZeroMemory(pheT, sizeof(HANDLEENTRY));
pheT->bType = TYPE_FREE;
pheT->wUniq = 1;
goto TryFreeHandle;
}
/*
* 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
*/
if ((bType == TYPE_WINDOW) && (iheFree & 1)) {
/*
* The handle following iheFree is the next handle to try
*/
pheT = &gSharedInfo.aheList[iheFree];
php->iheFree = (DWORD)pheT->phead;
/*
* add the old first free HE to the free odd list (of indices)
*/
pheT->phead = (PHEAD)iheFreeOdd;
iheFreeOdd = pheT - gSharedInfo.aheList;
goto TryFreeHandle;
}
if (iheFree == 0) {
RIPMSG0(RIP_WARNING, "USER: HMAllocObject: out of handles\n");
/*
* In a very rare case we can't allocate any more handles but
* we had some odd handles that couldn't be used; they're
* now the free list but usually iheFreeOdd == 0;
*/
php->iheFree = iheFreeOdd;
return NULL;
}
/*
* Now we have a free handle we can use, iheFree, so link in the Odd
* handles we couldn't use
*/
if (iheFreeOdd) {
DWORD iheNextFree;
/*
* link the start of the free odd list right after the first free
* then walk the odd list until the end and link the end of the
* odd list into the start or the free list.
*/
pheT = &gSharedInfo.aheList[iheFree];
iheNextFree = (DWORD)pheT->phead;
pheT->phead = (PHEAD)iheFreeOdd;
while (pheT->phead)
pheT = &gSharedInfo.aheList[(DWORD)pheT->phead];
pheT->phead = (PHEAD)iheNextFree;
}
/*
* Try to allocate the object. If this fails, bail out.
*/
bCreateFlags = gabObjectCreateFlags[bType];
switch (bType) {
case TYPE_WINDOW:
if (pdeskSrc == NULL) {
phead = (PHEAD)UserAllocPoolWithQuota(size, TAG_WINDOW);
break;
}
/*
* Fall through
*/
case TYPE_MENU:
case TYPE_HOOK:
case TYPE_CALLPROC:
#ifdef FE_IME
case TYPE_INPUTCONTEXT:
#endif
fPoolAlloc = FALSE;
/*
* Fail the allocation if the desktop is destroyed.
* LATER: GerardoB.
* Change DesktopAlloc so it takes the pdesk; the move this check
* in there. Sometimes we call DesktopAlloc directly (TYPE_CLASS).
*/
if (pdeskSrc->dwDTFlags & DF_DESTROYED) {
RIPMSG1(RIP_WARNING, "HMAllocObject: pdeskSrc is destroyed:%#lx", pdeskSrc);
return NULL;
}
phead = (PHEAD)DesktopAlloc(pdeskSrc->hheapDesktop, size);
if (phead == NULL)
break;
LockDesktop(&((PSHROBJHEAD)phead)->rpdesk, pdeskSrc);
((PSHROBJHEAD)phead)->pSelf = (PBYTE)phead;
break;
default:
if (bCreateFlags & OCF_USEQUOTA)
phead = (PHEAD)UserAllocPoolWithQuota(size, gdwAllocTag[bType]);
else
phead = (PHEAD)UserAllocPool(size, gdwAllocTag[bType]);
break;
}
if (phead == NULL) {
RIPERR0(ERROR_NOT_ENOUGH_MEMORY,
RIP_WARNING,
"USER: HMAllocObject: out of memory\n");
return NULL;
}
/*
* If the memory came from pool, zero it.
*/
if (fPoolAlloc)
RtlZeroMemory(phead, size);
/*
* The free handle pointer points to the next free handle.
*/
pheT = &gSharedInfo.aheList[iheFree];
php->iheFree = (DWORD)pheT->phead;
/*
* Track high water mark for handle allocation.
*/
if ((DWORD)iheFree > giheLast) {
giheLast = iheFree;
}
/*
* Setup the handle contents, plus initialize the object header.
*/
pheT->bType = bType;
pheT->phead = phead;
if (bCreateFlags & OCF_PROCESSOWNED) {
if (ptiOwner != NULL) {
((PPROCOBJHEAD)phead)->ppi = ptiOwner->ppi;
if ((ptiOwner->TIF_flags & TIF_16BIT) && (ptiOwner->ptdb)) {
((PPROCOBJHEAD)phead)->hTaskWow = ptiOwner->ptdb->hTaskWow;
} else {
((PPROCOBJHEAD)phead)->hTaskWow = 0;
}
pheT->pOwner = ptiOwner->ppi;
} else
pheT->pOwner = NULL;
} else if (bCreateFlags & OCF_THREADOWNED) {
if (bCreateFlags & OCF_MARKTHREAD)
((PTHROBJHEAD)phead)->pti = ptiOwner;
pheT->pOwner = ptiOwner;
}
phead->h = HMHandleFromIndex(iheFree);
/*
* Return a handle entry pointer.
*/
return pheT->phead;
}
#if 0
#define HANDLEF_FREECHECK 0x80
VOID CheckHMTable(
PVOID pobj)
{
PHE pheT, pheMax;
if (giheLast) {
pheMax = &gSharedInfo.aheList[giheLast];
for (pheT = gSharedInfo.aheList; pheT <= pheMax; pheT++) {
if (pheT->bType == TYPE_FREE) {
continue;
}
if (pheT->phead == pobj && !(pheT->bFlags & HANDLEF_FREECHECK)) {
UserAssert(FALSE);
}
}
}
}
#endif
/***************************************************************************\
* HMFreeObject
*
* This destroys an object - the handle and the referenced memory. To check
* to see if destroying is ok, HMMarkObjectDestroy() should be called.
*
* 01-13-92 ScottLu Created.
\***************************************************************************/
BOOL HMFreeObject(
PVOID pobj)
{
PHE pheT;
WORD wUniqT;
PHANDLEPAGE php;
DWORD i;
DWORD iheCurrent;
PSHROBJHEAD phead;
PDESKTOP pdesk;
#ifdef DEBUG
PLR plrT, plrNextT;
#endif
/*
* Free the object first.
*/
pheT = HMPheFromObject(pobj);
UserAssert(((PHEAD)pobj)->cLockObj == 0);
#ifndef DEBUG
switch(pheT->bType) {
case TYPE_MENU:
case TYPE_WINDOW:
case TYPE_HOOK:
case TYPE_CALLPROC:
#ifdef FE_IME
case TYPE_INPUTCONTEXT:
#endif
phead = (PSHROBJHEAD)pobj;
pdesk = phead->rpdesk;
if (pdesk != NULL) {
UnlockDesktop(&phead->rpdesk);
DesktopFree(pdesk->hheapDesktop, (HANDLE)phead);
} else {
UserFreePool(phead);
}
break;
case TYPE_SETWINDOWPOS:
if (((PSMWP)(pobj))->acvr != NULL)
UserFreePool(((PSMWP)(pobj))->acvr);
// FALL THROUGH!!!
default:
UserFreePool((HANDLE)pobj);
break;
}
#else // DEBUG
#if 0
pheT->bFlags |= HANDLEF_FREECHECK; // marker for later check.
#endif
/*
* 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.
*/
if (HMRevalidateHandle(pheT->phead->h) == NULL)
goto AlreadyFree;
switch (pheT->bType) {
#if 0
case TYPE_CURSOR:
/*
* Search all caches and make sure this bugger is not referenced
* and that the caches are cool.
*/
UserAssert(!(((PCURSOR)pobj)->CURSORF_flags & CURSORF_LINKED));
{
PCURSOR *ppcurT, *ppcurFirst;
PPROCESSINFO ppi;
ppcurFirst = &gpcurFirst;
for (ppcurT = ppcurFirst; *ppcurT != NULL; ppcurT = &((*ppcurT)->pcurNext)) {
if (*ppcurT == pobj) {
UserAssert(FALSE);
}
UserAssert(HtoP(PtoH(*ppcurT)));
}
for (ppi = gppiStarting; ppi != NULL; ppi = ppi->ppiNext) {
ppcurFirst = &ppi->pCursorCache;
for (ppcurT = ppcurFirst; *ppcurT != NULL; ppcurT = &((*ppcurT)->pcurNext)) {
if (*ppcurT == pobj) {
UserAssert(FALSE);
}
UserAssert(HtoP(PtoH(*ppcurT)));
}
}
}
UserFreePool((HANDLE)pobj);
break;
#endif
case TYPE_MENU:
case TYPE_WINDOW:
case TYPE_HOOK:
case TYPE_CALLPROC:
#ifdef FE_IME
case TYPE_INPUTCONTEXT:
#endif
phead = (PSHROBJHEAD)pobj;
pdesk = phead->rpdesk;
if (pdesk != NULL) {
UnlockDesktop(&phead->rpdesk);
if (DesktopFree(pdesk->hheapDesktop, pheT->phead))
goto AlreadyFree;
} else {
UserFreePool(phead);
}
break;
case TYPE_SETWINDOWPOS:
if (((PSMWP)(pobj))->acvr != NULL)
UserFreePool(((PSMWP)(pobj))->acvr);
/*
* fall through to default case.
*/
default:
UserFreePool((HANDLE)pobj);
break;
}
if (pheT->bType == TYPE_FREE) {
AlreadyFree:
RIPMSG1(RIP_ERROR, "Object already freed!!! %08lx", pheT);
return FALSE;
}
/*
* 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);
RtlZeroMemory(pheT, sizeof(HANDLEENTRY));
pheT->wUniq = wUniqT;
/*
* Change the handle type to TYPE_FREE so we know what type this handle
* is.
*/
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) {
pheT->phead = (PHEAD)php->iheFree;
php->iheFree = iheCurrent;
break;
}
}
pheT->pOwner = NULL;
return TRUE;
}
/***************************************************************************\
* HMMarkObjectDestroy
*
* Marks an object for destruction, returns TRUE if object can be destroyed.
*
* 02-10-92 ScottLu Created.
\***************************************************************************/
BOOL HMMarkObjectDestroy(
PVOID pobj)
{
PHE phe;
phe = HMPheFromObject(pobj);
#ifdef DEBUG
/*
* Record where the object was marked for destruction.
*/
if (gfTrackLocks) {
if (!(phe->bFlags & HANDLEF_DESTROY)) {
PVOID pfn1, pfn2;
RtlGetCallersAddress(&pfn1, &pfn2);
HMRecordLock(pfn1, pobj, ((PHEAD)pobj)->cLockObj, 0);
}
}
#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;
}
#ifdef DEBUG
/*
* 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 handles destruction of non-secure objects.
*
* 10-13-94 JimA Created.
\***************************************************************************/
BOOL HMDestroyObject(
PVOID pobj)
{
PHE phe;
phe = HMPheFromObject(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;
}
/***************************************************************************\
* HMRecordLock
*
* This routine records a lock on a "lock list", so that locks and unlocks
* can be tracked in the debugger. Only called if gfTrackLocks == TRUE.
*
* 02-27-92 ScottLu Created.
\***************************************************************************/
#ifdef DEBUG
NTSTATUS
InitLockRecordLookaside()
{
ULONG BlockSize;
ULONG InitialSegmentSize;
BlockSize = (sizeof(LOCKRECORD) + 7) & ~7;
InitialSegmentSize = 1000 * BlockSize + sizeof(ZONE_SEGMENT_HEADER);
LockRecordLookasideBase = UserAllocPool(InitialSegmentSize, TAG_LOOKASIDE);
if ( !LockRecordLookasideBase ) {
return STATUS_NO_MEMORY;
}
LockRecordLookasideBounds = (PVOID)((PUCHAR)LockRecordLookasideBase + InitialSegmentSize);
return ExInitializeZone(&LockRecordLookasideZone,
BlockSize,
LockRecordLookasideBase,
InitialSegmentSize);
}
PLR AllocLockRecord()
{
PLR plr;
/*
* Attempt to get a LOCKRECORD from the zone. If this fails, then
* LocalAlloc the LOCKRECORD
*/
plr = ExAllocateFromZone(&LockRecordLookasideZone);
if ( !plr ) {
/*
* Allocate a Q message structure.
*/
AllocLockRecordSlowCalls++;
if ((plr = (PLR)UserAllocPool(sizeof(LOCKRECORD), TAG_LOCKRECORD)) == NULL)
return NULL;
}
RtlZeroMemory(plr, sizeof(*plr));
AllocLockRecordCalls++;
if (AllocLockRecordCalls-DelLockRecordCalls > AllocLockRecordHiWater ) {
AllocLockRecordHiWater = AllocLockRecordCalls-DelLockRecordCalls;
}
return plr;
}
void FreeLockRecord(
PLR plr)
{
DelLockRecordCalls++;
/*
* If the plr was from zone, then free to zone
*/
if ( (PVOID)plr >= LockRecordLookasideBase && (PVOID)plr < LockRecordLookasideBounds ) {
ExFreeToZone(&LockRecordLookasideZone, plr);
} else {
DelLockRecordSlowCalls++;
UserFreePool((HLOCAL)plr);
}
}
void HMRecordLock(
PVOID ppobj,
PVOID pobj,
DWORD cLockObj,
PVOID pfn)
{
PHE phe;
PLR plr;
int i;
phe = HMPheFromObject(pobj);
if ((plr = AllocLockRecord()) == NULL)
return;
plr->plrNext = phe->plr;
phe->plr = plr;
if (((PHEAD)pobj)->cLockObj > cLockObj) {
i = (int)cLockObj;
i = -i;
cLockObj = (DWORD)i;
}
plr->ppobj = ppobj;
plr->cLockObj = cLockObj;
plr->pfn = pfn;
return;
}
#endif // DEBUG
/***************************************************************************\
* HMLockObject
*
* This routine locks an object. This is a macro in retail systems.
*
* 02-24-92 ScottLu Created.
\***************************************************************************/
#ifdef DEBUG
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 = HMRevalidateHandle(h);
if (!pobj || pobj != pobjValidate) {
RIPMSG2(RIP_ERROR,
"HMLockObject called with invalid object = %08lx, handle = %08lx",
pobj, h);
return;
}
/*
* Inc the reference count.
*/
((PHEAD)pobj)->cLockObj++;
if (((PHEAD)pobj)->cLockObj == 0)
RIPMSG1(RIP_ERROR, "Object lock count has overflowed: %08lx", pobj);
}
#endif // DEBUG
/***************************************************************************\
* HMUnlockObject
*
* This routine unlocks an object. pobj is returned if the object is still
* around after the unlock.
*
* 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) {
#ifdef DEBUG
PVOID pfn1, pfn2;
/*
* If DEBUG && gfTrackLocks, track assignment locks.
*/
if (gfTrackLocks) {
RtlGetCallersAddress(&pfn1, &pfn2);
if (!HMUnrecordLock(ppobj, pobjOld)) {
HMRecordLock(ppobj, pobjOld, ((PHEAD)pobjOld)->cLockObj - 1, pfn1);
}
}
#endif
/*
* 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 (pobj != NULL) {
#ifdef DEBUG
PVOID pfn1, pfn2;
UserAssert(HMValidateHandle(((PHEAD)pobj)->h, TYPE_GENERIC));
/*
* If DEBUG && gfTrackLocks, track assignment locks.
*/
if (gfTrackLocks) {
RtlGetCallersAddress(&pfn1, &pfn2);
HMRecordLock(ppobj, pobj, ((PHEAD)pobj)->cLockObj + 1, pfn1);
if (HMIsMarkDestroy(pobj))
RIPMSG1(RIP_WARNING, "Locking object marked for destruction (%lX)", pobj);
}
#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;
}
/***************************************************************************\
* 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 HMAssignmentUnlock(
PVOID *ppobj)
{
PVOID pobjOld;
pobjOld = *ppobj;
*ppobj = NULL;
/*
* Unlocks the old, locks the new.
*/
if (pobjOld != NULL) {
#ifdef DEBUG
PVOID pfn1, pfn2;
/*
* If DEBUG && gfTrackLocks, track assignment locks.
*/
if (gfTrackLocks) {
RtlGetCallersAddress(&pfn1, &pfn2);
if (!HMUnrecordLock(ppobj, pobjOld)) {
HMRecordLock(ppobj, pobjOld, ((PHEAD)pobjOld)->cLockObj - 1, pfn1);
}
}
#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.
\***************************************************************************/
#ifdef DEBUG
VOID
IsValidThreadLock(
PTHREADINFO pti,
PTL ptl)
{
PETHREAD Thread = PsGetCurrentThread();
if (ptl->pti != pti) {
RIPMSG1(RIP_ERROR,
"This thread lock does not belong to this thread %08lx\n",
ptl);
}
UserAssert((DWORD)ptl > (DWORD)&Thread);
UserAssert((DWORD)ptl < (DWORD)KeGetCurrentThread()->StackBase);
}
#endif
/***************************************************************************\
* ValidateThreadLocks
*
* This routine validates the thread lock list of a thread.
*
* 03-10-92 ScottLu Created.
\***************************************************************************/
#ifdef DEBUG
void
ValidateThreadLocks(
PTL NewLock,
PTL OldLock)
{
PTHREADINFO ptiCurrent = PtiCurrent();
/*
* Validate the new thread lock.
*/
IsValidThreadLock(ptiCurrent, NewLock);
/*
* 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.
*/
if (NewLock == OldLock) {
RIPMSG1(RIP_ERROR,
"This thread lock address is already in the thread list %08lx\n",
NewLock);
}
/*
* Validate the old thread lock.
*/
IsValidThreadLock(ptiCurrent, OldLock);
OldLock = OldLock->next;
}
}
#endif
/***************************************************************************\
* ThreadLock
*
* This api is used for locking objects across callbacks, so they are still
* there when the callback returns.
*
* 03-04-92 ScottLu Created.
\***************************************************************************/
#ifdef DEBUG
void
ThreadLock(
PVOID pobj,
PTL ptl)
{
PTHREADINFO ptiCurrent;
PVOID pfnT;
/*
* This is a handy place, because it is called so often, to see if User is
* eating up too much stack.
*/
UserAssert(((DWORD)&pfnT - (DWORD)KeGetCurrentThread()->StackLimit) > KERNEL_STACK_MINIMUM_RESERVE);
/*
* 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(HtoP(PtoH(pobj)) == pobj);
UserAssert(!(PpiCurrent()->W32PF_Flags & W32PF_TERMINATED));
ptiCurrent = PtiCurrent();
UserAssert(ptiCurrent);
/*
* Get the callers address and validate the thread lock list.
*/
RtlGetCallersAddress(&ptl->pfn, &pfnT);
ptl->pti = ptiCurrent;
ValidateThreadLocks(ptl, ptiCurrent->ptl);
ptl->next = ptiCurrent->ptl;
ptiCurrent->ptl = ptl;
ptl->pobj = pobj;
if (pobj != NULL) {
HMLockObject(pobj);
}
return;
}
#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.
\***************************************************************************/
#ifdef DEBUG
PVOID
ThreadUnlock1(
PTL ptlIn)
#else
PVOID
ThreadUnlock1(
VOID)
#endif
{
PHEAD phead;
PTHREADINFO ptiCurrent;
PTL ptl;
/*
* Remove the thread lock structure from the thread lock list.
*/
ptiCurrent = PtiCurrent();
ptl = ptiCurrent->ptl;
/*
* make sure that the ptl we are looking at is on the stack before
* our current stack position but not before the beginning of the stack
*/
UserAssert((DWORD)ptl > (DWORD)&ptl);
UserAssert((DWORD)ptl < (DWORD)KeGetCurrentThread()->StackBase);
UserAssert(((DWORD)ptl->next > (DWORD)&ptl) || ((DWORD)ptl->next == 0));
UserAssert(ptlIn == ptl);
ptiCurrent->ptl = ptl->next;
#ifdef DEBUG
/*
* Validate the thread lock list.
*/
ValidateThreadLocks(ptl, ptiCurrent->ptl);
#endif
/*
* 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);
}
return (PVOID)phead;
}
/*
* Switch back to default optimization.
*/
#pragma optimize("", on)
/***************************************************************************\
* CheckLock
*
* This routine only exists in DEBUG builds - it checks to make sure objects
* are thread locked.
*
* 03-09-92 ScottLu Created.
\***************************************************************************/
#ifdef DEBUG
void CheckLock(
PVOID pobj)
{
PTHREADINFO ptiCurrent;
PTL ptl;
if (pobj == NULL)
return;
// if (KeGetPreviousMode() != UserMode)
// return;
ptiCurrent = PtiCurrentShared();
UserAssert(ptiCurrent);
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! 0x%08lx", pobj);
}
#endif
/***************************************************************************\
* HMDestroyUnlockedObject
*
* We're destroying the object based on an unlock... which means 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)
{
PTHREADINFO ptiCurrent;
/*
* The object has been unlocked and needs to be destroyed. Change
* the ownership on this object to be the current thread: this'll
* make sure DestroyWindow() doesn't send destroy messages across
* threads.
*/
if (gabObjectCreateFlags[phe->bType] & OCF_PROCESSOWNED) {
((PPROCOBJHEAD)phe->phead)->ppi = (PPROCESSINFO)phe->pOwner =
PpiCurrent();
}
/*
* 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.
*/
switch(phe->bType) {
case TYPE_CURSOR:
_DestroyCursor((PCURSOR)phe->phead, CURSOR_THREADCLEANUP);
break;
case TYPE_HOOK:
FreeHook((PHOOK)phe->phead);
break;
case TYPE_ACCELTABLE:
case TYPE_SETWINDOWPOS:
case TYPE_CALLPROC:
/*
* Mark the object for destruction - if it says it's ok to free,
* then free it.
*/
if (HMMarkObjectDestroy(phe->phead))
HMFreeObject(phe->phead);
break;
case TYPE_MENU:
_DestroyMenu((PMENU)phe->phead);
break;
case TYPE_WINDOW:
ptiCurrent = PtiCurrent();
if ((PTHREADINFO)phe->pOwner != ptiCurrent) {
UserAssert(PsGetCurrentThread()->Tcb.Win32Thread);
if(PsGetCurrentThread()->Tcb.Win32Thread == NULL)
break;
HMChangeOwnerThread(phe->phead, ptiCurrent);
}
xxxDestroyWindow((PWND)phe->phead);
break;
case TYPE_DDECONV:
FreeDdeConv((PDDECONV)phe->phead);
break;
case TYPE_DDEXACT:
FreeDdeXact((PXSTATE)phe->phead);
break;
case TYPE_KBDLAYOUT:
DestroyKL((PKL)phe->phead);
break;
case TYPE_KBDFILE:
/*
* Remove keyboard file from global list.
*/
RemoveKeyboardLayoutFile((PKBDFILE)phe->phead);
UserFreePool(((PKBDFILE)phe->phead)->hBase);
HMFreeObject(phe->phead);
break;
#ifdef FE_IME
case TYPE_INPUTCONTEXT:
FreeInputContext((PIMC)phe->phead);
break;
#endif
}
}
/***************************************************************************\
* 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;
if (gabObjectCreateFlags[phe->bType] & OCF_MARKTHREAD)
((PTHROBJHEAD)(pobj))->pti = pti;
phe->pOwner = pti;
/*
* If this is a window, update the window counts.
*/
if (phe->bType == TYPE_WINDOW) {
UserAssert(ptiOld->cWindows > 0);
ptiOld->cWindows--;
pti->cWindows++;
/*
* If the owning process is changing, fix up
* the window class.
*/
if (pti->ppi != ptiOld->ppi) {
pwnd = (PWND)pobj;
ppcls = GetClassPtr(pwnd->pcls->atomClassName, pti->ppi, hModuleWin);
UserAssert(ppcls);
if (ppcls == NULL) {
if (pwnd->head.rpdesk)
ppi = pwnd->head.rpdesk->rpwinstaParent->ptiDesktop->ppi;
else
ppi == PpiCurrent();
ppcls = GetClassPtr(gpsi->atomSysClass[ICLS_ICONTITLE], ppi, hModuleWin);
}
UserAssert(ppcls);
DereferenceClass(pwnd);
pwnd->pcls = *ppcls;
ReferenceClass(pwnd->pcls, pwnd);
}
}
}
/***************************************************************************\
* 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()
{
PTHREADINFO ptiCurrent;
HANDLEENTRY volatile * (*pphe);
PHE pheT;
DWORD i;
ptiCurrent = PtiCurrent();
/*
* 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((DWORD)ptiCurrent->ptl > (DWORD)&i);
UserAssert((DWORD)ptiCurrent->ptl < (DWORD)KeGetCurrentThread()->StackBase);
ThreadUnlock(ptiCurrent->ptl);
}
while (ptiCurrent->ptlOb != NULL) {
ThreadUnlockObject(ptiCurrent);
}
while (ptiCurrent->ptlPool != NULL) {
ThreadUnlockAndFreePool(ptiCurrent, ptiCurrent->ptlPool);
}
/*
* 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 (gabObjectCreateFlags[pheT->bType] & 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;
/*
* 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);
}
}
#ifdef DEBUG
LPCSTR aszObjectTypes[TYPE_CTYPES] = {
"Free",
"Window",
"Menu",
"Icon/Cursor",
"WPI(SWP) structure",
"Hook",
"ThreadInfo",
"Input Queue",
"CallProcData",
"Accelerator",
"DDE access",
"DDE conv",
"DDE Transaction",
"Zombie",
"Keyboard Layout",
#ifdef FE_IME
"Input Context",
#endif
};
#endif
#ifdef DEBUG
VOID ShowLocks(
PHE phe)
{
PLR plr = phe->plr;
INT c;
KdPrint(("USERSRV: Lock records for %s %lx:\n",
aszObjectTypes[phe->bType], 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->pfn == NULL) {
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);
}
}
}
KdPrint((" %s cLock=%d, pobj at 0x%08lx, code at 0x%08lx\n",
achPrint,
abs((int)plr->cLockObj),
plr->ppobj,
plr->pfn));
plr = plr->plrNext;
c++;
}
RIPMSG1(RIP_WARNING, " 0x%lx records\n", c);
}
#endif
/***************************************************************************\
* 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;
/*
* Loop through the table destroying all objects created 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++) {
/*
* Check against free before we look at ppi... because pq is stored
* in the object itself, which won't be there if TYPE_FREE.
*/
if (pheT->bType == TYPE_FREE)
continue;
/*
* Destroy those objects created by this queue.
*/
if (!(gabObjectCreateFlags[pheT->bType] & OCF_PROCESSOWNED) ||
(PPROCESSINFO)pheT->pOwner != ppi)
continue;
/*
* 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) {
/*
* Clear this so it isn't referenced after being freed.
*/
pheT->pOwner = NULL;
continue;
}
/*
* Destroy this object.
*/
HMDestroyUnlockedObject(pheT);
}
}
/***************************************************************************\
* 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 (gabObjectCreateFlags[pheT->bType] & OCF_PROCESSOWNED ||
(PTHREADINFO)pheT->pOwner != pti)
continue;
HMChangeOwnerThread(pheT->phead, gptiRit);
#ifdef DEBUG
#ifdef DEBUG_LOCKS
/*
* Object still around: print warning message.
*/
if (pheT->bFlags & HANDLEF_DESTROY) {
if ((pheT->phead->cLockObj == 1)
&& (pheT->bFlags & HANDLEF_INWAITFORDEATH)) {
RIPMSG1(RIP_WARNING,
"USERSRV Warning: Only killer has thread object 0x%08lx locked (OK).\n",
pheT->phead->h);
} else {
RIPMSG2(RIP_WARNING,
"USERSRV Warning: Zombie %s 0x%08lx still locked\n",
aszObjectTypes[pheT->bType], pheT->phead->h);
}
} else {
RIPMSG1(RIP_WARNING, "USERSRV Warning: Thread object 0x%08lx not destroyed.\n", pheT->phead->h);
}
if (gfTrackLocks) {
ShowLocks(pheT);
}
#endif // DEBUG_LOCKS
#endif // DEBUG
}
}
/***************************************************************************\
* 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.
\***************************************************************************/
#ifdef DEBUG
BOOL HMRelocateLockRecord(
PVOID ppobjNew,
int 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) {
KdPrint(("HmRelocateLockRecord(%lx, %lx) - %lx is bad pobj\n",
ppobjNew, cbDelta, pobj));
return FALSE;
}
plr = phe->plr;
while (plr != NULL) {
if (plr->ppobj == ppobjOld) {
(PBYTE)(plr->ppobj) += cbDelta;
return TRUE;
}
plr = plr->plrNext;
}
KdPrint(("HmRelocateLockRecord(%lx, %lx) - couldn't find lock record\n",
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;
}
return FALSE;
}
#endif // DEBUG
/***************************************************************************\
* HMGetStats
*
* This function traverses the handle table and calculates statistics,
* either for the entire system or for objects owned by a specific
* process (and its threads)
*
* Parameters:
* hProcess - handle to the process to query for information about
* iPidType - whether to query for just the process pointed
* to by hProcess or all objects in the table
* (OBJECT_OWNER_CURRENT vs. OBJECT_OWNER_IGNORE)
* pResults - Pointer to the buffer to fill the data into
* cjResultSize - Size of the buffer to fill
*
* The user buffer is expected to be zero'd.
*
* returns: Success state
* 07-31-95 t-andsko Created.
\***************************************************************************/
#define OBJECT_OWNER_IGNORE (0x0001)
#define NUM_USER_TYPES TYPE_CTYPES + 1
NTSTATUS HMGetStats(
IN HANDLE hProcess,
IN int iPidType,
OUT PVOID pResults,
IN UINT cjResultSize)
{
NTSTATUS iRet = STATUS_SUCCESS;
LPDWORD pdwRes = (DWORD *) pResults; //Pointer to the result buffer
PEPROCESS peProcessInfo; //Pointer to process structure
PHE pheCurPos; //Current position in the table
PHE pheMax; //address of last table entry
//Check permissions flag
if (!( (*(DWORD *)NtGlobalFlag) & FLG_POOL_ENABLE_TAGGING))
{
iRet = STATUS_ACCESS_DENIED;
return iRet;
}
//Check buffer is large enough to take the results
if (cjResultSize < NUM_USER_TYPES)
{
iRet = STATUS_BUFFER_TOO_SMALL;
return iRet;
}
if (iPidType == OBJECT_OWNER_CURRENT)
{
//Get pointer to EPROCESS structure from the handle
iRet = PsLookupProcessByProcessId(hProcess, &peProcessInfo);
}
if (NT_SUCCESS(iRet))
{
try
{
//Test buffer
ProbeForWrite(pResults, cjResultSize, sizeof(UCHAR));
//now traverse the handle table to count
pheMax = &gSharedInfo.aheList[giheLast];
for(pheCurPos = gSharedInfo.aheList; pheCurPos <= pheMax; pheCurPos++)
{
if (iPidType == (int) OBJECT_OWNER_IGNORE)
{
if (pheCurPos->bType != TYPE_GENERIC)
pdwRes[pheCurPos->bType]++;
}
else
{
if ((pheCurPos->bType == TYPE_FREE) || (pheCurPos->bType == TYPE_GENERIC))
continue;
UserAssert(pheCurPos->bType < NUM_USER_TYPES);
if (gabObjectCreateFlags[pheCurPos->bType] & OCF_PROCESSOWNED)
{
//Object is owned by process
//some objects may not have an owner
if (pheCurPos->pOwner)
{
if (((PPROCESSINFO)pheCurPos->pOwner)->
Process == peProcessInfo)
{
pdwRes[pheCurPos->bType]++;
}
}
}
else if (gabObjectCreateFlags[pheCurPos->bType] & OCF_THREADOWNED)
{
//Object owned by thread
if (pheCurPos->pOwner)
{
//dereference the thread and from there the process
if (((PTHREADINFO)pheCurPos->pOwner)->Thread->ThreadsProcess == peProcessInfo)
{
pdwRes[pheCurPos->bType]++;
}
}
}
}
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
iRet = STATUS_ACCESS_VIOLATION;
}
}
return iRet;
}
/***************************************************************************\
* KernelPtoH
*
* This function is called from the client to convert pool-based object
* pointers, such as a cursor pointer, to a handle.
*
* HISTORY:
* 11/22/95 BradG Created
\***************************************************************************/
HANDLE KernelPtoH( PVOID pObj ) {
HANDLE h;
UserAssert( pObj != NULL );
try {
h = PtoHq( pObj );
} except (EXCEPTION_EXECUTE_HANDLER) {
h = NULL;
}
return h;
}