sw_engine: Fix potential memory violation in direct image drawing
Some checks failed
Android / build_x86_64 (push) Has been cancelled
Android / build_aarch64 (push) Has been cancelled
iOS / build_x86_64 (push) Has been cancelled
iOS / build_arm64 (push) Has been cancelled
macOS / build (push) Has been cancelled
macOS / compact_test (push) Has been cancelled
macOS / unit_test (push) Has been cancelled
Ubuntu / build (push) Has been cancelled
Ubuntu / compact_test (push) Has been cancelled
Ubuntu / unit_test (push) Has been cancelled
Windows / build (push) Has been cancelled
Windows / compact_test (push) Has been cancelled
Windows / unit_test (push) Has been cancelled

A potential memory violation issue was present when accessing image
buffers, especially when the image was offset outside the visible
screen region.

This issue was accidentally discovered while testing particle
effects. It is now properly handled with cleaner and safer code.
This commit is contained in:
Hermet Park 2025-06-26 23:02:30 +09:00 committed by Hermet Park
parent f3d7e232ce
commit 43f94f8b32

View file

@ -965,14 +965,14 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage& image, const M
/* Direct Image */ /* Direct Image */
/************************************************************************/ /************************************************************************/
static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity) static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int32_t w, int32_t h, uint8_t opacity)
{ {
TVGERR("SW_ENGINE", "Not Supported: Direct Masked Image"); TVGERR("SW_ENGINE", "Not Supported: Direct Masked Image");
return false; return false;
} }
static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity) static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int32_t w, int32_t h, uint8_t opacity)
{ {
auto csize = surface->compositor->image.channelSize; auto csize = surface->compositor->image.channelSize;
auto alpha = surface->alpha(surface->compositor->method); auto alpha = surface->alpha(surface->compositor->method);
@ -983,54 +983,48 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage& image, c
//32 bits //32 bits
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; auto dbuffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x;
for (uint32_t y = 0; y < bbox.h(); ++y) { for (auto y = 0; y < h; ++y, dbuffer += surface->stride, sbuffer += image.stride) {
auto dst = buffer;
auto cmp = cbuffer; auto cmp = cbuffer;
auto src = sbuffer; auto src = sbuffer;
if (opacity == 255) { if (opacity == 255) {
for (uint32_t x = 0; x < bbox.w(); ++x, ++dst, ++src, cmp += csize) { for (auto dst = dbuffer; dst < dbuffer + w; ++dst, ++src, cmp += csize) {
auto tmp = ALPHA_BLEND(*src, alpha(cmp)); auto tmp = ALPHA_BLEND(*src, alpha(cmp));
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
} }
} else { } else {
for (uint32_t x = 0; x < bbox.w(); ++x, ++dst, ++src, cmp += csize) { for (auto dst = dbuffer; dst < dbuffer + w; ++dst, ++src, cmp += csize) {
auto tmp = ALPHA_BLEND(*src, MULTIPLY(opacity, alpha(cmp))); auto tmp = ALPHA_BLEND(*src, MULTIPLY(opacity, alpha(cmp)));
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp)); *dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
} }
} }
buffer += surface->stride;
cbuffer += surface->compositor->image.stride * csize; cbuffer += surface->compositor->image.stride * csize;
sbuffer += image.stride;
} }
//8 bits //8 bits
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x; auto dbuffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x;
for (uint32_t y = 0; y < bbox.h(); ++y) { for (auto y = 0; y < h; ++y, dbuffer += surface->stride, sbuffer += image.stride) {
auto dst = buffer;
auto cmp = cbuffer; auto cmp = cbuffer;
auto src = sbuffer; auto src = sbuffer;
if (opacity == 255) { if (opacity == 255) {
for (uint32_t x = 0; x < bbox.w(); ++x, ++dst, ++src, cmp += csize) { for (auto dst = dbuffer; dst < dbuffer + w; ++dst, ++src, cmp += csize) {
auto tmp = MULTIPLY(A(*src), alpha(cmp)); auto tmp = MULTIPLY(A(*src), alpha(cmp));
*dst = tmp + MULTIPLY(*dst, 255 - tmp); *dst = tmp + MULTIPLY(*dst, 255 - tmp);
} }
} else { } else {
for (uint32_t x = 0; x < bbox.w(); ++x, ++dst, ++src, cmp += csize) { for (auto dst = dbuffer; dst < dbuffer + w; ++dst, ++src, cmp += csize) {
auto tmp = MULTIPLY(A(*src), MULTIPLY(opacity, alpha(cmp))); auto tmp = MULTIPLY(A(*src), MULTIPLY(opacity, alpha(cmp)));
*dst = tmp + MULTIPLY(*dst, 255 - tmp); *dst = tmp + MULTIPLY(*dst, 255 - tmp);
} }
} }
buffer += surface->stride;
cbuffer += surface->compositor->image.stride * csize; cbuffer += surface->compositor->image.stride * csize;
sbuffer += image.stride;
} }
} }
return true; return true;
} }
static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity) static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int32_t w, int32_t h, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale image!"); TVGERR("SW_ENGINE", "Not supported grayscale image!");
@ -1040,51 +1034,43 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage& image,
auto dbuffer = &surface->buf32[bbox.min.y * surface->stride + bbox.min.x]; auto dbuffer = &surface->buf32[bbox.min.y * surface->stride + bbox.min.x];
auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox); auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox);
for (auto y = bbox.min.y; y < bbox.max.y; ++y) { for (auto y = 0; y < h; ++y, dbuffer += surface->stride, sbuffer += image.stride) {
auto dst = dbuffer;
auto src = sbuffer; auto src = sbuffer;
if (opacity == 255) { if (opacity == 255) {
for (auto x = bbox.min.x; x < bbox.max.x; x++, dst++, src++) { for (auto dst = dbuffer; dst < dbuffer + w; dst++, src++) {
auto tmp = surface->blender(*src, *dst, 255); *dst = INTERPOLATE(surface->blender(*src, *dst, 255), *dst, A(*src));
*dst = INTERPOLATE(tmp, *dst, A(*src));
} }
} else { } else {
for (auto x = bbox.min.x; x < bbox.max.x; x++, dst++, src++) { for (auto dst = dbuffer; dst < dbuffer + w; dst++, src++) {
auto tmp = surface->blender(*src, *dst, 255); *dst = INTERPOLATE(surface->blender(*src, *dst, 255), *dst, MULTIPLY(opacity, A(*src)));
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(opacity, A(*src)));
} }
} }
dbuffer += surface->stride;
sbuffer += image.stride;
} }
return true; return true;
} }
static bool _rasterDirectImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity) static bool _rasterDirectImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int32_t w, int32_t h, uint8_t opacity)
{ {
auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox); auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox);
//32bits channels //32bits channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
auto dbuffer = &surface->buf32[bbox.min.y * surface->stride + bbox.min.x]; auto dbuffer = &surface->buf32[bbox.min.y * surface->stride + bbox.min.x];
for (auto y = bbox.min.y; y < bbox.max.y; ++y) { for (auto y = 0; y < h; ++y, dbuffer += surface->stride, sbuffer += image.stride) {
rasterTranslucentPixel32(dbuffer, sbuffer, bbox.max.x - bbox.min.x, opacity); rasterTranslucentPixel32(dbuffer, sbuffer, w, opacity);
dbuffer += surface->stride;
sbuffer += image.stride;
} }
//8bits grayscale //8bits grayscale
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
auto dbuffer = &surface->buf8[bbox.min.y * surface->stride + bbox.min.x]; auto dbuffer = &surface->buf8[bbox.min.y * surface->stride + bbox.min.x];
for (auto y = bbox.min.y; y < bbox.max.y; ++y, dbuffer += surface->stride, sbuffer += image.stride) { for (auto y = 0; y < h; ++y, dbuffer += surface->stride, sbuffer += image.stride) {
auto dst = dbuffer;
auto src = sbuffer; auto src = sbuffer;
if (opacity == 255) { if (opacity == 255) {
for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst, ++src) { for (auto dst = dbuffer; dst < dbuffer + w; dst++, src++) {
*dst = *src + MULTIPLY(*dst, IA(*src)); *dst = *src + MULTIPLY(*dst, IA(*src));
} }
} else { } else {
for (auto x = bbox.min.x; x < bbox.max.x; ++x, ++dst, ++src) { for (auto dst = dbuffer; dst < dbuffer + w; dst++, src++) {
*dst = INTERPOLATE8(A(*src), *dst, opacity); *dst = INTERPOLATE8(A(*src), *dst, opacity);
} }
} }
@ -1094,7 +1080,7 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage& image, const R
} }
static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity) static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int32_t w, int32_t h, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale image!"); TVGERR("SW_ENGINE", "Not supported grayscale image!");
@ -1105,26 +1091,23 @@ static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage&
auto alpha = surface->alpha(surface->compositor->method); auto alpha = surface->alpha(surface->compositor->method);
auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox); auto sbuffer = image.buf32 + (bbox.min.y + image.oy) * image.stride + (bbox.min.x + image.ox);
auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * surface->compositor->image.stride + bbox.min.x) * csize; //compositor buffer auto cbuffer = surface->compositor->image.buf8 + (bbox.min.y * surface->compositor->image.stride + bbox.min.x) * csize; //compositor buffer
auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x; auto dbuffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x;
for (uint32_t y = 0; y < bbox.h(); ++y) { for (auto y = 0; y < h; ++y, dbuffer += surface->stride, sbuffer += image.stride) {
auto dst = buffer;
auto cmp = cbuffer; auto cmp = cbuffer;
auto src = sbuffer; auto src = sbuffer;
if (opacity == 255) { if (opacity == 255) {
for (uint32_t x = 0; x < bbox.w(); ++x, ++dst, ++src, cmp += csize) { for (auto dst = dbuffer; dst < dbuffer + w; ++dst, ++src, cmp += csize) {
auto tmp = ALPHA_BLEND(*src, alpha(cmp)); auto tmp = ALPHA_BLEND(*src, alpha(cmp));
*dst = INTERPOLATE(surface->blender(tmp, *dst, 255), *dst, A(tmp)); *dst = INTERPOLATE(surface->blender(tmp, *dst, 255), *dst, A(tmp));
} }
} else { } else {
for (uint32_t x = 0; x < bbox.w(); ++x, ++dst, ++src, cmp += csize) { for (auto dst = dbuffer; dst < dbuffer + w; ++dst, ++src, cmp += csize) {
auto tmp = ALPHA_BLEND(*src, alpha(cmp)); auto tmp = ALPHA_BLEND(*src, alpha(cmp));
*dst = INTERPOLATE(surface->blender(tmp, *dst, 255), *dst, MULTIPLY(opacity, A(tmp))); *dst = INTERPOLATE(surface->blender(tmp, *dst, 255), *dst, MULTIPLY(opacity, A(tmp)));
} }
} }
buffer += surface->stride;
cbuffer += surface->compositor->image.stride * csize; cbuffer += surface->compositor->image.stride * csize;
sbuffer += image.stride;
} }
return true; return true;
} }
@ -1619,15 +1602,19 @@ bool rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix& t
bool rasterDirectImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity) bool rasterDirectImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity)
{ {
//calculate an actual drawing image size
auto w = std::min(bbox.max.x - bbox.min.x, int32_t(image.w) - (bbox.min.x + image.ox));
auto h = std::min(bbox.max.y - bbox.min.y, int32_t(image.h) - (bbox.min.y + image.oy));
if (_compositing(surface)) { if (_compositing(surface)) {
if (_matting(surface)) { if (_matting(surface)) {
if (_blending(surface)) return _rasterDirectMattedBlendingImage(surface, image, bbox, opacity); if (_blending(surface)) return _rasterDirectMattedBlendingImage(surface, image, bbox, w, h, opacity);
else return _rasterDirectMattedImage(surface, image, bbox, opacity); else return _rasterDirectMattedImage(surface, image, bbox, w, h, opacity);
} else return _rasterDirectMaskedImage(surface, image, bbox, opacity); } else return _rasterDirectMaskedImage(surface, image, bbox, w, h, opacity);
} else if (_blending(surface)) { } else if (_blending(surface)) {
return _rasterDirectBlendingImage(surface, image, bbox, opacity); return _rasterDirectBlendingImage(surface, image, bbox, w, h, opacity);
} else { } else {
return _rasterDirectImage(surface, image, bbox, opacity); return _rasterDirectImage(surface, image, bbox, w, h, opacity);
} }
return false; return false;
} }