@@ -840,6 +840,11 @@ Context2d::PutImageData(const Napi::CallbackInfo& info) {
840840
841841 int dstStride = canvas ()->stride ();
842842 int Bpp = dstStride / canvas ()->getWidth ();
843+
844+ if (imageData->width () > (uint32_t )INT32_MAX / Bpp) {
845+ Napi::Error::New (env, " ImageData width too large" ).ThrowAsJavaScriptException ();
846+ return ;
847+ }
843848 int srcStride = Bpp * imageData->width ();
844849
845850 int sx = 0
@@ -851,11 +856,20 @@ Context2d::PutImageData(const Napi::CallbackInfo& info) {
851856 , rows
852857 , cols;
853858
859+ if (imageData->height () > INT32_MAX) {
860+ Napi::Error::New (env, " ImageData height too large" ).ThrowAsJavaScriptException ();
861+ return ;
862+ }
863+
864+ int iw = imageData->width ();
865+ int ih = imageData->height ();
866+
867+ // https://searchfox.org/firefox-main/rev/0900c19de62c07b977fe103b3816616b75d63b0a/dom/canvas/CanvasRenderingContext2D.cpp#6736
854868 switch (info.Length ()) {
855869 // imageData, dx, dy
856870 case 3 :
857- sw = imageData-> width () ;
858- sh = imageData-> height () ;
871+ sw = iw ;
872+ sh = ih ;
859873 break ;
860874 // imageData, dx, dy, sx, sy, sw, sh
861875 case 7 :
@@ -864,23 +878,62 @@ Context2d::PutImageData(const Napi::CallbackInfo& info) {
864878 sw = info[5 ].ToNumber ().UnwrapOr (zero).Int32Value ();
865879 sh = info[6 ].ToNumber ().UnwrapOr (zero).Int32Value ();
866880 // fix up negative height, width
867- if (sw < 0 ) sx += sw, sw = -sw;
868- if (sh < 0 ) sy += sh, sh = -sh;
869- // clamp the left edge
870- if (sx < 0 ) sw += sx, sx = 0 ;
871- if (sy < 0 ) sh += sy, sy = 0 ;
872- // clamp the right edge
873- if (sx + sw > imageData->width ()) sw = imageData->width () - sx;
874- if (sy + sh > imageData->height ()) sh = imageData->height () - sy;
875- // start destination at source offset
876- dx += sx;
877- dy += sy;
881+ if (sw < 0 ) {
882+ if (sw == INT32_MIN) {
883+ Napi::Error::New (env, " dirty width invalid" ).ThrowAsJavaScriptException ();
884+ return ;
885+ }
886+ int64_t result = (int64_t )sx + sw;
887+ if (result < INT_MIN) {
888+ Napi::Error::New (env, " dirty width invalid" ).ThrowAsJavaScriptException ();
889+ return ;
890+ } else {
891+ sx = result;
892+ }
893+ sw = -sw;
894+ }
895+
896+ if (sh < 0 ) {
897+ if (sh == INT32_MIN) {
898+ Napi::Error::New (env, " dirty height invalid" ).ThrowAsJavaScriptException ();
899+ return ;
900+ }
901+ int64_t result = (int64_t )sy + sh;
902+ if (result < INT_MIN) {
903+ Napi::Error::New (env, " dirty height invalid" ).ThrowAsJavaScriptException ();
904+ return ;
905+ } else {
906+ sy = result;
907+ }
908+ sh = -sh;
909+ }
910+ // fix up negative x, y
911+ if (sx < 0 ) sw = std::max (0 , sx + sw), sx = 0 ;
912+ if (sy < 0 ) sh = std::max (0 , sy + sh), sy = 0 ;
913+ // clamp the right, bottom edges (sx + sw > iw reorganized for overflow)
914+ if (sx > iw - sw) sw = std::max (0 , iw - sx);
915+ if (sy > ih - sh) sh = std::max (0 , ih - sy);
878916 break ;
879917 default :
880918 Napi::Error::New (env, " invalid arguments" ).ThrowAsJavaScriptException ();
881919 return ;
882920 }
883921
922+ // start destination at source offset
923+ // eliminiating INT_MIN makes it safe to subtract dx, dy from sx, sy below
924+ if (int64_t result = (int64_t )dx + sx; dx > INT_MIN && result <= INT_MAX) {
925+ dx = result; // dx += sx
926+ } else {
927+ Napi::Error::New (env, " destination x invalid" ).ThrowAsJavaScriptException ();
928+ return ;
929+ }
930+ if (int64_t result = (int64_t )dy + sy; dy > INT_MIN && result <= INT_MAX) {
931+ dy = result; // dy += sy
932+ } else {
933+ Napi::Error::New (env, " destination y invalid" ).ThrowAsJavaScriptException ();
934+ return ;
935+ }
936+
884937 // chop off outlying source data
885938 if (dx < 0 ) sw += dx, sx -= dx, dx = 0 ;
886939 if (dy < 0 ) sh += dy, sy -= dy, dy = 0 ;
@@ -894,8 +947,8 @@ Context2d::PutImageData(const Napi::CallbackInfo& info) {
894947
895948 switch (canvas ()->getFormat ()) {
896949 case CAIRO_FORMAT_ARGB32: {
897- src += sy * srcStride + sx * 4 ;
898- dst += dstStride * dy + 4 * dx;
950+ src += ( ptrdiff_t ) sy * srcStride + sx * 4 ;
951+ dst += ( ptrdiff_t ) dstStride * dy + 4 * dx;
899952 for (int y = 0 ; y < rows; ++y) {
900953 uint8_t *dstRow = dst;
901954 uint8_t *srcRow = src;
@@ -933,8 +986,8 @@ Context2d::PutImageData(const Napi::CallbackInfo& info) {
933986 break ;
934987 }
935988 case CAIRO_FORMAT_RGB24: {
936- src += sy * srcStride + sx * 4 ;
937- dst += dstStride * dy + 4 * dx;
989+ src += ( ptrdiff_t ) sy * srcStride + sx * 4 ;
990+ dst += ( ptrdiff_t ) dstStride * dy + 4 * dx;
938991 for (int y = 0 ; y < rows; ++y) {
939992 uint8_t *dstRow = dst;
940993 uint8_t *srcRow = src;
@@ -957,8 +1010,8 @@ Context2d::PutImageData(const Napi::CallbackInfo& info) {
9571010 break ;
9581011 }
9591012 case CAIRO_FORMAT_A8: {
960- src += sy * srcStride + sx;
961- dst += dstStride * dy + dx;
1013+ src += ( ptrdiff_t ) sy * srcStride + sx;
1014+ dst += ( ptrdiff_t ) dstStride * dy + dx;
9621015 if (srcStride == dstStride && cols == dstStride) {
9631016 // fast path: strides are the same and doing a full-width put
9641017 memcpy (dst, src, cols * rows);
@@ -978,8 +1031,8 @@ Context2d::PutImageData(const Napi::CallbackInfo& info) {
9781031 break ;
9791032 }
9801033 case CAIRO_FORMAT_RGB16_565: {
981- src += sy * srcStride + sx * 2 ;
982- dst += dstStride * dy + 2 * dx;
1034+ src += ( ptrdiff_t ) sy * srcStride + sx * 2 ;
1035+ dst += ( ptrdiff_t ) dstStride * dy + 2 * dx;
9831036 for (int y = 0 ; y < rows; ++y) {
9841037 memcpy (dst, src, cols * 2 );
9851038 dst += dstStride;
0 commit comments