git.lucas.co / go_mono
git clone https://git.lucas.co/go_mono.git

commit12117c17ca67ffa1ce22e9409f3b0b0a93ac08c7
parente5db4c4663
authorJiulong Wang <[email protected]>
date2017-12-12 11:22
draw: fix crash caused by Scale by Copy shortcut

When DstMask is not nil, this shortcut causes stack overflow because
Copy function in turn will call Scale with same dr and sr.

Fixes golang/go#23107

Change-Id: I8ccadbd9b7f16363ac17b6114308527d6fa9456e
Reviewed-on: https://go-review.googlesource.com/83537
Reviewed-by: Nigel Tao <[email protected]>
Run-TryBot: Nigel Tao <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>

 draw/gen.go        |  5 +++--
 draw/impl.go       | 10 ++++++----
 draw/scale_test.go | 11 +++++++++++
 3 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/draw/gen.go b/draw/gen.go
index 65a7123..822bb6a 100644
--- a/draw/gen.go
+++ b/draw/gen.go
@@ -877,8 +877,9 @@ func relName(s string) string {
 const (
 	codeRoot = `
 		func (z $receiver) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) {
-			// Try to simplify a Scale to a Copy.
-			if dr.Size() == sr.Size() {
+			// Try to simplify a Scale to a Copy when DstMask is not specified.
+			// If DstMask is not nil, Copy will call Scale back with same dr and sr, and cause stack overflow.
+			if dr.Size() == sr.Size() && (opts == nil || opts.DstMask == nil) {
 				Copy(dst, dr.Min, src, sr, op, opts)
 				return
 			}
diff --git a/draw/impl.go b/draw/impl.go
index 637887b..75498ad 100644
--- a/draw/impl.go
+++ b/draw/impl.go
@@ -11,8 +11,9 @@ import (
 )
 
 func (z nnInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) {
-	// Try to simplify a Scale to a Copy.
-	if dr.Size() == sr.Size() {
+	// Try to simplify a Scale to a Copy when DstMask is not specified.
+	// If DstMask is not nil, Copy will call Scale back with same dr and sr, and cause stack overflow.
+	if dr.Size() == sr.Size() && (opts == nil || opts.DstMask == nil) {
 		Copy(dst, dr.Min, src, sr, op, opts)
 		return
 	}
@@ -1048,8 +1049,9 @@ func (nnInterpolator) transform_Image_Image_Src(dst Image, dr, adr image.Rectang
 }
 
 func (z ablInterpolator) Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, op Op, opts *Options) {
-	// Try to simplify a Scale to a Copy.
-	if dr.Size() == sr.Size() {
+	// Try to simplify a Scale to a Copy when DstMask is not specified.
+	// If DstMask is not nil, Copy will call Scale back with same dr and sr, and cause stack overflow.
+	if dr.Size() == sr.Size() && (opts == nil || opts.DstMask == nil) {
 		Copy(dst, dr.Min, src, sr, op, opts)
 		return
 	}
diff --git a/draw/scale_test.go b/draw/scale_test.go
index 5e184c2..ea41940 100644
--- a/draw/scale_test.go
+++ b/draw/scale_test.go
@@ -551,6 +551,17 @@ func TestRectDstMask(t *testing.T) {
 	}
 }
 
+func TestDstMaskSameSizeCopy(t *testing.T) {
+	bounds := image.Rect(0, 0, 42, 42)
+	src := image.Opaque
+	dst := image.NewRGBA(bounds)
+	mask := image.NewRGBA(bounds)
+
+	Copy(dst, image.ZP, src, bounds, Src, &Options{
+		DstMask: mask,
+	})
+}
+
 // TODO: delete this wrapper type once Go 1.5 is released, where an
 // image.Rectangle implements image.Image.
 type rectImage image.Rectangle