mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-06 11:14:06 +00:00
gdiplus: Replace GpImage's busy flag with SRWLOCK.
Today, the image_unlock() helper function has a data race due to non-atomic write to GpImage's 'busy' flag which is accessible by other threads. Also, it lacks a release fence, which means that other threads can observe the unlocked (busy = 0) state too early when the current thread unlocks the image; specifically, the write to the 'busy' field of the GpImage can be reordered before the last read/write to any other fields of the same GpImage. Fix this by replacing the 'busy' field of GpImage with SRWLOCK.
This commit is contained in:
parent
7abca9742a
commit
72c0c88f2a
|
@ -371,7 +371,7 @@ struct GpImage{
|
||||||
UINT frame_count, current_frame;
|
UINT frame_count, current_frame;
|
||||||
ColorPalette *palette;
|
ColorPalette *palette;
|
||||||
REAL xres, yres;
|
REAL xres, yres;
|
||||||
LONG busy;
|
SRWLOCK lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define EmfPlusObjectTableSize 64
|
#define EmfPlusObjectTableSize 64
|
||||||
|
@ -613,17 +613,14 @@ GpStatus gdip_format_string(HDC hdc,
|
||||||
|
|
||||||
void get_log_fontW(const GpFont *, GpGraphics *, LOGFONTW *) DECLSPEC_HIDDEN;
|
void get_log_fontW(const GpFont *, GpGraphics *, LOGFONTW *) DECLSPEC_HIDDEN;
|
||||||
|
|
||||||
static inline BOOL image_lock(GpImage *image, BOOL *unlock)
|
static inline BOOL image_lock(GpImage *image)
|
||||||
{
|
{
|
||||||
LONG tid = GetCurrentThreadId(), owner_tid;
|
return TryAcquireSRWLockExclusive(&image->lock);
|
||||||
owner_tid = InterlockedCompareExchange(&image->busy, tid, 0);
|
|
||||||
*unlock = !owner_tid;
|
|
||||||
return !owner_tid || owner_tid==tid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void image_unlock(GpImage *image, BOOL unlock)
|
static inline void image_unlock(GpImage *image)
|
||||||
{
|
{
|
||||||
if (unlock) image->busy = 0;
|
ReleaseSRWLockExclusive(&image->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void set_rect(GpRectF *rect, REAL x, REAL y, REAL width, REAL height)
|
static inline void set_rect(GpRectF *rect, REAL x, REAL y, REAL width, REAL height)
|
||||||
|
|
|
@ -1086,20 +1086,19 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
|
||||||
INT bitspp = PIXELFORMATBPP(format);
|
INT bitspp = PIXELFORMATBPP(format);
|
||||||
GpRect act_rect; /* actual rect to be used */
|
GpRect act_rect; /* actual rect to be used */
|
||||||
GpStatus stat;
|
GpStatus stat;
|
||||||
BOOL unlock;
|
|
||||||
|
|
||||||
TRACE("%p %p %d 0x%x %p\n", bitmap, rect, flags, format, lockeddata);
|
TRACE("%p %p %d 0x%x %p\n", bitmap, rect, flags, format, lockeddata);
|
||||||
|
|
||||||
if(!lockeddata || !bitmap)
|
if(!lockeddata || !bitmap)
|
||||||
return InvalidParameter;
|
return InvalidParameter;
|
||||||
if(!image_lock(&bitmap->image, &unlock))
|
if(!image_lock(&bitmap->image))
|
||||||
return ObjectBusy;
|
return ObjectBusy;
|
||||||
|
|
||||||
if(rect){
|
if(rect){
|
||||||
if(rect->X < 0 || rect->Y < 0 || (rect->X + rect->Width > bitmap->width) ||
|
if(rect->X < 0 || rect->Y < 0 || (rect->X + rect->Width > bitmap->width) ||
|
||||||
(rect->Y + rect->Height > bitmap->height) || !flags)
|
(rect->Y + rect->Height > bitmap->height) || !flags)
|
||||||
{
|
{
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return InvalidParameter;
|
return InvalidParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1114,7 +1113,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
|
||||||
if(bitmap->lockmode)
|
if(bitmap->lockmode)
|
||||||
{
|
{
|
||||||
WARN("bitmap is already locked and cannot be locked again\n");
|
WARN("bitmap is already locked and cannot be locked again\n");
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return WrongState;
|
return WrongState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1131,7 +1130,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
|
||||||
|
|
||||||
bitmap->lockmode = flags | ImageLockModeRead;
|
bitmap->lockmode = flags | ImageLockModeRead;
|
||||||
|
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1142,7 +1141,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
|
||||||
if (stat == NotImplemented)
|
if (stat == NotImplemented)
|
||||||
{
|
{
|
||||||
FIXME("cannot read bitmap from %x to %x\n", bitmap->format, format);
|
FIXME("cannot read bitmap from %x to %x\n", bitmap->format, format);
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return NotImplemented;
|
return NotImplemented;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1155,7 +1154,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
|
||||||
if (stat == NotImplemented)
|
if (stat == NotImplemented)
|
||||||
{
|
{
|
||||||
FIXME("cannot write bitmap from %x to %x\n", format, bitmap->format);
|
FIXME("cannot write bitmap from %x to %x\n", format, bitmap->format);
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return NotImplemented;
|
return NotImplemented;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1173,7 +1172,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
|
||||||
|
|
||||||
if (!bitmap->bitmapbits)
|
if (!bitmap->bitmapbits)
|
||||||
{
|
{
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return OutOfMemory;
|
return OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1200,7 +1199,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
|
||||||
{
|
{
|
||||||
heap_free(bitmap->bitmapbits);
|
heap_free(bitmap->bitmapbits);
|
||||||
bitmap->bitmapbits = NULL;
|
bitmap->bitmapbits = NULL;
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1209,7 +1208,7 @@ GpStatus WINGDIPAPI GdipBitmapLockBits(GpBitmap* bitmap, GDIPCONST GpRect* rect,
|
||||||
bitmap->lockx = act_rect.X;
|
bitmap->lockx = act_rect.X;
|
||||||
bitmap->locky = act_rect.Y;
|
bitmap->locky = act_rect.Y;
|
||||||
|
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1231,18 +1230,17 @@ GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap,
|
||||||
{
|
{
|
||||||
GpStatus stat;
|
GpStatus stat;
|
||||||
static BOOL fixme = FALSE;
|
static BOOL fixme = FALSE;
|
||||||
BOOL unlock;
|
|
||||||
|
|
||||||
TRACE("(%p,%p)\n", bitmap, lockeddata);
|
TRACE("(%p,%p)\n", bitmap, lockeddata);
|
||||||
|
|
||||||
if(!bitmap || !lockeddata)
|
if(!bitmap || !lockeddata)
|
||||||
return InvalidParameter;
|
return InvalidParameter;
|
||||||
if(!image_lock(&bitmap->image, &unlock))
|
if(!image_lock(&bitmap->image))
|
||||||
return ObjectBusy;
|
return ObjectBusy;
|
||||||
|
|
||||||
if(!bitmap->lockmode)
|
if(!bitmap->lockmode)
|
||||||
{
|
{
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return WrongState;
|
return WrongState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1250,7 +1248,7 @@ GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap,
|
||||||
bitmap->lockmode = 0;
|
bitmap->lockmode = 0;
|
||||||
heap_free(bitmap->bitmapbits);
|
heap_free(bitmap->bitmapbits);
|
||||||
bitmap->bitmapbits = NULL;
|
bitmap->bitmapbits = NULL;
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1258,7 +1256,7 @@ GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap,
|
||||||
{
|
{
|
||||||
/* we passed a direct reference; no need to do anything */
|
/* we passed a direct reference; no need to do anything */
|
||||||
bitmap->lockmode = 0;
|
bitmap->lockmode = 0;
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1284,7 +1282,7 @@ GpStatus WINGDIPAPI GdipBitmapUnlockBits(GpBitmap* bitmap,
|
||||||
bitmap->bitmapbits = NULL;
|
bitmap->bitmapbits = NULL;
|
||||||
bitmap->lockmode = 0;
|
bitmap->lockmode = 0;
|
||||||
|
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1517,12 +1515,11 @@ GpStatus WINGDIPAPI GdipCreateHBITMAPFromBitmap(GpBitmap* bitmap,
|
||||||
UINT width, height;
|
UINT width, height;
|
||||||
BITMAPINFOHEADER bih;
|
BITMAPINFOHEADER bih;
|
||||||
LPBYTE bits;
|
LPBYTE bits;
|
||||||
BOOL unlock;
|
|
||||||
|
|
||||||
TRACE("(%p,%p,%lx)\n", bitmap, hbmReturn, background);
|
TRACE("(%p,%p,%lx)\n", bitmap, hbmReturn, background);
|
||||||
|
|
||||||
if (!bitmap || !hbmReturn) return InvalidParameter;
|
if (!bitmap || !hbmReturn) return InvalidParameter;
|
||||||
if (!image_lock(&bitmap->image, &unlock)) return ObjectBusy;
|
if (!image_lock(&bitmap->image)) return ObjectBusy;
|
||||||
|
|
||||||
GdipGetImageWidth(&bitmap->image, &width);
|
GdipGetImageWidth(&bitmap->image, &width);
|
||||||
GdipGetImageHeight(&bitmap->image, &height);
|
GdipGetImageHeight(&bitmap->image, &height);
|
||||||
|
@ -1542,7 +1539,7 @@ GpStatus WINGDIPAPI GdipCreateHBITMAPFromBitmap(GpBitmap* bitmap,
|
||||||
result = CreateDIBSection(0, (BITMAPINFO*)&bih, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
|
result = CreateDIBSection(0, (BITMAPINFO*)&bih, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return GenericError;
|
return GenericError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1552,7 +1549,7 @@ GpStatus WINGDIPAPI GdipCreateHBITMAPFromBitmap(GpBitmap* bitmap,
|
||||||
if (stat != Ok)
|
if (stat != Ok)
|
||||||
{
|
{
|
||||||
DeleteObject(result);
|
DeleteObject(result);
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1568,7 +1565,7 @@ GpStatus WINGDIPAPI GdipCreateHBITMAPFromBitmap(GpBitmap* bitmap,
|
||||||
}
|
}
|
||||||
|
|
||||||
*hbmReturn = result;
|
*hbmReturn = result;
|
||||||
image_unlock(&bitmap->image, unlock);
|
image_unlock(&bitmap->image);
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3822,8 +3819,8 @@ static GpStatus select_frame_wic(GpImage *image, UINT active_frame)
|
||||||
new_image->encoder = image->encoder;
|
new_image->encoder = image->encoder;
|
||||||
image->encoder = NULL;
|
image->encoder = NULL;
|
||||||
free_image_data(image);
|
free_image_data(image);
|
||||||
memcpy(image, new_image, FIELD_OFFSET(GpImage, busy));
|
memcpy(image, new_image, FIELD_OFFSET(GpImage, lock));
|
||||||
body_offset = RTL_SIZEOF_THROUGH_FIELD(GpImage, busy);
|
body_offset = RTL_SIZEOF_THROUGH_FIELD(GpImage, lock);
|
||||||
memcpy((char *)image + body_offset, (char *)new_image + body_offset, obj_size - body_offset);
|
memcpy((char *)image + body_offset, (char *)new_image + body_offset, obj_size - body_offset);
|
||||||
new_image->type = ~0;
|
new_image->type = ~0;
|
||||||
heap_free(new_image);
|
heap_free(new_image);
|
||||||
|
@ -4361,39 +4358,38 @@ GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *d
|
||||||
{
|
{
|
||||||
GpStatus stat;
|
GpStatus stat;
|
||||||
const struct image_codec *codec = NULL;
|
const struct image_codec *codec = NULL;
|
||||||
BOOL unlock;
|
|
||||||
|
|
||||||
TRACE("(%p,%s,%u)\n", image, debugstr_guid(dimensionID), frame);
|
TRACE("(%p,%s,%u)\n", image, debugstr_guid(dimensionID), frame);
|
||||||
|
|
||||||
if (!image || !dimensionID)
|
if (!image || !dimensionID)
|
||||||
return InvalidParameter;
|
return InvalidParameter;
|
||||||
if(!image_lock(image, &unlock))
|
if(!image_lock(image))
|
||||||
return ObjectBusy;
|
return ObjectBusy;
|
||||||
|
|
||||||
if (frame >= image->frame_count)
|
if (frame >= image->frame_count)
|
||||||
{
|
{
|
||||||
WARN("requested frame %u, but image has only %u\n", frame, image->frame_count);
|
WARN("requested frame %u, but image has only %u\n", frame, image->frame_count);
|
||||||
image_unlock(image, unlock);
|
image_unlock(image);
|
||||||
return InvalidParameter;
|
return InvalidParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image->type != ImageTypeBitmap && image->type != ImageTypeMetafile)
|
if (image->type != ImageTypeBitmap && image->type != ImageTypeMetafile)
|
||||||
{
|
{
|
||||||
WARN("invalid image type %d\n", image->type);
|
WARN("invalid image type %d\n", image->type);
|
||||||
image_unlock(image, unlock);
|
image_unlock(image);
|
||||||
return InvalidParameter;
|
return InvalidParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image->current_frame == frame)
|
if (image->current_frame == frame)
|
||||||
{
|
{
|
||||||
image_unlock(image, unlock);
|
image_unlock(image);
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!image->decoder)
|
if (!image->decoder)
|
||||||
{
|
{
|
||||||
TRACE("image doesn't have an associated decoder\n");
|
TRACE("image doesn't have an associated decoder\n");
|
||||||
image_unlock(image, unlock);
|
image_unlock(image);
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4402,12 +4398,12 @@ GpStatus WINGDIPAPI GdipImageSelectActiveFrame(GpImage *image, GDIPCONST GUID *d
|
||||||
if (stat != Ok)
|
if (stat != Ok)
|
||||||
{
|
{
|
||||||
WARN("can't find decoder info\n");
|
WARN("can't find decoder info\n");
|
||||||
image_unlock(image, unlock);
|
image_unlock(image);
|
||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
stat = codec->select_func(image, frame);
|
stat = codec->select_func(image, frame);
|
||||||
image_unlock(image, unlock);
|
image_unlock(image);
|
||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5542,13 +5538,12 @@ GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
|
||||||
UINT x, y, width, height;
|
UINT x, y, width, height;
|
||||||
BitmapData dst_lock;
|
BitmapData dst_lock;
|
||||||
GpStatus stat;
|
GpStatus stat;
|
||||||
BOOL unlock;
|
|
||||||
|
|
||||||
TRACE("(%p, %u)\n", image, type);
|
TRACE("(%p, %u)\n", image, type);
|
||||||
|
|
||||||
if (!image)
|
if (!image)
|
||||||
return InvalidParameter;
|
return InvalidParameter;
|
||||||
if (!image_lock(image, &unlock))
|
if (!image_lock(image))
|
||||||
return ObjectBusy;
|
return ObjectBusy;
|
||||||
|
|
||||||
rotate_90 = type&1;
|
rotate_90 = type&1;
|
||||||
|
@ -5558,7 +5553,7 @@ GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
|
||||||
if (image->type != ImageTypeBitmap)
|
if (image->type != ImageTypeBitmap)
|
||||||
{
|
{
|
||||||
FIXME("Not implemented for type %i\n", image->type);
|
FIXME("Not implemented for type %i\n", image->type);
|
||||||
image_unlock(image, unlock);
|
image_unlock(image);
|
||||||
return NotImplemented;
|
return NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5568,7 +5563,7 @@ GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
|
||||||
if (bpp < 8)
|
if (bpp < 8)
|
||||||
{
|
{
|
||||||
FIXME("Not implemented for %i bit images\n", bpp);
|
FIXME("Not implemented for %i bit images\n", bpp);
|
||||||
image_unlock(image, unlock);
|
image_unlock(image);
|
||||||
return NotImplemented;
|
return NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5638,7 +5633,7 @@ GpStatus WINGDIPAPI GdipImageRotateFlip(GpImage *image, RotateFlipType type)
|
||||||
else GdipDisposeImage(&new_bitmap->image);
|
else GdipDisposeImage(&new_bitmap->image);
|
||||||
}
|
}
|
||||||
|
|
||||||
image_unlock(image, unlock);
|
image_unlock(image);
|
||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue