Module: wine
Branch: master
Commit: 72c0c88f2a5c9b52bb01f7981c0894a47efa55f3
URL: https://gitlab.winehq.org/wine/wine/-/commit/72c0c88f2a5c9b52bb01f7981c0894…
Author: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
Date: Sat Oct 22 21:25:37 2022 +0900
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.
---
dlls/gdiplus/gdiplus_private.h | 13 ++++----
dlls/gdiplus/image.c | 67 +++++++++++++++++++-----------------------
2 files changed, 36 insertions(+), 44 deletions(-)
Module: wine
Branch: master
Commit: 7abca9742a9410447636e0222e36d214449c90dd
URL: https://gitlab.winehq.org/wine/wine/-/commit/7abca9742a9410447636e0222e36d2…
Author: Jinoh Kang <jinoh.kang.kr(a)gmail.com>
Date: Sat Oct 22 21:23:18 2022 +0900
gdiplus: Avoid copying GpImage's busy flag in select_frame_wic().
The 'busy' field in GpImage is used as an atomic variable. The C11
standard (§5.1.2.4, paragraph 25) states that two conflicting actions to
a memory location shall be both atomic operations, or otherwise properly
synchronized; otherwise, it constitutes a data race.
However, select_frame_wic() performs a non-atomic access to the 'busy'
field on a GpImage that is potentially accessible by other threads.
This happens when select_frame_wic() copies new_image to the old image
object. Although it does attempt to preserve the value of the 'busy'
field by setting new_image->busy = image->busy first, thereby
effectively assigning an identical value to the field, it is unclear
that this does not actually constitute a theoretical, if not practical,
data race. This also prevents replacing the busy flag with a mutex or
other synchronization primitives.
Therefore, skip the 'busy' field when copying fields from the new image
to the original image object.
---
dlls/gdiplus/image.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/dlls/gdiplus/image.c b/dlls/gdiplus/image.c
index 93d2a366e82..629599ddf2c 100644
--- a/dlls/gdiplus/image.c
+++ b/dlls/gdiplus/image.c
@@ -3799,6 +3799,7 @@ static GpStatus decode_image_wic(IStream *stream, REFGUID container,
static GpStatus select_frame_wic(GpImage *image, UINT active_frame)
{
+ size_t obj_size, body_offset;
GpImage *new_image;
GpStatus status;
@@ -3806,15 +3807,24 @@ static GpStatus select_frame_wic(GpImage *image, UINT active_frame)
if(status != Ok)
return status;
- new_image->busy = image->busy;
+ if (image->type == ImageTypeBitmap)
+ obj_size = sizeof(GpBitmap);
+ else if (image->type == ImageTypeMetafile)
+ obj_size = sizeof(GpMetafile);
+ else
+ {
+ ERR("unknown image type: %d\n", image->type);
+ GdipDisposeImage(new_image);
+ return GenericError;
+ }
+
memcpy(&new_image->format, &image->format, sizeof(GUID));
new_image->encoder = image->encoder;
image->encoder = NULL;
free_image_data(image);
- if (image->type == ImageTypeBitmap)
- *(GpBitmap *)image = *(GpBitmap *)new_image;
- else if (image->type == ImageTypeMetafile)
- *(GpMetafile *)image = *(GpMetafile *)new_image;
+ memcpy(image, new_image, FIELD_OFFSET(GpImage, busy));
+ body_offset = RTL_SIZEOF_THROUGH_FIELD(GpImage, busy);
+ memcpy((char *)image + body_offset, (char *)new_image + body_offset, obj_size - body_offset);
new_image->type = ~0;
heap_free(new_image);
return Ok;