encoding/korean: make EUCKR replace with FFFD on error

Updates golang/go#18898

Change-Id: I9868004acb11abbfee8492c9de8ba374f6dcb2ac
Reviewed-on: https://go-review.googlesource.com/37319
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/encoding/korean/all_test.go b/encoding/korean/all_test.go
index 348770d..8225ce6 100644
--- a/encoding/korean/all_test.go
+++ b/encoding/korean/all_test.go
@@ -5,6 +5,7 @@
 package korean
 
 import (
+	"strings"
 	"testing"
 
 	"golang.org/x/text/encoding"
@@ -21,6 +22,9 @@
 }
 
 func TestNonRepertoire(t *testing.T) {
+	// Pick n large enough to cause an overflow in the destination buffer of
+	// transform.String.
+	const n = 10000
 	testCases := []struct {
 		init      func(e encoding.Encoding) (string, transform.Transformer, error)
 		e         encoding.Encoding
@@ -33,6 +37,16 @@
 		{enc, EUCKR, "aא", "a"},
 		{enc, EUCKR, "\uac00א", "\xb0\xa1"},
 		// TODO: should we also handle Jamo?
+
+		{dec, EUCKR, "\x80", "\ufffd"},
+		{dec, EUCKR, "\xff", "\ufffd"},
+		{dec, EUCKR, "\x81", "\ufffd"},
+		{dec, EUCKR, "\xb0\x40", "\ufffd@"},
+		{dec, EUCKR, "\xb0\xff", "\ufffd"},
+		{dec, EUCKR, "\xd0\x20", "\ufffd "},
+		{dec, EUCKR, "\xd0\xff", "\ufffd"},
+
+		{dec, EUCKR, strings.Repeat("\x81", n), strings.Repeat("걖", n/2)},
 	}
 	for _, tc := range testCases {
 		dir, tr, wantErr := tc.init(tc.e)
diff --git a/encoding/korean/euckr.go b/encoding/korean/euckr.go
index a4b9ff1..034337f 100644
--- a/encoding/korean/euckr.go
+++ b/encoding/korean/euckr.go
@@ -5,7 +5,6 @@
 package korean
 
 import (
-	"errors"
 	"unicode/utf8"
 
 	"golang.org/x/text/encoding"
@@ -26,8 +25,6 @@
 	identifier.EUCKR,
 }
 
-var errInvalidEUCKR = errors.New("korean: invalid EUC-KR encoding")
-
 type eucKRDecoder struct{ transform.NopResetter }
 
 func (eucKRDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
@@ -40,10 +37,15 @@
 
 		case 0x81 <= c0 && c0 < 0xff:
 			if nSrc+1 >= len(src) {
-				err = transform.ErrShortSrc
-				break loop
+				if !atEOF {
+					err = transform.ErrShortSrc
+					break loop
+				}
+				r, size = utf8.RuneError, 1
+				break
 			}
 			c1 := src[nSrc+1]
+			size = 2
 			if c0 < 0xc7 {
 				r = 178 * rune(c0-0x81)
 				switch {
@@ -54,39 +56,36 @@
 				case 0x81 <= c1 && c1 < 0xff:
 					r += rune(c1) - (0x81 - 2*26)
 				default:
-					err = errInvalidEUCKR
-					break loop
+					goto decError
 				}
 			} else if 0xa1 <= c1 && c1 < 0xff {
 				r = 178*(0xc7-0x81) + rune(c0-0xc7)*94 + rune(c1-0xa1)
 			} else {
-				err = errInvalidEUCKR
-				break loop
+				goto decError
 			}
 			if int(r) < len(decode) {
 				r = rune(decode[r])
-				if r == 0 {
-					r = '\ufffd'
+				if r != 0 {
+					break
 				}
-			} else {
-				r = '\ufffd'
 			}
-			size = 2
+		decError:
+			r = utf8.RuneError
+			if c1 < utf8.RuneSelf {
+				size = 1
+			}
 
 		default:
-			err = errInvalidEUCKR
-			break loop
+			r, size = utf8.RuneError, 1
+			break
 		}
 
 		if nDst+utf8.RuneLen(r) > len(dst) {
 			err = transform.ErrShortDst
-			break loop
+			break
 		}
 		nDst += utf8.EncodeRune(dst[nDst:], r)
 	}
-	if atEOF && err == transform.ErrShortSrc {
-		err = errInvalidEUCKR
-	}
 	return nDst, nSrc, err
 }