mirror of https://github.com/lianthony/NT4.0
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
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;
|
|
}
|