[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#986576: unblock: golang-golang-x-text/0.3.6-1



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
X-Debbugs-Cc: zhsj@debian.org

Please unblock package golang-golang-x-text

[ Reason ]
New upstream bugfix release, which fixes
1. CVE-2020-28851 In x/text in Go 1.15.4, an "index out of range" panic occurs
   in language.ParseAcceptLanguage while parsing the -u- extension.
   (x/text/language is supposed to be able to parse an HTTP Accept-Language
   header.)
2. x/text: panic in regionGroupDist
   https://github.com/golang/go/issues/43834

The package is key package, thus it needs unblock manually.

[ Impact ]
Security issue is not fixed.

[ Tests ]
Upstream thorough unit tests.

[ Risks ]
The change of the code is complex which I can't fully reviewed, but
since it fixes security problem, it may should be complex.
The code is reviewed by official Go team, and I trust them.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]
There are only three commits between 0.3.5 and 0.3.6
https://github.com/golang/text/compare/v0.3.5...v0.3.6
Two commits fix the issue mentioned above. And another one is code format,
which unfortunately makes the diff quite large.
I will omit the format change in debdiff attached in this request.


unblock golang-golang-x-text/0.3.6-1


diff -Nru golang-golang-x-text-0.3.5/debian/changelog golang-golang-x-text-0.3.6/debian/changelog
--- golang-golang-x-text-0.3.5/debian/changelog	2021-01-19 02:11:19.000000000 +0800
+++ golang-golang-x-text-0.3.6/debian/changelog	2021-04-07 02:18:47.000000000 +0800
@@ -1,3 +1,10 @@
+golang-golang-x-text (0.3.6-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream release v0.3.6 (Closes: #980001, CVE-2020-28851)
+
+ -- Shengjing Zhu <zhsj@debian.org>  Wed, 07 Apr 2021 02:18:47 +0800
+
 golang-golang-x-text (0.3.5-1) unstable; urgency=medium
 
   * Team upload.
diff -Nru golang-golang-x-text-0.3.5/debian/control golang-golang-x-text-0.3.6/debian/control
--- golang-golang-x-text-0.3.5/debian/control	2021-01-19 02:11:19.000000000 +0800
+++ golang-golang-x-text-0.3.6/debian/control	2021-04-07 02:18:47.000000000 +0800
@@ -1,13 +1,13 @@
 Source: golang-golang-x-text
 Maintainer: Debian Go Packaging Team <team+pkg-go@tracker.debian.org>
 Uploaders: Martín Ferrari <tincho@debian.org>,
-           Anthony Fok <foka@debian.org>
+           Anthony Fok <foka@debian.org>,
 Section: golang
 Testsuite: autopkgtest-pkg-go
 Priority: optional
 Build-Depends: debhelper-compat (= 13),
                dh-golang (>= 1.31~),
-               golang-any (>= 2:1.7~)
+               golang-any (>= 2:1.7~),
 Standards-Version: 4.5.1
 Vcs-Browser: https://salsa.debian.org/go-team/packages/golang-golang-x-text
 Vcs-Git: https://salsa.debian.org/go-team/packages/golang-golang-x-text.git
@@ -18,8 +18,8 @@
 Package: golang-golang-x-text-dev
 Architecture: all
 Multi-Arch: foreign
-Depends: ${shlibs:Depends},
-         ${misc:Depends}
+Depends: ${misc:Depends},
+         ${shlibs:Depends},
 Description: Supplementary Go text-related libraries
  golang.org/x/text is a repository of text-related packages, such as character
  encodings, text transformations, and locale-specific text handling.
diff -Nru golang-golang-x-text-0.3.5/debian/watch golang-golang-x-text-0.3.6/debian/watch
--- golang-golang-x-text-0.3.5/debian/watch	2021-01-19 02:11:19.000000000 +0800
+++ golang-golang-x-text-0.3.6/debian/watch	2021-04-07 02:18:47.000000000 +0800
@@ -1,4 +1,3 @@
 version=4
-opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%golang-golang-x-text-$1.tar.gz%" \
-    https://github.com/golang/text/releases \
-    (?:.*?/)?v?(\d[\d.]*)\.tar\.gz
+opts="mode=git, pgpmode=none" \
+  https://github.com/golang/text refs/tags/v([\d\.]+)
diff -Nru golang-golang-x-text-0.3.5/internal/language/language.go golang-golang-x-text-0.3.6/internal/language/language.go
--- golang-golang-x-text-0.3.5/internal/language/language.go	2020-12-08 08:13:44.000000000 +0800
+++ golang-golang-x-text-0.3.6/internal/language/language.go	2021-03-30 13:48:03.000000000 +0800
@@ -303,9 +303,17 @@
 // are of the allowed values defined for the Unicode locale extension ('u') in
 // https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers.
 // TypeForKey will traverse the inheritance chain to get the correct value.
+//
+// If there are multiple types associated with a key, only the first will be
+// returned. If there is no type associated with a key, it returns the empty
+// string.
 func (t Tag) TypeForKey(key string) string {
-	if start, end, _ := t.findTypeForKey(key); end != start {
-		return t.str[start:end]
+	if _, start, end, _ := t.findTypeForKey(key); end != start {
+		s := t.str[start:end]
+		if p := strings.IndexByte(s, '-'); p >= 0 {
+			s = s[:p]
+		}
+		return s
 	}
 	return ""
 }
@@ -329,13 +337,13 @@
 
 	// Remove the setting if value is "".
 	if value == "" {
-		start, end, _ := t.findTypeForKey(key)
-		if start != end {
-			// Remove key tag and leading '-'.
-			start -= 4
-
+		start, sep, end, _ := t.findTypeForKey(key)
+		if start != sep {
 			// Remove a possible empty extension.
-			if (end == len(t.str) || t.str[end+2] == '-') && t.str[start-2] == '-' {
+			switch {
+			case t.str[start-2] != '-': // has previous elements.
+			case end == len(t.str), // end of string
+				end+2 < len(t.str) && t.str[end+2] == '-': // end of extension
 				start -= 2
 			}
 			if start == int(t.pVariant) && end == len(t.str) {
@@ -381,14 +389,14 @@
 		t.str = string(buf[:uStart+len(b)])
 	} else {
 		s := t.str
-		start, end, hasExt := t.findTypeForKey(key)
-		if start == end {
+		start, sep, end, hasExt := t.findTypeForKey(key)
+		if start == sep {
 			if hasExt {
 				b = b[2:]
 			}
-			t.str = fmt.Sprintf("%s-%s%s", s[:start], b, s[end:])
+			t.str = fmt.Sprintf("%s-%s%s", s[:sep], b, s[end:])
 		} else {
-			t.str = fmt.Sprintf("%s%s%s", s[:start], value, s[end:])
+			t.str = fmt.Sprintf("%s-%s%s", s[:start+3], value, s[end:])
 		}
 	}
 	return t, nil
@@ -399,10 +407,10 @@
 // wasn't found. The hasExt return value reports whether an -u extension was present.
 // Note: the extensions are typically very small and are likely to contain
 // only one key-type pair.
-func (t Tag) findTypeForKey(key string) (start, end int, hasExt bool) {
+func (t Tag) findTypeForKey(key string) (start, sep, end int, hasExt bool) {
 	p := int(t.pExt)
 	if len(key) != 2 || p == len(t.str) || p == 0 {
-		return p, p, false
+		return p, p, p, false
 	}
 	s := t.str
 
@@ -410,10 +418,10 @@
 	for p++; s[p] != 'u'; p++ {
 		if s[p] > 'u' {
 			p--
-			return p, p, false
+			return p, p, p, false
 		}
 		if p = nextExtension(s, p); p == len(s) {
-			return len(s), len(s), false
+			return len(s), len(s), len(s), false
 		}
 	}
 	// Proceed to the hyphen following the extension name.
@@ -424,40 +432,28 @@
 
 	// Iterate over keys until we get the end of a section.
 	for {
-		// p points to the hyphen preceding the current token.
-		if p3 := p + 3; s[p3] == '-' {
-			// Found a key.
-			// Check whether we just processed the key that was requested.
-			if curKey == key {
-				return start, p, true
-			}
-			// Set to the next key and continue scanning type tokens.
-			curKey = s[p+1 : p3]
-			if curKey > key {
-				return p, p, true
-			}
-			// Start of the type token sequence.
-			start = p + 4
-			// A type is at least 3 characters long.
-			p += 7 // 4 + 3
-		} else {
-			// Attribute or type, which is at least 3 characters long.
-			p += 4
+		end = p
+		for p++; p < len(s) && s[p] != '-'; p++ {
 		}
-		// p points past the third character of a type or attribute.
-		max := p + 5 // maximum length of token plus hyphen.
-		if len(s) < max {
-			max = len(s)
-		}
-		for ; p < max && s[p] != '-'; p++ {
-		}
-		// Bail if we have exhausted all tokens or if the next token starts
-		// a new extension.
-		if p == len(s) || s[p+2] == '-' {
-			if curKey == key {
-				return start, p, true
+		n := p - end - 1
+		if n <= 2 && curKey == key {
+			if sep < end {
+				sep++
+			}
+			return start, sep, end, true
+		}
+		switch n {
+		case 0, // invalid string
+			1: // next extension
+			return end, end, end, true
+		case 2:
+			// next key
+			curKey = s[end+1 : p]
+			if curKey > key {
+				return end, end, end, true
 			}
-			return p, p, true
+			start = end
+			sep = p
 		}
 	}
 }
diff -Nru golang-golang-x-text-0.3.5/internal/language/language_test.go golang-golang-x-text-0.3.6/internal/language/language_test.go
--- golang-golang-x-text-0.3.5/internal/language/language_test.go	2020-12-08 08:13:44.000000000 +0800
+++ golang-golang-x-text-0.3.6/internal/language/language_test.go	2021-03-30 13:48:03.000000000 +0800
@@ -432,7 +432,9 @@
 		{"co", "pinyin", "en-u-co-phonebk-cu-xau", "en-u-co-pinyin-cu-xau", false},
 		{"co", "pinyin", "en-u-co-phonebk-v-xx", "en-u-co-pinyin-v-xx", false},
 		{"co", "pinyin", "en-u-co-phonebk-x-x", "en-u-co-pinyin-x-x", false},
+		{"co", "pinyin", "en-u-co-x-x", "en-u-co-pinyin-x-x", false},
 		{"nu", "arabic", "en-u-co-phonebk-nu-vaai", "en-u-co-phonebk-nu-arabic", false},
+		{"nu", "arabic", "en-u-co-phonebk-nu", "en-u-co-phonebk-nu-arabic", false},
 		// add to existing -u extension
 		{"co", "pinyin", "en-u-ca-gregory", "en-u-ca-gregory-co-pinyin", false},
 		{"co", "pinyin", "en-u-ca-gregory-nu-vaai", "en-u-ca-gregory-co-pinyin-nu-vaai", false},
@@ -441,8 +443,12 @@
 		{"ca", "gregory", "en-u-co-pinyin", "en-u-ca-gregory-co-pinyin", false},
 		// remove pair
 		{"co", "", "en-u-co-phonebk", "en", false},
+		{"co", "", "en-u-co", "en", false},
+		{"co", "", "en-u-co-v", "en", false},
+		{"co", "", "en-u-co-v-", "en", false},
 		{"co", "", "en-u-ca-gregory-co-phonebk", "en-u-ca-gregory", false},
 		{"co", "", "en-u-co-phonebk-nu-arabic", "en-u-nu-arabic", false},
+		{"co", "", "en-u-co-nu-arabic", "en-u-nu-arabic", false},
 		{"co", "", "en", "en", false},
 		// add -u extension
 		{"co", "pinyin", "en", "en-u-co-pinyin", false},
@@ -504,6 +510,8 @@
 		{"cu", false, "en-a-va-v-va", "en-a-va"},
 		{"cu", false, "en-x-a", "en"},
 		// Tags with the -u extension.
+		{"nu", true, "en-u-cu-nu", "en-u-cu"},
+		{"cu", true, "en-u-cu-nu", "en-u"},
 		{"co", true, "en-u-co-standard", "standard"},
 		{"co", true, "yue-u-co-pinyin", "pinyin"},
 		{"co", true, "en-u-co-abc", "abc"},
@@ -519,9 +527,9 @@
 		{"cu", true, "en-u-co-abc-def-nu-arabic", "en-u-co-abc-def"},
 	}
 	for i, tt := range tests {
-		start, end, hasExt := Make(tt.in).findTypeForKey(tt.key)
-		if start != end {
-			res := tt.in[start:end]
+		start, sep, end, hasExt := Make(tt.in).findTypeForKey(tt.key)
+		if sep != end {
+			res := tt.in[sep:end]
 			if res != tt.out {
 				t.Errorf("%d:%s: was %q; want %q", i, tt.in, res, tt.out)
 			}
diff -Nru golang-golang-x-text-0.3.5/internal/language/parse.go golang-golang-x-text-0.3.6/internal/language/parse.go
--- golang-golang-x-text-0.3.5/internal/language/parse.go	2020-12-08 08:13:44.000000000 +0800
+++ golang-golang-x-text-0.3.6/internal/language/parse.go	2021-03-30 13:48:03.000000000 +0800
@@ -138,7 +138,7 @@
 			b = make([]byte, n)
 			copy(b, s.b[:oldStart])
 		} else {
-			b = s.b[:n:n]
+			b = s.b[:n]
 		}
 		copy(b[end:], s.b[oldEnd:])
 		s.b = b
@@ -483,7 +483,7 @@
 func parseExtension(scan *scanner) int {
 	start, end := scan.start, scan.end
 	switch scan.token[0] {
-	case 'u':
+	case 'u': // https://www.ietf.org/rfc/rfc6067.txt
 		attrStart := end
 		scan.scan()
 		for last := []byte{}; len(scan.token) > 2; scan.scan() {
@@ -503,27 +503,29 @@
 			last = scan.token
 			end = scan.end
 		}
+		// Scan key-type sequences. A key is of length 2 and may be followed
+		// by 0 or more "type" subtags from 3 to the maximum of 8 letters.
 		var last, key []byte
 		for attrEnd := end; len(scan.token) == 2; last = key {
 			key = scan.token
-			keyEnd := scan.end
-			end = scan.acceptMinSize(3)
+			end = scan.end
+			for scan.scan(); end < scan.end && len(scan.token) > 2; scan.scan() {
+				end = scan.end
+			}
 			// TODO: check key value validity
-			if keyEnd == end || bytes.Compare(key, last) != 1 {
+			if bytes.Compare(key, last) != 1 || scan.err != nil {
 				// We have an invalid key or the keys are not sorted.
 				// Start scanning keys from scratch and reorder.
 				p := attrEnd + 1
 				scan.next = p
 				keys := [][]byte{}
 				for scan.scan(); len(scan.token) == 2; {
-					keyStart, keyEnd := scan.start, scan.end
-					end = scan.acceptMinSize(3)
-					if keyEnd != end {
-						keys = append(keys, scan.b[keyStart:end])
-					} else {
-						scan.setError(ErrSyntax)
-						end = keyStart
+					keyStart := scan.start
+					end = scan.end
+					for scan.scan(); end < scan.end && len(scan.token) > 2; scan.scan() {
+						end = scan.end
 					}
+					keys = append(keys, scan.b[keyStart:end])
 				}
 				sort.Stable(bytesSort{keys, 2})
 				if n := len(keys); n > 0 {
@@ -547,7 +549,7 @@
 				break
 			}
 		}
-	case 't':
+	case 't': // https://www.ietf.org/rfc/rfc6497.txt
 		scan.scan()
 		if n := len(scan.token); n >= 2 && n <= 3 && isAlpha(scan.token[1]) {
 			_, end = parseTag(scan)
diff -Nru golang-golang-x-text-0.3.5/internal/language/parse_test.go golang-golang-x-text-0.3.6/internal/language/parse_test.go
--- golang-golang-x-text-0.3.5/internal/language/parse_test.go	2020-12-08 08:13:44.000000000 +0800
+++ golang-golang-x-text-0.3.6/internal/language/parse_test.go	2021-03-30 13:48:03.000000000 +0800
@@ -164,13 +164,13 @@
 		{in: "en-9-aa-0-aa-z-bb-x-a", lang: "en", extList: []string{"0-aa", "9-aa", "z-bb", "x-a"}, changed: true},
 		{in: "en-u-c", lang: "en", ext: "", invalid: true},
 		{in: "en-u-co-phonebk", lang: "en", ext: "u-co-phonebk"},
-		{in: "en-u-co-phonebk-ca", lang: "en", ext: "u-co-phonebk", invalid: true},
-		{in: "en-u-nu-arabic-co-phonebk-ca", lang: "en", ext: "u-co-phonebk-nu-arabic", invalid: true, changed: true},
-		{in: "en-u-nu-arabic-co-phonebk-ca-x", lang: "en", ext: "u-co-phonebk-nu-arabic", invalid: true, changed: true},
-		{in: "en-u-nu-arabic-co-phonebk-ca-s", lang: "en", ext: "u-co-phonebk-nu-arabic", invalid: true, changed: true},
-		{in: "en-u-nu-arabic-co-phonebk-ca-a12345678", lang: "en", ext: "u-co-phonebk-nu-arabic", invalid: true, changed: true},
-		{in: "en-u-co-phonebook", lang: "en", ext: "", invalid: true},
-		{in: "en-u-co-phonebook-cu-xau", lang: "en", ext: "u-cu-xau", invalid: true, changed: true},
+		{in: "en-u-co-phonebk-ca", lang: "en", ext: "u-ca-co-phonebk", changed: true},
+		{in: "en-u-nu-arabic-co-phonebk-ca", lang: "en", ext: "u-ca-co-phonebk-nu-arabic", changed: true},
+		{in: "en-u-nu-arabic-co-phonebk-ca-x", lang: "en", ext: "u-ca-co-phonebk-nu-arabic", invalid: true, changed: true},
+		{in: "en-u-nu-arabic-co-phonebk-ca-s", lang: "en", ext: "u-ca-co-phonebk-nu-arabic", invalid: true, changed: true},
+		{in: "en-u-nu-arabic-co-phonebk-ca-a12345678", lang: "en", ext: "u-ca-co-phonebk-nu-arabic", invalid: true, changed: true},
+		{in: "en-u-co-phonebook", lang: "en", ext: "u-co", invalid: true},
+		{in: "en-u-co-phonebook-cu-xau", lang: "en", ext: "u-co-cu-xau", invalid: true, changed: true},
 		{in: "en-Cyrl-u-co-phonebk", lang: "en", script: "Cyrl", ext: "u-co-phonebk"},
 		{in: "en-US-u-co-phonebk", lang: "en", region: "US", ext: "u-co-phonebk"},
 		{in: "en-US-u-co-phonebk-cu-xau", lang: "en", region: "US", ext: "u-co-phonebk-cu-xau"},
@@ -179,9 +179,8 @@
 		{in: "en-u-def-abc-cu-xua-co-phonebk", lang: "en", ext: "u-abc-def-co-phonebk-cu-xua", changed: true},
 		{in: "en-u-def-abc", lang: "en", ext: "u-abc-def", changed: true},
 		{in: "en-u-cu-xua-co-phonebk-a-cd", lang: "en", extList: []string{"a-cd", "u-co-phonebk-cu-xua"}, changed: true},
-		// Invalid "u" extension. Drop invalid parts.
-		{in: "en-u-cu-co-phonebk", lang: "en", extList: []string{"u-co-phonebk"}, invalid: true, changed: true},
-		{in: "en-u-cu-xau-co", lang: "en", extList: []string{"u-cu-xau"}, invalid: true},
+		{in: "en-u-cu-co-phonebk", lang: "en", extList: []string{"u-co-phonebk-cu"}, changed: true},
+		{in: "en-u-cu-xau-co", lang: "en", extList: []string{"u-co-cu-xau"}, changed: true},
 		// LDML spec is not specific about it, but remove duplicates and return an error if the values differ.
 		{in: "en-u-cu-xau-co-phonebk-cu-xau", lang: "en", ext: "u-co-phonebk-cu-xau", changed: true},
 		// No change as the result is a substring of the original!
@@ -351,8 +350,8 @@
 		{"aa-AB", mkInvalid("AB")},
 		// ill-formed wins over invalid.
 		{"ac-u", ErrSyntax},
-		{"ac-u-ca", ErrSyntax},
-		{"ac-u-ca-co-pinyin", ErrSyntax},
+		{"ac-u-ca", mkInvalid("ac")},
+		{"ac-u-ca-co-pinyin", mkInvalid("ac")},
 		{"noob", ErrSyntax},
 	}
 	for _, tt := range tests {
diff -Nru golang-golang-x-text-0.3.5/language/language.go golang-golang-x-text-0.3.6/language/language.go
--- golang-golang-x-text-0.3.5/language/language.go	2020-12-08 08:13:44.000000000 +0800
+++ golang-golang-x-text-0.3.6/language/language.go	2021-03-30 13:48:03.000000000 +0800
@@ -412,6 +412,10 @@
 // are of the allowed values defined for the Unicode locale extension ('u') in
 // https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers.
 // TypeForKey will traverse the inheritance chain to get the correct value.
+//
+// If there are multiple types associated with a key, only the first will be
+// returned. If there is no type associated with a key, it returns the empty
+// string.
 func (t Tag) TypeForKey(key string) string {
 	if !compact.Tag(t).MayHaveExtensions() {
 		if key != "rg" && key != "va" {
diff -Nru golang-golang-x-text-0.3.5/language/language_test.go golang-golang-x-text-0.3.6/language/language_test.go
--- golang-golang-x-text-0.3.5/language/language_test.go	2020-12-08 08:13:44.000000000 +0800
+++ golang-golang-x-text-0.3.6/language/language_test.go	2021-03-30 13:48:03.000000000 +0800
@@ -523,6 +523,13 @@
 		{"en-GB-u-rg-usz", "en-GB-u-rg-usz", Raw},
 		{"en-GB-u-rg-usz-va-posix", "en-GB-u-rg-usz-va-posix", Raw},
 		{"en-GB-u-rg-usz-co-phonebk", "en-GB-u-co-phonebk-rg-usz", Raw},
+
+		// CVE-2020-28851
+		// invalid key-value pair of -u- extension.
+		{"ES-u-000-00", "es-u-000-00", Raw},
+		{"ES-u-000-00-v-00", "es-u-000-00-v-00", Raw},
+		// reordered and unknown extension.
+		{"ES-v-00-u-000-00", "es-u-000-00-v-00", Raw},
 	}
 	for i, tt := range tests {
 		in, _ := Raw.Parse(tt.in)
@@ -553,6 +560,12 @@
 		{"rg", "en-u-rg-gbzzzz", "gbzzzz"},
 		{"nu", "en-u-co-phonebk-nu-arabic", "arabic"},
 		{"kc", "cmn-u-co-stroke", ""},
+		{"rg", "cmn-u-rg", ""},
+		{"rg", "cmn-u-rg-co-stroke", ""},
+		{"co", "cmn-u-rg-co-stroke", "stroke"},
+		{"co", "cmn-u-co-rg-gbzzzz", ""},
+		{"rg", "cmn-u-co-rg-gbzzzz", "gbzzzz"},
+		{"rg", "cmn-u-rg-gbzzzz-nlzzzz", "gbzzzz"},
 	}
 	for _, tt := range tests {
 		if v := Make(tt.in).TypeForKey(tt.key); v != tt.out {
diff -Nru golang-golang-x-text-0.3.5/language/match_test.go golang-golang-x-text-0.3.6/language/match_test.go
--- golang-golang-x-text-0.3.5/language/match_test.go	2020-12-08 08:13:44.000000000 +0800
+++ golang-golang-x-text-0.3.6/language/match_test.go	2021-03-30 13:48:03.000000000 +0800
@@ -224,6 +224,20 @@
 	return fmt.Sprintf("%v:%d:%v:%v-%v|%v", t.tag, t.index, t.conf, t.maxRegion, t.maxScript, t.altScript)
 }
 
+func TestIssue43834(t *testing.T) {
+	matcher := NewMatcher([]Tag{English})
+
+	// ZZ is the largest region code and should not cause overflow.
+	desired, _, err := ParseAcceptLanguage("en-ZZ")
+	if err != nil {
+		t.Error(err)
+	}
+	_, i, _ := matcher.Match(desired...)
+	if i != 0 {
+		t.Errorf("got %v; want 0", i)
+	}
+}
+
 func TestBestMatchAlloc(t *testing.T) {
 	m := NewMatcher(makeTagList("en sr nl"))
 	// Go allocates when creating a list of tags from a single tag!
diff -Nru golang-golang-x-text-0.3.5/language/parse_test.go golang-golang-x-text-0.3.6/language/parse_test.go
--- golang-golang-x-text-0.3.5/language/parse_test.go	2020-12-08 08:13:44.000000000 +0800
+++ golang-golang-x-text-0.3.6/language/parse_test.go	2021-03-30 13:48:03.000000000 +0800
@@ -101,13 +101,13 @@
 		{in: "en-9-aa-0-aa-z-bb-x-a", lang: "en", extList: []string{"0-aa", "9-aa", "z-bb", "x-a"}, changed: true},
 		{in: "en-u-c", lang: "en", ext: "", invalid: true},
 		{in: "en-u-co-phonebk", lang: "en", ext: "u-co-phonebk"},
-		{in: "en-u-co-phonebk-ca", lang: "en", ext: "u-co-phonebk", invalid: true},
-		{in: "en-u-nu-arabic-co-phonebk-ca", lang: "en", ext: "u-co-phonebk-nu-arabic", invalid: true, changed: true},
-		{in: "en-u-nu-arabic-co-phonebk-ca-x", lang: "en", ext: "u-co-phonebk-nu-arabic", invalid: true, changed: true},
-		{in: "en-u-nu-arabic-co-phonebk-ca-s", lang: "en", ext: "u-co-phonebk-nu-arabic", invalid: true, changed: true},
-		{in: "en-u-nu-arabic-co-phonebk-ca-a12345678", lang: "en", ext: "u-co-phonebk-nu-arabic", invalid: true, changed: true},
-		{in: "en-u-co-phonebook", lang: "en", ext: "", invalid: true},
-		{in: "en-u-co-phonebook-cu-xau", lang: "en", ext: "u-cu-xau", invalid: true, changed: true},
+		{in: "en-u-co-phonebk-ca", lang: "en", ext: "u-ca-co-phonebk", invalid: true},
+		{in: "en-u-nu-arabic-co-phonebk-ca", lang: "en", ext: "u-ca-co-phonebk-nu-arabic", invalid: true, changed: true},
+		{in: "en-u-nu-arabic-co-phonebk-ca-x", lang: "en", ext: "u-ca-co-phonebk-nu-arabic", invalid: true, changed: true},
+		{in: "en-u-nu-arabic-co-phonebk-ca-s", lang: "en", ext: "u-ca-co-phonebk-nu-arabic", invalid: true, changed: true},
+		{in: "en-u-nu-arabic-co-phonebk-ca-a12345678", lang: "en", ext: "u-ca-co-phonebk-nu-arabic", invalid: true, changed: true},
+		{in: "en-u-co-phonebook", lang: "en", ext: "u-co", invalid: true},
+		{in: "en-u-co-phonebook-cu-xau", lang: "en", ext: "u-co-cu-xau", invalid: true, changed: true},
 		{in: "en-Cyrl-u-co-phonebk", lang: "en", script: "Cyrl", ext: "u-co-phonebk"},
 		{in: "en-US-u-co-phonebk", lang: "en", region: "US", ext: "u-co-phonebk"},
 		{in: "en-US-u-co-phonebk-cu-xau", lang: "en", region: "US", ext: "u-co-phonebk-cu-xau"},
@@ -117,8 +117,8 @@
 		{in: "en-u-def-abc", lang: "en", ext: "u-abc-def", changed: true},
 		{in: "en-u-cu-xua-co-phonebk-a-cd", lang: "en", extList: []string{"a-cd", "u-co-phonebk-cu-xua"}, changed: true},
 		// Invalid "u" extension. Drop invalid parts.
-		{in: "en-u-cu-co-phonebk", lang: "en", extList: []string{"u-co-phonebk"}, invalid: true, changed: true},
-		{in: "en-u-cu-xau-co", lang: "en", extList: []string{"u-cu-xau"}, invalid: true},
+		{in: "en-u-cu-co-phonebk", lang: "en", extList: []string{"u-co-phonebk-cu"}, invalid: true, changed: true},
+		{in: "en-u-cu-xau-co", lang: "en", extList: []string{"u-co-cu-xau"}, invalid: true},
 		// We allow duplicate keys as the LDML spec does not explicitly prohibit it.
 		// TODO: Consider eliminating duplicates and returning an error.
 		{in: "en-u-cu-xau-co-phonebk-cu-xau", lang: "en", ext: "u-co-phonebk-cu-xau", changed: true},
@@ -219,8 +219,8 @@
 		{"aa-AB", mkInvalid("AB")},
 		// ill-formed wins over invalid.
 		{"ac-u", errSyntax},
-		{"ac-u-ca", errSyntax},
-		{"ac-u-ca-co-pinyin", errSyntax},
+		{"ac-u-ca", mkInvalid("ac")},
+		{"ac-u-ca-co-pinyin", mkInvalid("ac")},
 		{"noob", errSyntax},
 	}
 	for _, tt := range tests {
diff -Nru golang-golang-x-text-0.3.5/language/tables.go golang-golang-x-text-0.3.6/language/tables.go
--- golang-golang-x-text-0.3.5/language/tables.go	2020-12-08 08:13:44.000000000 +0800
+++ golang-golang-x-text-0.3.6/language/tables.go	2021-03-30 13:48:03.000000000 +0800
@@ -47,7 +47,7 @@
 	_Zzzz = 251
 )
 
-var regionToGroups = []uint8{ // 357 elements
+var regionToGroups = []uint8{ // 358 elements
 	// Entry 0 - 3F
 	0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04,
 	0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00,
@@ -98,8 +98,8 @@
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00,
-} // Size: 381 bytes
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+} // Size: 382 bytes
 
 var paradigmLocales = [][3]uint16{ // 3 elements
 	0: [3]uint16{0x139, 0x0, 0x7b},
@@ -295,4 +295,4 @@
 	14: {lang: 0x529, script: 0x3c, group: 0x80, distance: 0x5},
 } // Size: 114 bytes
 
-// Total table size 1471 bytes (1KiB); checksum: 4CB1CD46
+// Total table size 1472 bytes (1KiB); checksum: F86C669


Reply to: