Module: wine Branch: master Commit: 7abca9742a9410447636e0222e36d214449c90dd URL: https://gitlab.winehq.org/wine/wine/-/commit/7abca9742a9410447636e0222e36d21...
Author: Jinoh Kang jinoh.kang.kr@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;