- Do not make callouts to user-mode while holding a lock on GDI objects.
Fixes crashes in user32_winetest:win and :msg

svn path=/trunk/; revision=64189
This commit is contained in:
Jérôme Gardou 2014-09-18 13:26:55 +00:00
parent 559bf96394
commit 631f96a8a3

View file

@ -1230,14 +1230,13 @@ co_UserGetUpdateRgn(PWND Window, PREGION Rgn, BOOL bErase)
REGION_SetRectRgn(Rgn, Rect.left, Rect.top, Rect.right, Rect.bottom); REGION_SetRectRgn(Rgn, Rect.left, Rect.top, Rect.right, Rect.bottom);
RegionType = IntGdiCombineRgn(Rgn, Rgn, UpdateRgn, RGN_AND); RegionType = IntGdiCombineRgn(Rgn, Rgn, UpdateRgn, RGN_AND);
IntGdiOffsetRgn(Rgn, -Window->rcClient.left, -Window->rcClient.top); IntGdiOffsetRgn(Rgn, -Window->rcClient.left, -Window->rcClient.top);
RGNOBJAPI_Unlock(UpdateRgn);
if (bErase && RegionType != NULLREGION && RegionType != ERROR) if (bErase && RegionType != NULLREGION && RegionType != ERROR)
{ {
co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN); co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
} }
RGNOBJAPI_Unlock(UpdateRgn);
return RegionType; return RegionType;
} }
@ -1265,7 +1264,8 @@ NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
RETURN(ERROR); RETURN(ERROR);
} }
Rgn = RGNOBJAPI_Lock(hRgn, NULL); /* Use a system region, we can't hold GDI locks when doing roundtrips to user mode */
Rgn = IntSysCreateRectpRgn(0, 0, 0, 0);
if (!Rgn) if (!Rgn)
RETURN(ERROR); RETURN(ERROR);
@ -1276,8 +1276,21 @@ NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
RETURN(ret); RETURN(ret);
CLEANUP: CLEANUP:
if (Rgn && (_ret_ != ERROR))
{
PREGION TheRgn = RGNOBJAPI_Lock(hRgn, NULL);
if (!TheRgn)
{
EngSetLastError(ERROR_INVALID_HANDLE);
_ret_ = ERROR;
}
IntGdiCombineRgn(TheRgn, Rgn, NULL, RGN_COPY);
RGNOBJAPI_Unlock(TheRgn);
}
if (Rgn) if (Rgn)
RGNOBJAPI_Unlock(Rgn); REGION_Delete(Rgn);
TRACE("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_); TRACE("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
UserLeave(); UserLeave();
END_CLEANUP; END_CLEANUP;
@ -1427,14 +1440,27 @@ NtUserRedrawWindow(
RETURN( FALSE); RETURN( FALSE);
} }
/* We can't hold lock on GDI obects while doing roundtrips to user mode,
* so use a copy instead */
if (hrgnUpdate) if (hrgnUpdate)
{ {
RgnUpdate = RGNOBJAPI_Lock(hrgnUpdate, NULL); PREGION RgnTemp;
RgnUpdate = IntSysCreateRectpRgn(0, 0, 0, 0);
if (!RgnUpdate) if (!RgnUpdate)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
RETURN(FALSE);
}
RgnTemp = RGNOBJAPI_Lock(hrgnUpdate, NULL);
if (!RgnTemp)
{ {
EngSetLastError(ERROR_INVALID_HANDLE); EngSetLastError(ERROR_INVALID_HANDLE);
RETURN(FALSE); RETURN(FALSE);
} }
IntGdiCombineRgn(RgnUpdate, RgnTemp, NULL, RGN_COPY);
RGNOBJAPI_Unlock(RgnTemp);
} }
UserRefObjectCo(Wnd, &Ref); UserRefObjectCo(Wnd, &Ref);
@ -1450,7 +1476,7 @@ NtUserRedrawWindow(
CLEANUP: CLEANUP:
if (RgnUpdate) if (RgnUpdate)
RGNOBJAPI_Unlock(RgnUpdate); REGION_Delete(RgnUpdate);
TRACE("Leave NtUserRedrawWindow, ret=%i\n",_ret_); TRACE("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
UserLeave(); UserLeave();
END_CLEANUP; END_CLEANUP;
@ -1687,11 +1713,10 @@ NtUserScrollWindowEx(
INT Result; INT Result;
PWND Window = NULL, CaretWnd; PWND Window = NULL, CaretWnd;
HDC hDC; HDC hDC;
PREGION RgnOwn = NULL, RgnTemp, RgnWinupd = NULL; PREGION RgnUpdate = NULL, RgnTemp, RgnWinupd = NULL;
HWND hwndCaret; HWND hwndCaret;
DWORD dcxflags = 0; DWORD dcxflags = 0;
int rdw_flags; int rdw_flags;
BOOL bOwnRgn = TRUE;
NTSTATUS Status = STATUS_SUCCESS; NTSTATUS Status = STATUS_SUCCESS;
DECLARE_RETURN(DWORD); DECLARE_RETURN(DWORD);
USER_REFERENCE_ENTRY Ref, CaretRef; USER_REFERENCE_ENTRY Ref, CaretRef;
@ -1743,17 +1768,26 @@ NtUserScrollWindowEx(
RETURN(NULLREGION); RETURN(NULLREGION);
} }
/* We must use a copy of the region, as we can't hold an exclusive lock
* on it while doing callouts to user-mode */
RgnUpdate = IntSysCreateRectpRgn(0, 0, 0, 0);
if(!RgnUpdate)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
RETURN(ERROR);
}
if (hrgnUpdate) if (hrgnUpdate)
{ {
RgnOwn = RGNOBJAPI_Lock(hrgnUpdate, NULL); RgnTemp = RGNOBJAPI_Lock(hrgnUpdate, NULL);
if (!RgnOwn) if (!RgnTemp)
{ {
RETURN(ERROR); EngSetLastError(ERROR_INVALID_HANDLE);
} RETURN(ERROR);
bOwnRgn = FALSE; }
IntGdiCombineRgn(RgnUpdate, RgnTemp, NULL, RGN_COPY);
RGNOBJAPI_Unlock(RgnTemp);
} }
else
RgnOwn = IntSysCreateRectpRgn(0, 0, 0, 0);
/* ScrollWindow uses the window DC, ScrollWindowEx doesn't */ /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
if (flags & SW_SCROLLWNDDCE) if (flags & SW_SCROLLWNDDCE)
@ -1788,9 +1822,9 @@ NtUserScrollWindowEx(
Result = UserScrollDC( hDC, Result = UserScrollDC( hDC,
dx, dx,
dy, dy,
&rcScroll, &rcScroll,
&rcClip, &rcClip,
RgnOwn, RgnUpdate,
prcUnsafeUpdate? &rcUpdate : NULL); prcUnsafeUpdate? &rcUpdate : NULL);
UserReleaseDC(Window, hDC, FALSE); UserReleaseDC(Window, hDC, FALSE);
@ -1801,29 +1835,32 @@ NtUserScrollWindowEx(
*/ */
RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0); RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0);
if (RgnTemp) if (!RgnTemp)
{ {
if (co_UserGetUpdateRgn(Window, RgnTemp, FALSE) != NULLREGION) EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
{ RETURN(ERROR);
PREGION RgnClip = IntSysCreateRectpRgnIndirect(&rcClip);
if (RgnClip)
{
if (!bOwnRgn)
{
RgnWinupd = IntSysCreateRectpRgn( 0, 0, 0, 0);
IntGdiCombineRgn( RgnWinupd, RgnTemp, 0, RGN_COPY);
}
IntGdiOffsetRgn(RgnTemp, dx, dy);
IntGdiCombineRgn(RgnTemp, RgnTemp, RgnClip, RGN_AND);
if (!bOwnRgn)
IntGdiCombineRgn( RgnWinupd, RgnWinupd, RgnTemp, RGN_OR );
co_UserRedrawWindow(Window, NULL, RgnTemp, rdw_flags );
REGION_Delete(RgnClip);
}
}
REGION_Delete(RgnTemp);
} }
if (co_UserGetUpdateRgn(Window, RgnTemp, FALSE) != NULLREGION)
{
PREGION RgnClip = IntSysCreateRectpRgnIndirect(&rcClip);
if (RgnClip)
{
if (hrgnUpdate)
{
RgnWinupd = IntSysCreateRectpRgn( 0, 0, 0, 0);
IntGdiCombineRgn( RgnWinupd, RgnTemp, 0, RGN_COPY);
}
IntGdiOffsetRgn(RgnTemp, dx, dy);
IntGdiCombineRgn(RgnTemp, RgnTemp, RgnClip, RGN_AND);
if (hrgnUpdate)
IntGdiCombineRgn( RgnWinupd, RgnWinupd, RgnTemp, RGN_OR );
co_UserRedrawWindow(Window, NULL, RgnTemp, rdw_flags );
REGION_Delete(RgnClip);
}
}
REGION_Delete(RgnTemp);
if (flags & SW_SCROLLCHILDREN) if (flags & SW_SCROLLCHILDREN)
{ {
PWND Child; PWND Child;
@ -1854,7 +1891,7 @@ NtUserScrollWindowEx(
if (flags & (SW_INVALIDATE | SW_ERASE)) if (flags & (SW_INVALIDATE | SW_ERASE))
{ {
co_UserRedrawWindow(Window, NULL, RgnOwn, rdw_flags | co_UserRedrawWindow(Window, NULL, RgnUpdate, rdw_flags |
((flags & SW_ERASE) ? RDW_ERASENOW : 0) | ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0)); ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
} }
@ -1893,19 +1930,27 @@ NtUserScrollWindowEx(
RETURN(Result); RETURN(Result);
CLEANUP: CLEANUP:
if (RgnWinupd && !bOwnRgn) if (hrgnUpdate && (Result != ERROR))
{ {
IntGdiCombineRgn( RgnOwn, RgnOwn, RgnWinupd, RGN_OR); /* Give everything back to the caller */
REGION_Delete(RgnWinupd); RgnTemp = RGNOBJAPI_Lock(hrgnUpdate, NULL);
/* The handle should still be valid */
ASSERT(RgnTemp);
if (RgnWinupd)
IntGdiCombineRgn(RgnTemp, RgnUpdate, RgnWinupd, RGN_OR);
else
IntGdiCombineRgn(RgnTemp, RgnUpdate, NULL, RGN_COPY);
RGNOBJAPI_Unlock(RgnTemp);
} }
if (RgnOwn && !hrgnUpdate) if (RgnWinupd)
{ {
REGION_Delete(RgnOwn); REGION_Delete(RgnWinupd);
} }
else if (RgnOwn)
if (RgnUpdate)
{ {
RGNOBJAPI_Unlock(RgnOwn); REGION_Delete(RgnUpdate);
} }
if (Window) if (Window)