- Don't delete an object that has a shared reference!

- Implement DC_vSelectSurface, that dereferences the old SURFACE and references the new. Use it instead of doing it manually.
- Select NULL surface when doing cleanup.
- Go back to ASSERT in GDIOBJ_ShareUnlockObjByPtr. Should (hopefully) not be hit anymore.
- Add additional functions for tracing shared locks in gdidbg.c.
- Add debugprints when leaking objects on process cleanup.


svn path=/trunk/; revision=40196
This commit is contained in:
Timo Kreuzer 2009-03-23 23:49:00 +00:00
parent 4724aa9fb0
commit c3927d7e15
6 changed files with 83 additions and 26 deletions

View file

@ -278,6 +278,20 @@ INT FASTCALL IntGdiGetDeviceCaps(PDC,INT);
extern PPDEVOBJ pPrimarySurface; extern PPDEVOBJ pPrimarySurface;
VOID
FORCEINLINE
DC_vSelectSurface(PDC pdc, PSURFACE psurfNew)
{
PSURFACE psurfOld = pdc->dclevel.pSurface;
if (psurfOld)
SURFACE_ShareUnlockSurface(psurfOld);
if (psurfNew)
GDIOBJ_IncrementShareCount((POBJ)psurfNew);
pdc->dclevel.pSurface = psurfNew;
}
BOOL FASTCALL BOOL FASTCALL
IntPrepareDriverIfNeeded(); IntPrepareDriverIfNeeded();
extern PDEVOBJ PrimarySurface; extern PDEVOBJ PrimarySurface;

View file

@ -104,16 +104,13 @@ FORCEINLINE
GDIOBJ_ShareUnlockObjByPtr(POBJ Object) GDIOBJ_ShareUnlockObjByPtr(POBJ Object)
{ {
INT cLocks = InterlockedDecrement((PLONG)&Object->ulShareCount); INT cLocks = InterlockedDecrement((PLONG)&Object->ulShareCount);
// ASSERT(cLocks >= 0); ASSERT(cLocks >= 0);
if (cLocks < 0)
{
DbgPrint("Unlocked object %p, that was not locked!\n", Object->hHmgr);
KdSystemDebugControl(TAG('R', 'o', 's', 'D'), NULL, 20, NULL, 0, NULL, KernelMode);
InterlockedIncrement((PLONG)&Object->ulShareCount);
}
return cLocks; return cLocks;
} }
#ifdef GDI_DEBUG
ULONG FASTCALL GDIOBJ_IncrementShareCount(POBJ Object);
#else
ULONG ULONG
FORCEINLINE FORCEINLINE
GDIOBJ_IncrementShareCount(POBJ Object) GDIOBJ_IncrementShareCount(POBJ Object)
@ -122,5 +119,6 @@ GDIOBJ_IncrementShareCount(POBJ Object)
ASSERT(cLocks >= 1); ASSERT(cLocks >= 1);
return cLocks; return cLocks;
} }
#endif
#endif #endif

View file

@ -932,7 +932,7 @@ NtGdiSelectBitmap(
PDC pDC; PDC pDC;
PDC_ATTR pdcattr; PDC_ATTR pdcattr;
HBITMAP hOrgBmp; HBITMAP hOrgBmp;
PSURFACE psurfBmp; PSURFACE psurfBmp, psurfOld;
HRGN hVisRgn; HRGN hVisRgn;
BOOLEAN bFailed; BOOLEAN bFailed;
PBRUSH pbrush; PBRUSH pbrush;
@ -961,23 +961,26 @@ NtGdiSelectBitmap(
return NULL; return NULL;
} }
/* Get the handle for the old bitmap */
psurfOld = pDC->dclevel.pSurface;
hOrgBmp = psurfOld ? psurfOld->BaseObject.hHmgr : NULL;
/* FIXME: ros hack */
hOrgBmp = pDC->rosdc.hBitmap; hOrgBmp = pDC->rosdc.hBitmap;
pDC->rosdc.hBitmap = hBmp; pDC->rosdc.hBitmap = hBmp;
/* Release the old bitmap */ /* Release the old bitmap, reference the new */
if (pDC->dclevel.pSurface) DC_vSelectSurface(pDC, psurfBmp);
SURFACE_ShareUnlockSurface(pDC->dclevel.pSurface);
// If Info DC this is zero and pSurface is moved to DC->pSurfInfo. // If Info DC this is zero and pSurface is moved to DC->pSurfInfo.
pDC->dclevel.pSurface = psurfBmp;
psurfBmp->hDC = hDC; psurfBmp->hDC = hDC;
// if we're working with a DIB, get the palette // if we're working with a DIB, get the palette
// [fixme: only create if the selected palette is null] // [fixme: only create if the selected palette is null]
if (psurfBmp->hSecure) if (psurfBmp->hSecure)
{ {
// pDC->rosdcbitsPerPixel = psurfBmp->dib->dsBmih.biBitCount; ??? // pDC->rosdc.bitsPerPixel = psurfBmp->dib->dsBmih.biBitCount; ???
pDC->rosdc.bitsPerPixel = BitsPerFormat(psurfBmp->SurfObj.iBitmapFormat); pDC->rosdc.bitsPerPixel = BitsPerFormat(psurfBmp->SurfObj.iBitmapFormat);
} }
else else
@ -985,16 +988,16 @@ NtGdiSelectBitmap(
pDC->rosdc.bitsPerPixel = BitsPerFormat(psurfBmp->SurfObj.iBitmapFormat); pDC->rosdc.bitsPerPixel = BitsPerFormat(psurfBmp->SurfObj.iBitmapFormat);
} }
/* FIXME; improve by using a region without a handle and selecting it */
hVisRgn = NtGdiCreateRectRgn(0, hVisRgn = NtGdiCreateRectRgn(0,
0, 0,
psurfBmp->SurfObj.sizlBitmap.cx, psurfBmp->SurfObj.sizlBitmap.cx,
psurfBmp->SurfObj.sizlBitmap.cy); psurfBmp->SurfObj.sizlBitmap.cy);
/* Keep a shared reference on the bitmap */ /* Release the exclusive lock */
GDIOBJ_IncrementShareCount((POBJ)psurfBmp);
SURFACE_UnlockSurface(psurfBmp); SURFACE_UnlockSurface(psurfBmp);
/* Regenerate the XLATEOBJs. */ /* Regenerate the XLATEOBJs. (hack!) */
pbrush = BRUSH_LockBrush(pdcattr->hbrush); pbrush = BRUSH_LockBrush(pdcattr->hbrush);
if (pbrush) if (pbrush)
{ {

View file

@ -140,6 +140,7 @@ DC_Cleanup(PVOID ObjectBody)
PDC pDC = (PDC)ObjectBody; PDC pDC = (PDC)ObjectBody;
if (pDC->rosdc.DriverName.Buffer) if (pDC->rosdc.DriverName.Buffer)
ExFreePoolWithTag(pDC->rosdc.DriverName.Buffer, TAG_DC); ExFreePoolWithTag(pDC->rosdc.DriverName.Buffer, TAG_DC);
DC_vSelectSurface(pDC, NULL);
return TRUE; return TRUE;
} }
@ -264,7 +265,7 @@ IntGdiCreateDC(PUNICODE_STRING Driver,
pdc->dhpdev = PrimarySurface.hPDev; pdc->dhpdev = PrimarySurface.hPDev;
if(pUMdhpdev) pUMdhpdev = pdc->dhpdev; // set DHPDEV for device. if(pUMdhpdev) pUMdhpdev = pdc->dhpdev; // set DHPDEV for device.
pdc->ppdev = (PVOID)&PrimarySurface; pdc->ppdev = (PVOID)&PrimarySurface;
pdc->rosdc.hBitmap = (HBITMAP)PrimarySurface.pSurface; pdc->rosdc.hBitmap = (HBITMAP)PrimarySurface.pSurface; // <- what kind of haxx0ry is that?
// ATM we only have one display. // ATM we only have one display.
pdcattr->ulDirty_ |= DC_PRIMARY_DISPLAY; pdcattr->ulDirty_ |= DC_PRIMARY_DISPLAY;
@ -310,7 +311,7 @@ IntGdiCreateDC(PUNICODE_STRING Driver,
*/ */
pdc->dctype = DC_TYPE_INFO; pdc->dctype = DC_TYPE_INFO;
// pdc->pSurfInfo = // pdc->pSurfInfo =
pdc->dclevel.pSurface = NULL; DC_vSelectSurface(pdc, NULL);
pdcattr->crBackgroundClr = pdcattr->ulBackgroundClr = RGB(255, 255, 255); pdcattr->crBackgroundClr = pdcattr->ulBackgroundClr = RGB(255, 255, 255);
pdcattr->crForegroundClr = RGB(0, 0, 0); pdcattr->crForegroundClr = RGB(0, 0, 0);
DC_UnlockDc( pdc ); DC_UnlockDc( pdc );

View file

@ -7,6 +7,7 @@ static int leak_reported = 0;
#define GDI_STACK_LEVELS 12 #define GDI_STACK_LEVELS 12
static ULONG GDIHandleAllocator[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1]; static ULONG GDIHandleAllocator[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
static ULONG GDIHandleLocker[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1]; static ULONG GDIHandleLocker[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
static ULONG GDIHandleShareLocker[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
static ULONG GDIHandleDeleter[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1]; static ULONG GDIHandleDeleter[GDI_HANDLE_COUNT][GDI_STACK_LEVELS+1];
struct DbgOpenGDIHandle struct DbgOpenGDIHandle
{ {
@ -233,7 +234,6 @@ GdiDbgHTIntegrityCheck()
return r; return r;
} }
#define GDIDBG_TRACECALLER() \ #define GDIDBG_TRACECALLER() \
DPRINT1("-> called from:\n"); \ DPRINT1("-> called from:\n"); \
KeRosDumpStackFrames(NULL, 20); KeRosDumpStackFrames(NULL, 20);
@ -243,6 +243,9 @@ GdiDbgHTIntegrityCheck()
#define GDIDBG_TRACELOCKER(handle) \ #define GDIDBG_TRACELOCKER(handle) \
DPRINT1("-> locked from:\n"); \ DPRINT1("-> locked from:\n"); \
KeRosDumpStackFrames(GDIHandleLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS); KeRosDumpStackFrames(GDIHandleLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
#define GDIDBG_TRACESHARELOCKER(handle) \
DPRINT1("-> locked from:\n"); \
KeRosDumpStackFrames(GDIHandleShareLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
#define GDIDBG_TRACEDELETER(handle) \ #define GDIDBG_TRACEDELETER(handle) \
DPRINT1("-> deleted from:\n"); \ DPRINT1("-> deleted from:\n"); \
KeRosDumpStackFrames(GDIHandleDeleter[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS); KeRosDumpStackFrames(GDIHandleDeleter[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
@ -250,6 +253,8 @@ GdiDbgHTIntegrityCheck()
CaptureStackBackTace((PVOID*)GDIHandleAllocator[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS); CaptureStackBackTace((PVOID*)GDIHandleAllocator[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
#define GDIDBG_CAPTURELOCKER(handle) \ #define GDIDBG_CAPTURELOCKER(handle) \
CaptureStackBackTace((PVOID*)GDIHandleLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS); CaptureStackBackTace((PVOID*)GDIHandleLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
#define GDIDBG_CAPTURESHARELOCKER(handle) \
CaptureStackBackTace((PVOID*)GDIHandleShareLocker[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
#define GDIDBG_CAPTUREDELETER(handle) \ #define GDIDBG_CAPTUREDELETER(handle) \
CaptureStackBackTace((PVOID*)GDIHandleDeleter[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS); CaptureStackBackTace((PVOID*)GDIHandleDeleter[GDI_HANDLE_GET_INDEX(handle)], GDI_STACK_LEVELS);
#define GDIDBG_DUMPHANDLETABLE() \ #define GDIDBG_DUMPHANDLETABLE() \
@ -262,13 +267,25 @@ GdiDbgHTIntegrityCheck()
DPRINT1("[%d] Handle 0x%p Locked by 0x%x (we're 0x%x)\n", Attempts, Handle, PrevThread, Thread); \ DPRINT1("[%d] Handle 0x%p Locked by 0x%x (we're 0x%x)\n", Attempts, Handle, PrevThread, Thread); \
} }
ULONG
FASTCALL
GDIOBJ_IncrementShareCount(POBJ Object)
{
INT cLocks = InterlockedIncrement((PLONG)&Object->ulShareCount);
GDIDBG_CAPTURESHARELOCKER(Object->hHmgr);
ASSERT(cLocks >= 1);
return cLocks;
}
#else #else
#define GDIDBG_TRACECALLER() #define GDIDBG_TRACECALLER()
#define GDIDBG_TRACEALLOCATOR(index) #define GDIDBG_TRACEALLOCATOR(index)
#define GDIDBG_TRACELOCKER(index) #define GDIDBG_TRACELOCKER(index)
#define GDIDBG_TRACESHARELOCKER(index)
#define GDIDBG_CAPTUREALLOCATOR(index) #define GDIDBG_CAPTUREALLOCATOR(index)
#define GDIDBG_CAPTURELOCKER(index) #define GDIDBG_CAPTURELOCKER(index)
#define GDIDBG_CAPTURESHARELOCKER(index)
#define GDIDBG_CAPTUREDELETER(handle) #define GDIDBG_CAPTUREDELETER(handle)
#define GDIDBG_DUMPHANDLETABLE() #define GDIDBG_DUMPHANDLETABLE()
#define GDIDBG_INITLOOPTRACE() #define GDIDBG_INITLOOPTRACE()

View file

@ -481,6 +481,8 @@ GDIOBJ_FreeObj(POBJ pObject, UCHAR BaseType)
* \return Returns TRUE if succesful. * \return Returns TRUE if succesful.
* \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong * \return Returns FALSE if the cleanup routine returned FALSE or the object doesn't belong
* to the calling process. * to the calling process.
*
* \bug This function should return VOID and kill the object no matter what...
*/ */
BOOL INTERNAL_CALL BOOL INTERNAL_CALL
GDIOBJ_FreeObjByHandle(HGDIOBJ hObj, DWORD ExpectedType) GDIOBJ_FreeObjByHandle(HGDIOBJ hObj, DWORD ExpectedType)
@ -537,8 +539,9 @@ LockHandle:
Object = Entry->KernelData; Object = Entry->KernelData;
if (Object->cExclusiveLock == 0 || if ((Object->cExclusiveLock == 0 ||
Object->Tid == (PW32THREAD)PsGetCurrentThreadWin32Thread()) Object->Tid == (PW32THREAD)PsGetCurrentThreadWin32Thread()) &&
Object->ulShareCount == 0)
{ {
BOOL Ret; BOOL Ret;
PW32PROCESS W32Process = PsGetCurrentProcessWin32Process(); PW32PROCESS W32Process = PsGetCurrentProcessWin32Process();
@ -569,6 +572,15 @@ LockHandle:
GDIDBG_CAPTUREDELETER(hObj); GDIDBG_CAPTUREDELETER(hObj);
return Ret; return Ret;
} }
else if (Object->ulShareCount != 0)
{
DPRINT("Object %p, ulShareCount = %d\n", Object->hHmgr, Object->ulShareCount);
GDIDBG_TRACECALLER();
GDIDBG_TRACESHARELOCKER(GDI_HANDLE_GET_INDEX(hObj));
(void)InterlockedExchangePointer((PVOID*)&Entry->ProcessId, PrevProcId);
/* Don't wait on shared locks */
return FALSE;
}
else else
{ {
/* /*
@ -702,8 +714,12 @@ IntDeleteHandlesForProcess(struct _EPROCESS *Process, ULONG ObjectType)
simply ignore this fact here. */ simply ignore this fact here. */
ObjectHandle = (HGDIOBJ)(Index | (Entry->Type << GDI_ENTRY_UPPER_SHIFT)); ObjectHandle = (HGDIOBJ)(Index | (Entry->Type << GDI_ENTRY_UPPER_SHIFT));
if (GDIOBJ_FreeObjByHandle(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE) && if (!GDIOBJ_FreeObjByHandle(ObjectHandle, GDI_OBJECT_TYPE_DONTCARE))
W32Process->GDIObjects == 0) {
DPRINT1("Failed to delete object %p!\n", ObjectHandle);
}
if (W32Process->GDIObjects == 0)
{ {
/* there are no more gdi handles for this process, bail */ /* there are no more gdi handles for this process, bail */
break; break;
@ -723,6 +739,7 @@ BOOL INTERNAL_CALL
GDI_CleanupForProcess(struct _EPROCESS *Process) GDI_CleanupForProcess(struct _EPROCESS *Process)
{ {
PEPROCESS CurrentProcess; PEPROCESS CurrentProcess;
PW32PROCESS W32Process;
DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Process->UniqueProcessId); DPRINT("Starting CleanupForProcess prochandle %x Pid %d\n", Process, Process->UniqueProcessId);
CurrentProcess = PsGetCurrentProcess(); CurrentProcess = PsGetCurrentProcess();
@ -731,6 +748,8 @@ GDI_CleanupForProcess(struct _EPROCESS *Process)
KeAttachProcess(&Process->Pcb); KeAttachProcess(&Process->Pcb);
} }
W32Process = (PW32PROCESS)CurrentProcess->Win32Process;
/* Delete objects. Begin with types that are not referenced by other types */ /* Delete objects. Begin with types that are not referenced by other types */
IntDeleteHandlesForProcess(Process, GDILoObjType_LO_DC_TYPE); IntDeleteHandlesForProcess(Process, GDILoObjType_LO_DC_TYPE);
IntDeleteHandlesForProcess(Process, GDILoObjType_LO_BRUSH_TYPE); IntDeleteHandlesForProcess(Process, GDILoObjType_LO_BRUSH_TYPE);
@ -749,6 +768,10 @@ GDI_CleanupForProcess(struct _EPROCESS *Process)
#endif #endif
DPRINT("Completed cleanup for process %d\n", Process->UniqueProcessId); DPRINT("Completed cleanup for process %d\n", Process->UniqueProcessId);
if (W32Process->GDIObjects > 0)
{
DPRINT1("Leaking %d handles!\n", W32Process->GDIObjects);
}
return TRUE; return TRUE;
} }
@ -973,11 +996,12 @@ GDIOBJ_ShareLockObj(HGDIOBJ hObj, DWORD ExpectedType)
{ {
Object = (POBJ)Entry->KernelData; Object = (POBJ)Entry->KernelData;
#ifdef GDI_DEBUG GDIDBG_CAPTURESHARELOCKER(HandleIndex);
#ifdef GDI_DEBUG3
if (InterlockedIncrement((PLONG)&Object->ulShareCount) == 1) if (InterlockedIncrement((PLONG)&Object->ulShareCount) == 1)
{ {
memset(GDIHandleLocker[HandleIndex], 0x00, GDI_STACK_LEVELS * sizeof(ULONG)); memset(GDIHandleLocker[HandleIndex], 0x00, GDI_STACK_LEVELS * sizeof(ULONG));
RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS, (PVOID*)GDIHandleLocker[HandleIndex], NULL); RtlCaptureStackBackTrace(1, GDI_STACK_LEVELS, (PVOID*)GDIHandleShareLocker[HandleIndex], NULL);
} }
#else #else
InterlockedIncrement((PLONG)&Object->ulShareCount); InterlockedIncrement((PLONG)&Object->ulShareCount);