internal/export/idna: avoid memory leak in validation codes

These functions have same issues.
* validateRegistration
* validateAndMap
* validateFromPunycode
When string does not hold enough bytes,
lookupString will return 0 for the size.
In this case, we should raise runeError.
This is the way that we can avoid infinite loop.

Fixes: golang/go#22184

Change-Id: I068e4cc9919777468922deed5d7a427996e0ad61
Reviewed-on: https://go-review.googlesource.com/73730
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/export/idna/idna.go b/internal/export/idna/idna.go
index 9feea64..fd8f3bd 100644
--- a/internal/export/idna/idna.go
+++ b/internal/export/idna/idna.go
@@ -405,6 +405,9 @@
 	}
 	for i := 0; i < len(s); {
 		v, sz := trie.lookupString(s[i:])
+		if sz == 0 {
+			return s, bidi, runeError(utf8.RuneError)
+		}
 		bidi = bidi || info(v).isBidi(s[i:])
 		// Copy bytes not copied so far.
 		switch p.simplify(info(v).category()) {
@@ -446,6 +449,15 @@
 	var combinedInfoBits info
 	for i := 0; i < len(s); {
 		v, sz := trie.lookupString(s[i:])
+		if sz == 0 {
+			b = append(b, s[k:i]...)
+			b = append(b, "\ufffd"...)
+			k = len(s)
+			if err == nil {
+				err = runeError(utf8.RuneError)
+			}
+			break
+		}
 		combinedInfoBits |= info(v)
 		bidi = bidi || info(v).isBidi(s[i:])
 		start := i
@@ -584,6 +596,9 @@
 	// loop.
 	for i := 0; i < len(s); {
 		v, sz := trie.lookupString(s[i:])
+		if sz == 0 {
+			return runeError(utf8.RuneError)
+		}
 		if c := p.simplify(info(v).category()); c != valid && c != deviation {
 			return &labelError{s, "V6"}
 		}
diff --git a/internal/export/idna/idna_test.go b/internal/export/idna/idna_test.go
index fed6e2e..ff878e5 100644
--- a/internal/export/idna/idna_test.go
+++ b/internal/export/idna/idna_test.go
@@ -167,6 +167,7 @@
 		{resolve, "\u3002b", ".b", ""},
 		{resolve, "..b", "..b", ""},
 		{resolve, "b..", "b..", ""},
+		{resolve, "\xed", "", "P1"},
 
 		// Raw punycode
 		{punyA, "", "", ""},