diff options
Diffstat (limited to 'meta/recipes-devtools/go/go-1.14')
76 files changed, 14406 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.14/0001-CVE-2022-32190.patch b/meta/recipes-devtools/go/go-1.14/0001-CVE-2022-32190.patch new file mode 100644 index 0000000000..ad263b8023 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/0001-CVE-2022-32190.patch | |||
@@ -0,0 +1,74 @@ | |||
1 | From 755f2dc35a19e6806de3ecbf836fa06ad875c67a Mon Sep 17 00:00:00 2001 | ||
2 | From: Carl Johnson <me@carlmjohnson.net> | ||
3 | Date: Fri, 4 Mar 2022 14:49:52 +0000 | ||
4 | Subject: [PATCH 1/4] net/url: add JoinPath, URL.JoinPath | ||
5 | |||
6 | Builds on CL 332209. | ||
7 | |||
8 | Fixes #47005 | ||
9 | |||
10 | Change-Id: I82708dede05d79a196ca63f5a4e7cb5ac9a041ea | ||
11 | GitHub-Last-Rev: 51b735066eef74f5e67c3e8899c58f44c0383c61 | ||
12 | GitHub-Pull-Request: golang/go#50383 | ||
13 | Reviewed-on: https://go-review.googlesource.com/c/go/+/374654 | ||
14 | Reviewed-by: Russ Cox <rsc@golang.org> | ||
15 | Auto-Submit: Russ Cox <rsc@golang.org> | ||
16 | Trust: Ian Lance Taylor <iant@golang.org> | ||
17 | Reviewed-by: Damien Neil <dneil@google.com> | ||
18 | Run-TryBot: Ian Lance Taylor <iant@golang.org> | ||
19 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
20 | |||
21 | Upstream-Status: Backport [https://github.com/golang/go/commit/604140d93111f89911e17cb147dcf6a02d2700d0] | ||
22 | CVE: CVE-2022-32190 | ||
23 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
24 | --- | ||
25 | src/net/url/url.go | 23 +++++++++++++++++++++++ | ||
26 | 1 file changed, 23 insertions(+) | ||
27 | |||
28 | diff --git a/src/net/url/url.go b/src/net/url/url.go | ||
29 | index 2880e82..dea8bfe 100644 | ||
30 | --- a/src/net/url/url.go | ||
31 | +++ b/src/net/url/url.go | ||
32 | @@ -13,6 +13,7 @@ package url | ||
33 | import ( | ||
34 | "errors" | ||
35 | "fmt" | ||
36 | + "path" | ||
37 | "sort" | ||
38 | "strconv" | ||
39 | "strings" | ||
40 | @@ -1104,6 +1105,17 @@ func (u *URL) UnmarshalBinary(text []byte) error { | ||
41 | return nil | ||
42 | } | ||
43 | |||
44 | +// JoinPath returns a new URL with the provided path elements joined to | ||
45 | +// any existing path and the resulting path cleaned of any ./ or ../ elements. | ||
46 | +func (u *URL) JoinPath(elem ...string) *URL { | ||
47 | + url := *u | ||
48 | + if len(elem) > 0 { | ||
49 | + elem = append([]string{u.Path}, elem...) | ||
50 | + url.setPath(path.Join(elem...)) | ||
51 | + } | ||
52 | + return &url | ||
53 | +} | ||
54 | + | ||
55 | // validUserinfo reports whether s is a valid userinfo string per RFC 3986 | ||
56 | // Section 3.2.1: | ||
57 | // userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) | ||
58 | @@ -1144,3 +1156,14 @@ func stringContainsCTLByte(s string) bool { | ||
59 | } | ||
60 | return false | ||
61 | } | ||
62 | + | ||
63 | +// JoinPath returns a URL string with the provided path elements joined to | ||
64 | +// the existing path of base and the resulting path cleaned of any ./ or ../ elements. | ||
65 | +func JoinPath(base string, elem ...string) (result string, err error) { | ||
66 | + url, err := Parse(base) | ||
67 | + if err != nil { | ||
68 | + return | ||
69 | + } | ||
70 | + result = url.JoinPath(elem...).String() | ||
71 | + return | ||
72 | +} | ||
73 | -- | ||
74 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/0002-CVE-2022-32190.patch b/meta/recipes-devtools/go/go-1.14/0002-CVE-2022-32190.patch new file mode 100644 index 0000000000..1a11cc72bc --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/0002-CVE-2022-32190.patch | |||
@@ -0,0 +1,48 @@ | |||
1 | From 985108de87e7d2ecb2b28cb53b323d530387b884 Mon Sep 17 00:00:00 2001 | ||
2 | From: Ian Lance Taylor <iant@golang.org> | ||
3 | Date: Thu, 31 Mar 2022 13:21:39 -0700 | ||
4 | Subject: [PATCH 2/4] net/url: preserve a trailing slash in JoinPath | ||
5 | |||
6 | Fixes #52074 | ||
7 | |||
8 | Change-Id: I30897f32e70a6ca0c4e11aaf07088c27336efaba | ||
9 | Reviewed-on: https://go-review.googlesource.com/c/go/+/397256 | ||
10 | Trust: Ian Lance Taylor <iant@golang.org> | ||
11 | Run-TryBot: Ian Lance Taylor <iant@golang.org> | ||
12 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
13 | Reviewed-by: Matt Layher <mdlayher@gmail.com> | ||
14 | Trust: Matt Layher <mdlayher@gmail.com> | ||
15 | |||
16 | Upstream-Status: Backport [https://github.com/golang/go/commit/dbb52cc9f3e83a3040f46c2ae7650c15ab342179] | ||
17 | CVE: CVE-2022-32190 | ||
18 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
19 | --- | ||
20 | src/net/url/url.go | 9 ++++++++- | ||
21 | 1 file changed, 8 insertions(+), 1 deletion(-) | ||
22 | |||
23 | diff --git a/src/net/url/url.go b/src/net/url/url.go | ||
24 | index dea8bfe..3436707 100644 | ||
25 | --- a/src/net/url/url.go | ||
26 | +++ b/src/net/url/url.go | ||
27 | @@ -1107,11 +1107,18 @@ func (u *URL) UnmarshalBinary(text []byte) error { | ||
28 | |||
29 | // JoinPath returns a new URL with the provided path elements joined to | ||
30 | // any existing path and the resulting path cleaned of any ./ or ../ elements. | ||
31 | +// Any sequences of multiple / characters will be reduced to a single /. | ||
32 | func (u *URL) JoinPath(elem ...string) *URL { | ||
33 | url := *u | ||
34 | if len(elem) > 0 { | ||
35 | elem = append([]string{u.Path}, elem...) | ||
36 | - url.setPath(path.Join(elem...)) | ||
37 | + p := path.Join(elem...) | ||
38 | + // path.Join will remove any trailing slashes. | ||
39 | + // Preserve at least one. | ||
40 | + if strings.HasSuffix(elem[len(elem)-1], "/") && !strings.HasSuffix(p, "/") { | ||
41 | + p += "/" | ||
42 | + } | ||
43 | + url.setPath(p) | ||
44 | } | ||
45 | return &url | ||
46 | } | ||
47 | -- | ||
48 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/0003-CVE-2022-32190.patch b/meta/recipes-devtools/go/go-1.14/0003-CVE-2022-32190.patch new file mode 100644 index 0000000000..816d914983 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/0003-CVE-2022-32190.patch | |||
@@ -0,0 +1,36 @@ | |||
1 | From 2c632b883b0f11084cc247c8b50ad6c71fa7b447 Mon Sep 17 00:00:00 2001 | ||
2 | From: Sean Liao <sean@liao.dev> | ||
3 | Date: Sat, 9 Jul 2022 18:38:45 +0100 | ||
4 | Subject: [PATCH 3/4] net/url: use EscapedPath for url.JoinPath | ||
5 | |||
6 | Fixes #53763 | ||
7 | |||
8 | Change-Id: I08b53f159ebdce7907e8cc17316fd0c982363239 | ||
9 | Reviewed-on: https://go-review.googlesource.com/c/go/+/416774 | ||
10 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
11 | Reviewed-by: Damien Neil <dneil@google.com> | ||
12 | Reviewed-by: Bryan Mills <bcmills@google.com> | ||
13 | Run-TryBot: Ian Lance Taylor <iant@golang.org> | ||
14 | |||
15 | Upstream-Status: Backport [https://github.com/golang/go/commit/bf5898ef53d1693aa572da0da746c05e9a6f15c5] | ||
16 | CVE: CVE-2022-32190 | ||
17 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
18 | --- | ||
19 | src/net/url/url.go | 2 +- | ||
20 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
21 | |||
22 | diff --git a/src/net/url/url.go b/src/net/url/url.go | ||
23 | index 3436707..73079a5 100644 | ||
24 | --- a/src/net/url/url.go | ||
25 | +++ b/src/net/url/url.go | ||
26 | @@ -1111,7 +1111,7 @@ func (u *URL) UnmarshalBinary(text []byte) error { | ||
27 | func (u *URL) JoinPath(elem ...string) *URL { | ||
28 | url := *u | ||
29 | if len(elem) > 0 { | ||
30 | - elem = append([]string{u.Path}, elem...) | ||
31 | + elem = append([]string{u.EscapedPath()}, elem...) | ||
32 | p := path.Join(elem...) | ||
33 | // path.Join will remove any trailing slashes. | ||
34 | // Preserve at least one. | ||
35 | -- | ||
36 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/0004-CVE-2022-32190.patch b/meta/recipes-devtools/go/go-1.14/0004-CVE-2022-32190.patch new file mode 100644 index 0000000000..4bdff3aed4 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/0004-CVE-2022-32190.patch | |||
@@ -0,0 +1,82 @@ | |||
1 | From f61e428699cbb52bab31fe2c124f49d085a209fe Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Fri, 12 Aug 2022 16:21:09 -0700 | ||
4 | Subject: [PATCH 4/4] net/url: consistently remove ../ elements in JoinPath | ||
5 | |||
6 | JoinPath would fail to remove relative elements from the start of | ||
7 | the path when the first path element is "". | ||
8 | |||
9 | In addition, JoinPath would return the original path unmodified | ||
10 | when provided with no elements to join, violating the documented | ||
11 | behavior of always cleaning the resulting path. | ||
12 | |||
13 | Correct both these cases. | ||
14 | |||
15 | JoinPath("http://go.dev", "../go") | ||
16 | // before: http://go.dev/../go | ||
17 | // after: http://go.dev/go | ||
18 | |||
19 | JoinPath("http://go.dev/../go") | ||
20 | // before: http://go.dev/../go | ||
21 | // after: http://go.dev/go | ||
22 | |||
23 | For #54385. | ||
24 | Fixes #54635. | ||
25 | Fixes CVE-2022-32190. | ||
26 | |||
27 | Change-Id: I6d22cd160d097c50703dd96e4f453c6c118fd5d9 | ||
28 | Reviewed-on: https://go-review.googlesource.com/c/go/+/423514 | ||
29 | Reviewed-by: David Chase <drchase@google.com> | ||
30 | Reviewed-by: Alan Donovan <adonovan@google.com> | ||
31 | (cherry picked from commit 0765da5884adcc8b744979303a36a27092d8fc51) | ||
32 | Reviewed-on: https://go-review.googlesource.com/c/go/+/425357 | ||
33 | Run-TryBot: Damien Neil <dneil@google.com> | ||
34 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
35 | |||
36 | Upstream-Status: Backport [https://github.com/golang/go/commit/28335508913a46e05ef0c04a18e8a1a6beb775ec] | ||
37 | CVE: CVE-2022-32190 | ||
38 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
39 | --- | ||
40 | src/net/url/url.go | 26 ++++++++++++++++---------- | ||
41 | 1 file changed, 16 insertions(+), 10 deletions(-) | ||
42 | |||
43 | diff --git a/src/net/url/url.go b/src/net/url/url.go | ||
44 | index 73079a5..1e8baf9 100644 | ||
45 | --- a/src/net/url/url.go | ||
46 | +++ b/src/net/url/url.go | ||
47 | @@ -1109,17 +1109,23 @@ func (u *URL) UnmarshalBinary(text []byte) error { | ||
48 | // any existing path and the resulting path cleaned of any ./ or ../ elements. | ||
49 | // Any sequences of multiple / characters will be reduced to a single /. | ||
50 | func (u *URL) JoinPath(elem ...string) *URL { | ||
51 | - url := *u | ||
52 | - if len(elem) > 0 { | ||
53 | - elem = append([]string{u.EscapedPath()}, elem...) | ||
54 | - p := path.Join(elem...) | ||
55 | - // path.Join will remove any trailing slashes. | ||
56 | - // Preserve at least one. | ||
57 | - if strings.HasSuffix(elem[len(elem)-1], "/") && !strings.HasSuffix(p, "/") { | ||
58 | - p += "/" | ||
59 | - } | ||
60 | - url.setPath(p) | ||
61 | + elem = append([]string{u.EscapedPath()}, elem...) | ||
62 | + var p string | ||
63 | + if !strings.HasPrefix(elem[0], "/") { | ||
64 | + // Return a relative path if u is relative, | ||
65 | + // but ensure that it contains no ../ elements. | ||
66 | + elem[0] = "/" + elem[0] | ||
67 | + p = path.Join(elem...)[1:] | ||
68 | + } else { | ||
69 | + p = path.Join(elem...) | ||
70 | } | ||
71 | + // path.Join will remove any trailing slashes. | ||
72 | + // Preserve at least one. | ||
73 | + if strings.HasSuffix(elem[len(elem)-1], "/") && !strings.HasSuffix(p, "/") { | ||
74 | + p += "/" | ||
75 | + } | ||
76 | + url := *u | ||
77 | + url.setPath(p) | ||
78 | return &url | ||
79 | } | ||
80 | |||
81 | -- | ||
82 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2020-29510.patch b/meta/recipes-devtools/go/go-1.14/CVE-2020-29510.patch new file mode 100644 index 0000000000..e1c9e0bdb9 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2020-29510.patch | |||
@@ -0,0 +1,65 @@ | |||
1 | From a0bf4d38dc2057d28396594264bbdd43d412de22 Mon Sep 17 00:00:00 2001 | ||
2 | From: Filippo Valsorda <filippo@golang.org> | ||
3 | Date: Tue, 27 Oct 2020 00:21:30 +0100 | ||
4 | Subject: [PATCH] encoding/xml: replace comments inside directives with a space | ||
5 | |||
6 | A Directive (like <!ENTITY xxx []>) can't have other nodes nested inside | ||
7 | it (in our data structure representation), so there is no way to | ||
8 | preserve comments. The previous behavior was to just elide them, which | ||
9 | however might change the semantic meaning of the surrounding markup. | ||
10 | Instead, replace them with a space which hopefully has the same semantic | ||
11 | effect of the comment. | ||
12 | |||
13 | Directives are not actually a node type in the XML spec, which instead | ||
14 | specifies each of them separately (<!ENTITY, <!DOCTYPE, etc.), each with | ||
15 | its own grammar. The rules for where and when the comments are allowed | ||
16 | are not straightforward, and can't be implemented without implementing | ||
17 | custom logic for each of the directives. | ||
18 | |||
19 | Simply preserving the comments in the body of the directive would be | ||
20 | problematic, as there can be unmatched quotes inside the comment. | ||
21 | Whether those quotes are considered meaningful semantically or not, | ||
22 | other parsers might disagree and interpret the output differently. | ||
23 | |||
24 | This issue was reported by Juho Nurminen of Mattermost as it leads to | ||
25 | round-trip mismatches. See #43168. It's not being fixed in a security | ||
26 | release because round-trip stability is not a currently supported | ||
27 | security property of encoding/xml, and we don't believe these fixes | ||
28 | would be sufficient to reliably guarantee it in the future. | ||
29 | |||
30 | Fixes CVE-2020-29510 | ||
31 | Updates #43168 | ||
32 | |||
33 | Change-Id: Icd86c75beff3e1e0689543efebdad10ed5178ce3 | ||
34 | Reviewed-on: https://go-review.googlesource.com/c/go/+/277893 | ||
35 | Run-TryBot: Filippo Valsorda <filippo@golang.org> | ||
36 | TryBot-Result: Go Bot <gobot@golang.org> | ||
37 | Trust: Filippo Valsorda <filippo@golang.org> | ||
38 | Reviewed-by: Katie Hockman <katie@golang.org> | ||
39 | |||
40 | Upstream-Status: Backport from https://github.com/golang/go/commit/a9cfd55e2b09735a25976d1b008a0a3c767494f8 | ||
41 | CVE: CVE-2020-29510 | ||
42 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
43 | --- | ||
44 | src/encoding/xml/xml.go | 6 ++++++ | ||
45 | 1 file changed, 6 insertions(+) | ||
46 | |||
47 | diff --git a/src/encoding/xml/xml.go b/src/encoding/xml/xml.go | ||
48 | index 01a1460..98647b2 100644 | ||
49 | --- a/src/encoding/xml/xml.go | ||
50 | +++ b/src/encoding/xml/xml.go | ||
51 | @@ -768,6 +768,12 @@ func (d *Decoder) rawToken() (Token, error) { | ||
52 | } | ||
53 | b0, b1 = b1, b | ||
54 | } | ||
55 | + | ||
56 | + // Replace the comment with a space in the returned Directive | ||
57 | + // body, so that markup parts that were separated by the comment | ||
58 | + // (like a "<" and a "!") don't get joined when re-encoding the | ||
59 | + // Directive, taking new semantic meaning. | ||
60 | + d.buf.WriteByte(' ') | ||
61 | } | ||
62 | } | ||
63 | return Directive(d.buf.Bytes()), nil | ||
64 | -- | ||
65 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-27918.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-27918.patch new file mode 100644 index 0000000000..faa3f7f641 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-27918.patch | |||
@@ -0,0 +1,191 @@ | |||
1 | From d0b79e3513a29628f3599dc8860666b6eed75372 Mon Sep 17 00:00:00 2001 | ||
2 | From: Katie Hockman <katie@golang.org> | ||
3 | Date: Mon, 1 Mar 2021 09:54:00 -0500 | ||
4 | Subject: [PATCH] encoding/xml: prevent infinite loop while decoding | ||
5 | |||
6 | This change properly handles a TokenReader which | ||
7 | returns an EOF in the middle of an open XML | ||
8 | element. | ||
9 | |||
10 | Thanks to Sam Whited for reporting this. | ||
11 | |||
12 | Fixes CVE-2021-27918 | ||
13 | Fixes #44913 | ||
14 | |||
15 | Change-Id: Id02a3f3def4a1b415fa2d9a8e3b373eb6cb0f433 | ||
16 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1004594 | ||
17 | Reviewed-by: Russ Cox <rsc@google.com> | ||
18 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
19 | Reviewed-by: Filippo Valsorda <valsorda@google.com> | ||
20 | Reviewed-on: https://go-review.googlesource.com/c/go/+/300391 | ||
21 | Trust: Katie Hockman <katie@golang.org> | ||
22 | Run-TryBot: Katie Hockman <katie@golang.org> | ||
23 | TryBot-Result: Go Bot <gobot@golang.org> | ||
24 | Reviewed-by: Alexander Rakoczy <alex@golang.org> | ||
25 | Reviewed-by: Filippo Valsorda <filippo@golang.org> | ||
26 | |||
27 | https://github.com/golang/go/commit/d0b79e3513a29628f3599dc8860666b6eed75372 | ||
28 | CVE: CVE-2021-27918 | ||
29 | Upstream-Status: Backport | ||
30 | Signed-off-by: Chee Yang Lee <chee.yang.lee@intel.com> | ||
31 | --- | ||
32 | src/encoding/xml/xml.go | 19 ++++--- | ||
33 | src/encoding/xml/xml_test.go | 104 +++++++++++++++++++++++++++-------- | ||
34 | 2 files changed, 92 insertions(+), 31 deletions(-) | ||
35 | |||
36 | diff --git a/src/encoding/xml/xml.go b/src/encoding/xml/xml.go | ||
37 | index adaf4daf198b9..6f9594d7ba7a3 100644 | ||
38 | --- a/src/encoding/xml/xml.go | ||
39 | +++ b/src/encoding/xml/xml.go | ||
40 | @@ -271,7 +271,7 @@ func NewTokenDecoder(t TokenReader) *Decoder { | ||
41 | // it will return an error. | ||
42 | // | ||
43 | // Token implements XML name spaces as described by | ||
44 | -// https://www.w3.org/TR/REC-xml-names/. Each of the | ||
45 | +// https://www.w3.org/TR/REC-xml-names/. Each of the | ||
46 | // Name structures contained in the Token has the Space | ||
47 | // set to the URL identifying its name space when known. | ||
48 | // If Token encounters an unrecognized name space prefix, | ||
49 | @@ -285,16 +285,17 @@ func (d *Decoder) Token() (Token, error) { | ||
50 | if d.nextToken != nil { | ||
51 | t = d.nextToken | ||
52 | d.nextToken = nil | ||
53 | - } else if t, err = d.rawToken(); err != nil { | ||
54 | - switch { | ||
55 | - case err == io.EOF && d.t != nil: | ||
56 | - err = nil | ||
57 | - case err == io.EOF && d.stk != nil && d.stk.kind != stkEOF: | ||
58 | - err = d.syntaxError("unexpected EOF") | ||
59 | + } else { | ||
60 | + if t, err = d.rawToken(); t == nil && err != nil { | ||
61 | + if err == io.EOF && d.stk != nil && d.stk.kind != stkEOF { | ||
62 | + err = d.syntaxError("unexpected EOF") | ||
63 | + } | ||
64 | + return nil, err | ||
65 | } | ||
66 | - return t, err | ||
67 | + // We still have a token to process, so clear any | ||
68 | + // errors (e.g. EOF) and proceed. | ||
69 | + err = nil | ||
70 | } | ||
71 | - | ||
72 | if !d.Strict { | ||
73 | if t1, ok := d.autoClose(t); ok { | ||
74 | d.nextToken = t | ||
75 | diff --git a/src/encoding/xml/xml_test.go b/src/encoding/xml/xml_test.go | ||
76 | index efddca43e9102..5672ebb375f0d 100644 | ||
77 | --- a/src/encoding/xml/xml_test.go | ||
78 | +++ b/src/encoding/xml/xml_test.go | ||
79 | @@ -33,30 +33,90 @@ func (t *toks) Token() (Token, error) { | ||
80 | |||
81 | func TestDecodeEOF(t *testing.T) { | ||
82 | start := StartElement{Name: Name{Local: "test"}} | ||
83 | - t.Run("EarlyEOF", func(t *testing.T) { | ||
84 | - d := NewTokenDecoder(&toks{earlyEOF: true, t: []Token{ | ||
85 | - start, | ||
86 | - start.End(), | ||
87 | - }}) | ||
88 | - err := d.Decode(&struct { | ||
89 | - XMLName Name `xml:"test"` | ||
90 | - }{}) | ||
91 | - if err != nil { | ||
92 | - t.Error(err) | ||
93 | + tests := []struct { | ||
94 | + name string | ||
95 | + tokens []Token | ||
96 | + ok bool | ||
97 | + }{ | ||
98 | + { | ||
99 | + name: "OK", | ||
100 | + tokens: []Token{ | ||
101 | + start, | ||
102 | + start.End(), | ||
103 | + }, | ||
104 | + ok: true, | ||
105 | + }, | ||
106 | + { | ||
107 | + name: "Malformed", | ||
108 | + tokens: []Token{ | ||
109 | + start, | ||
110 | + StartElement{Name: Name{Local: "bad"}}, | ||
111 | + start.End(), | ||
112 | + }, | ||
113 | + ok: false, | ||
114 | + }, | ||
115 | + } | ||
116 | + for _, tc := range tests { | ||
117 | + for _, eof := range []bool{true, false} { | ||
118 | + name := fmt.Sprintf("%s/earlyEOF=%v", tc.name, eof) | ||
119 | + t.Run(name, func(t *testing.T) { | ||
120 | + d := NewTokenDecoder(&toks{ | ||
121 | + earlyEOF: eof, | ||
122 | + t: tc.tokens, | ||
123 | + }) | ||
124 | + err := d.Decode(&struct { | ||
125 | + XMLName Name `xml:"test"` | ||
126 | + }{}) | ||
127 | + if tc.ok && err != nil { | ||
128 | + t.Fatalf("d.Decode: expected nil error, got %v", err) | ||
129 | + } | ||
130 | + if _, ok := err.(*SyntaxError); !tc.ok && !ok { | ||
131 | + t.Errorf("d.Decode: expected syntax error, got %v", err) | ||
132 | + } | ||
133 | + }) | ||
134 | } | ||
135 | - }) | ||
136 | - t.Run("LateEOF", func(t *testing.T) { | ||
137 | - d := NewTokenDecoder(&toks{t: []Token{ | ||
138 | - start, | ||
139 | - start.End(), | ||
140 | - }}) | ||
141 | - err := d.Decode(&struct { | ||
142 | - XMLName Name `xml:"test"` | ||
143 | - }{}) | ||
144 | - if err != nil { | ||
145 | - t.Error(err) | ||
146 | + } | ||
147 | +} | ||
148 | + | ||
149 | +type toksNil struct { | ||
150 | + returnEOF bool | ||
151 | + t []Token | ||
152 | +} | ||
153 | + | ||
154 | +func (t *toksNil) Token() (Token, error) { | ||
155 | + if len(t.t) == 0 { | ||
156 | + if !t.returnEOF { | ||
157 | + // Return nil, nil before returning an EOF. It's legal, but | ||
158 | + // discouraged. | ||
159 | + t.returnEOF = true | ||
160 | + return nil, nil | ||
161 | } | ||
162 | - }) | ||
163 | + return nil, io.EOF | ||
164 | + } | ||
165 | + var tok Token | ||
166 | + tok, t.t = t.t[0], t.t[1:] | ||
167 | + return tok, nil | ||
168 | +} | ||
169 | + | ||
170 | +func TestDecodeNilToken(t *testing.T) { | ||
171 | + for _, strict := range []bool{true, false} { | ||
172 | + name := fmt.Sprintf("Strict=%v", strict) | ||
173 | + t.Run(name, func(t *testing.T) { | ||
174 | + start := StartElement{Name: Name{Local: "test"}} | ||
175 | + bad := StartElement{Name: Name{Local: "bad"}} | ||
176 | + d := NewTokenDecoder(&toksNil{ | ||
177 | + // Malformed | ||
178 | + t: []Token{start, bad, start.End()}, | ||
179 | + }) | ||
180 | + d.Strict = strict | ||
181 | + err := d.Decode(&struct { | ||
182 | + XMLName Name `xml:"test"` | ||
183 | + }{}) | ||
184 | + if _, ok := err.(*SyntaxError); !ok { | ||
185 | + t.Errorf("d.Decode: expected syntax error, got %v", err) | ||
186 | + } | ||
187 | + }) | ||
188 | + } | ||
189 | } | ||
190 | |||
191 | const testInput = ` | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-31525.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-31525.patch new file mode 100644 index 0000000000..afe4b0d2b8 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-31525.patch | |||
@@ -0,0 +1,38 @@ | |||
1 | From efb465ada003d23353a91ef930be408eb575dba6 Mon Sep 17 00:00:00 2001 | ||
2 | From: Hitendra Prajapati <hprajapati@mvista.com> | ||
3 | Date: Thu, 16 Jun 2022 17:40:12 +0530 | ||
4 | Subject: [PATCH] CVE-2021-31525 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/argoheyard/lang-net/commit/701957006ef151feb43f86aa99c8a1f474f69282] | ||
7 | CVE: CVE-2021-31525 | ||
8 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
9 | |||
10 | --- | ||
11 | src/vendor/golang.org/x/net/http/httpguts/httplex.go | 10 ++++++---- | ||
12 | 1 file changed, 6 insertions(+), 4 deletions(-) | ||
13 | |||
14 | diff --git a/src/vendor/golang.org/x/net/http/httpguts/httplex.go b/src/vendor/golang.org/x/net/http/httpguts/httplex.go | ||
15 | index e7de24e..c79aa73 100644 | ||
16 | --- a/src/vendor/golang.org/x/net/http/httpguts/httplex.go | ||
17 | +++ b/src/vendor/golang.org/x/net/http/httpguts/httplex.go | ||
18 | @@ -137,11 +137,13 @@ func trimOWS(x string) string { | ||
19 | // contains token amongst its comma-separated tokens, ASCII | ||
20 | // case-insensitively. | ||
21 | func headerValueContainsToken(v string, token string) bool { | ||
22 | - v = trimOWS(v) | ||
23 | - if comma := strings.IndexByte(v, ','); comma != -1 { | ||
24 | - return tokenEqual(trimOWS(v[:comma]), token) || headerValueContainsToken(v[comma+1:], token) | ||
25 | + for comma := strings.IndexByte(v, ','); comma != -1; comma = strings.IndexByte(v, ',') { | ||
26 | + if tokenEqual(trimOWS(v[:comma]), token) { | ||
27 | + return true | ||
28 | + } | ||
29 | + v = v[comma+1:] | ||
30 | } | ||
31 | - return tokenEqual(v, token) | ||
32 | + return tokenEqual(trimOWS(v), token) | ||
33 | } | ||
34 | |||
35 | // lowerASCII returns the ASCII lowercase version of b. | ||
36 | -- | ||
37 | 2.25.1 | ||
38 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-33195.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-33195.patch new file mode 100644 index 0000000000..3d9de888ff --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-33195.patch | |||
@@ -0,0 +1,373 @@ | |||
1 | From 9324d7e53151e9dfa4b25af994a28c2e0b11f729 Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <roland@golang.org> | ||
3 | Date: Thu, 27 May 2021 10:40:06 -0700 | ||
4 | Subject: [PATCH] net: verify results from Lookup* are valid domain names | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/31d60cda1f58b7558fc5725d2b9e4531655d980e] | ||
7 | CVE: CVE-2021-33195 | ||
8 | Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org> | ||
9 | |||
10 | |||
11 | For the methods LookupCNAME, LookupSRV, LookupMX, LookupNS, and | ||
12 | LookupAddr check that the returned domain names are in fact valid DNS | ||
13 | names using the existing isDomainName function. | ||
14 | |||
15 | Thanks to Philipp Jeitner and Haya Shulman from Fraunhofer SIT for | ||
16 | reporting this issue. | ||
17 | |||
18 | Updates #46241 | ||
19 | Fixes #46356 | ||
20 | Fixes CVE-2021-33195 | ||
21 | |||
22 | Change-Id: I47a4f58c031cb752f732e88bbdae7f819f0af4f3 | ||
23 | Reviewed-on: https://go-review.googlesource.com/c/go/+/323131 | ||
24 | Trust: Roland Shoemaker <roland@golang.org> | ||
25 | Run-TryBot: Roland Shoemaker <roland@golang.org> | ||
26 | TryBot-Result: Go Bot <gobot@golang.org> | ||
27 | Reviewed-by: Filippo Valsorda <filippo@golang.org> | ||
28 | Reviewed-by: Katie Hockman <katie@golang.org> | ||
29 | (cherry picked from commit cdcd02842da7c004efd023881e3719105209c908) | ||
30 | Reviewed-on: https://go-review.googlesource.com/c/go/+/323269 | ||
31 | --- | ||
32 | src/net/dnsclient_unix_test.go | 157 +++++++++++++++++++++++++++++++++ | ||
33 | src/net/lookup.go | 111 ++++++++++++++++++++--- | ||
34 | 2 files changed, 255 insertions(+), 13 deletions(-) | ||
35 | |||
36 | diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go | ||
37 | index 2ad40df..b8617d9 100644 | ||
38 | --- a/src/net/dnsclient_unix_test.go | ||
39 | +++ b/src/net/dnsclient_unix_test.go | ||
40 | @@ -1800,3 +1800,160 @@ func TestPTRandNonPTR(t *testing.T) { | ||
41 | t.Errorf("names = %q; want %q", names, want) | ||
42 | } | ||
43 | } | ||
44 | + | ||
45 | +func TestCVE202133195(t *testing.T) { | ||
46 | + fake := fakeDNSServer{ | ||
47 | + rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { | ||
48 | + r := dnsmessage.Message{ | ||
49 | + Header: dnsmessage.Header{ | ||
50 | + ID: q.Header.ID, | ||
51 | + Response: true, | ||
52 | + RCode: dnsmessage.RCodeSuccess, | ||
53 | + RecursionAvailable: true, | ||
54 | + }, | ||
55 | + Questions: q.Questions, | ||
56 | + } | ||
57 | + switch q.Questions[0].Type { | ||
58 | + case dnsmessage.TypeCNAME: | ||
59 | + r.Answers = []dnsmessage.Resource{} | ||
60 | + case dnsmessage.TypeA: // CNAME lookup uses a A/AAAA as a proxy | ||
61 | + r.Answers = append(r.Answers, | ||
62 | + dnsmessage.Resource{ | ||
63 | + Header: dnsmessage.ResourceHeader{ | ||
64 | + Name: dnsmessage.MustNewName("<html>.golang.org."), | ||
65 | + Type: dnsmessage.TypeA, | ||
66 | + Class: dnsmessage.ClassINET, | ||
67 | + Length: 4, | ||
68 | + }, | ||
69 | + Body: &dnsmessage.AResource{ | ||
70 | + A: TestAddr, | ||
71 | + }, | ||
72 | + }, | ||
73 | + ) | ||
74 | + case dnsmessage.TypeSRV: | ||
75 | + n := q.Questions[0].Name | ||
76 | + if n.String() == "_hdr._tcp.golang.org." { | ||
77 | + n = dnsmessage.MustNewName("<html>.golang.org.") | ||
78 | + } | ||
79 | + r.Answers = append(r.Answers, | ||
80 | + dnsmessage.Resource{ | ||
81 | + Header: dnsmessage.ResourceHeader{ | ||
82 | + Name: n, | ||
83 | + Type: dnsmessage.TypeSRV, | ||
84 | + Class: dnsmessage.ClassINET, | ||
85 | + Length: 4, | ||
86 | + }, | ||
87 | + Body: &dnsmessage.SRVResource{ | ||
88 | + Target: dnsmessage.MustNewName("<html>.golang.org."), | ||
89 | + }, | ||
90 | + }, | ||
91 | + ) | ||
92 | + case dnsmessage.TypeMX: | ||
93 | + r.Answers = append(r.Answers, | ||
94 | + dnsmessage.Resource{ | ||
95 | + Header: dnsmessage.ResourceHeader{ | ||
96 | + Name: dnsmessage.MustNewName("<html>.golang.org."), | ||
97 | + Type: dnsmessage.TypeMX, | ||
98 | + Class: dnsmessage.ClassINET, | ||
99 | + Length: 4, | ||
100 | + }, | ||
101 | + Body: &dnsmessage.MXResource{ | ||
102 | + MX: dnsmessage.MustNewName("<html>.golang.org."), | ||
103 | + }, | ||
104 | + }, | ||
105 | + ) | ||
106 | + case dnsmessage.TypeNS: | ||
107 | + r.Answers = append(r.Answers, | ||
108 | + dnsmessage.Resource{ | ||
109 | + Header: dnsmessage.ResourceHeader{ | ||
110 | + Name: dnsmessage.MustNewName("<html>.golang.org."), | ||
111 | + Type: dnsmessage.TypeNS, | ||
112 | + Class: dnsmessage.ClassINET, | ||
113 | + Length: 4, | ||
114 | + }, | ||
115 | + Body: &dnsmessage.NSResource{ | ||
116 | + NS: dnsmessage.MustNewName("<html>.golang.org."), | ||
117 | + }, | ||
118 | + }, | ||
119 | + ) | ||
120 | + case dnsmessage.TypePTR: | ||
121 | + r.Answers = append(r.Answers, | ||
122 | + dnsmessage.Resource{ | ||
123 | + Header: dnsmessage.ResourceHeader{ | ||
124 | + Name: dnsmessage.MustNewName("<html>.golang.org."), | ||
125 | + Type: dnsmessage.TypePTR, | ||
126 | + Class: dnsmessage.ClassINET, | ||
127 | + Length: 4, | ||
128 | + }, | ||
129 | + Body: &dnsmessage.PTRResource{ | ||
130 | + PTR: dnsmessage.MustNewName("<html>.golang.org."), | ||
131 | + }, | ||
132 | + }, | ||
133 | + ) | ||
134 | + } | ||
135 | + return r, nil | ||
136 | + }, | ||
137 | + } | ||
138 | + | ||
139 | + r := Resolver{PreferGo: true, Dial: fake.DialContext} | ||
140 | + // Change the default resolver to match our manipulated resolver | ||
141 | + originalDefault := DefaultResolver | ||
142 | + DefaultResolver = &r | ||
143 | + defer func() { | ||
144 | + DefaultResolver = originalDefault | ||
145 | + }() | ||
146 | + | ||
147 | + _, err := r.LookupCNAME(context.Background(), "golang.org") | ||
148 | + if expected := "lookup golang.org: CNAME target is invalid"; err == nil || err.Error() != expected { | ||
149 | + t.Errorf("Resolver.LookupCNAME returned unexpected error, got %q, want %q", err.Error(), expected) | ||
150 | + } | ||
151 | + _, err = LookupCNAME("golang.org") | ||
152 | + if expected := "lookup golang.org: CNAME target is invalid"; err == nil || err.Error() != expected { | ||
153 | + t.Errorf("LookupCNAME returned unexpected error, got %q, want %q", err.Error(), expected) | ||
154 | + } | ||
155 | + | ||
156 | + _, _, err = r.LookupSRV(context.Background(), "target", "tcp", "golang.org") | ||
157 | + if expected := "lookup golang.org: SRV target is invalid"; err == nil || err.Error() != expected { | ||
158 | + t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", err.Error(), expected) | ||
159 | + } | ||
160 | + _, _, err = LookupSRV("target", "tcp", "golang.org") | ||
161 | + if expected := "lookup golang.org: SRV target is invalid"; err == nil || err.Error() != expected { | ||
162 | + t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err.Error(), expected) | ||
163 | + } | ||
164 | + | ||
165 | + _, _, err = r.LookupSRV(context.Background(), "hdr", "tcp", "golang.org") | ||
166 | + if expected := "lookup golang.org: SRV header name is invalid"; err == nil || err.Error() != expected { | ||
167 | + t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", err.Error(), expected) | ||
168 | + } | ||
169 | + _, _, err = LookupSRV("hdr", "tcp", "golang.org") | ||
170 | + if expected := "lookup golang.org: SRV header name is invalid"; err == nil || err.Error() != expected { | ||
171 | + t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err.Error(), expected) | ||
172 | + } | ||
173 | + | ||
174 | + _, err = r.LookupMX(context.Background(), "golang.org") | ||
175 | + if expected := "lookup golang.org: MX target is invalid"; err == nil || err.Error() != expected { | ||
176 | + t.Errorf("Resolver.LookupMX returned unexpected error, got %q, want %q", err.Error(), expected) | ||
177 | + } | ||
178 | + _, err = LookupMX("golang.org") | ||
179 | + if expected := "lookup golang.org: MX target is invalid"; err == nil || err.Error() != expected { | ||
180 | + t.Errorf("LookupMX returned unexpected error, got %q, want %q", err.Error(), expected) | ||
181 | + } | ||
182 | + | ||
183 | + _, err = r.LookupNS(context.Background(), "golang.org") | ||
184 | + if expected := "lookup golang.org: NS target is invalid"; err == nil || err.Error() != expected { | ||
185 | + t.Errorf("Resolver.LookupNS returned unexpected error, got %q, want %q", err.Error(), expected) | ||
186 | + } | ||
187 | + _, err = LookupNS("golang.org") | ||
188 | + if expected := "lookup golang.org: NS target is invalid"; err == nil || err.Error() != expected { | ||
189 | + t.Errorf("LookupNS returned unexpected error, got %q, want %q", err.Error(), expected) | ||
190 | + } | ||
191 | + | ||
192 | + _, err = r.LookupAddr(context.Background(), "1.2.3.4") | ||
193 | + if expected := "lookup 1.2.3.4: PTR target is invalid"; err == nil || err.Error() != expected { | ||
194 | + t.Errorf("Resolver.LookupAddr returned unexpected error, got %q, want %q", err.Error(), expected) | ||
195 | + } | ||
196 | + _, err = LookupAddr("1.2.3.4") | ||
197 | + if expected := "lookup 1.2.3.4: PTR target is invalid"; err == nil || err.Error() != expected { | ||
198 | + t.Errorf("LookupAddr returned unexpected error, got %q, want %q", err.Error(), expected) | ||
199 | + } | ||
200 | +} | ||
201 | diff --git a/src/net/lookup.go b/src/net/lookup.go | ||
202 | index 9cebd10..05e88e4 100644 | ||
203 | --- a/src/net/lookup.go | ||
204 | +++ b/src/net/lookup.go | ||
205 | @@ -364,8 +364,11 @@ func (r *Resolver) LookupPort(ctx context.Context, network, service string) (por | ||
206 | // LookupCNAME does not return an error if host does not | ||
207 | // contain DNS "CNAME" records, as long as host resolves to | ||
208 | // address records. | ||
209 | +// | ||
210 | +// The returned canonical name is validated to be a properly | ||
211 | +// formatted presentation-format domain name. | ||
212 | func LookupCNAME(host string) (cname string, err error) { | ||
213 | - return DefaultResolver.lookupCNAME(context.Background(), host) | ||
214 | + return DefaultResolver.LookupCNAME(context.Background(), host) | ||
215 | } | ||
216 | |||
217 | // LookupCNAME returns the canonical name for the given host. | ||
218 | @@ -378,8 +381,18 @@ func LookupCNAME(host string) (cname string, err error) { | ||
219 | // LookupCNAME does not return an error if host does not | ||
220 | // contain DNS "CNAME" records, as long as host resolves to | ||
221 | // address records. | ||
222 | -func (r *Resolver) LookupCNAME(ctx context.Context, host string) (cname string, err error) { | ||
223 | - return r.lookupCNAME(ctx, host) | ||
224 | +// | ||
225 | +// The returned canonical name is validated to be a properly | ||
226 | +// formatted presentation-format domain name. | ||
227 | +func (r *Resolver) LookupCNAME(ctx context.Context, host string) (string, error) { | ||
228 | + cname, err := r.lookupCNAME(ctx, host) | ||
229 | + if err != nil { | ||
230 | + return "", err | ||
231 | + } | ||
232 | + if !isDomainName(cname) { | ||
233 | + return "", &DNSError{Err: "CNAME target is invalid", Name: host} | ||
234 | + } | ||
235 | + return cname, nil | ||
236 | } | ||
237 | |||
238 | // LookupSRV tries to resolve an SRV query of the given service, | ||
239 | @@ -391,8 +404,11 @@ func (r *Resolver) LookupCNAME(ctx context.Context, host string) (cname string, | ||
240 | // That is, it looks up _service._proto.name. To accommodate services | ||
241 | // publishing SRV records under non-standard names, if both service | ||
242 | // and proto are empty strings, LookupSRV looks up name directly. | ||
243 | +// | ||
244 | +// The returned service names are validated to be properly | ||
245 | +// formatted presentation-format domain names. | ||
246 | func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { | ||
247 | - return DefaultResolver.lookupSRV(context.Background(), service, proto, name) | ||
248 | + return DefaultResolver.LookupSRV(context.Background(), service, proto, name) | ||
249 | } | ||
250 | |||
251 | // LookupSRV tries to resolve an SRV query of the given service, | ||
252 | @@ -404,28 +420,82 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err | ||
253 | // That is, it looks up _service._proto.name. To accommodate services | ||
254 | // publishing SRV records under non-standard names, if both service | ||
255 | // and proto are empty strings, LookupSRV looks up name directly. | ||
256 | -func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) { | ||
257 | - return r.lookupSRV(ctx, service, proto, name) | ||
258 | +// | ||
259 | +// The returned service names are validated to be properly | ||
260 | +// formatted presentation-format domain names. | ||
261 | +func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) { | ||
262 | + cname, addrs, err := r.lookupSRV(ctx, service, proto, name) | ||
263 | + if err != nil { | ||
264 | + return "", nil, err | ||
265 | + } | ||
266 | + if cname != "" && !isDomainName(cname) { | ||
267 | + return "", nil, &DNSError{Err: "SRV header name is invalid", Name: name} | ||
268 | + } | ||
269 | + for _, addr := range addrs { | ||
270 | + if addr == nil { | ||
271 | + continue | ||
272 | + } | ||
273 | + if !isDomainName(addr.Target) { | ||
274 | + return "", nil, &DNSError{Err: "SRV target is invalid", Name: name} | ||
275 | + } | ||
276 | + } | ||
277 | + return cname, addrs, nil | ||
278 | } | ||
279 | |||
280 | // LookupMX returns the DNS MX records for the given domain name sorted by preference. | ||
281 | +// | ||
282 | +// The returned mail server names are validated to be properly | ||
283 | +// formatted presentation-format domain names. | ||
284 | func LookupMX(name string) ([]*MX, error) { | ||
285 | - return DefaultResolver.lookupMX(context.Background(), name) | ||
286 | + return DefaultResolver.LookupMX(context.Background(), name) | ||
287 | } | ||
288 | |||
289 | // LookupMX returns the DNS MX records for the given domain name sorted by preference. | ||
290 | +// | ||
291 | +// The returned mail server names are validated to be properly | ||
292 | +// formatted presentation-format domain names. | ||
293 | func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) { | ||
294 | - return r.lookupMX(ctx, name) | ||
295 | + records, err := r.lookupMX(ctx, name) | ||
296 | + if err != nil { | ||
297 | + return nil, err | ||
298 | + } | ||
299 | + for _, mx := range records { | ||
300 | + if mx == nil { | ||
301 | + continue | ||
302 | + } | ||
303 | + if !isDomainName(mx.Host) { | ||
304 | + return nil, &DNSError{Err: "MX target is invalid", Name: name} | ||
305 | + } | ||
306 | + } | ||
307 | + return records, nil | ||
308 | } | ||
309 | |||
310 | // LookupNS returns the DNS NS records for the given domain name. | ||
311 | +// | ||
312 | +// The returned name server names are validated to be properly | ||
313 | +// formatted presentation-format domain names. | ||
314 | func LookupNS(name string) ([]*NS, error) { | ||
315 | - return DefaultResolver.lookupNS(context.Background(), name) | ||
316 | + return DefaultResolver.LookupNS(context.Background(), name) | ||
317 | } | ||
318 | |||
319 | // LookupNS returns the DNS NS records for the given domain name. | ||
320 | +// | ||
321 | +// The returned name server names are validated to be properly | ||
322 | +// formatted presentation-format domain names. | ||
323 | func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) { | ||
324 | - return r.lookupNS(ctx, name) | ||
325 | + records, err := r.lookupNS(ctx, name) | ||
326 | + if err != nil { | ||
327 | + return nil, err | ||
328 | + } | ||
329 | + for _, ns := range records { | ||
330 | + if ns == nil { | ||
331 | + continue | ||
332 | + } | ||
333 | + if !isDomainName(ns.Host) { | ||
334 | + return nil, &DNSError{Err: "NS target is invalid", Name: name} | ||
335 | + } | ||
336 | + } | ||
337 | + return records, nil | ||
338 | } | ||
339 | |||
340 | // LookupTXT returns the DNS TXT records for the given domain name. | ||
341 | @@ -441,14 +511,29 @@ func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) | ||
342 | // LookupAddr performs a reverse lookup for the given address, returning a list | ||
343 | // of names mapping to that address. | ||
344 | // | ||
345 | +// The returned names are validated to be properly formatted presentation-format | ||
346 | +// domain names. | ||
347 | +// | ||
348 | // When using the host C library resolver, at most one result will be | ||
349 | // returned. To bypass the host resolver, use a custom Resolver. | ||
350 | func LookupAddr(addr string) (names []string, err error) { | ||
351 | - return DefaultResolver.lookupAddr(context.Background(), addr) | ||
352 | + return DefaultResolver.LookupAddr(context.Background(), addr) | ||
353 | } | ||
354 | |||
355 | // LookupAddr performs a reverse lookup for the given address, returning a list | ||
356 | // of names mapping to that address. | ||
357 | -func (r *Resolver) LookupAddr(ctx context.Context, addr string) (names []string, err error) { | ||
358 | - return r.lookupAddr(ctx, addr) | ||
359 | +// | ||
360 | +// The returned names are validated to be properly formatted presentation-format | ||
361 | +// domain names. | ||
362 | +func (r *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error) { | ||
363 | + names, err := r.lookupAddr(ctx, addr) | ||
364 | + if err != nil { | ||
365 | + return nil, err | ||
366 | + } | ||
367 | + for _, name := range names { | ||
368 | + if !isDomainName(name) { | ||
369 | + return nil, &DNSError{Err: "PTR target is invalid", Name: addr} | ||
370 | + } | ||
371 | + } | ||
372 | + return names, nil | ||
373 | } | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-33196.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-33196.patch new file mode 100644 index 0000000000..2e2dc62c49 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-33196.patch | |||
@@ -0,0 +1,124 @@ | |||
1 | From 74242baa4136c7a9132a8ccd9881354442788c8c Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <roland@golang.org> | ||
3 | Date: Tue, 11 May 2021 11:31:31 -0700 | ||
4 | Subject: [PATCH] archive/zip: only preallocate File slice if reasonably sized | ||
5 | |||
6 | Since the number of files in the EOCD record isn't validated, it isn't | ||
7 | safe to preallocate Reader.Files using that field. A malformed archive | ||
8 | can indicate it contains up to 1 << 128 - 1 files. We can still safely | ||
9 | preallocate the slice by checking if the specified number of files in | ||
10 | the archive is reasonable, given the size of the archive. | ||
11 | |||
12 | Thanks to the OSS-Fuzz project for discovering this issue and to | ||
13 | Emmanuel Odeke for reporting it. | ||
14 | |||
15 | Fixes #46242 | ||
16 | Fixes CVE-2021-33196 | ||
17 | |||
18 | Change-Id: I3c76d8eec178468b380d87fdb4a3f2cb06f0ee76 | ||
19 | Reviewed-on: https://go-review.googlesource.com/c/go/+/318909 | ||
20 | Trust: Roland Shoemaker <roland@golang.org> | ||
21 | Trust: Katie Hockman <katie@golang.org> | ||
22 | Trust: Joe Tsai <thebrokentoaster@gmail.com> | ||
23 | Run-TryBot: Roland Shoemaker <roland@golang.org> | ||
24 | TryBot-Result: Go Bot <gobot@golang.org> | ||
25 | Reviewed-by: Katie Hockman <katie@golang.org> | ||
26 | Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com> | ||
27 | |||
28 | Upstream-Status: Backport | ||
29 | CVE: CVE-2021-33196 | ||
30 | Signed-off-by: Armin Kuster <akuster@mvista.com> | ||
31 | |||
32 | --- | ||
33 | src/archive/zip/reader.go | 10 +++++- | ||
34 | src/archive/zip/reader_test.go | 59 ++++++++++++++++++++++++++++++++++ | ||
35 | 2 files changed, 68 insertions(+), 1 deletion(-) | ||
36 | |||
37 | Index: go/src/archive/zip/reader.go | ||
38 | =================================================================== | ||
39 | --- go.orig/src/archive/zip/reader.go | ||
40 | +++ go/src/archive/zip/reader.go | ||
41 | @@ -84,7 +84,15 @@ func (z *Reader) init(r io.ReaderAt, siz | ||
42 | return err | ||
43 | } | ||
44 | z.r = r | ||
45 | - z.File = make([]*File, 0, end.directoryRecords) | ||
46 | + // Since the number of directory records is not validated, it is not | ||
47 | + // safe to preallocate z.File without first checking that the specified | ||
48 | + // number of files is reasonable, since a malformed archive may | ||
49 | + // indicate it contains up to 1 << 128 - 1 files. Since each file has a | ||
50 | + // header which will be _at least_ 30 bytes we can safely preallocate | ||
51 | + // if (data size / 30) >= end.directoryRecords. | ||
52 | + if (uint64(size)-end.directorySize)/30 >= end.directoryRecords { | ||
53 | + z.File = make([]*File, 0, end.directoryRecords) | ||
54 | + } | ||
55 | z.Comment = end.comment | ||
56 | rs := io.NewSectionReader(r, 0, size) | ||
57 | if _, err = rs.Seek(int64(end.directoryOffset), io.SeekStart); err != nil { | ||
58 | Index: go/src/archive/zip/reader_test.go | ||
59 | =================================================================== | ||
60 | --- go.orig/src/archive/zip/reader_test.go | ||
61 | +++ go/src/archive/zip/reader_test.go | ||
62 | @@ -1070,3 +1070,62 @@ func TestIssue12449(t *testing.T) { | ||
63 | t.Errorf("Error reading the archive: %v", err) | ||
64 | } | ||
65 | } | ||
66 | + | ||
67 | +func TestCVE202133196(t *testing.T) { | ||
68 | + // Archive that indicates it has 1 << 128 -1 files, | ||
69 | + // this would previously cause a panic due to attempting | ||
70 | + // to allocate a slice with 1 << 128 -1 elements. | ||
71 | + data := []byte{ | ||
72 | + 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x08, | ||
73 | + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
74 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
75 | + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, | ||
76 | + 0x03, 0x62, 0x61, 0x65, 0x03, 0x04, 0x00, 0x00, | ||
77 | + 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0xbe, 0x20, | ||
78 | + 0x5c, 0x6c, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, | ||
79 | + 0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, | ||
80 | + 0x14, 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, | ||
81 | + 0x00, 0x00, 0xbe, 0x20, 0x5c, 0x6c, 0x09, 0x00, | ||
82 | + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, | ||
83 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
84 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
85 | + 0x01, 0x02, 0x03, 0x50, 0x4b, 0x06, 0x06, 0x2c, | ||
86 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, | ||
87 | + 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
88 | + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, | ||
89 | + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
90 | + 0xff, 0xff, 0xff, 0x31, 0x00, 0x00, 0x00, 0x00, | ||
91 | + 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, | ||
92 | + 0x00, 0x00, 0x00, 0x50, 0x4b, 0x06, 0x07, 0x00, | ||
93 | + 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, | ||
94 | + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, | ||
95 | + 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0xff, | ||
96 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
97 | + 0xff, 0xff, 0xff, 0x00, 0x00, | ||
98 | + } | ||
99 | + _, err := NewReader(bytes.NewReader(data), int64(len(data))) | ||
100 | + if err != ErrFormat { | ||
101 | + t.Fatalf("unexpected error, got: %v, want: %v", err, ErrFormat) | ||
102 | + } | ||
103 | + | ||
104 | + // Also check that an archive containing a handful of empty | ||
105 | + // files doesn't cause an issue | ||
106 | + b := bytes.NewBuffer(nil) | ||
107 | + w := NewWriter(b) | ||
108 | + for i := 0; i < 5; i++ { | ||
109 | + _, err := w.Create("") | ||
110 | + if err != nil { | ||
111 | + t.Fatalf("Writer.Create failed: %s", err) | ||
112 | + } | ||
113 | + } | ||
114 | + if err := w.Close(); err != nil { | ||
115 | + t.Fatalf("Writer.Close failed: %s", err) | ||
116 | + } | ||
117 | + r, err := NewReader(bytes.NewReader(b.Bytes()), int64(b.Len())) | ||
118 | + if err != nil { | ||
119 | + t.Fatalf("NewReader failed: %s", err) | ||
120 | + } | ||
121 | + if len(r.File) != 5 { | ||
122 | + t.Errorf("Archive has unexpected number of files, got %d, want 5", len(r.File)) | ||
123 | + } | ||
124 | +} | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-33197.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-33197.patch new file mode 100644 index 0000000000..2052b1d3db --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-33197.patch | |||
@@ -0,0 +1,152 @@ | |||
1 | From cbd1ca84453fecf3825a6bb9f985823e8bc32b76 Mon Sep 17 00:00:00 2001 | ||
2 | From: Filippo Valsorda <filippo@golang.org> | ||
3 | Date: Fri, 21 May 2021 14:02:30 -0400 | ||
4 | Subject: [PATCH] [release-branch.go1.15] net/http/httputil: always remove | ||
5 | hop-by-hop headers | ||
6 | |||
7 | Previously, we'd fail to remove the Connection header from a request | ||
8 | like this: | ||
9 | |||
10 | Connection: | ||
11 | Connection: x-header | ||
12 | |||
13 | Updates #46313 | ||
14 | Fixes #46314 | ||
15 | Fixes CVE-2021-33197 | ||
16 | |||
17 | Change-Id: Ie3009e926ceecfa86dfa6bcc6fe14ff01086be7d | ||
18 | Reviewed-on: https://go-review.googlesource.com/c/go/+/321929 | ||
19 | Run-TryBot: Filippo Valsorda <filippo@golang.org> | ||
20 | Reviewed-by: Katie Hockman <katie@golang.org> | ||
21 | Trust: Katie Hockman <katie@golang.org> | ||
22 | Trust: Filippo Valsorda <filippo@golang.org> | ||
23 | TryBot-Result: Go Bot <gobot@golang.org> | ||
24 | Reviewed-on: https://go-review.googlesource.com/c/go/+/323091 | ||
25 | Run-TryBot: Katie Hockman <katie@golang.org> | ||
26 | |||
27 | Upstream-Status: Backport | ||
28 | CVE: CVE-2021-33197 | ||
29 | Signed-off-by: Armin Kuster <akuster@mvista.com> | ||
30 | |||
31 | --- | ||
32 | src/net/http/httputil/reverseproxy.go | 22 ++++---- | ||
33 | src/net/http/httputil/reverseproxy_test.go | 63 +++++++++++++++++++++- | ||
34 | 2 files changed, 70 insertions(+), 15 deletions(-) | ||
35 | |||
36 | Index: go/src/net/http/httputil/reverseproxy.go | ||
37 | =================================================================== | ||
38 | --- go.orig/src/net/http/httputil/reverseproxy.go | ||
39 | +++ go/src/net/http/httputil/reverseproxy.go | ||
40 | @@ -221,22 +221,18 @@ func (p *ReverseProxy) ServeHTTP(rw http | ||
41 | // important is "Connection" because we want a persistent | ||
42 | // connection, regardless of what the client sent to us. | ||
43 | for _, h := range hopHeaders { | ||
44 | - hv := outreq.Header.Get(h) | ||
45 | - if hv == "" { | ||
46 | - continue | ||
47 | - } | ||
48 | - if h == "Te" && hv == "trailers" { | ||
49 | - // Issue 21096: tell backend applications that | ||
50 | - // care about trailer support that we support | ||
51 | - // trailers. (We do, but we don't go out of | ||
52 | - // our way to advertise that unless the | ||
53 | - // incoming client request thought it was | ||
54 | - // worth mentioning) | ||
55 | - continue | ||
56 | - } | ||
57 | outreq.Header.Del(h) | ||
58 | } | ||
59 | |||
60 | + // Issue 21096: tell backend applications that care about trailer support | ||
61 | + // that we support trailers. (We do, but we don't go out of our way to | ||
62 | + // advertise that unless the incoming client request thought it was worth | ||
63 | + // mentioning.) Note that we look at req.Header, not outreq.Header, since | ||
64 | + // the latter has passed through removeConnectionHeaders. | ||
65 | + if httpguts.HeaderValuesContainsToken(req.Header["Te"], "trailers") { | ||
66 | + outreq.Header.Set("Te", "trailers") | ||
67 | + } | ||
68 | + | ||
69 | // After stripping all the hop-by-hop connection headers above, add back any | ||
70 | // necessary for protocol upgrades, such as for websockets. | ||
71 | if reqUpType != "" { | ||
72 | Index: go/src/net/http/httputil/reverseproxy_test.go | ||
73 | =================================================================== | ||
74 | --- go.orig/src/net/http/httputil/reverseproxy_test.go | ||
75 | +++ go/src/net/http/httputil/reverseproxy_test.go | ||
76 | @@ -91,8 +91,9 @@ func TestReverseProxy(t *testing.T) { | ||
77 | |||
78 | getReq, _ := http.NewRequest("GET", frontend.URL, nil) | ||
79 | getReq.Host = "some-name" | ||
80 | - getReq.Header.Set("Connection", "close") | ||
81 | - getReq.Header.Set("Te", "trailers") | ||
82 | + getReq.Header.Set("Connection", "close, TE") | ||
83 | + getReq.Header.Add("Te", "foo") | ||
84 | + getReq.Header.Add("Te", "bar, trailers") | ||
85 | getReq.Header.Set("Proxy-Connection", "should be deleted") | ||
86 | getReq.Header.Set("Upgrade", "foo") | ||
87 | getReq.Close = true | ||
88 | @@ -236,6 +237,64 @@ func TestReverseProxyStripHeadersPresent | ||
89 | } | ||
90 | } | ||
91 | |||
92 | +func TestReverseProxyStripEmptyConnection(t *testing.T) { | ||
93 | + // See Issue 46313. | ||
94 | + const backendResponse = "I am the backend" | ||
95 | + | ||
96 | + // someConnHeader is some arbitrary header to be declared as a hop-by-hop header | ||
97 | + // in the Request's Connection header. | ||
98 | + const someConnHeader = "X-Some-Conn-Header" | ||
99 | + | ||
100 | + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
101 | + if c := r.Header.Values("Connection"); len(c) != 0 { | ||
102 | + t.Errorf("handler got header %q = %v; want empty", "Connection", c) | ||
103 | + } | ||
104 | + if c := r.Header.Get(someConnHeader); c != "" { | ||
105 | + t.Errorf("handler got header %q = %q; want empty", someConnHeader, c) | ||
106 | + } | ||
107 | + w.Header().Add("Connection", "") | ||
108 | + w.Header().Add("Connection", someConnHeader) | ||
109 | + w.Header().Set(someConnHeader, "should be deleted") | ||
110 | + io.WriteString(w, backendResponse) | ||
111 | + })) | ||
112 | + defer backend.Close() | ||
113 | + backendURL, err := url.Parse(backend.URL) | ||
114 | + if err != nil { | ||
115 | + t.Fatal(err) | ||
116 | + } | ||
117 | + proxyHandler := NewSingleHostReverseProxy(backendURL) | ||
118 | + frontend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
119 | + proxyHandler.ServeHTTP(w, r) | ||
120 | + if c := r.Header.Get(someConnHeader); c != "should be deleted" { | ||
121 | + t.Errorf("handler modified header %q = %q; want %q", someConnHeader, c, "should be deleted") | ||
122 | + } | ||
123 | + })) | ||
124 | + defer frontend.Close() | ||
125 | + | ||
126 | + getReq, _ := http.NewRequest("GET", frontend.URL, nil) | ||
127 | + getReq.Header.Add("Connection", "") | ||
128 | + getReq.Header.Add("Connection", someConnHeader) | ||
129 | + getReq.Header.Set(someConnHeader, "should be deleted") | ||
130 | + res, err := frontend.Client().Do(getReq) | ||
131 | + if err != nil { | ||
132 | + t.Fatalf("Get: %v", err) | ||
133 | + } | ||
134 | + defer res.Body.Close() | ||
135 | + bodyBytes, err := ioutil.ReadAll(res.Body) | ||
136 | + if err != nil { | ||
137 | + t.Fatalf("reading body: %v", err) | ||
138 | + } | ||
139 | + if got, want := string(bodyBytes), backendResponse; got != want { | ||
140 | + t.Errorf("got body %q; want %q", got, want) | ||
141 | + } | ||
142 | + if c := res.Header.Get("Connection"); c != "" { | ||
143 | + t.Errorf("handler got header %q = %q; want empty", "Connection", c) | ||
144 | + } | ||
145 | + if c := res.Header.Get(someConnHeader); c != "" { | ||
146 | + t.Errorf("handler got header %q = %q; want empty", someConnHeader, c) | ||
147 | + } | ||
148 | +} | ||
149 | + | ||
150 | func TestXForwardedFor(t *testing.T) { | ||
151 | const prevForwardedFor = "client ip" | ||
152 | const backendResponse = "I am the backend" | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-33198.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-33198.patch new file mode 100644 index 0000000000..241c08dad7 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-33198.patch | |||
@@ -0,0 +1,113 @@ | |||
1 | From c8866491ac424cdf39aedb325e6dec9e54418cfb Mon Sep 17 00:00:00 2001 | ||
2 | From: Robert Griesemer <gri@golang.org> | ||
3 | Date: Sun, 2 May 2021 11:27:03 -0700 | ||
4 | Subject: [PATCH] math/big: check for excessive exponents in Rat.SetString | ||
5 | |||
6 | CVE-2021-33198 | ||
7 | |||
8 | Upstream-Status: Backport [https://github.com/golang/go/commit/df9ce19db6df32d94eae8760927bdfbc595433c3] | ||
9 | CVE: CVE-2021-33198 | ||
10 | Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org> | ||
11 | |||
12 | |||
13 | Found by OSS-Fuzz https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=33284 | ||
14 | |||
15 | Thanks to Emmanuel Odeke for reporting this issue. | ||
16 | |||
17 | Updates #45910 | ||
18 | Fixes #46305 | ||
19 | Fixes CVE-2021-33198 | ||
20 | |||
21 | Change-Id: I61e7b04dbd80343420b57eede439e361c0f7b79c | ||
22 | Reviewed-on: https://go-review.googlesource.com/c/go/+/316149 | ||
23 | Trust: Robert Griesemer <gri@golang.org> | ||
24 | Trust: Katie Hockman <katie@golang.org> | ||
25 | Run-TryBot: Robert Griesemer <gri@golang.org> | ||
26 | TryBot-Result: Go Bot <gobot@golang.org> | ||
27 | Reviewed-by: Katie Hockman <katie@golang.org> | ||
28 | Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com> | ||
29 | (cherry picked from commit 6c591f79b0b5327549bd4e94970f7a279efb4ab0) | ||
30 | Reviewed-on: https://go-review.googlesource.com/c/go/+/321831 | ||
31 | Run-TryBot: Katie Hockman <katie@golang.org> | ||
32 | Reviewed-by: Roland Shoemaker <roland@golang.org> | ||
33 | --- | ||
34 | src/math/big/ratconv.go | 15 ++++++++------- | ||
35 | src/math/big/ratconv_test.go | 25 +++++++++++++++++++++++++ | ||
36 | 2 files changed, 33 insertions(+), 7 deletions(-) | ||
37 | |||
38 | diff --git a/src/math/big/ratconv.go b/src/math/big/ratconv.go | ||
39 | index e8cbdbe..90053a9 100644 | ||
40 | --- a/src/math/big/ratconv.go | ||
41 | +++ b/src/math/big/ratconv.go | ||
42 | @@ -51,7 +51,8 @@ func (z *Rat) Scan(s fmt.ScanState, ch rune) error { | ||
43 | // An optional base-10 ``e'' or base-2 ``p'' (or their upper-case variants) | ||
44 | // exponent may be provided as well, except for hexadecimal floats which | ||
45 | // only accept an (optional) ``p'' exponent (because an ``e'' or ``E'' cannot | ||
46 | -// be distinguished from a mantissa digit). | ||
47 | +// be distinguished from a mantissa digit). If the exponent's absolute value | ||
48 | +// is too large, the operation may fail. | ||
49 | // The entire string, not just a prefix, must be valid for success. If the | ||
50 | // operation failed, the value of z is undefined but the returned value is nil. | ||
51 | func (z *Rat) SetString(s string) (*Rat, bool) { | ||
52 | @@ -174,6 +175,9 @@ func (z *Rat) SetString(s string) (*Rat, bool) { | ||
53 | return nil, false | ||
54 | } | ||
55 | } | ||
56 | + if n > 1e6 { | ||
57 | + return nil, false // avoid excessively large exponents | ||
58 | + } | ||
59 | pow5 := z.b.abs.expNN(natFive, nat(nil).setWord(Word(n)), nil) // use underlying array of z.b.abs | ||
60 | if exp5 > 0 { | ||
61 | z.a.abs = z.a.abs.mul(z.a.abs, pow5) | ||
62 | @@ -186,15 +190,12 @@ func (z *Rat) SetString(s string) (*Rat, bool) { | ||
63 | } | ||
64 | |||
65 | // apply exp2 contributions | ||
66 | + if exp2 < -1e7 || exp2 > 1e7 { | ||
67 | + return nil, false // avoid excessively large exponents | ||
68 | + } | ||
69 | if exp2 > 0 { | ||
70 | - if int64(uint(exp2)) != exp2 { | ||
71 | - panic("exponent too large") | ||
72 | - } | ||
73 | z.a.abs = z.a.abs.shl(z.a.abs, uint(exp2)) | ||
74 | } else if exp2 < 0 { | ||
75 | - if int64(uint(-exp2)) != -exp2 { | ||
76 | - panic("exponent too large") | ||
77 | - } | ||
78 | z.b.abs = z.b.abs.shl(z.b.abs, uint(-exp2)) | ||
79 | } | ||
80 | |||
81 | diff --git a/src/math/big/ratconv_test.go b/src/math/big/ratconv_test.go | ||
82 | index b820df4..e55e655 100644 | ||
83 | --- a/src/math/big/ratconv_test.go | ||
84 | +++ b/src/math/big/ratconv_test.go | ||
85 | @@ -590,3 +590,28 @@ func TestIssue31184(t *testing.T) { | ||
86 | } | ||
87 | } | ||
88 | } | ||
89 | + | ||
90 | +func TestIssue45910(t *testing.T) { | ||
91 | + var x Rat | ||
92 | + for _, test := range []struct { | ||
93 | + input string | ||
94 | + want bool | ||
95 | + }{ | ||
96 | + {"1e-1000001", false}, | ||
97 | + {"1e-1000000", true}, | ||
98 | + {"1e+1000000", true}, | ||
99 | + {"1e+1000001", false}, | ||
100 | + | ||
101 | + {"0p1000000000000", true}, | ||
102 | + {"1p-10000001", false}, | ||
103 | + {"1p-10000000", true}, | ||
104 | + {"1p+10000000", true}, | ||
105 | + {"1p+10000001", false}, | ||
106 | + {"1.770p02041010010011001001", false}, // test case from issue | ||
107 | + } { | ||
108 | + _, got := x.SetString(test.input) | ||
109 | + if got != test.want { | ||
110 | + t.Errorf("SetString(%s) got ok = %v; want %v", test.input, got, test.want) | ||
111 | + } | ||
112 | + } | ||
113 | +} | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-34558.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-34558.patch new file mode 100644 index 0000000000..8fb346d622 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-34558.patch | |||
@@ -0,0 +1,51 @@ | |||
1 | From a98589711da5e9d935e8d690cfca92892e86d557 Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <roland@golang.org> | ||
3 | Date: Wed, 9 Jun 2021 11:31:27 -0700 | ||
4 | Subject: [PATCH] crypto/tls: test key type when casting | ||
5 | |||
6 | When casting the certificate public key in generateClientKeyExchange, | ||
7 | check the type is appropriate. This prevents a panic when a server | ||
8 | agrees to a RSA based key exchange, but then sends an ECDSA (or | ||
9 | other) certificate. | ||
10 | |||
11 | Fixes #47143 | ||
12 | Fixes CVE-2021-34558 | ||
13 | |||
14 | Thanks to Imre Rad for reporting this issue. | ||
15 | |||
16 | Change-Id: Iabccacca6052769a605cccefa1216a9f7b7f6aea | ||
17 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1116723 | ||
18 | Reviewed-by: Filippo Valsorda <valsorda@google.com> | ||
19 | Reviewed-by: Katie Hockman <katiehockman@google.com> | ||
20 | Reviewed-on: https://go-review.googlesource.com/c/go/+/334031 | ||
21 | Trust: Filippo Valsorda <filippo@golang.org> | ||
22 | Run-TryBot: Filippo Valsorda <filippo@golang.org> | ||
23 | TryBot-Result: Go Bot <gobot@golang.org> | ||
24 | Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> | ||
25 | |||
26 | Upstream-Status: Backport | ||
27 | https://github.com/golang/go/commit/a98589711da5e9d935e8d690cfca92892e86d557 | ||
28 | CVE: CVE-2021-34558 | ||
29 | Signed-off-by: Armin Kuster <akuster@mvista.com> | ||
30 | |||
31 | --- | ||
32 | src/crypto/tls/key_agreement.go | 6 +++++- | ||
33 | 1 file changed, 5 insertions(+), 1 deletion(-) | ||
34 | |||
35 | Index: go/src/crypto/tls/key_agreement.go | ||
36 | =================================================================== | ||
37 | --- go.orig/src/crypto/tls/key_agreement.go | ||
38 | +++ go/src/crypto/tls/key_agreement.go | ||
39 | @@ -67,7 +67,11 @@ func (ka rsaKeyAgreement) generateClient | ||
40 | return nil, nil, err | ||
41 | } | ||
42 | |||
43 | - encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), preMasterSecret) | ||
44 | + rsaKey, ok := cert.PublicKey.(*rsa.PublicKey) | ||
45 | + if !ok { | ||
46 | + return nil, nil, errors.New("tls: server certificate contains incorrect key type for selected ciphersuite") | ||
47 | + } | ||
48 | + encrypted, err := rsa.EncryptPKCS1v15(config.rand(), rsaKey, preMasterSecret) | ||
49 | if err != nil { | ||
50 | return nil, nil, err | ||
51 | } | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-36221.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-36221.patch new file mode 100644 index 0000000000..9c00d4ebb2 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-36221.patch | |||
@@ -0,0 +1,101 @@ | |||
1 | From b7a85e0003cedb1b48a1fd3ae5b746ec6330102e Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Wed, 7 Jul 2021 16:34:34 -0700 | ||
4 | Subject: [PATCH] net/http/httputil: close incoming ReverseProxy request body | ||
5 | |||
6 | Reading from an incoming request body after the request handler aborts | ||
7 | with a panic can cause a panic, becuse http.Server does not (contrary | ||
8 | to its documentation) close the request body in this case. | ||
9 | |||
10 | Always close the incoming request body in ReverseProxy.ServeHTTP to | ||
11 | ensure that any in-flight outgoing requests using the body do not | ||
12 | read from it. | ||
13 | |||
14 | Updates #46866 | ||
15 | Fixes CVE-2021-36221 | ||
16 | |||
17 | Change-Id: I310df269200ad8732c5d9f1a2b00de68725831df | ||
18 | Reviewed-on: https://go-review.googlesource.com/c/go/+/333191 | ||
19 | Trust: Damien Neil <dneil@google.com> | ||
20 | Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> | ||
21 | Reviewed-by: Filippo Valsorda <filippo@golang.org> | ||
22 | |||
23 | https://github.com/golang/go/commit/b7a85e0003cedb1b48a1fd3ae5b746ec6330102e | ||
24 | CVE: CVE-2021-36221 | ||
25 | Upstream-Status: Backport | ||
26 | Signed-off-by: Chee Yang Lee <chee.yang.lee@intel.com> | ||
27 | --- | ||
28 | src/net/http/httputil/reverseproxy.go | 9 +++++ | ||
29 | src/net/http/httputil/reverseproxy_test.go | 39 ++++++++++++++++++++++ | ||
30 | 2 files changed, 48 insertions(+) | ||
31 | |||
32 | diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go | ||
33 | index 5d39955d62d15..8b63368386f43 100644 | ||
34 | --- a/src/net/http/httputil/reverseproxy.go | ||
35 | +++ b/src/net/http/httputil/reverseproxy.go | ||
36 | @@ -235,6 +235,15 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { | ||
37 | if req.ContentLength == 0 { | ||
38 | outreq.Body = nil // Issue 16036: nil Body for http.Transport retries | ||
39 | } | ||
40 | + if outreq.Body != nil { | ||
41 | + // Reading from the request body after returning from a handler is not | ||
42 | + // allowed, and the RoundTrip goroutine that reads the Body can outlive | ||
43 | + // this handler. This can lead to a crash if the handler panics (see | ||
44 | + // Issue 46866). Although calling Close doesn't guarantee there isn't | ||
45 | + // any Read in flight after the handle returns, in practice it's safe to | ||
46 | + // read after closing it. | ||
47 | + defer outreq.Body.Close() | ||
48 | + } | ||
49 | if outreq.Header == nil { | ||
50 | outreq.Header = make(http.Header) // Issue 33142: historical behavior was to always allocate | ||
51 | } | ||
52 | diff --git a/src/net/http/httputil/reverseproxy_test.go b/src/net/http/httputil/reverseproxy_test.go | ||
53 | index 1898ed8b8afde..4b6ad77a29466 100644 | ||
54 | --- a/src/net/http/httputil/reverseproxy_test.go | ||
55 | +++ b/src/net/http/httputil/reverseproxy_test.go | ||
56 | @@ -1122,6 +1122,45 @@ func TestReverseProxy_PanicBodyError(t *testing.T) { | ||
57 | rproxy.ServeHTTP(httptest.NewRecorder(), req) | ||
58 | } | ||
59 | |||
60 | +// Issue #46866: panic without closing incoming request body causes a panic | ||
61 | +func TestReverseProxy_PanicClosesIncomingBody(t *testing.T) { | ||
62 | + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
63 | + out := "this call was relayed by the reverse proxy" | ||
64 | + // Coerce a wrong content length to induce io.ErrUnexpectedEOF | ||
65 | + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(out)*2)) | ||
66 | + fmt.Fprintln(w, out) | ||
67 | + })) | ||
68 | + defer backend.Close() | ||
69 | + backendURL, err := url.Parse(backend.URL) | ||
70 | + if err != nil { | ||
71 | + t.Fatal(err) | ||
72 | + } | ||
73 | + proxyHandler := NewSingleHostReverseProxy(backendURL) | ||
74 | + proxyHandler.ErrorLog = log.New(io.Discard, "", 0) // quiet for tests | ||
75 | + frontend := httptest.NewServer(proxyHandler) | ||
76 | + defer frontend.Close() | ||
77 | + frontendClient := frontend.Client() | ||
78 | + | ||
79 | + var wg sync.WaitGroup | ||
80 | + for i := 0; i < 2; i++ { | ||
81 | + wg.Add(1) | ||
82 | + go func() { | ||
83 | + defer wg.Done() | ||
84 | + for j := 0; j < 10; j++ { | ||
85 | + const reqLen = 6 * 1024 * 1024 | ||
86 | + req, _ := http.NewRequest("POST", frontend.URL, &io.LimitedReader{R: neverEnding('x'), N: reqLen}) | ||
87 | + req.ContentLength = reqLen | ||
88 | + resp, _ := frontendClient.Transport.RoundTrip(req) | ||
89 | + if resp != nil { | ||
90 | + io.Copy(io.Discard, resp.Body) | ||
91 | + resp.Body.Close() | ||
92 | + } | ||
93 | + } | ||
94 | + }() | ||
95 | + } | ||
96 | + wg.Wait() | ||
97 | +} | ||
98 | + | ||
99 | func TestSelectFlushInterval(t *testing.T) { | ||
100 | tests := []struct { | ||
101 | name string | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-38297.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-38297.patch new file mode 100644 index 0000000000..24ceabf808 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-38297.patch | |||
@@ -0,0 +1,97 @@ | |||
1 | From 4548fcc8dfd933c237f29bba6f90040a85922564 Mon Sep 17 00:00:00 2001 | ||
2 | From: Michael Knyszek <mknyszek@google.com> | ||
3 | Date: Thu, 2 Sep 2021 16:51:59 -0400 | ||
4 | Subject: [PATCH] [release-branch.go1.16] misc/wasm, cmd/link: do not let | ||
5 | command line args overwrite global data | ||
6 | |||
7 | On Wasm, wasm_exec.js puts command line arguments at the beginning | ||
8 | of the linear memory (following the "zero page"). Currently there | ||
9 | is no limit for this, and a very long command line can overwrite | ||
10 | the program's data section. Prevent this by limiting the command | ||
11 | line to 4096 bytes, and in the linker ensuring the data section | ||
12 | starts at a high enough address (8192). | ||
13 | |||
14 | (Arguably our address assignment on Wasm is a bit confusing. This | ||
15 | is the minimum fix I can come up with.) | ||
16 | |||
17 | Thanks to Ben Lubar for reporting this issue. | ||
18 | |||
19 | Change by Cherry Mui <cherryyz@google.com>. | ||
20 | |||
21 | For #48797 | ||
22 | Fixes #48799 | ||
23 | Fixes CVE-2021-38297 | ||
24 | |||
25 | Change-Id: I0f50fbb2a5b6d0d047e3c134a88988d9133e4ab3 | ||
26 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1205933 | ||
27 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
28 | Reviewed-by: Than McIntosh <thanm@google.com> | ||
29 | Reviewed-on: https://go-review.googlesource.com/c/go/+/354591 | ||
30 | Trust: Michael Knyszek <mknyszek@google.com> | ||
31 | Reviewed-by: Heschi Kreinick <heschi@google.com> | ||
32 | |||
33 | CVE: CVE-2021-38297 | ||
34 | |||
35 | Upstream-Status: Backport: | ||
36 | https://github.com/golang/go/commit/4548fcc8dfd933c237f29bba6f90040a85922564 | ||
37 | |||
38 | Inline of ctxt.isWAsm followin this implemetation: | ||
39 | https://github.com/golang/go/blob/4548fcc8dfd933c237f29bba6f90040a85922564/src/cmd/link/internal/ld/target.go#L127 | ||
40 | |||
41 | Signed-off-by: Davide Gardenal <davide.gardenal@huawei.com> | ||
42 | --- | ||
43 | misc/wasm/wasm_exec.js | 7 +++++++ | ||
44 | src/cmd/link/internal/ld/data.go | 11 ++++++++++- | ||
45 | 2 files changed, 17 insertions(+), 1 deletion(-) | ||
46 | |||
47 | diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js | ||
48 | index 82041e6bb901..a0a264278b1b 100644 | ||
49 | --- a/misc/wasm/wasm_exec.js | ||
50 | +++ b/misc/wasm/wasm_exec.js | ||
51 | @@ -564,6 +564,13 @@ | ||
52 | offset += 8; | ||
53 | }); | ||
54 | |||
55 | + // The linker guarantees global data starts from at least wasmMinDataAddr. | ||
56 | + // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr. | ||
57 | + const wasmMinDataAddr = 4096 + 4096; | ||
58 | + if (offset >= wasmMinDataAddr) { | ||
59 | + throw new Error("command line too long"); | ||
60 | + } | ||
61 | + | ||
62 | this._inst.exports.run(argc, argv); | ||
63 | if (this.exited) { | ||
64 | this._resolveExitPromise(); | ||
65 | diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go | ||
66 | index 52035e96301c..54a1d188cdb9 100644 | ||
67 | --- a/src/cmd/link/internal/ld/data.go | ||
68 | +++ b/src/cmd/link/internal/ld/data.go | ||
69 | @@ -2330,6 +2330,11 @@ func assignAddress(ctxt *Link, sect *sym.Section, n int, s loader.Sym, va uint64 | ||
70 | return sect, n, va | ||
71 | } | ||
72 | |||
73 | +// On Wasm, we reserve 4096 bytes for zero page, then 4096 bytes for wasm_exec.js | ||
74 | +// to store command line args. Data sections starts from at least address 8192. | ||
75 | +// Keep in sync with wasm_exec.js. | ||
76 | +const wasmMinDataAddr = 4096 + 4096 | ||
77 | + | ||
78 | // address assigns virtual addresses to all segments and sections and | ||
79 | // returns all segments in file order. | ||
80 | func (ctxt *Link) address() []*sym.Segment { | ||
81 | @@ -2339,10 +2344,14 @@ func (ctxt *Link) address() []*sym.Segment { | ||
82 | order = append(order, &Segtext) | ||
83 | Segtext.Rwx = 05 | ||
84 | Segtext.Vaddr = va | ||
85 | - for _, s := range Segtext.Sections { | ||
86 | + for i, s := range Segtext.Sections { | ||
87 | va = uint64(Rnd(int64(va), int64(s.Align))) | ||
88 | s.Vaddr = va | ||
89 | va += s.Length | ||
90 | + | ||
91 | + if ctxt.Arch.Family == sys.Wasm && i == 0 && va < wasmMinDataAddr { | ||
92 | + va = wasmMinDataAddr | ||
93 | + } | ||
94 | } | ||
95 | |||
96 | Segtext.Length = va - uint64(*FlagTextAddr) | ||
97 | \ No newline at end of file | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-39293.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-39293.patch new file mode 100644 index 0000000000..88fca9cad9 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-39293.patch | |||
@@ -0,0 +1,79 @@ | |||
1 | From 6c480017ae600b2c90a264a922e041df04dfa785 Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <roland@golang.org> | ||
3 | Date: Wed, 18 Aug 2021 11:49:29 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.16] archive/zip: prevent preallocation | ||
5 | check from overflowing | ||
6 | |||
7 | If the indicated directory size in the archive header is so large that | ||
8 | subtracting it from the archive size overflows a uint64, the check that | ||
9 | the indicated number of files in the archive can be effectively | ||
10 | bypassed. Prevent this from happening by checking that the indicated | ||
11 | directory size is less than the size of the archive. | ||
12 | |||
13 | Thanks to the OSS-Fuzz project for discovering this issue and to | ||
14 | Emmanuel Odeke for reporting it. | ||
15 | |||
16 | Fixes #47985 | ||
17 | Updates #47801 | ||
18 | Fixes CVE-2021-39293 | ||
19 | |||
20 | Change-Id: Ifade26b98a40f3b37398ca86bd5252d12394dd24 | ||
21 | Reviewed-on: https://go-review.googlesource.com/c/go/+/343434 | ||
22 | Trust: Roland Shoemaker <roland@golang.org> | ||
23 | Run-TryBot: Roland Shoemaker <roland@golang.org> | ||
24 | TryBot-Result: Go Bot <gobot@golang.org> | ||
25 | Reviewed-by: Russ Cox <rsc@golang.org> | ||
26 | (cherry picked from commit bacbc33439b124ffd7392c91a5f5d96eca8c0c0b) | ||
27 | Reviewed-on: https://go-review.googlesource.com/c/go/+/345409 | ||
28 | Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com> | ||
29 | Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com> | ||
30 | Trust: Cherry Mui <cherryyz@google.com> | ||
31 | |||
32 | https://github.com/golang/go/commit/6c480017ae600b2c90a264a922e041df04dfa785 | ||
33 | CVE: CVE-2021-39293 | ||
34 | Upstream-Status: Backport | ||
35 | Signed-off-by: Chee Yang Lee <chee.yang.lee@intel.com> | ||
36 | --- | ||
37 | src/archive/zip/reader.go | 2 +- | ||
38 | src/archive/zip/reader_test.go | 18 ++++++++++++++++++ | ||
39 | 2 files changed, 19 insertions(+), 1 deletion(-) | ||
40 | |||
41 | diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go | ||
42 | index ddef2b7b5a517..801d1313b6c32 100644 | ||
43 | --- a/src/archive/zip/reader.go | ||
44 | +++ b/src/archive/zip/reader.go | ||
45 | @@ -105,7 +105,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error { | ||
46 | // indicate it contains up to 1 << 128 - 1 files. Since each file has a | ||
47 | // header which will be _at least_ 30 bytes we can safely preallocate | ||
48 | // if (data size / 30) >= end.directoryRecords. | ||
49 | - if (uint64(size)-end.directorySize)/30 >= end.directoryRecords { | ||
50 | + if end.directorySize < uint64(size) && (uint64(size)-end.directorySize)/30 >= end.directoryRecords { | ||
51 | z.File = make([]*File, 0, end.directoryRecords) | ||
52 | } | ||
53 | z.Comment = end.comment | ||
54 | diff --git a/src/archive/zip/reader_test.go b/src/archive/zip/reader_test.go | ||
55 | index 471be27bb1004..99f13345d8d06 100644 | ||
56 | --- a/src/archive/zip/reader_test.go | ||
57 | +++ b/src/archive/zip/reader_test.go | ||
58 | @@ -1225,3 +1225,21 @@ func TestCVE202133196(t *testing.T) { | ||
59 | t.Errorf("Archive has unexpected number of files, got %d, want 5", len(r.File)) | ||
60 | } | ||
61 | } | ||
62 | + | ||
63 | +func TestCVE202139293(t *testing.T) { | ||
64 | + // directory size is so large, that the check in Reader.init | ||
65 | + // overflows when subtracting from the archive size, causing | ||
66 | + // the pre-allocation check to be bypassed. | ||
67 | + data := []byte{ | ||
68 | + 0x50, 0x4b, 0x06, 0x06, 0x05, 0x06, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, | ||
69 | + 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, | ||
70 | + 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b, | ||
71 | + 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, | ||
72 | + 0x00, 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, | ||
73 | + 0xff, 0x50, 0xfe, 0x00, 0xff, 0x00, 0x3a, 0x00, 0x00, 0x00, 0xff, | ||
74 | + } | ||
75 | + _, err := NewReader(bytes.NewReader(data), int64(len(data))) | ||
76 | + if err != ErrFormat { | ||
77 | + t.Fatalf("unexpected error, got: %v, want: %v", err, ErrFormat) | ||
78 | + } | ||
79 | +} | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-41771.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-41771.patch new file mode 100644 index 0000000000..526796dbcb --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-41771.patch | |||
@@ -0,0 +1,86 @@ | |||
1 | From d19c5bdb24e093a2d5097b7623284eb02726cede Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <roland@golang.org> | ||
3 | Date: Thu, 14 Oct 2021 13:02:01 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.16] debug/macho: fail on invalid dynamic | ||
5 | symbol table command | ||
6 | MIME-Version: 1.0 | ||
7 | Content-Type: text/plain; charset=UTF-8 | ||
8 | Content-Transfer-Encoding: 8bit | ||
9 | |||
10 | Fail out when loading a file that contains a dynamic symbol table | ||
11 | command that indicates a larger number of symbols than exist in the | ||
12 | loaded symbol table. | ||
13 | |||
14 | Thanks to Burak Çarıkçı - Yunus Yıldırım (CT-Zer0 Crypttech) for | ||
15 | reporting this issue. | ||
16 | |||
17 | Updates #48990 | ||
18 | Fixes #48991 | ||
19 | Fixes CVE-2021-41771 | ||
20 | |||
21 | Change-Id: Ic3d6e6529241afcc959544b326b21b663262bad5 | ||
22 | Reviewed-on: https://go-review.googlesource.com/c/go/+/355990 | ||
23 | Reviewed-by: Julie Qiu <julie@golang.org> | ||
24 | Reviewed-by: Katie Hockman <katie@golang.org> | ||
25 | Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com> | ||
26 | Run-TryBot: Roland Shoemaker <roland@golang.org> | ||
27 | TryBot-Result: Go Bot <gobot@golang.org> | ||
28 | Trust: Katie Hockman <katie@golang.org> | ||
29 | (cherry picked from commit 61536ec03063b4951163bd09609c86d82631fa27) | ||
30 | Reviewed-on: https://go-review.googlesource.com/c/go/+/359454 | ||
31 | Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> | ||
32 | |||
33 | https://github.com/golang/go/commit/d19c5bdb24e093a2d5097b7623284eb02726cede | ||
34 | CVE: CVE-2021-41771 | ||
35 | Upstream-Status: Backport | ||
36 | Signed-off-by: Chee Yang Lee <chee.yang.lee@intel.com> | ||
37 | --- | ||
38 | src/debug/macho/file.go | 9 +++++++++ | ||
39 | src/debug/macho/file_test.go | 7 +++++++ | ||
40 | .../testdata/gcc-amd64-darwin-exec-with-bad-dysym.base64 | 1 + | ||
41 | 3 files changed, 17 insertions(+) | ||
42 | create mode 100644 src/debug/macho/testdata/gcc-amd64-darwin-exec-with-bad-dysym.base64 | ||
43 | |||
44 | diff --git a/src/debug/macho/file.go b/src/debug/macho/file.go | ||
45 | index 085b0c8219bad..73cfce3c7606e 100644 | ||
46 | --- a/src/debug/macho/file.go | ||
47 | +++ b/src/debug/macho/file.go | ||
48 | @@ -345,6 +345,15 @@ func NewFile(r io.ReaderAt) (*File, error) { | ||
49 | if err := binary.Read(b, bo, &hdr); err != nil { | ||
50 | return nil, err | ||
51 | } | ||
52 | + if hdr.Iundefsym > uint32(len(f.Symtab.Syms)) { | ||
53 | + return nil, &FormatError{offset, fmt.Sprintf( | ||
54 | + "undefined symbols index in dynamic symbol table command is greater than symbol table length (%d > %d)", | ||
55 | + hdr.Iundefsym, len(f.Symtab.Syms)), nil} | ||
56 | + } else if hdr.Iundefsym+hdr.Nundefsym > uint32(len(f.Symtab.Syms)) { | ||
57 | + return nil, &FormatError{offset, fmt.Sprintf( | ||
58 | + "number of undefined symbols after index in dynamic symbol table command is greater than symbol table length (%d > %d)", | ||
59 | + hdr.Iundefsym+hdr.Nundefsym, len(f.Symtab.Syms)), nil} | ||
60 | + } | ||
61 | dat := make([]byte, hdr.Nindirectsyms*4) | ||
62 | if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil { | ||
63 | return nil, err | ||
64 | diff --git a/src/debug/macho/file_test.go b/src/debug/macho/file_test.go | ||
65 | index 03915c86e23d9..9beeb80dd27c1 100644 | ||
66 | --- a/src/debug/macho/file_test.go | ||
67 | +++ b/src/debug/macho/file_test.go | ||
68 | @@ -416,3 +416,10 @@ func TestTypeString(t *testing.T) { | ||
69 | t.Errorf("got %v, want %v", TypeExec.GoString(), "macho.Exec") | ||
70 | } | ||
71 | } | ||
72 | + | ||
73 | +func TestOpenBadDysymCmd(t *testing.T) { | ||
74 | + _, err := openObscured("testdata/gcc-amd64-darwin-exec-with-bad-dysym.base64") | ||
75 | + if err == nil { | ||
76 | + t.Fatal("openObscured did not fail when opening a file with an invalid dynamic symbol table command") | ||
77 | + } | ||
78 | +} | ||
79 | diff --git a/src/debug/macho/testdata/gcc-amd64-darwin-exec-with-bad-dysym.base64 b/src/debug/macho/testdata/gcc-amd64-darwin-exec-with-bad-dysym.base64 | ||
80 | new file mode 100644 | ||
81 | index 0000000000000..8e0436639c109 | ||
82 | --- /dev/null | ||
83 | +++ b/src/debug/macho/testdata/gcc-amd64-darwin-exec-with-bad-dysym.base64 | ||
84 | @@ -0,0 +1 @@ | ||
85 | +z/rt/gcAAAEDAACAAgAAAAsAAABoBQAAhQAAAAAAAAAZAAAASAAAAF9fUEFHRVpFUk8AAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAA2AEAAF9fVEVYVAAAAAAAAAAAAAAAAAAAAQAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAcAAAAFAAAABQAAAAAAAABfX3RleHQAAAAAAAAAAAAAX19URVhUAAAAAAAAAAAAABQPAAABAAAAbQAAAAAAAAAUDwAAAgAAAAAAAAAAAAAAAAQAgAAAAAAAAAAAAAAAAF9fc3ltYm9sX3N0dWIxAABfX1RFWFQAAAAAAAAAAAAAgQ8AAAEAAAAMAAAAAAAAAIEPAAAAAAAAAAAAAAAAAAAIBACAAAAAAAYAAAAAAAAAX19zdHViX2hlbHBlcgAAAF9fVEVYVAAAAAAAAAAAAACQDwAAAQAAABgAAAAAAAAAkA8AAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfX2NzdHJpbmcAAAAAAAAAX19URVhUAAAAAAAAAAAAAKgPAAABAAAADQAAAAAAAACoDwAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAF9fZWhfZnJhbWUAAAAAAABfX1RFWFQAAAAAAAAAAAAAuA8AAAEAAABIAAAAAAAAALgPAAADAAAAAAAAAAAAAAALAABgAAAAAAAAAAAAAAAAGQAAADgBAABfX0RBVEEAAAAAAAAAAAAAABAAAAEAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAHAAAAAwAAAAMAAAAAAAAAX19kYXRhAAAAAAAAAAAAAF9fREFUQQAAAAAAAAAAAAAAEAAAAQAAABwAAAAAAAAAABAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfX2R5bGQAAAAAAAAAAAAAX19EQVRBAAAAAAAAAAAAACAQAAABAAAAOAAAAAAAAAAgEAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF9fbGFfc3ltYm9sX3B0cgBfX0RBVEEAAAAAAAAAAAAAWBAAAAEAAAAQAAAAAAAAAFgQAAACAAAAAAAAAAAAAAAHAAAAAgAAAAAAAAAAAAAAGQAAAEgAAABfX0xJTktFRElUAAAAAAAAACAAAAEAAAAAEAAAAAAAAAAgAAAAAAAAQAEAAAAAAAAHAAAAAQAAAAAAAAAAAAAAAgAAABgAAAAAIAAACwAAAMAgAACAAAAACwAAAFAAAAAAAAAAAgAAAAIAAAAHAAAACQAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwIAAABAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAIAAAAAwAAAAvdXNyL2xpYi9keWxkAAAAAAAAABsAAAAYAAAAOyS4cg5FdtQoqu6JsMEhXQUAAAC4AAAABAAAACoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQPAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAOAAAABgAAAACAAAAAAABAAAAAQAvdXNyL2xpYi9saWJnY2Nfcy4xLmR5bGliAAAAAAAAAAwAAAA4AAAAGAAAAAIAAAAEAW8AAAABAC91c3IvbGliL2xpYlN5c3RlbS5CLmR5bGliAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqAEiJ5UiD5PBIi30ISI11EIn6g8IBweIDSAHySInR6wRIg8EISIM5AHX2SIPBCOgiAAAAicfoMgAAAPRBU0yNHafw//9BU/8lvwAAAA8fAP8lvgAAAFVIieVIjT0zAAAA6A0AAAC4AAAAAMnD/yXRAAAA/yXTAAAAAAAATI0dwQAAAOm0////TI0dvQAAAOmo////aGVsbG8sIHdvcmxkAAAAABQAAAAAAAAAAXpSAAF4EAEQDAcIkAEAACwAAAAcAAAAkv////////8XAAAAAAAAAAAEAQAAAA4QhgIEAwAAAA0GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDAX/9/AAAIEMBf/38AAAAAAAABAAAAGBAAAAEAAAAQEAAAAQAAAAgQAAABAAAAABAAAAEAAACQDwAAAQAAAJwPAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAHgEAAFAPAAABAAAAGwAAAB4BAABkDwAAAQAAAC4AAAAPBgAAGBAAAAEAAAA2AAAADwYAABAQAAABAAAAPgAAAA8GAAAAEAAAAQAAAEoAAAADABAAAAAAAAEAAABeAAAADwYAAAgQAAABAAAAZwAAAA8BAABqDwAAAQAAAG0AAAAPAQAAFA8AAAEAAABzAAAAAQABAgAAAAAAAAAAeQAAAAEAAQIAAAAAAAAAAAkAAAAKAAAACQAAAAoAAAAgAGR5bGRfc3R1Yl9iaW5kaW5nX2hlbHBlcgBfX2R5bGRfZnVuY19sb29rdXAAX05YQXJnYwBfTlhBcmd2AF9fX3Byb2duYW1lAF9fbWhfZXhlY3V0ZV9oZWFkZXIAX2Vudmlyb24AX21haW4Ac3RhcnQAX2V4aXQAX3B1dHMAAA== | ||
86 | \ No newline at end of file | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-44716.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-44716.patch new file mode 100644 index 0000000000..9c4fee2db4 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-44716.patch | |||
@@ -0,0 +1,93 @@ | |||
1 | From 9f1860075990e7bf908ca7cc329d1d3ef91741c8 Mon Sep 17 00:00:00 2001 | ||
2 | From: Filippo Valsorda <filippo@golang.org> | ||
3 | Date: Thu, 9 Dec 2021 06:13:31 -0500 | ||
4 | Subject: [PATCH] net/http: update bundled golang.org/x/net/http2 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/d0aebe3e74fe14799f97ddd3f01129697c6a290a] | ||
7 | CVE: CVE-2021-44716 | ||
8 | Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org> | ||
9 | |||
10 | |||
11 | Pull in security fix | ||
12 | |||
13 | a5309b3 http2: cap the size of the server's canonical header cache | ||
14 | |||
15 | Updates #50058 | ||
16 | Fixes CVE-2021-44716 | ||
17 | |||
18 | Change-Id: Ifdd13f97fce168de5fb4b2e74ef2060d059800b9 | ||
19 | Reviewed-on: https://go-review.googlesource.com/c/go/+/370575 | ||
20 | Trust: Filippo Valsorda <filippo@golang.org> | ||
21 | Run-TryBot: Filippo Valsorda <filippo@golang.org> | ||
22 | Reviewed-by: Alex Rakoczy <alex@golang.org> | ||
23 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
24 | (cherry picked from commit d0aebe3e74fe14799f97ddd3f01129697c6a290a) | ||
25 | --- | ||
26 | src/go.mod | 2 +- | ||
27 | src/go.sum | 4 ++-- | ||
28 | src/net/http/h2_bundle.go | 10 +++++++++- | ||
29 | src/vendor/modules.txt | 2 +- | ||
30 | 4 files changed, 13 insertions(+), 5 deletions(-) | ||
31 | |||
32 | diff --git a/src/go.mod b/src/go.mod | ||
33 | index ec6bd98..56f2fbb 100644 | ||
34 | --- a/src/go.mod | ||
35 | +++ b/src/go.mod | ||
36 | @@ -4,7 +4,7 @@ go 1.14 | ||
37 | |||
38 | require ( | ||
39 | golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d | ||
40 | - golang.org/x/net v0.0.0-20210129194117-4acb7895a057 | ||
41 | + golang.org/x/net v0.0.0-20211209100217-a5309b321dca | ||
42 | golang.org/x/sys v0.0.0-20200201011859-915c9c3d4ccf // indirect | ||
43 | golang.org/x/text v0.3.3-0.20191031172631-4b67af870c6f // indirect | ||
44 | ) | ||
45 | diff --git a/src/go.sum b/src/go.sum | ||
46 | index 171e083..1ceba05 100644 | ||
47 | --- a/src/go.sum | ||
48 | +++ b/src/go.sum | ||
49 | @@ -2,8 +2,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk | ||
50 | golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= | ||
51 | golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||
52 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||
53 | -golang.org/x/net v0.0.0-20210129194117-4acb7895a057 h1:HThQeV5c0Ab/Puir+q6mC97b7+3dfZdsLWMLoBrzo68= | ||
54 | -golang.org/x/net v0.0.0-20210129194117-4acb7895a057/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||
55 | +golang.org/x/net v0.0.0-20211209100217-a5309b321dca h1:UmeWAm8AwB6NA/e4FSaGlK1EKTLXKX3utx4Si+6kfPg= | ||
56 | +golang.org/x/net v0.0.0-20211209100217-a5309b321dca/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||
57 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
58 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
59 | golang.org/x/sys v0.0.0-20200201011859-915c9c3d4ccf h1:+4j7oujXP478CVb/AFvHJmVX5+Pczx2NGts5yirA0oY= | ||
60 | diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go | ||
61 | index 702fd5a..83f2a72 100644 | ||
62 | --- a/src/net/http/h2_bundle.go | ||
63 | +++ b/src/net/http/h2_bundle.go | ||
64 | @@ -4293,7 +4293,15 @@ func (sc *http2serverConn) canonicalHeader(v string) string { | ||
65 | sc.canonHeader = make(map[string]string) | ||
66 | } | ||
67 | cv = CanonicalHeaderKey(v) | ||
68 | - sc.canonHeader[v] = cv | ||
69 | + // maxCachedCanonicalHeaders is an arbitrarily-chosen limit on the number of | ||
70 | + // entries in the canonHeader cache. This should be larger than the number | ||
71 | + // of unique, uncommon header keys likely to be sent by the peer, while not | ||
72 | + // so high as to permit unreaasonable memory usage if the peer sends an unbounded | ||
73 | + // number of unique header keys. | ||
74 | + const maxCachedCanonicalHeaders = 32 | ||
75 | + if len(sc.canonHeader) < maxCachedCanonicalHeaders { | ||
76 | + sc.canonHeader[v] = cv | ||
77 | + } | ||
78 | return cv | ||
79 | } | ||
80 | |||
81 | diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt | ||
82 | index 669bd9b..1d67183 100644 | ||
83 | --- a/src/vendor/modules.txt | ||
84 | +++ b/src/vendor/modules.txt | ||
85 | @@ -8,7 +8,7 @@ golang.org/x/crypto/curve25519 | ||
86 | golang.org/x/crypto/hkdf | ||
87 | golang.org/x/crypto/internal/subtle | ||
88 | golang.org/x/crypto/poly1305 | ||
89 | -# golang.org/x/net v0.0.0-20210129194117-4acb7895a057 | ||
90 | +# golang.org/x/net v0.0.0-20211209100217-a5309b321dca | ||
91 | ## explicit | ||
92 | golang.org/x/net/dns/dnsmessage | ||
93 | golang.org/x/net/http/httpguts | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2021-44717.patch b/meta/recipes-devtools/go/go-1.14/CVE-2021-44717.patch new file mode 100644 index 0000000000..17cac7a5ba --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2021-44717.patch | |||
@@ -0,0 +1,83 @@ | |||
1 | From 9171c664e7af479aa26bc72f2e7cf4e69d8e0a6f Mon Sep 17 00:00:00 2001 | ||
2 | From: Hitendra Prajapati <hprajapati@mvista.com> | ||
3 | Date: Fri, 17 Jun 2022 10:22:47 +0530 | ||
4 | Subject: [PATCH] CVE-2021-44717 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/44a3fb49] | ||
7 | CVE: CVE-2021-44717 | ||
8 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
9 | |||
10 | syscall: fix ForkLock spurious close(0) on pipe failure | ||
11 | Pipe (and therefore forkLockPipe) does not make any guarantees | ||
12 | about the state of p after a failed Pipe(p). Avoid that assumption | ||
13 | and the too-clever goto, so that we don't accidentally Close a real fd | ||
14 | if the failed pipe leaves p[0] or p[1] set >= 0. | ||
15 | |||
16 | Updates #50057 | ||
17 | Fixes CVE-2021-44717 | ||
18 | |||
19 | Change-Id: Iff8e19a6efbba0c73cc8b13ecfae381c87600bb4 | ||
20 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1291270 | ||
21 | Reviewed-by: Ian Lance Taylor <iant@google.com> | ||
22 | Reviewed-on: https://go-review.googlesource.com/c/go/+/370514 | ||
23 | Trust: Filippo Valsorda <filippo@golang.org> | ||
24 | Run-TryBot: Filippo Valsorda <filippo@golang.org> | ||
25 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
26 | Reviewed-by: Alex Rakoczy <alex@golang.org> | ||
27 | --- | ||
28 | src/syscall/exec_unix.go | 20 ++++++-------------- | ||
29 | 1 file changed, 6 insertions(+), 14 deletions(-) | ||
30 | |||
31 | diff --git a/src/syscall/exec_unix.go b/src/syscall/exec_unix.go | ||
32 | index b3798b6..b73782c 100644 | ||
33 | --- a/src/syscall/exec_unix.go | ||
34 | +++ b/src/syscall/exec_unix.go | ||
35 | @@ -151,9 +151,6 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) | ||
36 | sys = &zeroSysProcAttr | ||
37 | } | ||
38 | |||
39 | - p[0] = -1 | ||
40 | - p[1] = -1 | ||
41 | - | ||
42 | // Convert args to C form. | ||
43 | argv0p, err := BytePtrFromString(argv0) | ||
44 | if err != nil { | ||
45 | @@ -194,14 +191,17 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) | ||
46 | |||
47 | // Allocate child status pipe close on exec. | ||
48 | if err = forkExecPipe(p[:]); err != nil { | ||
49 | - goto error | ||
50 | + ForkLock.Unlock() | ||
51 | + return 0, err | ||
52 | } | ||
53 | |||
54 | // Kick off child. | ||
55 | pid, err1 = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1]) | ||
56 | if err1 != 0 { | ||
57 | - err = Errno(err1) | ||
58 | - goto error | ||
59 | + Close(p[0]) | ||
60 | + Close(p[1]) | ||
61 | + ForkLock.Unlock() | ||
62 | + return 0, Errno(err1) | ||
63 | } | ||
64 | ForkLock.Unlock() | ||
65 | |||
66 | @@ -228,14 +228,6 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) | ||
67 | |||
68 | // Read got EOF, so pipe closed on exec, so exec succeeded. | ||
69 | return pid, nil | ||
70 | - | ||
71 | -error: | ||
72 | - if p[0] >= 0 { | ||
73 | - Close(p[0]) | ||
74 | - Close(p[1]) | ||
75 | - } | ||
76 | - ForkLock.Unlock() | ||
77 | - return 0, err | ||
78 | } | ||
79 | |||
80 | // Combination of fork and exec, careful to be thread safe. | ||
81 | -- | ||
82 | 2.25.1 | ||
83 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-1962.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-1962.patch new file mode 100644 index 0000000000..b2ab5d0669 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-1962.patch | |||
@@ -0,0 +1,357 @@ | |||
1 | From ba8788ebcead55e99e631c6a1157ad7b35535d11 Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Wed, 15 Jun 2022 10:43:05 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.17] go/parser: limit recursion depth | ||
5 | |||
6 | Limit nested parsing to 100,000, which prevents stack exhaustion when | ||
7 | parsing deeply nested statements, types, and expressions. Also limit | ||
8 | the scope depth to 1,000 during object resolution. | ||
9 | |||
10 | Thanks to Juho Nurminen of Mattermost for reporting this issue. | ||
11 | |||
12 | Fixes #53707 | ||
13 | Updates #53616 | ||
14 | Fixes CVE-2022-1962 | ||
15 | |||
16 | Change-Id: I4d7b86c1d75d0bf3c7af1fdea91582aa74272c64 | ||
17 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1491025 | ||
18 | Reviewed-by: Russ Cox <rsc@google.com> | ||
19 | Reviewed-by: Damien Neil <dneil@google.com> | ||
20 | (cherry picked from commit 6a856f08d58e4b6705c0c337d461c540c1235c83) | ||
21 | Reviewed-on: https://go-review.googlesource.com/c/go/+/417070 | ||
22 | Reviewed-by: Heschi Kreinick <heschi@google.com> | ||
23 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
24 | Run-TryBot: Michael Knyszek <mknyszek@google.com> | ||
25 | |||
26 | Upstream-Status: Backport [https://github.com/golang/go/commit/ba8788ebcead55e99e631c6a1157ad7b35535d11] | ||
27 | CVE: CVE-2022-1962 | ||
28 | Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com> | ||
29 | --- | ||
30 | src/go/parser/interface.go | 10 ++- | ||
31 | src/go/parser/parser.go | 48 ++++++++-- | ||
32 | src/go/parser/parser_test.go | 169 +++++++++++++++++++++++++++++++++++ | ||
33 | 3 files changed, 220 insertions(+), 7 deletions(-) | ||
34 | |||
35 | diff --git a/src/go/parser/interface.go b/src/go/parser/interface.go | ||
36 | index 54f9d7b..537b327 100644 | ||
37 | --- a/src/go/parser/interface.go | ||
38 | +++ b/src/go/parser/interface.go | ||
39 | @@ -92,8 +92,11 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) | ||
40 | defer func() { | ||
41 | if e := recover(); e != nil { | ||
42 | // resume same panic if it's not a bailout | ||
43 | - if _, ok := e.(bailout); !ok { | ||
44 | + bail, ok := e.(bailout) | ||
45 | + if !ok { | ||
46 | panic(e) | ||
47 | + } else if bail.msg != "" { | ||
48 | + p.errors.Add(p.file.Position(bail.pos), bail.msg) | ||
49 | } | ||
50 | } | ||
51 | |||
52 | @@ -188,8 +191,11 @@ func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode M | ||
53 | defer func() { | ||
54 | if e := recover(); e != nil { | ||
55 | // resume same panic if it's not a bailout | ||
56 | - if _, ok := e.(bailout); !ok { | ||
57 | + bail, ok := e.(bailout) | ||
58 | + if !ok { | ||
59 | panic(e) | ||
60 | + } else if bail.msg != "" { | ||
61 | + p.errors.Add(p.file.Position(bail.pos), bail.msg) | ||
62 | } | ||
63 | } | ||
64 | p.errors.Sort() | ||
65 | diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go | ||
66 | index 31a7398..586fe90 100644 | ||
67 | --- a/src/go/parser/parser.go | ||
68 | +++ b/src/go/parser/parser.go | ||
69 | @@ -64,6 +64,10 @@ type parser struct { | ||
70 | unresolved []*ast.Ident // unresolved identifiers | ||
71 | imports []*ast.ImportSpec // list of imports | ||
72 | |||
73 | + // nestLev is used to track and limit the recursion depth | ||
74 | + // during parsing. | ||
75 | + nestLev int | ||
76 | + | ||
77 | // Label scopes | ||
78 | // (maintained by open/close LabelScope) | ||
79 | labelScope *ast.Scope // label scope for current function | ||
80 | @@ -236,6 +240,24 @@ func un(p *parser) { | ||
81 | p.printTrace(")") | ||
82 | } | ||
83 | |||
84 | +// maxNestLev is the deepest we're willing to recurse during parsing | ||
85 | +const maxNestLev int = 1e5 | ||
86 | + | ||
87 | +func incNestLev(p *parser) *parser { | ||
88 | + p.nestLev++ | ||
89 | + if p.nestLev > maxNestLev { | ||
90 | + p.error(p.pos, "exceeded max nesting depth") | ||
91 | + panic(bailout{}) | ||
92 | + } | ||
93 | + return p | ||
94 | +} | ||
95 | + | ||
96 | +// decNestLev is used to track nesting depth during parsing to prevent stack exhaustion. | ||
97 | +// It is used along with incNestLev in a similar fashion to how un and trace are used. | ||
98 | +func decNestLev(p *parser) { | ||
99 | + p.nestLev-- | ||
100 | +} | ||
101 | + | ||
102 | // Advance to the next token. | ||
103 | func (p *parser) next0() { | ||
104 | // Because of one-token look-ahead, print the previous token | ||
105 | @@ -348,8 +370,12 @@ func (p *parser) next() { | ||
106 | } | ||
107 | } | ||
108 | |||
109 | -// A bailout panic is raised to indicate early termination. | ||
110 | -type bailout struct{} | ||
111 | +// A bailout panic is raised to indicate early termination. pos and msg are | ||
112 | +// only populated when bailing out of object resolution. | ||
113 | +type bailout struct { | ||
114 | + pos token.Pos | ||
115 | + msg string | ||
116 | +} | ||
117 | |||
118 | func (p *parser) error(pos token.Pos, msg string) { | ||
119 | epos := p.file.Position(pos) | ||
120 | @@ -1030,6 +1056,8 @@ func (p *parser) parseChanType() *ast.ChanType { | ||
121 | |||
122 | // If the result is an identifier, it is not resolved. | ||
123 | func (p *parser) tryIdentOrType() ast.Expr { | ||
124 | + defer decNestLev(incNestLev(p)) | ||
125 | + | ||
126 | switch p.tok { | ||
127 | case token.IDENT: | ||
128 | return p.parseTypeName() | ||
129 | @@ -1609,7 +1637,13 @@ func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr { | ||
130 | } | ||
131 | |||
132 | x := p.parseUnaryExpr(lhs) | ||
133 | - for { | ||
134 | + // We track the nesting here rather than at the entry for the function, | ||
135 | + // since it can iteratively produce a nested output, and we want to | ||
136 | + // limit how deep a structure we generate. | ||
137 | + var n int | ||
138 | + defer func() { p.nestLev -= n }() | ||
139 | + for n = 1; ; n++ { | ||
140 | + incNestLev(p) | ||
141 | op, oprec := p.tokPrec() | ||
142 | if oprec < prec1 { | ||
143 | return x | ||
144 | @@ -1628,7 +1662,7 @@ func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr { | ||
145 | // The result may be a type or even a raw type ([...]int). Callers must | ||
146 | // check the result (using checkExpr or checkExprOrType), depending on | ||
147 | // context. | ||
148 | -func (p *parser) parseExpr(lhs bool) ast.Expr { | ||
149 | +func (p *parser) parseExpr(lhs bool) ast.Expr { | ||
150 | if p.trace { | ||
151 | defer un(trace(p, "Expression")) | ||
152 | } | ||
153 | @@ -1899,6 +1933,8 @@ func (p *parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) { | ||
154 | } | ||
155 | |||
156 | func (p *parser) parseIfStmt() *ast.IfStmt { | ||
157 | + defer decNestLev(incNestLev(p)) | ||
158 | + | ||
159 | if p.trace { | ||
160 | defer un(trace(p, "IfStmt")) | ||
161 | } | ||
162 | @@ -2214,6 +2250,8 @@ func (p *parser) parseForStmt() ast.Stmt { | ||
163 | } | ||
164 | |||
165 | func (p *parser) parseStmt() (s ast.Stmt) { | ||
166 | + defer decNestLev(incNestLev(p)) | ||
167 | + | ||
168 | if p.trace { | ||
169 | defer un(trace(p, "Statement")) | ||
170 | } | ||
171 | diff --git a/src/go/parser/parser_test.go b/src/go/parser/parser_test.go | ||
172 | index 25a374e..37a6a2b 100644 | ||
173 | --- a/src/go/parser/parser_test.go | ||
174 | +++ b/src/go/parser/parser_test.go | ||
175 | @@ -10,6 +10,7 @@ import ( | ||
176 | "go/ast" | ||
177 | "go/token" | ||
178 | "os" | ||
179 | + "runtime" | ||
180 | "strings" | ||
181 | "testing" | ||
182 | ) | ||
183 | @@ -569,3 +570,171 @@ type x int // comment | ||
184 | t.Errorf("got %q, want %q", comment, "// comment") | ||
185 | } | ||
186 | } | ||
187 | + | ||
188 | +var parseDepthTests = []struct { | ||
189 | + name string | ||
190 | + format string | ||
191 | + // multipler is used when a single statement may result in more than one | ||
192 | + // change in the depth level, for instance "1+(..." produces a BinaryExpr | ||
193 | + // followed by a UnaryExpr, which increments the depth twice. The test | ||
194 | + // case comment explains which nodes are triggering the multiple depth | ||
195 | + // changes. | ||
196 | + parseMultiplier int | ||
197 | + // scope is true if we should also test the statement for the resolver scope | ||
198 | + // depth limit. | ||
199 | + scope bool | ||
200 | + // scopeMultiplier does the same as parseMultiplier, but for the scope | ||
201 | + // depths. | ||
202 | + scopeMultiplier int | ||
203 | +}{ | ||
204 | + // The format expands the part inside « » many times. | ||
205 | + // A second set of brackets nested inside the first stops the repetition, | ||
206 | + // so that for example «(«1»)» expands to (((...((((1))))...))). | ||
207 | + {name: "array", format: "package main; var x «[1]»int"}, | ||
208 | + {name: "slice", format: "package main; var x «[]»int"}, | ||
209 | + {name: "struct", format: "package main; var x «struct { X «int» }»", scope: true}, | ||
210 | + {name: "pointer", format: "package main; var x «*»int"}, | ||
211 | + {name: "func", format: "package main; var x «func()»int", scope: true}, | ||
212 | + {name: "chan", format: "package main; var x «chan »int"}, | ||
213 | + {name: "chan2", format: "package main; var x «<-chan »int"}, | ||
214 | + {name: "interface", format: "package main; var x «interface { M() «int» }»", scope: true, scopeMultiplier: 2}, // Scopes: InterfaceType, FuncType | ||
215 | + {name: "map", format: "package main; var x «map[int]»int"}, | ||
216 | + {name: "slicelit", format: "package main; var x = «[]any{«»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit | ||
217 | + {name: "arraylit", format: "package main; var x = «[1]any{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit | ||
218 | + {name: "structlit", format: "package main; var x = «struct{x any}{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit | ||
219 | + {name: "maplit", format: "package main; var x = «map[int]any{1:«nil»}»", parseMultiplier: 2}, // Parser nodes: CompositeLit, KeyValueExpr | ||
220 | + {name: "dot", format: "package main; var x = «x.»x"}, | ||
221 | + {name: "index", format: "package main; var x = x«[1]»"}, | ||
222 | + {name: "slice", format: "package main; var x = x«[1:2]»"}, | ||
223 | + {name: "slice3", format: "package main; var x = x«[1:2:3]»"}, | ||
224 | + {name: "dottype", format: "package main; var x = x«.(any)»"}, | ||
225 | + {name: "callseq", format: "package main; var x = x«()»"}, | ||
226 | + {name: "methseq", format: "package main; var x = x«.m()»", parseMultiplier: 2}, // Parser nodes: SelectorExpr, CallExpr | ||
227 | + {name: "binary", format: "package main; var x = «1+»1"}, | ||
228 | + {name: "binaryparen", format: "package main; var x = «1+(«1»)»", parseMultiplier: 2}, // Parser nodes: BinaryExpr, ParenExpr | ||
229 | + {name: "unary", format: "package main; var x = «^»1"}, | ||
230 | + {name: "addr", format: "package main; var x = «& »x"}, | ||
231 | + {name: "star", format: "package main; var x = «*»x"}, | ||
232 | + {name: "recv", format: "package main; var x = «<-»x"}, | ||
233 | + {name: "call", format: "package main; var x = «f(«1»)»", parseMultiplier: 2}, // Parser nodes: Ident, CallExpr | ||
234 | + {name: "conv", format: "package main; var x = «(*T)(«1»)»", parseMultiplier: 2}, // Parser nodes: ParenExpr, CallExpr | ||
235 | + {name: "label", format: "package main; func main() { «Label:» }"}, | ||
236 | + {name: "if", format: "package main; func main() { «if true { «» }»}", parseMultiplier: 2, scope: true, scopeMultiplier: 2}, // Parser nodes: IfStmt, BlockStmt. Scopes: IfStmt, BlockStmt | ||
237 | + {name: "ifelse", format: "package main; func main() { «if true {} else » {} }", scope: true}, | ||
238 | + {name: "switch", format: "package main; func main() { «switch { default: «» }»}", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause | ||
239 | + {name: "typeswitch", format: "package main; func main() { «switch x.(type) { default: «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause | ||
240 | + {name: "for0", format: "package main; func main() { «for { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt | ||
241 | + {name: "for1", format: "package main; func main() { «for x { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt | ||
242 | + {name: "for3", format: "package main; func main() { «for f(); g(); h() { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt | ||
243 | + {name: "forrange0", format: "package main; func main() { «for range x { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt | ||
244 | + {name: "forrange1", format: "package main; func main() { «for x = range z { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt | ||
245 | + {name: "forrange2", format: "package main; func main() { «for x, y = range z { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt | ||
246 | + {name: "go", format: "package main; func main() { «go func() { «» }()» }", parseMultiplier: 2, scope: true}, // Parser nodes: GoStmt, FuncLit | ||
247 | + {name: "defer", format: "package main; func main() { «defer func() { «» }()» }", parseMultiplier: 2, scope: true}, // Parser nodes: DeferStmt, FuncLit | ||
248 | + {name: "select", format: "package main; func main() { «select { default: «» }» }", scope: true}, | ||
249 | +} | ||
250 | + | ||
251 | +// split splits pre«mid»post into pre, mid, post. | ||
252 | +// If the string does not have that form, split returns x, "", "". | ||
253 | +func split(x string) (pre, mid, post string) { | ||
254 | + start, end := strings.Index(x, "«"), strings.LastIndex(x, "»") | ||
255 | + if start < 0 || end < 0 { | ||
256 | + return x, "", "" | ||
257 | + } | ||
258 | + return x[:start], x[start+len("«") : end], x[end+len("»"):] | ||
259 | +} | ||
260 | + | ||
261 | +func TestParseDepthLimit(t *testing.T) { | ||
262 | + if runtime.GOARCH == "wasm" { | ||
263 | + t.Skip("causes call stack exhaustion on js/wasm") | ||
264 | + } | ||
265 | + for _, tt := range parseDepthTests { | ||
266 | + for _, size := range []string{"small", "big"} { | ||
267 | + t.Run(tt.name+"/"+size, func(t *testing.T) { | ||
268 | + n := maxNestLev + 1 | ||
269 | + if tt.parseMultiplier > 0 { | ||
270 | + n /= tt.parseMultiplier | ||
271 | + } | ||
272 | + if size == "small" { | ||
273 | + // Decrease the number of statements by 10, in order to check | ||
274 | + // that we do not fail when under the limit. 10 is used to | ||
275 | + // provide some wiggle room for cases where the surrounding | ||
276 | + // scaffolding syntax adds some noise to the depth that changes | ||
277 | + // on a per testcase basis. | ||
278 | + n -= 10 | ||
279 | + } | ||
280 | + | ||
281 | + pre, mid, post := split(tt.format) | ||
282 | + if strings.Contains(mid, "«") { | ||
283 | + left, base, right := split(mid) | ||
284 | + mid = strings.Repeat(left, n) + base + strings.Repeat(right, n) | ||
285 | + } else { | ||
286 | + mid = strings.Repeat(mid, n) | ||
287 | + } | ||
288 | + input := pre + mid + post | ||
289 | + | ||
290 | + fset := token.NewFileSet() | ||
291 | + _, err := ParseFile(fset, "", input, ParseComments|SkipObjectResolution) | ||
292 | + if size == "small" { | ||
293 | + if err != nil { | ||
294 | + t.Errorf("ParseFile(...): %v (want success)", err) | ||
295 | + } | ||
296 | + } else { | ||
297 | + expected := "exceeded max nesting depth" | ||
298 | + if err == nil || !strings.HasSuffix(err.Error(), expected) { | ||
299 | + t.Errorf("ParseFile(...) = _, %v, want %q", err, expected) | ||
300 | + } | ||
301 | + } | ||
302 | + }) | ||
303 | + } | ||
304 | + } | ||
305 | +} | ||
306 | + | ||
307 | +func TestScopeDepthLimit(t *testing.T) { | ||
308 | + if runtime.GOARCH == "wasm" { | ||
309 | + t.Skip("causes call stack exhaustion on js/wasm") | ||
310 | + } | ||
311 | + for _, tt := range parseDepthTests { | ||
312 | + if !tt.scope { | ||
313 | + continue | ||
314 | + } | ||
315 | + for _, size := range []string{"small", "big"} { | ||
316 | + t.Run(tt.name+"/"+size, func(t *testing.T) { | ||
317 | + n := maxScopeDepth + 1 | ||
318 | + if tt.scopeMultiplier > 0 { | ||
319 | + n /= tt.scopeMultiplier | ||
320 | + } | ||
321 | + if size == "small" { | ||
322 | + // Decrease the number of statements by 10, in order to check | ||
323 | + // that we do not fail when under the limit. 10 is used to | ||
324 | + // provide some wiggle room for cases where the surrounding | ||
325 | + // scaffolding syntax adds some noise to the depth that changes | ||
326 | + // on a per testcase basis. | ||
327 | + n -= 10 | ||
328 | + } | ||
329 | + | ||
330 | + pre, mid, post := split(tt.format) | ||
331 | + if strings.Contains(mid, "«") { | ||
332 | + left, base, right := split(mid) | ||
333 | + mid = strings.Repeat(left, n) + base + strings.Repeat(right, n) | ||
334 | + } else { | ||
335 | + mid = strings.Repeat(mid, n) | ||
336 | + } | ||
337 | + input := pre + mid + post | ||
338 | + | ||
339 | + fset := token.NewFileSet() | ||
340 | + _, err := ParseFile(fset, "", input, DeclarationErrors) | ||
341 | + if size == "small" { | ||
342 | + if err != nil { | ||
343 | + t.Errorf("ParseFile(...): %v (want success)", err) | ||
344 | + } | ||
345 | + } else { | ||
346 | + expected := "exceeded max scope depth during object resolution" | ||
347 | + if err == nil || !strings.HasSuffix(err.Error(), expected) { | ||
348 | + t.Errorf("ParseFile(...) = _, %v, want %q", err, expected) | ||
349 | + } | ||
350 | + } | ||
351 | + }) | ||
352 | + } | ||
353 | + } | ||
354 | +} | ||
355 | -- | ||
356 | 2.30.2 | ||
357 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-23772.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-23772.patch new file mode 100644 index 0000000000..f0daee3624 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-23772.patch | |||
@@ -0,0 +1,50 @@ | |||
1 | From 70882eedccac803ddcf1c3215e0ae8fd59847e39 Mon Sep 17 00:00:00 2001 | ||
2 | From: Katie Hockman <katie@golang.org> | ||
3 | Date: Sat, 26 Feb 2022 20:03:38 +0000 | ||
4 | Subject: [PATCH] [release-branch.go1.16] math/big: prevent overflow in | ||
5 | (*Rat).SetString | ||
6 | |||
7 | Credit to rsc@ for the original patch. | ||
8 | |||
9 | Thanks to the OSS-Fuzz project for discovering this | ||
10 | issue and to Emmanuel Odeke (@odeke_et) for reporting it. | ||
11 | |||
12 | Updates #50699 | ||
13 | Fixes #50700 | ||
14 | Fixes CVE-2022-23772 | ||
15 | --- | ||
16 | src/math/big/ratconv.go | 5 +++++ | ||
17 | src/math/big/ratconv_test.go | 1 + | ||
18 | 2 files changed, 6 insertions(+) | ||
19 | |||
20 | diff --git a/src/math/big/ratconv.go b/src/math/big/ratconv.go | ||
21 | index 941139e..e8cbdbe 100644 | ||
22 | --- a/src/math/big/ratconv.go | ||
23 | +++ b/src/math/big/ratconv.go | ||
24 | @@ -168,6 +168,11 @@ func (z *Rat) SetString(s string) (*Rat, bool) { | ||
25 | n := exp5 | ||
26 | if n < 0 { | ||
27 | n = -n | ||
28 | + if n < 0 { | ||
29 | + // This can occur if -n overflows. -(-1 << 63) would become | ||
30 | + // -1 << 63, which is still negative. | ||
31 | + return nil, false | ||
32 | + } | ||
33 | } | ||
34 | pow5 := z.b.abs.expNN(natFive, nat(nil).setWord(Word(n)), nil) // use underlying array of z.b.abs | ||
35 | if exp5 > 0 { | ||
36 | diff --git a/src/math/big/ratconv_test.go b/src/math/big/ratconv_test.go | ||
37 | index ba0d1ba..b820df4 100644 | ||
38 | --- a/src/math/big/ratconv_test.go | ||
39 | +++ b/src/math/big/ratconv_test.go | ||
40 | @@ -104,6 +104,7 @@ var setStringTests = []StringTest{ | ||
41 | {in: "4/3/"}, | ||
42 | {in: "4/3."}, | ||
43 | {in: "4/"}, | ||
44 | + {in: "13e-9223372036854775808"}, // CVE-2022-23772 | ||
45 | |||
46 | // valid | ||
47 | {"0", "0", true}, | ||
48 | -- | ||
49 | 2.17.1 | ||
50 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-23806.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-23806.patch new file mode 100644 index 0000000000..772acdcbf6 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-23806.patch | |||
@@ -0,0 +1,142 @@ | |||
1 | From 5b376a209d1c61e10847e062d78c4b1aa90dff0c Mon Sep 17 00:00:00 2001 | ||
2 | From: Filippo Valsorda <filippo@golang.org> | ||
3 | Date: Sat, 26 Feb 2022 10:40:57 +0000 | ||
4 | Subject: [PATCH] crypto/elliptic: make IsOnCurve return false for invalid | ||
5 | |||
6 | field elements | ||
7 | |||
8 | Updates #50974 | ||
9 | Fixes #50977 | ||
10 | Fixes CVE-2022-23806 | ||
11 | |||
12 | Signed-off-by: Minjae Kim <flowergom@gmail.com> | ||
13 | |||
14 | --- | ||
15 | src/crypto/elliptic/elliptic.go | 6 +++ | ||
16 | src/crypto/elliptic/elliptic_test.go | 81 ++++++++++++++++++++++++++++ | ||
17 | src/crypto/elliptic/p224.go | 6 +++ | ||
18 | 3 files changed, 93 insertions(+) | ||
19 | |||
20 | diff --git a/src/crypto/elliptic/elliptic.go b/src/crypto/elliptic/elliptic.go | ||
21 | index e2f71cd..bd574a4 100644 | ||
22 | --- a/src/crypto/elliptic/elliptic.go | ||
23 | +++ b/src/crypto/elliptic/elliptic.go | ||
24 | @@ -53,6 +53,12 @@ func (curve *CurveParams) Params() *CurveParams { | ||
25 | } | ||
26 | |||
27 | func (curve *CurveParams) IsOnCurve(x, y *big.Int) bool { | ||
28 | + | ||
29 | + if x.Sign() < 0 || x.Cmp(curve.P) >= 0 || | ||
30 | + y.Sign() < 0 || y.Cmp(curve.P) >= 0 { | ||
31 | + return false | ||
32 | + } | ||
33 | + | ||
34 | // y² = x³ - 3x + b | ||
35 | y2 := new(big.Int).Mul(y, y) | ||
36 | y2.Mod(y2, curve.P) | ||
37 | diff --git a/src/crypto/elliptic/elliptic_test.go b/src/crypto/elliptic/elliptic_test.go | ||
38 | index 09c5483..b13a620 100644 | ||
39 | --- a/src/crypto/elliptic/elliptic_test.go | ||
40 | +++ b/src/crypto/elliptic/elliptic_test.go | ||
41 | @@ -628,3 +628,84 @@ func TestUnmarshalToLargeCoordinates(t *testing.T) { | ||
42 | t.Errorf("Unmarshal accepts invalid Y coordinate") | ||
43 | } | ||
44 | } | ||
45 | + | ||
46 | +func testAllCurves(t *testing.T, f func(*testing.T, Curve)) { | ||
47 | + tests := []struct { | ||
48 | + name string | ||
49 | + curve Curve | ||
50 | + }{ | ||
51 | + {"P256", P256()}, | ||
52 | + {"P256/Params", P256().Params()}, | ||
53 | + {"P224", P224()}, | ||
54 | + {"P224/Params", P224().Params()}, | ||
55 | + {"P384", P384()}, | ||
56 | + {"P384/Params", P384().Params()}, | ||
57 | + {"P521", P521()}, | ||
58 | + {"P521/Params", P521().Params()}, | ||
59 | + } | ||
60 | + if testing.Short() { | ||
61 | + tests = tests[:1] | ||
62 | + } | ||
63 | + for _, test := range tests { | ||
64 | + curve := test.curve | ||
65 | + t.Run(test.name, func(t *testing.T) { | ||
66 | + t.Parallel() | ||
67 | + f(t, curve) | ||
68 | + }) | ||
69 | + } | ||
70 | +} | ||
71 | + | ||
72 | +// TestInvalidCoordinates tests big.Int values that are not valid field elements | ||
73 | +// (negative or bigger than P). They are expected to return false from | ||
74 | +// IsOnCurve, all other behavior is undefined. | ||
75 | +func TestInvalidCoordinates(t *testing.T) { | ||
76 | + testAllCurves(t, testInvalidCoordinates) | ||
77 | +} | ||
78 | + | ||
79 | +func testInvalidCoordinates(t *testing.T, curve Curve) { | ||
80 | + checkIsOnCurveFalse := func(name string, x, y *big.Int) { | ||
81 | + if curve.IsOnCurve(x, y) { | ||
82 | + t.Errorf("IsOnCurve(%s) unexpectedly returned true", name) | ||
83 | + } | ||
84 | + } | ||
85 | + | ||
86 | + p := curve.Params().P | ||
87 | + _, x, y, _ := GenerateKey(curve, rand.Reader) | ||
88 | + xx, yy := new(big.Int), new(big.Int) | ||
89 | + | ||
90 | + // Check if the sign is getting dropped. | ||
91 | + xx.Neg(x) | ||
92 | + checkIsOnCurveFalse("-x, y", xx, y) | ||
93 | + yy.Neg(y) | ||
94 | + checkIsOnCurveFalse("x, -y", x, yy) | ||
95 | + | ||
96 | + // Check if negative values are reduced modulo P. | ||
97 | + xx.Sub(x, p) | ||
98 | + checkIsOnCurveFalse("x-P, y", xx, y) | ||
99 | + yy.Sub(y, p) | ||
100 | + checkIsOnCurveFalse("x, y-P", x, yy) | ||
101 | + | ||
102 | + // Check if positive values are reduced modulo P. | ||
103 | + xx.Add(x, p) | ||
104 | + checkIsOnCurveFalse("x+P, y", xx, y) | ||
105 | + yy.Add(y, p) | ||
106 | + checkIsOnCurveFalse("x, y+P", x, yy) | ||
107 | + | ||
108 | + // Check if the overflow is dropped. | ||
109 | + xx.Add(x, new(big.Int).Lsh(big.NewInt(1), 535)) | ||
110 | + checkIsOnCurveFalse("x+2⁵³⁵, y", xx, y) | ||
111 | + yy.Add(y, new(big.Int).Lsh(big.NewInt(1), 535)) | ||
112 | + checkIsOnCurveFalse("x, y+2⁵³⁵", x, yy) | ||
113 | + | ||
114 | + // Check if P is treated like zero (if possible). | ||
115 | + // y^2 = x^3 - 3x + B | ||
116 | + // y = mod_sqrt(x^3 - 3x + B) | ||
117 | + // y = mod_sqrt(B) if x = 0 | ||
118 | + // If there is no modsqrt, there is no point with x = 0, can't test x = P. | ||
119 | + if yy := new(big.Int).ModSqrt(curve.Params().B, p); yy != nil { | ||
120 | + if !curve.IsOnCurve(big.NewInt(0), yy) { | ||
121 | + t.Fatal("(0, mod_sqrt(B)) is not on the curve?") | ||
122 | + } | ||
123 | + checkIsOnCurveFalse("P, y", p, yy) | ||
124 | + } | ||
125 | +} | ||
126 | diff --git a/src/crypto/elliptic/p224.go b/src/crypto/elliptic/p224.go | ||
127 | index 8c76021..f1bfd7e 100644 | ||
128 | --- a/src/crypto/elliptic/p224.go | ||
129 | +++ b/src/crypto/elliptic/p224.go | ||
130 | @@ -48,6 +48,12 @@ func (curve p224Curve) Params() *CurveParams { | ||
131 | } | ||
132 | |||
133 | func (curve p224Curve) IsOnCurve(bigX, bigY *big.Int) bool { | ||
134 | + | ||
135 | + if bigX.Sign() < 0 || bigX.Cmp(curve.P) >= 0 || | ||
136 | + bigY.Sign() < 0 || bigY.Cmp(curve.P) >= 0 { | ||
137 | + return false | ||
138 | + } | ||
139 | + | ||
140 | var x, y p224FieldElement | ||
141 | p224FromBig(&x, bigX) | ||
142 | p224FromBig(&y, bigY) | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-24675.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-24675.patch new file mode 100644 index 0000000000..4bc012be21 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-24675.patch | |||
@@ -0,0 +1,271 @@ | |||
1 | From 1eb931d60a24501a9668e5cb4647593e19115507 Mon Sep 17 00:00:00 2001 | ||
2 | From: Hitendra Prajapati <hprajapati@mvista.com> | ||
3 | Date: Fri, 17 Jun 2022 12:22:53 +0530 | ||
4 | Subject: [PATCH] CVE-2022-24675 | ||
5 | |||
6 | Upstream-Status: Backport [https://go-review.googlesource.com/c/go/+/399816/] | ||
7 | CVE: CVE-2022-24675 | ||
8 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
9 | --- | ||
10 | src/encoding/pem/pem.go | 174 +++++++++++++++-------------------- | ||
11 | src/encoding/pem/pem_test.go | 28 +++++- | ||
12 | 2 files changed, 101 insertions(+), 101 deletions(-) | ||
13 | |||
14 | diff --git a/src/encoding/pem/pem.go b/src/encoding/pem/pem.go | ||
15 | index a7272da..1bee1c1 100644 | ||
16 | --- a/src/encoding/pem/pem.go | ||
17 | +++ b/src/encoding/pem/pem.go | ||
18 | @@ -87,123 +87,97 @@ func Decode(data []byte) (p *Block, rest []byte) { | ||
19 | // pemStart begins with a newline. However, at the very beginning of | ||
20 | // the byte array, we'll accept the start string without it. | ||
21 | rest = data | ||
22 | - if bytes.HasPrefix(data, pemStart[1:]) { | ||
23 | - rest = rest[len(pemStart)-1 : len(data)] | ||
24 | - } else if i := bytes.Index(data, pemStart); i >= 0 { | ||
25 | - rest = rest[i+len(pemStart) : len(data)] | ||
26 | - } else { | ||
27 | - return nil, data | ||
28 | - } | ||
29 | - | ||
30 | - typeLine, rest := getLine(rest) | ||
31 | - if !bytes.HasSuffix(typeLine, pemEndOfLine) { | ||
32 | - return decodeError(data, rest) | ||
33 | - } | ||
34 | - typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)] | ||
35 | - | ||
36 | - p = &Block{ | ||
37 | - Headers: make(map[string]string), | ||
38 | - Type: string(typeLine), | ||
39 | - } | ||
40 | - | ||
41 | for { | ||
42 | - // This loop terminates because getLine's second result is | ||
43 | - // always smaller than its argument. | ||
44 | - if len(rest) == 0 { | ||
45 | + if bytes.HasPrefix(rest, pemStart[1:]) { | ||
46 | + rest = rest[len(pemStart)-1:] | ||
47 | + } else if i := bytes.Index(rest, pemStart); i >= 0 { | ||
48 | + rest = rest[i+len(pemStart) : len(rest)] | ||
49 | + } else { | ||
50 | return nil, data | ||
51 | } | ||
52 | - line, next := getLine(rest) | ||
53 | |||
54 | - i := bytes.IndexByte(line, ':') | ||
55 | - if i == -1 { | ||
56 | - break | ||
57 | + var typeLine []byte | ||
58 | + typeLine, rest = getLine(rest) | ||
59 | + if !bytes.HasSuffix(typeLine, pemEndOfLine) { | ||
60 | + continue | ||
61 | } | ||
62 | + typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)] | ||
63 | |||
64 | - // TODO(agl): need to cope with values that spread across lines. | ||
65 | - key, val := line[:i], line[i+1:] | ||
66 | - key = bytes.TrimSpace(key) | ||
67 | - val = bytes.TrimSpace(val) | ||
68 | - p.Headers[string(key)] = string(val) | ||
69 | - rest = next | ||
70 | - } | ||
71 | + p = &Block{ | ||
72 | + Headers: make(map[string]string), | ||
73 | + Type: string(typeLine), | ||
74 | + } | ||
75 | |||
76 | - var endIndex, endTrailerIndex int | ||
77 | + for { | ||
78 | + // This loop terminates because getLine's second result is | ||
79 | + // always smaller than its argument. | ||
80 | + if len(rest) == 0 { | ||
81 | + return nil, data | ||
82 | + } | ||
83 | + line, next := getLine(rest) | ||
84 | |||
85 | - // If there were no headers, the END line might occur | ||
86 | - // immediately, without a leading newline. | ||
87 | - if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) { | ||
88 | - endIndex = 0 | ||
89 | - endTrailerIndex = len(pemEnd) - 1 | ||
90 | - } else { | ||
91 | - endIndex = bytes.Index(rest, pemEnd) | ||
92 | - endTrailerIndex = endIndex + len(pemEnd) | ||
93 | - } | ||
94 | + i := bytes.IndexByte(line, ':') | ||
95 | + if i == -1 { | ||
96 | + break | ||
97 | + } | ||
98 | |||
99 | - if endIndex < 0 { | ||
100 | - return decodeError(data, rest) | ||
101 | - } | ||
102 | + // TODO(agl): need to cope with values that spread across lines. | ||
103 | + key, val := line[:i], line[i+1:] | ||
104 | + key = bytes.TrimSpace(key) | ||
105 | + val = bytes.TrimSpace(val) | ||
106 | + p.Headers[string(key)] = string(val) | ||
107 | + rest = next | ||
108 | + } | ||
109 | |||
110 | - // After the "-----" of the ending line, there should be the same type | ||
111 | - // and then a final five dashes. | ||
112 | - endTrailer := rest[endTrailerIndex:] | ||
113 | - endTrailerLen := len(typeLine) + len(pemEndOfLine) | ||
114 | - if len(endTrailer) < endTrailerLen { | ||
115 | - return decodeError(data, rest) | ||
116 | - } | ||
117 | + var endIndex, endTrailerIndex int | ||
118 | |||
119 | - restOfEndLine := endTrailer[endTrailerLen:] | ||
120 | - endTrailer = endTrailer[:endTrailerLen] | ||
121 | - if !bytes.HasPrefix(endTrailer, typeLine) || | ||
122 | - !bytes.HasSuffix(endTrailer, pemEndOfLine) { | ||
123 | - return decodeError(data, rest) | ||
124 | - } | ||
125 | + // If there were no headers, the END line might occur | ||
126 | + // immediately, without a leading newline. | ||
127 | + if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) { | ||
128 | + endIndex = 0 | ||
129 | + endTrailerIndex = len(pemEnd) - 1 | ||
130 | + } else { | ||
131 | + endIndex = bytes.Index(rest, pemEnd) | ||
132 | + endTrailerIndex = endIndex + len(pemEnd) | ||
133 | + } | ||
134 | |||
135 | - // The line must end with only whitespace. | ||
136 | - if s, _ := getLine(restOfEndLine); len(s) != 0 { | ||
137 | - return decodeError(data, rest) | ||
138 | - } | ||
139 | + if endIndex < 0 { | ||
140 | + continue | ||
141 | + } | ||
142 | |||
143 | - base64Data := removeSpacesAndTabs(rest[:endIndex]) | ||
144 | - p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data))) | ||
145 | - n, err := base64.StdEncoding.Decode(p.Bytes, base64Data) | ||
146 | - if err != nil { | ||
147 | - return decodeError(data, rest) | ||
148 | - } | ||
149 | - p.Bytes = p.Bytes[:n] | ||
150 | + // After the "-----" of the ending line, there should be the same type | ||
151 | + // and then a final five dashes. | ||
152 | + endTrailer := rest[endTrailerIndex:] | ||
153 | + endTrailerLen := len(typeLine) + len(pemEndOfLine) | ||
154 | + if len(endTrailer) < endTrailerLen { | ||
155 | + continue | ||
156 | + } | ||
157 | + | ||
158 | + restOfEndLine := endTrailer[endTrailerLen:] | ||
159 | + endTrailer = endTrailer[:endTrailerLen] | ||
160 | + if !bytes.HasPrefix(endTrailer, typeLine) || | ||
161 | + !bytes.HasSuffix(endTrailer, pemEndOfLine) { | ||
162 | + continue | ||
163 | + } | ||
164 | |||
165 | - // the -1 is because we might have only matched pemEnd without the | ||
166 | - // leading newline if the PEM block was empty. | ||
167 | - _, rest = getLine(rest[endIndex+len(pemEnd)-1:]) | ||
168 | + // The line must end with only whitespace. | ||
169 | + if s, _ := getLine(restOfEndLine); len(s) != 0 { | ||
170 | + continue | ||
171 | + } | ||
172 | |||
173 | - return | ||
174 | -} | ||
175 | + base64Data := removeSpacesAndTabs(rest[:endIndex]) | ||
176 | + p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data))) | ||
177 | + n, err := base64.StdEncoding.Decode(p.Bytes, base64Data) | ||
178 | + if err != nil { | ||
179 | + continue | ||
180 | + } | ||
181 | + p.Bytes = p.Bytes[:n] | ||
182 | |||
183 | -func decodeError(data, rest []byte) (*Block, []byte) { | ||
184 | - // If we get here then we have rejected a likely looking, but | ||
185 | - // ultimately invalid PEM block. We need to start over from a new | ||
186 | - // position. We have consumed the preamble line and will have consumed | ||
187 | - // any lines which could be header lines. However, a valid preamble | ||
188 | - // line is not a valid header line, therefore we cannot have consumed | ||
189 | - // the preamble line for the any subsequent block. Thus, we will always | ||
190 | - // find any valid block, no matter what bytes precede it. | ||
191 | - // | ||
192 | - // For example, if the input is | ||
193 | - // | ||
194 | - // -----BEGIN MALFORMED BLOCK----- | ||
195 | - // junk that may look like header lines | ||
196 | - // or data lines, but no END line | ||
197 | - // | ||
198 | - // -----BEGIN ACTUAL BLOCK----- | ||
199 | - // realdata | ||
200 | - // -----END ACTUAL BLOCK----- | ||
201 | - // | ||
202 | - // we've failed to parse using the first BEGIN line | ||
203 | - // and now will try again, using the second BEGIN line. | ||
204 | - p, rest := Decode(rest) | ||
205 | - if p == nil { | ||
206 | - rest = data | ||
207 | + // the -1 is because we might have only matched pemEnd without the | ||
208 | + // leading newline if the PEM block was empty. | ||
209 | + _, rest = getLine(rest[endIndex+len(pemEnd)-1:]) | ||
210 | + return p, rest | ||
211 | } | ||
212 | - return p, rest | ||
213 | } | ||
214 | |||
215 | const pemLineLength = 64 | ||
216 | diff --git a/src/encoding/pem/pem_test.go b/src/encoding/pem/pem_test.go | ||
217 | index 8515b46..4485581 100644 | ||
218 | --- a/src/encoding/pem/pem_test.go | ||
219 | +++ b/src/encoding/pem/pem_test.go | ||
220 | @@ -107,6 +107,12 @@ const pemMissingEndingSpace = ` | ||
221 | dGVzdA== | ||
222 | -----ENDBAR-----` | ||
223 | |||
224 | +const pemMissingEndLine = ` | ||
225 | +-----BEGIN FOO----- | ||
226 | +Header: 1` | ||
227 | + | ||
228 | +var pemRepeatingBegin = strings.Repeat("-----BEGIN \n", 10) | ||
229 | + | ||
230 | var badPEMTests = []struct { | ||
231 | name string | ||
232 | input string | ||
233 | @@ -131,14 +137,34 @@ var badPEMTests = []struct { | ||
234 | "missing ending space", | ||
235 | pemMissingEndingSpace, | ||
236 | }, | ||
237 | + { | ||
238 | + "repeating begin", | ||
239 | + pemRepeatingBegin, | ||
240 | + }, | ||
241 | + { | ||
242 | + "missing end line", | ||
243 | + pemMissingEndLine, | ||
244 | + }, | ||
245 | } | ||
246 | |||
247 | func TestBadDecode(t *testing.T) { | ||
248 | for _, test := range badPEMTests { | ||
249 | - result, _ := Decode([]byte(test.input)) | ||
250 | + result, rest := Decode([]byte(test.input)) | ||
251 | if result != nil { | ||
252 | t.Errorf("unexpected success while parsing %q", test.name) | ||
253 | } | ||
254 | + if string(rest) != test.input { | ||
255 | + t.Errorf("unexpected rest: %q; want = %q", rest, test.input) | ||
256 | + } | ||
257 | + } | ||
258 | +} | ||
259 | + | ||
260 | +func TestCVE202224675(t *testing.T) { | ||
261 | + // Prior to CVE-2022-24675, this input would cause a stack overflow. | ||
262 | + input := []byte(strings.Repeat("-----BEGIN \n", 10000000)) | ||
263 | + result, rest := Decode(input) | ||
264 | + if result != nil || !reflect.DeepEqual(rest, input) { | ||
265 | + t.Errorf("Encode of %#v decoded as %#v", input, rest) | ||
266 | } | ||
267 | } | ||
268 | |||
269 | -- | ||
270 | 2.25.1 | ||
271 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-24921.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-24921.patch new file mode 100644 index 0000000000..e4270d8a75 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-24921.patch | |||
@@ -0,0 +1,198 @@ | |||
1 | From ba99f699d26483ea1045f47c760e9be30799e311 Mon Sep 17 00:00:00 2001 | ||
2 | From: Russ Cox <rsc@golang.org> | ||
3 | Date: Wed, 2 Feb 2022 16:41:32 -0500 | ||
4 | Subject: [PATCH] regexp/syntax: reject very deeply nested regexps in Parse | ||
5 | MIME-Version: 1.0 | ||
6 | Content-Type: text/plain; charset=UTF-8 | ||
7 | Content-Transfer-Encoding: 8bit | ||
8 | |||
9 | Upstream-Status: Backport [https://github.com/golang/go/commit/2b65cde5868d8245ef8a0b8eba1e361440252d3b] | ||
10 | CVE: CVE-2022-24921 | ||
11 | Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org | ||
12 | |||
13 | |||
14 | The regexp code assumes it can recurse over the structure of | ||
15 | a regexp safely. Go's growable stacks make that reasonable | ||
16 | for all plausible regexps, but implausible ones can reach the | ||
17 | “infinite recursion?” stack limit. | ||
18 | |||
19 | This CL limits the depth of any parsed regexp to 1000. | ||
20 | That is, the depth of the parse tree is required to be ≤ 1000. | ||
21 | Regexps that require deeper parse trees will return ErrInternalError. | ||
22 | A future CL will change the error to ErrInvalidDepth, | ||
23 | but using ErrInternalError for now avoids introducing new API | ||
24 | in point releases when this is backported. | ||
25 | |||
26 | Fixes #51112. | ||
27 | Fixes #51117. | ||
28 | |||
29 | Change-Id: I97d2cd82195946eb43a4ea8561f5b95f91fb14c5 | ||
30 | Reviewed-on: https://go-review.googlesource.com/c/go/+/384616 | ||
31 | Trust: Russ Cox <rsc@golang.org> | ||
32 | Run-TryBot: Russ Cox <rsc@golang.org> | ||
33 | Reviewed-by: Ian Lance Taylor <iant@golang.org> | ||
34 | Reviewed-on: https://go-review.googlesource.com/c/go/+/384855 | ||
35 | --- | ||
36 | src/regexp/syntax/parse.go | 72 ++++++++++++++++++++++++++++++++- | ||
37 | src/regexp/syntax/parse_test.go | 7 ++++ | ||
38 | 2 files changed, 77 insertions(+), 2 deletions(-) | ||
39 | |||
40 | diff --git a/src/regexp/syntax/parse.go b/src/regexp/syntax/parse.go | ||
41 | index 8c6d43a..55bd20d 100644 | ||
42 | --- a/src/regexp/syntax/parse.go | ||
43 | +++ b/src/regexp/syntax/parse.go | ||
44 | @@ -76,13 +76,29 @@ const ( | ||
45 | opVerticalBar | ||
46 | ) | ||
47 | |||
48 | +// maxHeight is the maximum height of a regexp parse tree. | ||
49 | +// It is somewhat arbitrarily chosen, but the idea is to be large enough | ||
50 | +// that no one will actually hit in real use but at the same time small enough | ||
51 | +// that recursion on the Regexp tree will not hit the 1GB Go stack limit. | ||
52 | +// The maximum amount of stack for a single recursive frame is probably | ||
53 | +// closer to 1kB, so this could potentially be raised, but it seems unlikely | ||
54 | +// that people have regexps nested even this deeply. | ||
55 | +// We ran a test on Google's C++ code base and turned up only | ||
56 | +// a single use case with depth > 100; it had depth 128. | ||
57 | +// Using depth 1000 should be plenty of margin. | ||
58 | +// As an optimization, we don't even bother calculating heights | ||
59 | +// until we've allocated at least maxHeight Regexp structures. | ||
60 | +const maxHeight = 1000 | ||
61 | + | ||
62 | type parser struct { | ||
63 | flags Flags // parse mode flags | ||
64 | stack []*Regexp // stack of parsed expressions | ||
65 | free *Regexp | ||
66 | numCap int // number of capturing groups seen | ||
67 | wholeRegexp string | ||
68 | - tmpClass []rune // temporary char class work space | ||
69 | + tmpClass []rune // temporary char class work space | ||
70 | + numRegexp int // number of regexps allocated | ||
71 | + height map[*Regexp]int // regexp height for height limit check | ||
72 | } | ||
73 | |||
74 | func (p *parser) newRegexp(op Op) *Regexp { | ||
75 | @@ -92,16 +108,52 @@ func (p *parser) newRegexp(op Op) *Regexp { | ||
76 | *re = Regexp{} | ||
77 | } else { | ||
78 | re = new(Regexp) | ||
79 | + p.numRegexp++ | ||
80 | } | ||
81 | re.Op = op | ||
82 | return re | ||
83 | } | ||
84 | |||
85 | func (p *parser) reuse(re *Regexp) { | ||
86 | + if p.height != nil { | ||
87 | + delete(p.height, re) | ||
88 | + } | ||
89 | re.Sub0[0] = p.free | ||
90 | p.free = re | ||
91 | } | ||
92 | |||
93 | +func (p *parser) checkHeight(re *Regexp) { | ||
94 | + if p.numRegexp < maxHeight { | ||
95 | + return | ||
96 | + } | ||
97 | + if p.height == nil { | ||
98 | + p.height = make(map[*Regexp]int) | ||
99 | + for _, re := range p.stack { | ||
100 | + p.checkHeight(re) | ||
101 | + } | ||
102 | + } | ||
103 | + if p.calcHeight(re, true) > maxHeight { | ||
104 | + panic(ErrInternalError) | ||
105 | + } | ||
106 | +} | ||
107 | + | ||
108 | +func (p *parser) calcHeight(re *Regexp, force bool) int { | ||
109 | + if !force { | ||
110 | + if h, ok := p.height[re]; ok { | ||
111 | + return h | ||
112 | + } | ||
113 | + } | ||
114 | + h := 1 | ||
115 | + for _, sub := range re.Sub { | ||
116 | + hsub := p.calcHeight(sub, false) | ||
117 | + if h < 1+hsub { | ||
118 | + h = 1 + hsub | ||
119 | + } | ||
120 | + } | ||
121 | + p.height[re] = h | ||
122 | + return h | ||
123 | +} | ||
124 | + | ||
125 | // Parse stack manipulation. | ||
126 | |||
127 | // push pushes the regexp re onto the parse stack and returns the regexp. | ||
128 | @@ -137,6 +189,7 @@ func (p *parser) push(re *Regexp) *Regexp { | ||
129 | } | ||
130 | |||
131 | p.stack = append(p.stack, re) | ||
132 | + p.checkHeight(re) | ||
133 | return re | ||
134 | } | ||
135 | |||
136 | @@ -252,6 +305,7 @@ func (p *parser) repeat(op Op, min, max int, before, after, lastRepeat string) ( | ||
137 | re.Sub = re.Sub0[:1] | ||
138 | re.Sub[0] = sub | ||
139 | p.stack[n-1] = re | ||
140 | + p.checkHeight(re) | ||
141 | |||
142 | if op == OpRepeat && (min >= 2 || max >= 2) && !repeatIsValid(re, 1000) { | ||
143 | return "", &Error{ErrInvalidRepeatSize, before[:len(before)-len(after)]} | ||
144 | @@ -699,6 +753,21 @@ func literalRegexp(s string, flags Flags) *Regexp { | ||
145 | // Flags, and returns a regular expression parse tree. The syntax is | ||
146 | // described in the top-level comment. | ||
147 | func Parse(s string, flags Flags) (*Regexp, error) { | ||
148 | + return parse(s, flags) | ||
149 | +} | ||
150 | + | ||
151 | +func parse(s string, flags Flags) (_ *Regexp, err error) { | ||
152 | + defer func() { | ||
153 | + switch r := recover(); r { | ||
154 | + default: | ||
155 | + panic(r) | ||
156 | + case nil: | ||
157 | + // ok | ||
158 | + case ErrInternalError: | ||
159 | + err = &Error{Code: ErrInternalError, Expr: s} | ||
160 | + } | ||
161 | + }() | ||
162 | + | ||
163 | if flags&Literal != 0 { | ||
164 | // Trivial parser for literal string. | ||
165 | if err := checkUTF8(s); err != nil { | ||
166 | @@ -710,7 +779,6 @@ func Parse(s string, flags Flags) (*Regexp, error) { | ||
167 | // Otherwise, must do real work. | ||
168 | var ( | ||
169 | p parser | ||
170 | - err error | ||
171 | c rune | ||
172 | op Op | ||
173 | lastRepeat string | ||
174 | diff --git a/src/regexp/syntax/parse_test.go b/src/regexp/syntax/parse_test.go | ||
175 | index 5581ba1..1ef6d8a 100644 | ||
176 | --- a/src/regexp/syntax/parse_test.go | ||
177 | +++ b/src/regexp/syntax/parse_test.go | ||
178 | @@ -207,6 +207,11 @@ var parseTests = []parseTest{ | ||
179 | // Valid repetitions. | ||
180 | {`((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}))`, ``}, | ||
181 | {`((((((((((x{1}){2}){2}){2}){2}){2}){2}){2}){2}){2})`, ``}, | ||
182 | + | ||
183 | + // Valid nesting. | ||
184 | + {strings.Repeat("(", 999) + strings.Repeat(")", 999), ``}, | ||
185 | + {strings.Repeat("(?:", 999) + strings.Repeat(")*", 999), ``}, | ||
186 | + {"(" + strings.Repeat("|", 12345) + ")", ``}, // not nested at all | ||
187 | } | ||
188 | |||
189 | const testFlags = MatchNL | PerlX | UnicodeGroups | ||
190 | @@ -482,6 +487,8 @@ var invalidRegexps = []string{ | ||
191 | `a{100000}`, | ||
192 | `a{100000,}`, | ||
193 | "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})", | ||
194 | + strings.Repeat("(", 1000) + strings.Repeat(")", 1000), | ||
195 | + strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000), | ||
196 | `\Q\E*`, | ||
197 | } | ||
198 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-27664.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-27664.patch new file mode 100644 index 0000000000..238c3eac5b --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-27664.patch | |||
@@ -0,0 +1,68 @@ | |||
1 | From 48c9076dcfc2dc894842ff758c8cfae7957c9565 Mon Sep 17 00:00:00 2001 | ||
2 | From: Hitendra Prajapati <hprajapati@mvista.com> | ||
3 | Date: Thu, 29 Sep 2022 17:06:18 +0530 | ||
4 | Subject: [PATCH] CVE-2022-27664 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/5bc9106458fc07851ac324a4157132a91b1f3479] | ||
7 | CVE: CVE-2022-27664 | ||
8 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
9 | --- | ||
10 | src/net/http/h2_bundle.go | 21 +++++++++++++-------- | ||
11 | 1 file changed, 13 insertions(+), 8 deletions(-) | ||
12 | |||
13 | diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go | ||
14 | index 65d851d..83f2a72 100644 | ||
15 | --- a/src/net/http/h2_bundle.go | ||
16 | +++ b/src/net/http/h2_bundle.go | ||
17 | @@ -3254,10 +3254,11 @@ var ( | ||
18 | // name (key). See httpguts.ValidHeaderName for the base rules. | ||
19 | // | ||
20 | // Further, http2 says: | ||
21 | -// "Just as in HTTP/1.x, header field names are strings of ASCII | ||
22 | -// characters that are compared in a case-insensitive | ||
23 | -// fashion. However, header field names MUST be converted to | ||
24 | -// lowercase prior to their encoding in HTTP/2. " | ||
25 | +// | ||
26 | +// "Just as in HTTP/1.x, header field names are strings of ASCII | ||
27 | +// characters that are compared in a case-insensitive | ||
28 | +// fashion. However, header field names MUST be converted to | ||
29 | +// lowercase prior to their encoding in HTTP/2. " | ||
30 | func http2validWireHeaderFieldName(v string) bool { | ||
31 | if len(v) == 0 { | ||
32 | return false | ||
33 | @@ -3446,8 +3447,8 @@ func (s *http2sorter) SortStrings(ss []string) { | ||
34 | // validPseudoPath reports whether v is a valid :path pseudo-header | ||
35 | // value. It must be either: | ||
36 | // | ||
37 | -// *) a non-empty string starting with '/' | ||
38 | -// *) the string '*', for OPTIONS requests. | ||
39 | +// *) a non-empty string starting with '/' | ||
40 | +// *) the string '*', for OPTIONS requests. | ||
41 | // | ||
42 | // For now this is only used a quick check for deciding when to clean | ||
43 | // up Opaque URLs before sending requests from the Transport. | ||
44 | @@ -4897,6 +4898,9 @@ func (sc *http2serverConn) startGracefulShutdownInternal() { | ||
45 | func (sc *http2serverConn) goAway(code http2ErrCode) { | ||
46 | sc.serveG.check() | ||
47 | if sc.inGoAway { | ||
48 | + if sc.goAwayCode == http2ErrCodeNo { | ||
49 | + sc.goAwayCode = code | ||
50 | + } | ||
51 | return | ||
52 | } | ||
53 | sc.inGoAway = true | ||
54 | @@ -6091,8 +6095,9 @@ func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) { | ||
55 | // prior to the headers being written. If the set of trailers is fixed | ||
56 | // or known before the header is written, the normal Go trailers mechanism | ||
57 | // is preferred: | ||
58 | -// https://golang.org/pkg/net/http/#ResponseWriter | ||
59 | -// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers | ||
60 | +// | ||
61 | +// https://golang.org/pkg/net/http/#ResponseWriter | ||
62 | +// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers | ||
63 | const http2TrailerPrefix = "Trailer:" | ||
64 | |||
65 | // promoteUndeclaredTrailers permits http.Handlers to set trailers | ||
66 | -- | ||
67 | 2.25.1 | ||
68 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-28131.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-28131.patch new file mode 100644 index 0000000000..8afa292144 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-28131.patch | |||
@@ -0,0 +1,104 @@ | |||
1 | From 8136eb2e5c316a51d0da710fbd0504cbbefee526 Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <roland@golang.org> | ||
3 | Date: Mon, 28 Mar 2022 18:41:26 -0700 | ||
4 | Subject: [PATCH] encoding/xml: use iterative Skip, rather than recursive | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/58facfbe7db2fbb9afed794b281a70bdb12a60ae] | ||
7 | CVE: CVE-2022-28131 | ||
8 | Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org> | ||
9 | |||
10 | |||
11 | Prevents exhausting the stack limit in _incredibly_ deeply nested | ||
12 | structures. | ||
13 | |||
14 | Fixes #53711 | ||
15 | Updates #53614 | ||
16 | Fixes CVE-2022-28131 | ||
17 | |||
18 | Change-Id: I47db4595ce10cecc29fbd06afce7b299868599e6 | ||
19 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1419912 | ||
20 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
21 | Reviewed-by: Damien Neil <dneil@google.com> | ||
22 | (cherry picked from commit 9278cb78443d2b4deb24cbb5b61c9ba5ac688d49) | ||
23 | Reviewed-on: https://go-review.googlesource.com/c/go/+/417068 | ||
24 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
25 | Reviewed-by: Heschi Kreinick <heschi@google.com> | ||
26 | Run-TryBot: Michael Knyszek <mknyszek@google.com> | ||
27 | --- | ||
28 | src/encoding/xml/read.go | 15 ++++++++------- | ||
29 | src/encoding/xml/read_test.go | 18 ++++++++++++++++++ | ||
30 | 2 files changed, 26 insertions(+), 7 deletions(-) | ||
31 | |||
32 | diff --git a/src/encoding/xml/read.go b/src/encoding/xml/read.go | ||
33 | index 4ffed80..3fac859 100644 | ||
34 | --- a/src/encoding/xml/read.go | ||
35 | +++ b/src/encoding/xml/read.go | ||
36 | @@ -743,12 +743,12 @@ Loop: | ||
37 | } | ||
38 | |||
39 | // Skip reads tokens until it has consumed the end element | ||
40 | -// matching the most recent start element already consumed. | ||
41 | -// It recurs if it encounters a start element, so it can be used to | ||
42 | -// skip nested structures. | ||
43 | +// matching the most recent start element already consumed, | ||
44 | +// skipping nested structures. | ||
45 | // It returns nil if it finds an end element matching the start | ||
46 | // element; otherwise it returns an error describing the problem. | ||
47 | func (d *Decoder) Skip() error { | ||
48 | + var depth int64 | ||
49 | for { | ||
50 | tok, err := d.Token() | ||
51 | if err != nil { | ||
52 | @@ -756,11 +756,12 @@ func (d *Decoder) Skip() error { | ||
53 | } | ||
54 | switch tok.(type) { | ||
55 | case StartElement: | ||
56 | - if err := d.Skip(); err != nil { | ||
57 | - return err | ||
58 | - } | ||
59 | + depth++ | ||
60 | case EndElement: | ||
61 | - return nil | ||
62 | + if depth == 0 { | ||
63 | + return nil | ||
64 | + } | ||
65 | + depth-- | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | diff --git a/src/encoding/xml/read_test.go b/src/encoding/xml/read_test.go | ||
70 | index 6a20b1a..7a621a5 100644 | ||
71 | --- a/src/encoding/xml/read_test.go | ||
72 | +++ b/src/encoding/xml/read_test.go | ||
73 | @@ -5,9 +5,11 @@ | ||
74 | package xml | ||
75 | |||
76 | import ( | ||
77 | + "bytes" | ||
78 | "errors" | ||
79 | "io" | ||
80 | "reflect" | ||
81 | + "runtime" | ||
82 | "strings" | ||
83 | "testing" | ||
84 | "time" | ||
85 | @@ -1093,3 +1095,19 @@ func TestCVE202228131(t *testing.T) { | ||
86 | t.Fatalf("Unmarshal unexpected error: got %q, want %q", err, errExeceededMaxUnmarshalDepth) | ||
87 | } | ||
88 | } | ||
89 | + | ||
90 | +func TestCVE202230633(t *testing.T) { | ||
91 | + if runtime.GOARCH == "wasm" { | ||
92 | + t.Skip("causes memory exhaustion on js/wasm") | ||
93 | + } | ||
94 | + defer func() { | ||
95 | + p := recover() | ||
96 | + if p != nil { | ||
97 | + t.Fatal("Unmarshal panicked") | ||
98 | + } | ||
99 | + }() | ||
100 | + var example struct { | ||
101 | + Things []string | ||
102 | + } | ||
103 | + Unmarshal(bytes.Repeat([]byte("<a>"), 17_000_000), &example) | ||
104 | +} | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-28327.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-28327.patch new file mode 100644 index 0000000000..6361deec7d --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-28327.patch | |||
@@ -0,0 +1,36 @@ | |||
1 | From 34d9ab78568d63d8097911237897b188bdaba9c2 Mon Sep 17 00:00:00 2001 | ||
2 | From: Filippo Valsorda <filippo@golang.org> | ||
3 | Date: Thu, 31 Mar 2022 12:31:58 -0400 | ||
4 | Subject: [PATCH] crypto/elliptic: tolerate zero-padded scalars in generic | ||
5 | P-256 | ||
6 | |||
7 | Upstream-Status: Backport [https://github.com/golang/go/commit/7139e8b024604ab168b51b99c6e8168257a5bf58] | ||
8 | CVE: CVE-2022-28327 | ||
9 | Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org> | ||
10 | |||
11 | |||
12 | Updates #52075 | ||
13 | Fixes #52076 | ||
14 | Fixes CVE-2022-28327 | ||
15 | |||
16 | Change-Id: I595a7514c9a0aa1b9c76aedfc2307e1124271f27 | ||
17 | Reviewed-on: https://go-review.googlesource.com/c/go/+/397136 | ||
18 | Trust: Filippo Valsorda <filippo@golang.org> | ||
19 | Reviewed-by: Julie Qiu <julie@golang.org> | ||
20 | --- | ||
21 | src/crypto/elliptic/p256.go | 2 +- | ||
22 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
23 | |||
24 | diff --git a/src/crypto/elliptic/p256.go b/src/crypto/elliptic/p256.go | ||
25 | index c23e414..787e3e7 100644 | ||
26 | --- a/src/crypto/elliptic/p256.go | ||
27 | +++ b/src/crypto/elliptic/p256.go | ||
28 | @@ -51,7 +51,7 @@ func p256GetScalar(out *[32]byte, in []byte) { | ||
29 | n := new(big.Int).SetBytes(in) | ||
30 | var scalarBytes []byte | ||
31 | |||
32 | - if n.Cmp(p256Params.N) >= 0 { | ||
33 | + if n.Cmp(p256Params.N) >= 0 || len(in) > len(out) { | ||
34 | n.Mod(n, p256Params.N) | ||
35 | scalarBytes = n.Bytes() | ||
36 | } else { | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-2879.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-2879.patch new file mode 100644 index 0000000000..ea04a82d16 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-2879.patch | |||
@@ -0,0 +1,111 @@ | |||
1 | From 9d339f1d0f53c4116a7cb4acfa895f31a07212ee Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Fri, 2 Sep 2022 20:45:18 -0700 | ||
4 | Subject: [PATCH] archive/tar: limit size of headers | ||
5 | |||
6 | Set a 1MiB limit on special file blocks (PAX headers, GNU long names, | ||
7 | GNU link names), to avoid reading arbitrarily large amounts of data | ||
8 | into memory. | ||
9 | |||
10 | Thanks to Adam Korczynski (ADA Logics) and OSS-Fuzz for reporting | ||
11 | this issue. | ||
12 | |||
13 | Fixes CVE-2022-2879 | ||
14 | Updates #54853 | ||
15 | Fixes #55926 | ||
16 | |||
17 | Change-Id: I85136d6ff1e0af101a112190e027987ab4335680 | ||
18 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1565555 | ||
19 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
20 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
21 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
22 | (cherry picked from commit 6ee768cef6b82adf7a90dcf367a1699ef694f3b2) | ||
23 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1591053 | ||
24 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
25 | Reviewed-by: Damien Neil <dneil@google.com> | ||
26 | Reviewed-on: https://go-review.googlesource.com/c/go/+/438498 | ||
27 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
28 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
29 | Reviewed-by: Carlos Amedee <carlos@golang.org> | ||
30 | Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> | ||
31 | Run-TryBot: Carlos Amedee <carlos@golang.org> | ||
32 | |||
33 | Upstream-Status: Backport [https://github.com/golang/go/commit/0a723816cd2] | ||
34 | CVE: CVE-2022-2879 | ||
35 | Signed-off-by: Sunil Kumar <sukumar@mvista.com> | ||
36 | --- | ||
37 | src/archive/tar/format.go | 4 ++++ | ||
38 | src/archive/tar/reader.go | 14 ++++++++++++-- | ||
39 | src/archive/tar/writer.go | 3 +++ | ||
40 | 3 files changed, 19 insertions(+), 2 deletions(-) | ||
41 | |||
42 | diff --git a/src/archive/tar/format.go b/src/archive/tar/format.go | ||
43 | index cfe24a5..6642364 100644 | ||
44 | --- a/src/archive/tar/format.go | ||
45 | +++ b/src/archive/tar/format.go | ||
46 | @@ -143,6 +143,10 @@ const ( | ||
47 | blockSize = 512 // Size of each block in a tar stream | ||
48 | nameSize = 100 // Max length of the name field in USTAR format | ||
49 | prefixSize = 155 // Max length of the prefix field in USTAR format | ||
50 | + | ||
51 | + // Max length of a special file (PAX header, GNU long name or link). | ||
52 | + // This matches the limit used by libarchive. | ||
53 | + maxSpecialFileSize = 1 << 20 | ||
54 | ) | ||
55 | |||
56 | // blockPadding computes the number of bytes needed to pad offset up to the | ||
57 | diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go | ||
58 | index 4f9135b..e996595 100644 | ||
59 | --- a/src/archive/tar/reader.go | ||
60 | +++ b/src/archive/tar/reader.go | ||
61 | @@ -104,7 +104,7 @@ func (tr *Reader) next() (*Header, error) { | ||
62 | continue // This is a meta header affecting the next header | ||
63 | case TypeGNULongName, TypeGNULongLink: | ||
64 | format.mayOnlyBe(FormatGNU) | ||
65 | - realname, err := ioutil.ReadAll(tr) | ||
66 | + realname, err := readSpecialFile(tr) | ||
67 | if err != nil { | ||
68 | return nil, err | ||
69 | } | ||
70 | @@ -294,7 +294,7 @@ func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) { | ||
71 | // parsePAX parses PAX headers. | ||
72 | // If an extended header (type 'x') is invalid, ErrHeader is returned | ||
73 | func parsePAX(r io.Reader) (map[string]string, error) { | ||
74 | - buf, err := ioutil.ReadAll(r) | ||
75 | + buf, err := readSpecialFile(r) | ||
76 | if err != nil { | ||
77 | return nil, err | ||
78 | } | ||
79 | @@ -827,6 +827,16 @@ func tryReadFull(r io.Reader, b []byte) (n int, err error) { | ||
80 | return n, err | ||
81 | } | ||
82 | |||
83 | +// readSpecialFile is like ioutil.ReadAll except it returns | ||
84 | +// ErrFieldTooLong if more than maxSpecialFileSize is read. | ||
85 | +func readSpecialFile(r io.Reader) ([]byte, error) { | ||
86 | + buf, err := ioutil.ReadAll(io.LimitReader(r, maxSpecialFileSize+1)) | ||
87 | + if len(buf) > maxSpecialFileSize { | ||
88 | + return nil, ErrFieldTooLong | ||
89 | + } | ||
90 | + return buf, err | ||
91 | +} | ||
92 | + | ||
93 | // discard skips n bytes in r, reporting an error if unable to do so. | ||
94 | func discard(r io.Reader, n int64) error { | ||
95 | // If possible, Seek to the last byte before the end of the data section. | ||
96 | diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go | ||
97 | index e80498d..893eac0 100644 | ||
98 | --- a/src/archive/tar/writer.go | ||
99 | +++ b/src/archive/tar/writer.go | ||
100 | @@ -199,6 +199,9 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error { | ||
101 | flag = TypeXHeader | ||
102 | } | ||
103 | data := buf.String() | ||
104 | + if len(data) > maxSpecialFileSize { | ||
105 | + return ErrFieldTooLong | ||
106 | + } | ||
107 | if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal { | ||
108 | return err // Global headers return here | ||
109 | } | ||
110 | -- | ||
111 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-2880.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-2880.patch new file mode 100644 index 0000000000..8376dc45ba --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-2880.patch | |||
@@ -0,0 +1,164 @@ | |||
1 | From 753e3f8da191c2ac400407d83c70f46900769417 Mon Sep 17 00:00:00 2001 | ||
2 | From: Hitendra Prajapati <hprajapati@mvista.com> | ||
3 | Date: Thu, 27 Oct 2022 12:22:41 +0530 | ||
4 | Subject: [PATCH] CVE-2022-2880 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/9d2c73a9fd69e45876509bb3bdb2af99bf77da1e] | ||
7 | CVE: CVE-2022-2880 | ||
8 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
9 | |||
10 | net/http/httputil: avoid query parameter | ||
11 | |||
12 | Query parameter smuggling occurs when a proxy's interpretation | ||
13 | of query parameters differs from that of a downstream server. | ||
14 | Change ReverseProxy to avoid forwarding ignored query parameters. | ||
15 | |||
16 | Remove unparsable query parameters from the outbound request | ||
17 | |||
18 | * if req.Form != nil after calling ReverseProxy.Director; and | ||
19 | * before calling ReverseProxy.Rewrite. | ||
20 | |||
21 | This change preserves the existing behavior of forwarding the | ||
22 | raw query untouched if a Director hook does not parse the query | ||
23 | by calling Request.ParseForm (possibly indirectly). | ||
24 | --- | ||
25 | src/net/http/httputil/reverseproxy.go | 36 +++++++++++ | ||
26 | src/net/http/httputil/reverseproxy_test.go | 74 ++++++++++++++++++++++ | ||
27 | 2 files changed, 110 insertions(+) | ||
28 | |||
29 | diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go | ||
30 | index 2072a5f..c6fb873 100644 | ||
31 | --- a/src/net/http/httputil/reverseproxy.go | ||
32 | +++ b/src/net/http/httputil/reverseproxy.go | ||
33 | @@ -212,6 +212,9 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { | ||
34 | } | ||
35 | |||
36 | p.Director(outreq) | ||
37 | + if outreq.Form != nil { | ||
38 | + outreq.URL.RawQuery = cleanQueryParams(outreq.URL.RawQuery) | ||
39 | + } | ||
40 | outreq.Close = false | ||
41 | |||
42 | reqUpType := upgradeType(outreq.Header) | ||
43 | @@ -561,3 +564,36 @@ func (c switchProtocolCopier) copyToBackend(errc chan<- error) { | ||
44 | _, err := io.Copy(c.backend, c.user) | ||
45 | errc <- err | ||
46 | } | ||
47 | + | ||
48 | +func cleanQueryParams(s string) string { | ||
49 | + reencode := func(s string) string { | ||
50 | + v, _ := url.ParseQuery(s) | ||
51 | + return v.Encode() | ||
52 | + } | ||
53 | + for i := 0; i < len(s); { | ||
54 | + switch s[i] { | ||
55 | + case ';': | ||
56 | + return reencode(s) | ||
57 | + case '%': | ||
58 | + if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { | ||
59 | + return reencode(s) | ||
60 | + } | ||
61 | + i += 3 | ||
62 | + default: | ||
63 | + i++ | ||
64 | + } | ||
65 | + } | ||
66 | + return s | ||
67 | +} | ||
68 | + | ||
69 | +func ishex(c byte) bool { | ||
70 | + switch { | ||
71 | + case '0' <= c && c <= '9': | ||
72 | + return true | ||
73 | + case 'a' <= c && c <= 'f': | ||
74 | + return true | ||
75 | + case 'A' <= c && c <= 'F': | ||
76 | + return true | ||
77 | + } | ||
78 | + return false | ||
79 | +} | ||
80 | diff --git a/src/net/http/httputil/reverseproxy_test.go b/src/net/http/httputil/reverseproxy_test.go | ||
81 | index 9a7223a..bc87a3b 100644 | ||
82 | --- a/src/net/http/httputil/reverseproxy_test.go | ||
83 | +++ b/src/net/http/httputil/reverseproxy_test.go | ||
84 | @@ -1269,3 +1269,77 @@ func TestSingleJoinSlash(t *testing.T) { | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | + | ||
89 | +const ( | ||
90 | + testWantsCleanQuery = true | ||
91 | + testWantsRawQuery = false | ||
92 | +) | ||
93 | + | ||
94 | +func TestReverseProxyQueryParameterSmugglingDirectorDoesNotParseForm(t *testing.T) { | ||
95 | + testReverseProxyQueryParameterSmuggling(t, testWantsRawQuery, func(u *url.URL) *ReverseProxy { | ||
96 | + proxyHandler := NewSingleHostReverseProxy(u) | ||
97 | + oldDirector := proxyHandler.Director | ||
98 | + proxyHandler.Director = func(r *http.Request) { | ||
99 | + oldDirector(r) | ||
100 | + } | ||
101 | + return proxyHandler | ||
102 | + }) | ||
103 | +} | ||
104 | + | ||
105 | +func TestReverseProxyQueryParameterSmugglingDirectorParsesForm(t *testing.T) { | ||
106 | + testReverseProxyQueryParameterSmuggling(t, testWantsCleanQuery, func(u *url.URL) *ReverseProxy { | ||
107 | + proxyHandler := NewSingleHostReverseProxy(u) | ||
108 | + oldDirector := proxyHandler.Director | ||
109 | + proxyHandler.Director = func(r *http.Request) { | ||
110 | + // Parsing the form causes ReverseProxy to remove unparsable | ||
111 | + // query parameters before forwarding. | ||
112 | + r.FormValue("a") | ||
113 | + oldDirector(r) | ||
114 | + } | ||
115 | + return proxyHandler | ||
116 | + }) | ||
117 | +} | ||
118 | + | ||
119 | +func testReverseProxyQueryParameterSmuggling(t *testing.T, wantCleanQuery bool, newProxy func(*url.URL) *ReverseProxy) { | ||
120 | + const content = "response_content" | ||
121 | + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
122 | + w.Write([]byte(r.URL.RawQuery)) | ||
123 | + })) | ||
124 | + defer backend.Close() | ||
125 | + backendURL, err := url.Parse(backend.URL) | ||
126 | + if err != nil { | ||
127 | + t.Fatal(err) | ||
128 | + } | ||
129 | + proxyHandler := newProxy(backendURL) | ||
130 | + frontend := httptest.NewServer(proxyHandler) | ||
131 | + defer frontend.Close() | ||
132 | + | ||
133 | + // Don't spam output with logs of queries containing semicolons. | ||
134 | + backend.Config.ErrorLog = log.New(io.Discard, "", 0) | ||
135 | + frontend.Config.ErrorLog = log.New(io.Discard, "", 0) | ||
136 | + | ||
137 | + for _, test := range []struct { | ||
138 | + rawQuery string | ||
139 | + cleanQuery string | ||
140 | + }{{ | ||
141 | + rawQuery: "a=1&a=2;b=3", | ||
142 | + cleanQuery: "a=1", | ||
143 | + }, { | ||
144 | + rawQuery: "a=1&a=%zz&b=3", | ||
145 | + cleanQuery: "a=1&b=3", | ||
146 | + }} { | ||
147 | + res, err := frontend.Client().Get(frontend.URL + "?" + test.rawQuery) | ||
148 | + if err != nil { | ||
149 | + t.Fatalf("Get: %v", err) | ||
150 | + } | ||
151 | + defer res.Body.Close() | ||
152 | + body, _ := io.ReadAll(res.Body) | ||
153 | + wantQuery := test.rawQuery | ||
154 | + if wantCleanQuery { | ||
155 | + wantQuery = test.cleanQuery | ||
156 | + } | ||
157 | + if got, want := string(body), wantQuery; got != want { | ||
158 | + t.Errorf("proxy forwarded raw query %q as %q, want %q", test.rawQuery, got, want) | ||
159 | + } | ||
160 | + } | ||
161 | +} | ||
162 | -- | ||
163 | 2.25.1 | ||
164 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-30629.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-30629.patch new file mode 100644 index 0000000000..47313a547f --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-30629.patch | |||
@@ -0,0 +1,47 @@ | |||
1 | From 8d0bbb5a6280c2cf951241ec7f6579c90d38df57 Mon Sep 17 00:00:00 2001 | ||
2 | From: Hitendra Prajapati <hprajapati@mvista.com> | ||
3 | Date: Thu, 25 Aug 2022 10:55:08 +0530 | ||
4 | Subject: [PATCH] CVE-2022-30629 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/c15a8e2dbb5ac376a6ed890735341b812d6b965c] | ||
7 | CVE: CVE-2022-30629 | ||
8 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
9 | --- | ||
10 | src/crypto/tls/handshake_server_tls13.go | 14 ++++++++++++++ | ||
11 | 1 file changed, 14 insertions(+) | ||
12 | |||
13 | diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go | ||
14 | index 5432145..d91797e 100644 | ||
15 | --- a/src/crypto/tls/handshake_server_tls13.go | ||
16 | +++ b/src/crypto/tls/handshake_server_tls13.go | ||
17 | @@ -9,6 +9,7 @@ import ( | ||
18 | "crypto" | ||
19 | "crypto/hmac" | ||
20 | "crypto/rsa" | ||
21 | + "encoding/binary" | ||
22 | "errors" | ||
23 | "hash" | ||
24 | "io" | ||
25 | @@ -742,6 +743,19 @@ func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { | ||
26 | } | ||
27 | m.lifetime = uint32(maxSessionTicketLifetime / time.Second) | ||
28 | |||
29 | + // ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1 | ||
30 | + // The value is not stored anywhere; we never need to check the ticket age | ||
31 | + // because 0-RTT is not supported. | ||
32 | + ageAdd := make([]byte, 4) | ||
33 | + _, err = hs.c.config.rand().Read(ageAdd) | ||
34 | + if err != nil { | ||
35 | + return err | ||
36 | + } | ||
37 | + m.ageAdd = binary.LittleEndian.Uint32(ageAdd) | ||
38 | + | ||
39 | + // ticket_nonce, which must be unique per connection, is always left at | ||
40 | + // zero because we only ever send one ticket per connection. | ||
41 | + | ||
42 | if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil { | ||
43 | return err | ||
44 | } | ||
45 | -- | ||
46 | 2.25.1 | ||
47 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-30631.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-30631.patch new file mode 100644 index 0000000000..5dcfd27f16 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-30631.patch | |||
@@ -0,0 +1,116 @@ | |||
1 | From d10fc3a84e3344f2421c1dd3046faa50709ab4d5 Mon Sep 17 00:00:00 2001 | ||
2 | From: Hitendra Prajapati <hprajapati@mvista.com> | ||
3 | Date: Thu, 25 Aug 2022 11:01:21 +0530 | ||
4 | Subject: [PATCH] CVE-2022-30631 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/0117dee7dccbbd7803d88f65a2ce8bd686219ad3] | ||
7 | CVE: CVE-2022-30631 | ||
8 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
9 | --- | ||
10 | src/compress/gzip/gunzip.go | 60 +++++++++++++++----------------- | ||
11 | src/compress/gzip/gunzip_test.go | 16 +++++++++ | ||
12 | 2 files changed, 45 insertions(+), 31 deletions(-) | ||
13 | |||
14 | diff --git a/src/compress/gzip/gunzip.go b/src/compress/gzip/gunzip.go | ||
15 | index 924bce1..237b2b9 100644 | ||
16 | --- a/src/compress/gzip/gunzip.go | ||
17 | +++ b/src/compress/gzip/gunzip.go | ||
18 | @@ -248,42 +248,40 @@ func (z *Reader) Read(p []byte) (n int, err error) { | ||
19 | return 0, z.err | ||
20 | } | ||
21 | |||
22 | - n, z.err = z.decompressor.Read(p) | ||
23 | - z.digest = crc32.Update(z.digest, crc32.IEEETable, p[:n]) | ||
24 | - z.size += uint32(n) | ||
25 | - if z.err != io.EOF { | ||
26 | - // In the normal case we return here. | ||
27 | - return n, z.err | ||
28 | - } | ||
29 | + for n == 0 { | ||
30 | + n, z.err = z.decompressor.Read(p) | ||
31 | + z.digest = crc32.Update(z.digest, crc32.IEEETable, p[:n]) | ||
32 | + z.size += uint32(n) | ||
33 | + if z.err != io.EOF { | ||
34 | + // In the normal case we return here. | ||
35 | + return n, z.err | ||
36 | + } | ||
37 | |||
38 | - // Finished file; check checksum and size. | ||
39 | - if _, err := io.ReadFull(z.r, z.buf[:8]); err != nil { | ||
40 | - z.err = noEOF(err) | ||
41 | - return n, z.err | ||
42 | - } | ||
43 | - digest := le.Uint32(z.buf[:4]) | ||
44 | - size := le.Uint32(z.buf[4:8]) | ||
45 | - if digest != z.digest || size != z.size { | ||
46 | - z.err = ErrChecksum | ||
47 | - return n, z.err | ||
48 | - } | ||
49 | - z.digest, z.size = 0, 0 | ||
50 | + // Finished file; check checksum and size. | ||
51 | + if _, err := io.ReadFull(z.r, z.buf[:8]); err != nil { | ||
52 | + z.err = noEOF(err) | ||
53 | + return n, z.err | ||
54 | + } | ||
55 | + digest := le.Uint32(z.buf[:4]) | ||
56 | + size := le.Uint32(z.buf[4:8]) | ||
57 | + if digest != z.digest || size != z.size { | ||
58 | + z.err = ErrChecksum | ||
59 | + return n, z.err | ||
60 | + } | ||
61 | + z.digest, z.size = 0, 0 | ||
62 | |||
63 | - // File is ok; check if there is another. | ||
64 | - if !z.multistream { | ||
65 | - return n, io.EOF | ||
66 | - } | ||
67 | - z.err = nil // Remove io.EOF | ||
68 | + // File is ok; check if there is another. | ||
69 | + if !z.multistream { | ||
70 | + return n, io.EOF | ||
71 | + } | ||
72 | + z.err = nil // Remove io.EOF | ||
73 | |||
74 | - if _, z.err = z.readHeader(); z.err != nil { | ||
75 | - return n, z.err | ||
76 | + if _, z.err = z.readHeader(); z.err != nil { | ||
77 | + return n, z.err | ||
78 | + } | ||
79 | } | ||
80 | |||
81 | - // Read from next file, if necessary. | ||
82 | - if n > 0 { | ||
83 | - return n, nil | ||
84 | - } | ||
85 | - return z.Read(p) | ||
86 | + return n, nil | ||
87 | } | ||
88 | |||
89 | // Close closes the Reader. It does not close the underlying io.Reader. | ||
90 | diff --git a/src/compress/gzip/gunzip_test.go b/src/compress/gzip/gunzip_test.go | ||
91 | index 1b01404..95220ae 100644 | ||
92 | --- a/src/compress/gzip/gunzip_test.go | ||
93 | +++ b/src/compress/gzip/gunzip_test.go | ||
94 | @@ -516,3 +516,19 @@ func TestTruncatedStreams(t *testing.T) { | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | + | ||
99 | +func TestCVE202230631(t *testing.T) { | ||
100 | + var empty = []byte{0x1f, 0x8b, 0x08, 0x00, 0xa7, 0x8f, 0x43, 0x62, 0x00, | ||
101 | + 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} | ||
102 | + r := bytes.NewReader(bytes.Repeat(empty, 4e6)) | ||
103 | + z, err := NewReader(r) | ||
104 | + if err != nil { | ||
105 | + t.Fatalf("NewReader: got %v, want nil", err) | ||
106 | + } | ||
107 | + // Prior to CVE-2022-30631 fix, this would cause an unrecoverable panic due | ||
108 | + // to stack exhaustion. | ||
109 | + _, err = z.Read(make([]byte, 10)) | ||
110 | + if err != io.EOF { | ||
111 | + t.Errorf("Reader.Read: got %v, want %v", err, io.EOF) | ||
112 | + } | ||
113 | +} | ||
114 | -- | ||
115 | 2.25.1 | ||
116 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-30632.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-30632.patch new file mode 100644 index 0000000000..c54ef56a0e --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-30632.patch | |||
@@ -0,0 +1,71 @@ | |||
1 | From 35d1dfe9746029aea9027b405c75555d41ffd2f8 Mon Sep 17 00:00:00 2001 | ||
2 | From: Hitendra Prajapati <hprajapati@mvista.com> | ||
3 | Date: Thu, 25 Aug 2022 13:12:40 +0530 | ||
4 | Subject: [PATCH] CVE-2022-30632 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/76f8b7304d1f7c25834e2a0cc9e88c55276c47df] | ||
7 | CVE: CVE-2022-30632 | ||
8 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
9 | --- | ||
10 | src/path/filepath/match.go | 16 +++++++++++++++- | ||
11 | src/path/filepath/match_test.go | 10 ++++++++++ | ||
12 | 2 files changed, 25 insertions(+), 1 deletion(-) | ||
13 | |||
14 | diff --git a/src/path/filepath/match.go b/src/path/filepath/match.go | ||
15 | index 46badb5..ba68daa 100644 | ||
16 | --- a/src/path/filepath/match.go | ||
17 | +++ b/src/path/filepath/match.go | ||
18 | @@ -232,6 +232,20 @@ func getEsc(chunk string) (r rune, nchunk string, err error) { | ||
19 | // The only possible returned error is ErrBadPattern, when pattern | ||
20 | // is malformed. | ||
21 | func Glob(pattern string) (matches []string, err error) { | ||
22 | + return globWithLimit(pattern, 0) | ||
23 | +} | ||
24 | + | ||
25 | +func globWithLimit(pattern string, depth int) (matches []string, err error) { | ||
26 | + // This limit is used prevent stack exhaustion issues. See CVE-2022-30632. | ||
27 | + const pathSeparatorsLimit = 10000 | ||
28 | + if depth == pathSeparatorsLimit { | ||
29 | + return nil, ErrBadPattern | ||
30 | + } | ||
31 | + | ||
32 | + // Check pattern is well-formed. | ||
33 | + if _, err := Match(pattern, ""); err != nil { | ||
34 | + return nil, err | ||
35 | + } | ||
36 | if !hasMeta(pattern) { | ||
37 | if _, err = os.Lstat(pattern); err != nil { | ||
38 | return nil, nil | ||
39 | @@ -257,7 +271,7 @@ func Glob(pattern string) (matches []string, err error) { | ||
40 | } | ||
41 | |||
42 | var m []string | ||
43 | - m, err = Glob(dir) | ||
44 | + m, err = globWithLimit(dir, depth+1) | ||
45 | if err != nil { | ||
46 | return | ||
47 | } | ||
48 | diff --git a/src/path/filepath/match_test.go b/src/path/filepath/match_test.go | ||
49 | index b865762..c37c812 100644 | ||
50 | --- a/src/path/filepath/match_test.go | ||
51 | +++ b/src/path/filepath/match_test.go | ||
52 | @@ -154,6 +154,16 @@ func TestGlob(t *testing.T) { | ||
53 | } | ||
54 | } | ||
55 | |||
56 | +func TestCVE202230632(t *testing.T) { | ||
57 | + // Prior to CVE-2022-30632, this would cause a stack exhaustion given a | ||
58 | + // large number of separators (more than 4,000,000). There is now a limit | ||
59 | + // of 10,000. | ||
60 | + _, err := Glob("/*" + strings.Repeat("/", 10001)) | ||
61 | + if err != ErrBadPattern { | ||
62 | + t.Fatalf("Glob returned err=%v, want ErrBadPattern", err) | ||
63 | + } | ||
64 | +} | ||
65 | + | ||
66 | func TestGlobError(t *testing.T) { | ||
67 | _, err := Glob("[]") | ||
68 | if err == nil { | ||
69 | -- | ||
70 | 2.25.1 | ||
71 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-30633.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-30633.patch new file mode 100644 index 0000000000..c16cb5f50c --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-30633.patch | |||
@@ -0,0 +1,131 @@ | |||
1 | From ab6e2ffdcab0501bcc2de4b196c1c18ae2301d4b Mon Sep 17 00:00:00 2001 | ||
2 | From: Hitendra Prajapati <hprajapati@mvista.com> | ||
3 | Date: Thu, 25 Aug 2022 13:29:55 +0530 | ||
4 | Subject: [PATCH] CVE-2022-30633 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/2678d0c957193dceef336c969a9da74dd716a827] | ||
7 | CVE: CVE-2022-30633 | ||
8 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
9 | --- | ||
10 | src/encoding/xml/read.go | 27 +++++++++++++++++++-------- | ||
11 | src/encoding/xml/read_test.go | 14 ++++++++++++++ | ||
12 | 2 files changed, 33 insertions(+), 8 deletions(-) | ||
13 | |||
14 | diff --git a/src/encoding/xml/read.go b/src/encoding/xml/read.go | ||
15 | index 10a60ee..4ffed80 100644 | ||
16 | --- a/src/encoding/xml/read.go | ||
17 | +++ b/src/encoding/xml/read.go | ||
18 | @@ -148,7 +148,7 @@ func (d *Decoder) DecodeElement(v interface{}, start *StartElement) error { | ||
19 | if val.Kind() != reflect.Ptr { | ||
20 | return errors.New("non-pointer passed to Unmarshal") | ||
21 | } | ||
22 | - return d.unmarshal(val.Elem(), start) | ||
23 | + return d.unmarshal(val.Elem(), start, 0) | ||
24 | } | ||
25 | |||
26 | // An UnmarshalError represents an error in the unmarshaling process. | ||
27 | @@ -304,8 +304,15 @@ var ( | ||
28 | textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() | ||
29 | ) | ||
30 | |||
31 | +const maxUnmarshalDepth = 10000 | ||
32 | + | ||
33 | +var errExeceededMaxUnmarshalDepth = errors.New("exceeded max depth") | ||
34 | + | ||
35 | // Unmarshal a single XML element into val. | ||
36 | -func (d *Decoder) unmarshal(val reflect.Value, start *StartElement) error { | ||
37 | +func (d *Decoder) unmarshal(val reflect.Value, start *StartElement, depth int) error { | ||
38 | + if depth >= maxUnmarshalDepth { | ||
39 | + return errExeceededMaxUnmarshalDepth | ||
40 | + } | ||
41 | // Find start element if we need it. | ||
42 | if start == nil { | ||
43 | for { | ||
44 | @@ -398,7 +405,7 @@ func (d *Decoder) unmarshal(val reflect.Value, start *StartElement) error { | ||
45 | v.Set(reflect.Append(val, reflect.Zero(v.Type().Elem()))) | ||
46 | |||
47 | // Recur to read element into slice. | ||
48 | - if err := d.unmarshal(v.Index(n), start); err != nil { | ||
49 | + if err := d.unmarshal(v.Index(n), start, depth+1); err != nil { | ||
50 | v.SetLen(n) | ||
51 | return err | ||
52 | } | ||
53 | @@ -521,13 +528,15 @@ Loop: | ||
54 | case StartElement: | ||
55 | consumed := false | ||
56 | if sv.IsValid() { | ||
57 | - consumed, err = d.unmarshalPath(tinfo, sv, nil, &t) | ||
58 | + // unmarshalPath can call unmarshal, so we need to pass the depth through so that | ||
59 | + // we can continue to enforce the maximum recusion limit. | ||
60 | + consumed, err = d.unmarshalPath(tinfo, sv, nil, &t, depth) | ||
61 | if err != nil { | ||
62 | return err | ||
63 | } | ||
64 | if !consumed && saveAny.IsValid() { | ||
65 | consumed = true | ||
66 | - if err := d.unmarshal(saveAny, &t); err != nil { | ||
67 | + if err := d.unmarshal(saveAny, &t, depth+1); err != nil { | ||
68 | return err | ||
69 | } | ||
70 | } | ||
71 | @@ -672,7 +681,7 @@ func copyValue(dst reflect.Value, src []byte) (err error) { | ||
72 | // The consumed result tells whether XML elements have been consumed | ||
73 | // from the Decoder until start's matching end element, or if it's | ||
74 | // still untouched because start is uninteresting for sv's fields. | ||
75 | -func (d *Decoder) unmarshalPath(tinfo *typeInfo, sv reflect.Value, parents []string, start *StartElement) (consumed bool, err error) { | ||
76 | +func (d *Decoder) unmarshalPath(tinfo *typeInfo, sv reflect.Value, parents []string, start *StartElement, depth int) (consumed bool, err error) { | ||
77 | recurse := false | ||
78 | Loop: | ||
79 | for i := range tinfo.fields { | ||
80 | @@ -687,7 +696,7 @@ Loop: | ||
81 | } | ||
82 | if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local { | ||
83 | // It's a perfect match, unmarshal the field. | ||
84 | - return true, d.unmarshal(finfo.value(sv), start) | ||
85 | + return true, d.unmarshal(finfo.value(sv), start, depth+1) | ||
86 | } | ||
87 | if len(finfo.parents) > len(parents) && finfo.parents[len(parents)] == start.Name.Local { | ||
88 | // It's a prefix for the field. Break and recurse | ||
89 | @@ -716,7 +725,9 @@ Loop: | ||
90 | } | ||
91 | switch t := tok.(type) { | ||
92 | case StartElement: | ||
93 | - consumed2, err := d.unmarshalPath(tinfo, sv, parents, &t) | ||
94 | + // the recursion depth of unmarshalPath is limited to the path length specified | ||
95 | + // by the struct field tag, so we don't increment the depth here. | ||
96 | + consumed2, err := d.unmarshalPath(tinfo, sv, parents, &t, depth) | ||
97 | if err != nil { | ||
98 | return true, err | ||
99 | } | ||
100 | diff --git a/src/encoding/xml/read_test.go b/src/encoding/xml/read_test.go | ||
101 | index 8c2e70f..6a20b1a 100644 | ||
102 | --- a/src/encoding/xml/read_test.go | ||
103 | +++ b/src/encoding/xml/read_test.go | ||
104 | @@ -5,6 +5,7 @@ | ||
105 | package xml | ||
106 | |||
107 | import ( | ||
108 | + "errors" | ||
109 | "io" | ||
110 | "reflect" | ||
111 | "strings" | ||
112 | @@ -1079,3 +1080,16 @@ func TestUnmarshalWhitespaceAttrs(t *testing.T) { | ||
113 | t.Fatalf("whitespace attrs: Unmarshal:\nhave: %#+v\nwant: %#+v", v, want) | ||
114 | } | ||
115 | } | ||
116 | + | ||
117 | +func TestCVE202228131(t *testing.T) { | ||
118 | + type nested struct { | ||
119 | + Parent *nested `xml:",any"` | ||
120 | + } | ||
121 | + var n nested | ||
122 | + err := Unmarshal(bytes.Repeat([]byte("<a>"), maxUnmarshalDepth+1), &n) | ||
123 | + if err == nil { | ||
124 | + t.Fatal("Unmarshal did not fail") | ||
125 | + } else if !errors.Is(err, errExeceededMaxUnmarshalDepth) { | ||
126 | + t.Fatalf("Unmarshal unexpected error: got %q, want %q", err, errExeceededMaxUnmarshalDepth) | ||
127 | + } | ||
128 | +} | ||
129 | -- | ||
130 | 2.25.1 | ||
131 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-30635.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-30635.patch new file mode 100644 index 0000000000..73959f70fa --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-30635.patch | |||
@@ -0,0 +1,120 @@ | |||
1 | From fdd4316737ed5681689a1f40802ffa0805e5b11c Mon Sep 17 00:00:00 2001 | ||
2 | From: Hitendra Prajapati <hprajapati@mvista.com> | ||
3 | Date: Fri, 26 Aug 2022 12:17:05 +0530 | ||
4 | Subject: [PATCH] CVE-2022-30635 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/cd54600b866db0ad068ab8df06c7f5f6cb55c9b3] | ||
7 | CVE-2022-30635 | ||
8 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
9 | --- | ||
10 | src/encoding/gob/decode.go | 19 ++++++++++++------- | ||
11 | src/encoding/gob/gobencdec_test.go | 24 ++++++++++++++++++++++++ | ||
12 | 2 files changed, 36 insertions(+), 7 deletions(-) | ||
13 | |||
14 | diff --git a/src/encoding/gob/decode.go b/src/encoding/gob/decode.go | ||
15 | index d2f6c74..0e0ec75 100644 | ||
16 | --- a/src/encoding/gob/decode.go | ||
17 | +++ b/src/encoding/gob/decode.go | ||
18 | @@ -871,8 +871,13 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg | ||
19 | return &op | ||
20 | } | ||
21 | |||
22 | +var maxIgnoreNestingDepth = 10000 | ||
23 | + | ||
24 | // decIgnoreOpFor returns the decoding op for a field that has no destination. | ||
25 | -func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp) *decOp { | ||
26 | +func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp, depth int) *decOp { | ||
27 | + if depth > maxIgnoreNestingDepth { | ||
28 | + error_(errors.New("invalid nesting depth")) | ||
29 | + } | ||
30 | // If this type is already in progress, it's a recursive type (e.g. map[string]*T). | ||
31 | // Return the pointer to the op we're already building. | ||
32 | if opPtr := inProgress[wireId]; opPtr != nil { | ||
33 | @@ -896,7 +901,7 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp) | ||
34 | errorf("bad data: undefined type %s", wireId.string()) | ||
35 | case wire.ArrayT != nil: | ||
36 | elemId := wire.ArrayT.Elem | ||
37 | - elemOp := dec.decIgnoreOpFor(elemId, inProgress) | ||
38 | + elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1) | ||
39 | op = func(i *decInstr, state *decoderState, value reflect.Value) { | ||
40 | state.dec.ignoreArray(state, *elemOp, wire.ArrayT.Len) | ||
41 | } | ||
42 | @@ -904,15 +909,15 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp) | ||
43 | case wire.MapT != nil: | ||
44 | keyId := dec.wireType[wireId].MapT.Key | ||
45 | elemId := dec.wireType[wireId].MapT.Elem | ||
46 | - keyOp := dec.decIgnoreOpFor(keyId, inProgress) | ||
47 | - elemOp := dec.decIgnoreOpFor(elemId, inProgress) | ||
48 | + keyOp := dec.decIgnoreOpFor(keyId, inProgress, depth+1) | ||
49 | + elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1) | ||
50 | op = func(i *decInstr, state *decoderState, value reflect.Value) { | ||
51 | state.dec.ignoreMap(state, *keyOp, *elemOp) | ||
52 | } | ||
53 | |||
54 | case wire.SliceT != nil: | ||
55 | elemId := wire.SliceT.Elem | ||
56 | - elemOp := dec.decIgnoreOpFor(elemId, inProgress) | ||
57 | + elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1) | ||
58 | op = func(i *decInstr, state *decoderState, value reflect.Value) { | ||
59 | state.dec.ignoreSlice(state, *elemOp) | ||
60 | } | ||
61 | @@ -1073,7 +1078,7 @@ func (dec *Decoder) compileSingle(remoteId typeId, ut *userTypeInfo) (engine *de | ||
62 | func (dec *Decoder) compileIgnoreSingle(remoteId typeId) *decEngine { | ||
63 | engine := new(decEngine) | ||
64 | engine.instr = make([]decInstr, 1) // one item | ||
65 | - op := dec.decIgnoreOpFor(remoteId, make(map[typeId]*decOp)) | ||
66 | + op := dec.decIgnoreOpFor(remoteId, make(map[typeId]*decOp), 0) | ||
67 | ovfl := overflow(dec.typeString(remoteId)) | ||
68 | engine.instr[0] = decInstr{*op, 0, nil, ovfl} | ||
69 | engine.numInstr = 1 | ||
70 | @@ -1118,7 +1123,7 @@ func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEn | ||
71 | localField, present := srt.FieldByName(wireField.Name) | ||
72 | // TODO(r): anonymous names | ||
73 | if !present || !isExported(wireField.Name) { | ||
74 | - op := dec.decIgnoreOpFor(wireField.Id, make(map[typeId]*decOp)) | ||
75 | + op := dec.decIgnoreOpFor(wireField.Id, make(map[typeId]*decOp), 0) | ||
76 | engine.instr[fieldnum] = decInstr{*op, fieldnum, nil, ovfl} | ||
77 | continue | ||
78 | } | ||
79 | diff --git a/src/encoding/gob/gobencdec_test.go b/src/encoding/gob/gobencdec_test.go | ||
80 | index 6d2c8db..1b52ecc 100644 | ||
81 | --- a/src/encoding/gob/gobencdec_test.go | ||
82 | +++ b/src/encoding/gob/gobencdec_test.go | ||
83 | @@ -12,6 +12,7 @@ import ( | ||
84 | "fmt" | ||
85 | "io" | ||
86 | "net" | ||
87 | + "reflect" | ||
88 | "strings" | ||
89 | "testing" | ||
90 | "time" | ||
91 | @@ -796,3 +797,26 @@ func TestNetIP(t *testing.T) { | ||
92 | t.Errorf("decoded to %v, want 1.2.3.4", ip.String()) | ||
93 | } | ||
94 | } | ||
95 | + | ||
96 | +func TestIngoreDepthLimit(t *testing.T) { | ||
97 | + // We don't test the actual depth limit because it requires building an | ||
98 | + // extremely large message, which takes quite a while. | ||
99 | + oldNestingDepth := maxIgnoreNestingDepth | ||
100 | + maxIgnoreNestingDepth = 100 | ||
101 | + defer func() { maxIgnoreNestingDepth = oldNestingDepth }() | ||
102 | + b := new(bytes.Buffer) | ||
103 | + enc := NewEncoder(b) | ||
104 | + typ := reflect.TypeOf(int(0)) | ||
105 | + nested := reflect.ArrayOf(1, typ) | ||
106 | + for i := 0; i < 100; i++ { | ||
107 | + nested = reflect.ArrayOf(1, nested) | ||
108 | + } | ||
109 | + badStruct := reflect.New(reflect.StructOf([]reflect.StructField{{Name: "F", Type: nested}})) | ||
110 | + enc.Encode(badStruct.Interface()) | ||
111 | + dec := NewDecoder(b) | ||
112 | + var output struct{ Hello int } | ||
113 | + expectedErr := "invalid nesting depth" | ||
114 | + if err := dec.Decode(&output); err == nil || err.Error() != expectedErr { | ||
115 | + t.Errorf("Decode didn't fail with depth limit of 100: want %q, got %q", expectedErr, err) | ||
116 | + } | ||
117 | +} | ||
118 | -- | ||
119 | 2.25.1 | ||
120 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-32148.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-32148.patch new file mode 100644 index 0000000000..aab98e99fd --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-32148.patch | |||
@@ -0,0 +1,49 @@ | |||
1 | From 0fe3adec199e8cd2c101933f75d8cd617de70350 Mon Sep 17 00:00:00 2001 | ||
2 | From: Hitendra Prajapati <hprajapati@mvista.com> | ||
3 | Date: Fri, 26 Aug 2022 12:48:13 +0530 | ||
4 | Subject: [PATCH] CVE-2022-32148 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/ed2f33e1a7e0d18f61bd56f7ee067331d612c27e] | ||
7 | CVE: CVE-2022-32148 | ||
8 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
9 | --- | ||
10 | src/net/http/header.go | 6 ++++++ | ||
11 | src/net/http/header_test.go | 5 +++++ | ||
12 | 2 files changed, 11 insertions(+) | ||
13 | |||
14 | diff --git a/src/net/http/header.go b/src/net/http/header.go | ||
15 | index b9b5391..221f613 100644 | ||
16 | --- a/src/net/http/header.go | ||
17 | +++ b/src/net/http/header.go | ||
18 | @@ -100,6 +100,12 @@ func (h Header) Clone() Header { | ||
19 | sv := make([]string, nv) // shared backing array for headers' values | ||
20 | h2 := make(Header, len(h)) | ||
21 | for k, vv := range h { | ||
22 | + if vv == nil { | ||
23 | + // Preserve nil values. ReverseProxy distinguishes | ||
24 | + // between nil and zero-length header values. | ||
25 | + h2[k] = nil | ||
26 | + continue | ||
27 | + } | ||
28 | n := copy(sv, vv) | ||
29 | h2[k] = sv[:n:n] | ||
30 | sv = sv[n:] | ||
31 | diff --git a/src/net/http/header_test.go b/src/net/http/header_test.go | ||
32 | index 4789362..80c0035 100644 | ||
33 | --- a/src/net/http/header_test.go | ||
34 | +++ b/src/net/http/header_test.go | ||
35 | @@ -235,6 +235,11 @@ func TestCloneOrMakeHeader(t *testing.T) { | ||
36 | in: Header{"foo": {"bar"}}, | ||
37 | want: Header{"foo": {"bar"}}, | ||
38 | }, | ||
39 | + { | ||
40 | + name: "nil value", | ||
41 | + in: Header{"foo": nil}, | ||
42 | + want: Header{"foo": nil}, | ||
43 | + }, | ||
44 | } | ||
45 | |||
46 | for _, tt := range tests { | ||
47 | -- | ||
48 | 2.25.1 | ||
49 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-32189.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-32189.patch new file mode 100644 index 0000000000..15fda7de1b --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-32189.patch | |||
@@ -0,0 +1,113 @@ | |||
1 | From 027e7e1578d3d7614f7586eff3894b83d9709e14 Mon Sep 17 00:00:00 2001 | ||
2 | From: Hitendra Prajapati <hprajapati@mvista.com> | ||
3 | Date: Mon, 29 Aug 2022 10:08:34 +0530 | ||
4 | Subject: [PATCH] CVE-2022-32189 | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/golang/go/commit/703c8ab7e5ba75c95553d4e249309297abad7102] | ||
7 | CVE: CVE-2022-32189 | ||
8 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
9 | --- | ||
10 | src/math/big/floatmarsh.go | 7 +++++++ | ||
11 | src/math/big/floatmarsh_test.go | 12 ++++++++++++ | ||
12 | src/math/big/ratmarsh.go | 6 ++++++ | ||
13 | src/math/big/ratmarsh_test.go | 12 ++++++++++++ | ||
14 | 4 files changed, 37 insertions(+) | ||
15 | |||
16 | diff --git a/src/math/big/floatmarsh.go b/src/math/big/floatmarsh.go | ||
17 | index d1c1dab..990e085 100644 | ||
18 | --- a/src/math/big/floatmarsh.go | ||
19 | +++ b/src/math/big/floatmarsh.go | ||
20 | @@ -8,6 +8,7 @@ package big | ||
21 | |||
22 | import ( | ||
23 | "encoding/binary" | ||
24 | + "errors" | ||
25 | "fmt" | ||
26 | ) | ||
27 | |||
28 | @@ -67,6 +68,9 @@ func (z *Float) GobDecode(buf []byte) error { | ||
29 | *z = Float{} | ||
30 | return nil | ||
31 | } | ||
32 | + if len(buf) < 6 { | ||
33 | + return errors.New("Float.GobDecode: buffer too small") | ||
34 | + } | ||
35 | |||
36 | if buf[0] != floatGobVersion { | ||
37 | return fmt.Errorf("Float.GobDecode: encoding version %d not supported", buf[0]) | ||
38 | @@ -83,6 +87,9 @@ func (z *Float) GobDecode(buf []byte) error { | ||
39 | z.prec = binary.BigEndian.Uint32(buf[2:]) | ||
40 | |||
41 | if z.form == finite { | ||
42 | + if len(buf) < 10 { | ||
43 | + return errors.New("Float.GobDecode: buffer too small for finite form float") | ||
44 | + } | ||
45 | z.exp = int32(binary.BigEndian.Uint32(buf[6:])) | ||
46 | z.mant = z.mant.setBytes(buf[10:]) | ||
47 | } | ||
48 | diff --git a/src/math/big/floatmarsh_test.go b/src/math/big/floatmarsh_test.go | ||
49 | index c056d78..401f45a 100644 | ||
50 | --- a/src/math/big/floatmarsh_test.go | ||
51 | +++ b/src/math/big/floatmarsh_test.go | ||
52 | @@ -137,3 +137,15 @@ func TestFloatJSONEncoding(t *testing.T) { | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | + | ||
57 | +func TestFloatGobDecodeShortBuffer(t *testing.T) { | ||
58 | + for _, tc := range [][]byte{ | ||
59 | + []byte{0x1, 0x0, 0x0, 0x0}, | ||
60 | + []byte{0x1, 0xfa, 0x0, 0x0, 0x0, 0x0}, | ||
61 | + } { | ||
62 | + err := NewFloat(0).GobDecode(tc) | ||
63 | + if err == nil { | ||
64 | + t.Error("expected GobDecode to return error for malformed input") | ||
65 | + } | ||
66 | + } | ||
67 | +} | ||
68 | diff --git a/src/math/big/ratmarsh.go b/src/math/big/ratmarsh.go | ||
69 | index fbc7b60..56102e8 100644 | ||
70 | --- a/src/math/big/ratmarsh.go | ||
71 | +++ b/src/math/big/ratmarsh.go | ||
72 | @@ -45,12 +45,18 @@ func (z *Rat) GobDecode(buf []byte) error { | ||
73 | *z = Rat{} | ||
74 | return nil | ||
75 | } | ||
76 | + if len(buf) < 5 { | ||
77 | + return errors.New("Rat.GobDecode: buffer too small") | ||
78 | + } | ||
79 | b := buf[0] | ||
80 | if b>>1 != ratGobVersion { | ||
81 | return fmt.Errorf("Rat.GobDecode: encoding version %d not supported", b>>1) | ||
82 | } | ||
83 | const j = 1 + 4 | ||
84 | i := j + binary.BigEndian.Uint32(buf[j-4:j]) | ||
85 | + if len(buf) < int(i) { | ||
86 | + return errors.New("Rat.GobDecode: buffer too small") | ||
87 | + } | ||
88 | z.a.neg = b&1 != 0 | ||
89 | z.a.abs = z.a.abs.setBytes(buf[j:i]) | ||
90 | z.b.abs = z.b.abs.setBytes(buf[i:]) | ||
91 | diff --git a/src/math/big/ratmarsh_test.go b/src/math/big/ratmarsh_test.go | ||
92 | index 351d109..55a9878 100644 | ||
93 | --- a/src/math/big/ratmarsh_test.go | ||
94 | +++ b/src/math/big/ratmarsh_test.go | ||
95 | @@ -123,3 +123,15 @@ func TestRatXMLEncoding(t *testing.T) { | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | + | ||
100 | +func TestRatGobDecodeShortBuffer(t *testing.T) { | ||
101 | + for _, tc := range [][]byte{ | ||
102 | + []byte{0x2}, | ||
103 | + []byte{0x2, 0x0, 0x0, 0x0, 0xff}, | ||
104 | + } { | ||
105 | + err := NewRat(1, 2).GobDecode(tc) | ||
106 | + if err == nil { | ||
107 | + t.Error("expected GobDecode to return error for malformed input") | ||
108 | + } | ||
109 | + } | ||
110 | +} | ||
111 | -- | ||
112 | 2.25.1 | ||
113 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41715.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41715.patch new file mode 100644 index 0000000000..fac0ebe94c --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41715.patch | |||
@@ -0,0 +1,271 @@ | |||
1 | From e9017c2416ad0ef642f5e0c2eab2dbf3cba4d997 Mon Sep 17 00:00:00 2001 | ||
2 | From: Russ Cox <rsc@golang.org> | ||
3 | Date: Wed, 28 Sep 2022 11:18:51 -0400 | ||
4 | Subject: [PATCH] [release-branch.go1.18] regexp: limit size of parsed regexps | ||
5 | |||
6 | Set a 128 MB limit on the amount of space used by []syntax.Inst | ||
7 | in the compiled form corresponding to a given regexp. | ||
8 | |||
9 | Also set a 128 MB limit on the rune storage in the *syntax.Regexp | ||
10 | tree itself. | ||
11 | |||
12 | Thanks to Adam Korczynski (ADA Logics) and OSS-Fuzz for reporting this issue. | ||
13 | |||
14 | Fixes CVE-2022-41715. | ||
15 | Updates #55949. | ||
16 | Fixes #55950. | ||
17 | |||
18 | Change-Id: Ia656baed81564436368cf950e1c5409752f28e1b | ||
19 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1592136 | ||
20 | TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> | ||
21 | Reviewed-by: Damien Neil <dneil@google.com> | ||
22 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
23 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
24 | Reviewed-on: https://go-review.googlesource.com/c/go/+/438501 | ||
25 | Run-TryBot: Carlos Amedee <carlos@golang.org> | ||
26 | Reviewed-by: Carlos Amedee <carlos@golang.org> | ||
27 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
28 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
29 | Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> | ||
30 | |||
31 | Upstream-Status: Backport [https://github.com/golang/go/commit/e9017c2416ad0ef642f5e0c2eab2dbf3cba4d997] | ||
32 | CVE: CVE-2022-41715 | ||
33 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
34 | |||
35 | --- | ||
36 | src/regexp/syntax/parse.go | 145 ++++++++++++++++++++++++++++++-- | ||
37 | src/regexp/syntax/parse_test.go | 13 +-- | ||
38 | 2 files changed, 148 insertions(+), 10 deletions(-) | ||
39 | |||
40 | diff --git a/src/regexp/syntax/parse.go b/src/regexp/syntax/parse.go | ||
41 | index 55bd20d..60491d5 100644 | ||
42 | --- a/src/regexp/syntax/parse.go | ||
43 | +++ b/src/regexp/syntax/parse.go | ||
44 | @@ -90,15 +90,49 @@ const ( | ||
45 | // until we've allocated at least maxHeight Regexp structures. | ||
46 | const maxHeight = 1000 | ||
47 | |||
48 | +// maxSize is the maximum size of a compiled regexp in Insts. | ||
49 | +// It too is somewhat arbitrarily chosen, but the idea is to be large enough | ||
50 | +// to allow significant regexps while at the same time small enough that | ||
51 | +// the compiled form will not take up too much memory. | ||
52 | +// 128 MB is enough for a 3.3 million Inst structures, which roughly | ||
53 | +// corresponds to a 3.3 MB regexp. | ||
54 | +const ( | ||
55 | + maxSize = 128 << 20 / instSize | ||
56 | + instSize = 5 * 8 // byte, 2 uint32, slice is 5 64-bit words | ||
57 | +) | ||
58 | + | ||
59 | +// maxRunes is the maximum number of runes allowed in a regexp tree | ||
60 | +// counting the runes in all the nodes. | ||
61 | +// Ignoring character classes p.numRunes is always less than the length of the regexp. | ||
62 | +// Character classes can make it much larger: each \pL adds 1292 runes. | ||
63 | +// 128 MB is enough for 32M runes, which is over 26k \pL instances. | ||
64 | +// Note that repetitions do not make copies of the rune slices, | ||
65 | +// so \pL{1000} is only one rune slice, not 1000. | ||
66 | +// We could keep a cache of character classes we've seen, | ||
67 | +// so that all the \pL we see use the same rune list, | ||
68 | +// but that doesn't remove the problem entirely: | ||
69 | +// consider something like [\pL01234][\pL01235][\pL01236]...[\pL^&*()]. | ||
70 | +// And because the Rune slice is exposed directly in the Regexp, | ||
71 | +// there is not an opportunity to change the representation to allow | ||
72 | +// partial sharing between different character classes. | ||
73 | +// So the limit is the best we can do. | ||
74 | +const ( | ||
75 | + maxRunes = 128 << 20 / runeSize | ||
76 | + runeSize = 4 // rune is int32 | ||
77 | +) | ||
78 | + | ||
79 | type parser struct { | ||
80 | flags Flags // parse mode flags | ||
81 | stack []*Regexp // stack of parsed expressions | ||
82 | free *Regexp | ||
83 | numCap int // number of capturing groups seen | ||
84 | wholeRegexp string | ||
85 | - tmpClass []rune // temporary char class work space | ||
86 | - numRegexp int // number of regexps allocated | ||
87 | - height map[*Regexp]int // regexp height for height limit check | ||
88 | + tmpClass []rune // temporary char class work space | ||
89 | + numRegexp int // number of regexps allocated | ||
90 | + numRunes int // number of runes in char classes | ||
91 | + repeats int64 // product of all repetitions seen | ||
92 | + height map[*Regexp]int // regexp height, for height limit check | ||
93 | + size map[*Regexp]int64 // regexp compiled size, for size limit check | ||
94 | } | ||
95 | |||
96 | func (p *parser) newRegexp(op Op) *Regexp { | ||
97 | @@ -122,6 +156,104 @@ func (p *parser) reuse(re *Regexp) { | ||
98 | p.free = re | ||
99 | } | ||
100 | |||
101 | +func (p *parser) checkLimits(re *Regexp) { | ||
102 | + if p.numRunes > maxRunes { | ||
103 | + panic(ErrInternalError) | ||
104 | + } | ||
105 | + p.checkSize(re) | ||
106 | + p.checkHeight(re) | ||
107 | +} | ||
108 | + | ||
109 | +func (p *parser) checkSize(re *Regexp) { | ||
110 | + if p.size == nil { | ||
111 | + // We haven't started tracking size yet. | ||
112 | + // Do a relatively cheap check to see if we need to start. | ||
113 | + // Maintain the product of all the repeats we've seen | ||
114 | + // and don't track if the total number of regexp nodes | ||
115 | + // we've seen times the repeat product is in budget. | ||
116 | + if p.repeats == 0 { | ||
117 | + p.repeats = 1 | ||
118 | + } | ||
119 | + if re.Op == OpRepeat { | ||
120 | + n := re.Max | ||
121 | + if n == -1 { | ||
122 | + n = re.Min | ||
123 | + } | ||
124 | + if n <= 0 { | ||
125 | + n = 1 | ||
126 | + } | ||
127 | + if int64(n) > maxSize/p.repeats { | ||
128 | + p.repeats = maxSize | ||
129 | + } else { | ||
130 | + p.repeats *= int64(n) | ||
131 | + } | ||
132 | + } | ||
133 | + if int64(p.numRegexp) < maxSize/p.repeats { | ||
134 | + return | ||
135 | + } | ||
136 | + | ||
137 | + // We need to start tracking size. | ||
138 | + // Make the map and belatedly populate it | ||
139 | + // with info about everything we've constructed so far. | ||
140 | + p.size = make(map[*Regexp]int64) | ||
141 | + for _, re := range p.stack { | ||
142 | + p.checkSize(re) | ||
143 | + } | ||
144 | + } | ||
145 | + | ||
146 | + if p.calcSize(re, true) > maxSize { | ||
147 | + panic(ErrInternalError) | ||
148 | + } | ||
149 | +} | ||
150 | + | ||
151 | +func (p *parser) calcSize(re *Regexp, force bool) int64 { | ||
152 | + if !force { | ||
153 | + if size, ok := p.size[re]; ok { | ||
154 | + return size | ||
155 | + } | ||
156 | + } | ||
157 | + | ||
158 | + var size int64 | ||
159 | + switch re.Op { | ||
160 | + case OpLiteral: | ||
161 | + size = int64(len(re.Rune)) | ||
162 | + case OpCapture, OpStar: | ||
163 | + // star can be 1+ or 2+; assume 2 pessimistically | ||
164 | + size = 2 + p.calcSize(re.Sub[0], false) | ||
165 | + case OpPlus, OpQuest: | ||
166 | + size = 1 + p.calcSize(re.Sub[0], false) | ||
167 | + case OpConcat: | ||
168 | + for _, sub := range re.Sub { | ||
169 | + size += p.calcSize(sub, false) | ||
170 | + } | ||
171 | + case OpAlternate: | ||
172 | + for _, sub := range re.Sub { | ||
173 | + size += p.calcSize(sub, false) | ||
174 | + } | ||
175 | + if len(re.Sub) > 1 { | ||
176 | + size += int64(len(re.Sub)) - 1 | ||
177 | + } | ||
178 | + case OpRepeat: | ||
179 | + sub := p.calcSize(re.Sub[0], false) | ||
180 | + if re.Max == -1 { | ||
181 | + if re.Min == 0 { | ||
182 | + size = 2 + sub // x* | ||
183 | + } else { | ||
184 | + size = 1 + int64(re.Min)*sub // xxx+ | ||
185 | + } | ||
186 | + break | ||
187 | + } | ||
188 | + // x{2,5} = xx(x(x(x)?)?)? | ||
189 | + size = int64(re.Max)*sub + int64(re.Max-re.Min) | ||
190 | + } | ||
191 | + | ||
192 | + if size < 1 { | ||
193 | + size = 1 | ||
194 | + } | ||
195 | + p.size[re] = size | ||
196 | + return size | ||
197 | +} | ||
198 | + | ||
199 | func (p *parser) checkHeight(re *Regexp) { | ||
200 | if p.numRegexp < maxHeight { | ||
201 | return | ||
202 | @@ -158,6 +290,7 @@ func (p *parser) calcHeight(re *Regexp, force bool) int { | ||
203 | |||
204 | // push pushes the regexp re onto the parse stack and returns the regexp. | ||
205 | func (p *parser) push(re *Regexp) *Regexp { | ||
206 | + p.numRunes += len(re.Rune) | ||
207 | if re.Op == OpCharClass && len(re.Rune) == 2 && re.Rune[0] == re.Rune[1] { | ||
208 | // Single rune. | ||
209 | if p.maybeConcat(re.Rune[0], p.flags&^FoldCase) { | ||
210 | @@ -189,7 +322,7 @@ func (p *parser) push(re *Regexp) *Regexp { | ||
211 | } | ||
212 | |||
213 | p.stack = append(p.stack, re) | ||
214 | - p.checkHeight(re) | ||
215 | + p.checkLimits(re) | ||
216 | return re | ||
217 | } | ||
218 | |||
219 | @@ -305,7 +438,7 @@ func (p *parser) repeat(op Op, min, max int, before, after, lastRepeat string) ( | ||
220 | re.Sub = re.Sub0[:1] | ||
221 | re.Sub[0] = sub | ||
222 | p.stack[n-1] = re | ||
223 | - p.checkHeight(re) | ||
224 | + p.checkLimits(re) | ||
225 | |||
226 | if op == OpRepeat && (min >= 2 || max >= 2) && !repeatIsValid(re, 1000) { | ||
227 | return "", &Error{ErrInvalidRepeatSize, before[:len(before)-len(after)]} | ||
228 | @@ -509,6 +642,7 @@ func (p *parser) factor(sub []*Regexp) []*Regexp { | ||
229 | |||
230 | for j := start; j < i; j++ { | ||
231 | sub[j] = p.removeLeadingString(sub[j], len(str)) | ||
232 | + p.checkLimits(sub[j]) | ||
233 | } | ||
234 | suffix := p.collapse(sub[start:i], OpAlternate) // recurse | ||
235 | |||
236 | @@ -566,6 +700,7 @@ func (p *parser) factor(sub []*Regexp) []*Regexp { | ||
237 | for j := start; j < i; j++ { | ||
238 | reuse := j != start // prefix came from sub[start] | ||
239 | sub[j] = p.removeLeadingRegexp(sub[j], reuse) | ||
240 | + p.checkLimits(sub[j]) | ||
241 | } | ||
242 | suffix := p.collapse(sub[start:i], OpAlternate) // recurse | ||
243 | |||
244 | diff --git a/src/regexp/syntax/parse_test.go b/src/regexp/syntax/parse_test.go | ||
245 | index 1ef6d8a..67e3c56 100644 | ||
246 | --- a/src/regexp/syntax/parse_test.go | ||
247 | +++ b/src/regexp/syntax/parse_test.go | ||
248 | @@ -484,12 +484,15 @@ var invalidRegexps = []string{ | ||
249 | `(?P<>a)`, | ||
250 | `[a-Z]`, | ||
251 | `(?i)[a-Z]`, | ||
252 | - `a{100000}`, | ||
253 | - `a{100000,}`, | ||
254 | - "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})", | ||
255 | - strings.Repeat("(", 1000) + strings.Repeat(")", 1000), | ||
256 | - strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000), | ||
257 | `\Q\E*`, | ||
258 | + `a{100000}`, // too much repetition | ||
259 | + `a{100000,}`, // too much repetition | ||
260 | + "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})", // too much repetition | ||
261 | + strings.Repeat("(", 1000) + strings.Repeat(")", 1000), // too deep | ||
262 | + strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000), // too deep | ||
263 | + "(" + strings.Repeat("(xx?)", 1000) + "){1000}", // too long | ||
264 | + strings.Repeat("(xx?){1000}", 1000), // too long | ||
265 | + strings.Repeat(`\pL`, 27000), // too many runes | ||
266 | } | ||
267 | |||
268 | var onlyPerl = []string{ | ||
269 | -- | ||
270 | 2.25.1 | ||
271 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41717.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41717.patch new file mode 100644 index 0000000000..8bf22ee4d4 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41717.patch | |||
@@ -0,0 +1,75 @@ | |||
1 | From 618120c165669c00a1606505defea6ca755cdc27 Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Wed, 30 Nov 2022 16:46:33 -0500 | ||
4 | Subject: [PATCH] [release-branch.go1.19] net/http: update bundled | ||
5 | golang.org/x/net/http2 | ||
6 | |||
7 | Disable cmd/internal/moddeps test, since this update includes PRIVATE | ||
8 | track fixes. | ||
9 | |||
10 | For #56350. | ||
11 | For #57009. | ||
12 | Fixes CVE-2022-41717. | ||
13 | |||
14 | Change-Id: I5c6ce546add81f361dcf0d5123fa4eaaf8f0a03b | ||
15 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1663835 | ||
16 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
17 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
18 | Reviewed-on: https://go-review.googlesource.com/c/go/+/455363 | ||
19 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
20 | Run-TryBot: Jenny Rakoczy <jenny@golang.org> | ||
21 | Reviewed-by: Michael Pratt <mpratt@google.com> | ||
22 | |||
23 | Upstream-Status: Backport [https://github.com/golang/go/commit/618120c165669c00a1606505defea6ca755cdc27] | ||
24 | CVE-2022-41717 | ||
25 | Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com> | ||
26 | --- | ||
27 | src/net/http/h2_bundle.go | 18 +++++++++++------- | ||
28 | 1 file changed, 11 insertions(+), 7 deletions(-) | ||
29 | |||
30 | diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go | ||
31 | index 83f2a72..cc03a62 100644 | ||
32 | --- a/src/net/http/h2_bundle.go | ||
33 | +++ b/src/net/http/h2_bundle.go | ||
34 | @@ -4096,6 +4096,7 @@ type http2serverConn struct { | ||
35 | headerTableSize uint32 | ||
36 | peerMaxHeaderListSize uint32 // zero means unknown (default) | ||
37 | canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case | ||
38 | + canonHeaderKeysSize int // canonHeader keys size in bytes | ||
39 | writingFrame bool // started writing a frame (on serve goroutine or separate) | ||
40 | writingFrameAsync bool // started a frame on its own goroutine but haven't heard back on wroteFrameCh | ||
41 | needsFrameFlush bool // last frame write wasn't a flush | ||
42 | @@ -4278,6 +4279,13 @@ func (sc *http2serverConn) condlogf(err error, format string, args ...interface{ | ||
43 | } | ||
44 | } | ||
45 | |||
46 | +// maxCachedCanonicalHeadersKeysSize is an arbitrarily-chosen limit on the size | ||
47 | +// of the entries in the canonHeader cache. | ||
48 | +// This should be larger than the size of unique, uncommon header keys likely to | ||
49 | +// be sent by the peer, while not so high as to permit unreasonable memory usage | ||
50 | +// if the peer sends an unbounded number of unique header keys. | ||
51 | +const http2maxCachedCanonicalHeadersKeysSize = 2048 | ||
52 | + | ||
53 | func (sc *http2serverConn) canonicalHeader(v string) string { | ||
54 | sc.serveG.check() | ||
55 | http2buildCommonHeaderMapsOnce() | ||
56 | @@ -4293,14 +4301,10 @@ func (sc *http2serverConn) canonicalHeader(v string) string { | ||
57 | sc.canonHeader = make(map[string]string) | ||
58 | } | ||
59 | cv = CanonicalHeaderKey(v) | ||
60 | - // maxCachedCanonicalHeaders is an arbitrarily-chosen limit on the number of | ||
61 | - // entries in the canonHeader cache. This should be larger than the number | ||
62 | - // of unique, uncommon header keys likely to be sent by the peer, while not | ||
63 | - // so high as to permit unreaasonable memory usage if the peer sends an unbounded | ||
64 | - // number of unique header keys. | ||
65 | - const maxCachedCanonicalHeaders = 32 | ||
66 | - if len(sc.canonHeader) < maxCachedCanonicalHeaders { | ||
67 | + size := 100 + len(v)*2 // 100 bytes of map overhead + key + value | ||
68 | + if sc.canonHeaderKeysSize+size <= http2maxCachedCanonicalHeadersKeysSize { | ||
69 | sc.canonHeader[v] = cv | ||
70 | + sc.canonHeaderKeysSize += size | ||
71 | } | ||
72 | return cv | ||
73 | } | ||
74 | -- | ||
75 | 2.30.2 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41722-1.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41722-1.patch new file mode 100644 index 0000000000..f5bffd7a0b --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41722-1.patch | |||
@@ -0,0 +1,53 @@ | |||
1 | From 94e0c36694fb044e81381d112fef3692de7cdf52 Mon Sep 17 00:00:00 2001 | ||
2 | From: Yasuhiro Matsumoto <mattn.jp@gmail.com> | ||
3 | Date: Fri, 22 Apr 2022 10:07:51 +0900 | ||
4 | Subject: [PATCH 1/2] path/filepath: do not remove prefix "." when following | ||
5 | path contains ":". | ||
6 | |||
7 | Fixes #52476 | ||
8 | |||
9 | Change-Id: I9eb72ac7dbccd6322d060291f31831dc389eb9bb | ||
10 | Reviewed-on: https://go-review.googlesource.com/c/go/+/401595 | ||
11 | Auto-Submit: Ian Lance Taylor <iant@google.com> | ||
12 | Reviewed-by: Alex Brainman <alex.brainman@gmail.com> | ||
13 | Run-TryBot: Ian Lance Taylor <iant@google.com> | ||
14 | Reviewed-by: Ian Lance Taylor <iant@google.com> | ||
15 | Reviewed-by: Damien Neil <dneil@google.com> | ||
16 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
17 | |||
18 | Upstream-Status: Backport from https://github.com/golang/go/commit/9cd1818a7d019c02fa4898b3e45a323e35033290 | ||
19 | CVE: CVE-2022-41722 | ||
20 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
21 | --- | ||
22 | src/path/filepath/path.go | 14 +++++++++++++- | ||
23 | 1 file changed, 13 insertions(+), 1 deletion(-) | ||
24 | |||
25 | diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go | ||
26 | index 26f1833..92dc090 100644 | ||
27 | --- a/src/path/filepath/path.go | ||
28 | +++ b/src/path/filepath/path.go | ||
29 | @@ -116,9 +116,21 @@ func Clean(path string) string { | ||
30 | case os.IsPathSeparator(path[r]): | ||
31 | // empty path element | ||
32 | r++ | ||
33 | - case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): | ||
34 | + case path[r] == '.' && r+1 == n: | ||
35 | // . element | ||
36 | r++ | ||
37 | + case path[r] == '.' && os.IsPathSeparator(path[r+1]): | ||
38 | + // ./ element | ||
39 | + r++ | ||
40 | + | ||
41 | + for r < len(path) && os.IsPathSeparator(path[r]) { | ||
42 | + r++ | ||
43 | + } | ||
44 | + if out.w == 0 && volumeNameLen(path[r:]) > 0 { | ||
45 | + // When joining prefix "." and an absolute path on Windows, | ||
46 | + // the prefix should not be removed. | ||
47 | + out.append('.') | ||
48 | + } | ||
49 | case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): | ||
50 | // .. element: remove to last separator | ||
51 | r += 2 | ||
52 | -- | ||
53 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41722-2.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41722-2.patch new file mode 100644 index 0000000000..e1f7a55581 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41722-2.patch | |||
@@ -0,0 +1,104 @@ | |||
1 | From b8803cb711ae163b8e67897deb6cf8c49702227c Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Mon, 12 Dec 2022 16:43:37 -0800 | ||
4 | Subject: [PATCH 2/2] path/filepath: do not Clean("a/../c:/b") into c:\b on | ||
5 | Windows | ||
6 | |||
7 | Do not permit Clean to convert a relative path into one starting | ||
8 | with a drive reference. This change causes Clean to insert a . | ||
9 | path element at the start of a path when the original path does not | ||
10 | start with a volume name, and the first path element would contain | ||
11 | a colon. | ||
12 | |||
13 | This may introduce a spurious but harmless . path element under | ||
14 | some circumstances. For example, Clean("a/../b:/../c") becomes `.\c`. | ||
15 | |||
16 | This reverts CL 401595, since the change here supersedes the one | ||
17 | in that CL. | ||
18 | |||
19 | Thanks to RyotaK (https://twitter.com/ryotkak) for reporting this issue. | ||
20 | |||
21 | Updates #57274 | ||
22 | Fixes #57276 | ||
23 | Fixes CVE-2022-41722 | ||
24 | |||
25 | Change-Id: I837446285a03aa74c79d7642720e01f354c2ca17 | ||
26 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1675249 | ||
27 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
28 | Run-TryBot: Damien Neil <dneil@google.com> | ||
29 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
30 | TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> | ||
31 | (cherry picked from commit 8ca37f4813ef2f64600c92b83f17c9f3ca6c03a5) | ||
32 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1728944 | ||
33 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
34 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
35 | Reviewed-by: Damien Neil <dneil@google.com> | ||
36 | Reviewed-on: https://go-review.googlesource.com/c/go/+/468119 | ||
37 | Reviewed-by: Than McIntosh <thanm@google.com> | ||
38 | Run-TryBot: Michael Pratt <mpratt@google.com> | ||
39 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
40 | Auto-Submit: Michael Pratt <mpratt@google.com> | ||
41 | |||
42 | Upstream-Status: Backport from https://github.com/golang/go/commit/bdf07c2e168baf736e4c057279ca12a4d674f18c | ||
43 | CVE: CVE-2022-41722 | ||
44 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
45 | --- | ||
46 | src/path/filepath/path.go | 27 ++++++++++++++------------- | ||
47 | 1 file changed, 14 insertions(+), 13 deletions(-) | ||
48 | |||
49 | diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go | ||
50 | index 92dc090..f0f095e 100644 | ||
51 | --- a/src/path/filepath/path.go | ||
52 | +++ b/src/path/filepath/path.go | ||
53 | @@ -14,6 +14,7 @@ package filepath | ||
54 | import ( | ||
55 | "errors" | ||
56 | "os" | ||
57 | + "runtime" | ||
58 | "sort" | ||
59 | "strings" | ||
60 | ) | ||
61 | @@ -116,21 +117,9 @@ func Clean(path string) string { | ||
62 | case os.IsPathSeparator(path[r]): | ||
63 | // empty path element | ||
64 | r++ | ||
65 | - case path[r] == '.' && r+1 == n: | ||
66 | + case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): | ||
67 | // . element | ||
68 | r++ | ||
69 | - case path[r] == '.' && os.IsPathSeparator(path[r+1]): | ||
70 | - // ./ element | ||
71 | - r++ | ||
72 | - | ||
73 | - for r < len(path) && os.IsPathSeparator(path[r]) { | ||
74 | - r++ | ||
75 | - } | ||
76 | - if out.w == 0 && volumeNameLen(path[r:]) > 0 { | ||
77 | - // When joining prefix "." and an absolute path on Windows, | ||
78 | - // the prefix should not be removed. | ||
79 | - out.append('.') | ||
80 | - } | ||
81 | case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): | ||
82 | // .. element: remove to last separator | ||
83 | r += 2 | ||
84 | @@ -156,6 +145,18 @@ func Clean(path string) string { | ||
85 | if rooted && out.w != 1 || !rooted && out.w != 0 { | ||
86 | out.append(Separator) | ||
87 | } | ||
88 | + // If a ':' appears in the path element at the start of a Windows path, | ||
89 | + // insert a .\ at the beginning to avoid converting relative paths | ||
90 | + // like a/../c: into c:. | ||
91 | + if runtime.GOOS == "windows" && out.w == 0 && out.volLen == 0 && r != 0 { | ||
92 | + for i := r; i < n && !os.IsPathSeparator(path[i]); i++ { | ||
93 | + if path[i] == ':' { | ||
94 | + out.append('.') | ||
95 | + out.append(Separator) | ||
96 | + break | ||
97 | + } | ||
98 | + } | ||
99 | + } | ||
100 | // copy element | ||
101 | for ; r < n && !os.IsPathSeparator(path[r]); r++ { | ||
102 | out.append(path[r]) | ||
103 | -- | ||
104 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41723.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41723.patch new file mode 100644 index 0000000000..a93fa31dcd --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41723.patch | |||
@@ -0,0 +1,156 @@ | |||
1 | From 451766789f646617157c725e20c955d4a9a70d4e Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Mon, 6 Feb 2023 10:03:44 -0800 | ||
4 | Subject: [PATCH] net/http: update bundled golang.org/x/net/http2 | ||
5 | |||
6 | Disable cmd/internal/moddeps test, since this update includes PRIVATE | ||
7 | track fixes. | ||
8 | |||
9 | Fixes CVE-2022-41723 | ||
10 | Fixes #58355 | ||
11 | Updates #57855 | ||
12 | |||
13 | Change-Id: Ie870562a6f6e44e4e8f57db6a0dde1a41a2b090c | ||
14 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1728939 | ||
15 | Reviewed-by: Damien Neil <dneil@google.com> | ||
16 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
17 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
18 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
19 | Reviewed-on: https://go-review.googlesource.com/c/go/+/468118 | ||
20 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
21 | Run-TryBot: Michael Pratt <mpratt@google.com> | ||
22 | Auto-Submit: Michael Pratt <mpratt@google.com> | ||
23 | Reviewed-by: Than McIntosh <thanm@google.com> | ||
24 | |||
25 | Upstream-Status: Backport [https://github.com/golang/go/commit/5c3e11bd0b5c0a86e5beffcd4339b86a902b21c3] | ||
26 | CVE: CVE-2022-41723 | ||
27 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
28 | --- | ||
29 | src/vendor/golang.org/x/net/http2/hpack/hpack.go | 79 +++++++++++++++--------- | ||
30 | 1 file changed, 49 insertions(+), 30 deletions(-) | ||
31 | |||
32 | diff --git a/src/vendor/golang.org/x/net/http2/hpack/hpack.go b/src/vendor/golang.org/x/net/http2/hpack/hpack.go | ||
33 | index 85f18a2..02e80e3 100644 | ||
34 | --- a/src/vendor/golang.org/x/net/http2/hpack/hpack.go | ||
35 | +++ b/src/vendor/golang.org/x/net/http2/hpack/hpack.go | ||
36 | @@ -359,6 +359,7 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error { | ||
37 | |||
38 | var hf HeaderField | ||
39 | wantStr := d.emitEnabled || it.indexed() | ||
40 | + var undecodedName undecodedString | ||
41 | if nameIdx > 0 { | ||
42 | ihf, ok := d.at(nameIdx) | ||
43 | if !ok { | ||
44 | @@ -366,15 +367,27 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error { | ||
45 | } | ||
46 | hf.Name = ihf.Name | ||
47 | } else { | ||
48 | - hf.Name, buf, err = d.readString(buf, wantStr) | ||
49 | + undecodedName, buf, err = d.readString(buf) | ||
50 | if err != nil { | ||
51 | return err | ||
52 | } | ||
53 | } | ||
54 | - hf.Value, buf, err = d.readString(buf, wantStr) | ||
55 | + undecodedValue, buf, err := d.readString(buf) | ||
56 | if err != nil { | ||
57 | return err | ||
58 | } | ||
59 | + if wantStr { | ||
60 | + if nameIdx <= 0 { | ||
61 | + hf.Name, err = d.decodeString(undecodedName) | ||
62 | + if err != nil { | ||
63 | + return err | ||
64 | + } | ||
65 | + } | ||
66 | + hf.Value, err = d.decodeString(undecodedValue) | ||
67 | + if err != nil { | ||
68 | + return err | ||
69 | + } | ||
70 | + } | ||
71 | d.buf = buf | ||
72 | if it.indexed() { | ||
73 | d.dynTab.add(hf) | ||
74 | @@ -459,46 +472,52 @@ func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) { | ||
75 | return 0, origP, errNeedMore | ||
76 | } | ||
77 | |||
78 | -// readString decodes an hpack string from p. | ||
79 | +// readString reads an hpack string from p. | ||
80 | // | ||
81 | -// wantStr is whether s will be used. If false, decompression and | ||
82 | -// []byte->string garbage are skipped if s will be ignored | ||
83 | -// anyway. This does mean that huffman decoding errors for non-indexed | ||
84 | -// strings past the MAX_HEADER_LIST_SIZE are ignored, but the server | ||
85 | -// is returning an error anyway, and because they're not indexed, the error | ||
86 | -// won't affect the decoding state. | ||
87 | -func (d *Decoder) readString(p []byte, wantStr bool) (s string, remain []byte, err error) { | ||
88 | +// It returns a reference to the encoded string data to permit deferring decode costs | ||
89 | +// until after the caller verifies all data is present. | ||
90 | +func (d *Decoder) readString(p []byte) (u undecodedString, remain []byte, err error) { | ||
91 | if len(p) == 0 { | ||
92 | - return "", p, errNeedMore | ||
93 | + return u, p, errNeedMore | ||
94 | } | ||
95 | isHuff := p[0]&128 != 0 | ||
96 | strLen, p, err := readVarInt(7, p) | ||
97 | if err != nil { | ||
98 | - return "", p, err | ||
99 | + return u, p, err | ||
100 | } | ||
101 | if d.maxStrLen != 0 && strLen > uint64(d.maxStrLen) { | ||
102 | - return "", nil, ErrStringLength | ||
103 | + // Returning an error here means Huffman decoding errors | ||
104 | + // for non-indexed strings past the maximum string length | ||
105 | + // are ignored, but the server is returning an error anyway | ||
106 | + // and because the string is not indexed the error will not | ||
107 | + // affect the decoding state. | ||
108 | + return u, nil, ErrStringLength | ||
109 | } | ||
110 | if uint64(len(p)) < strLen { | ||
111 | - return "", p, errNeedMore | ||
112 | - } | ||
113 | - if !isHuff { | ||
114 | - if wantStr { | ||
115 | - s = string(p[:strLen]) | ||
116 | - } | ||
117 | - return s, p[strLen:], nil | ||
118 | + return u, p, errNeedMore | ||
119 | } | ||
120 | + u.isHuff = isHuff | ||
121 | + u.b = p[:strLen] | ||
122 | + return u, p[strLen:], nil | ||
123 | +} | ||
124 | |||
125 | - if wantStr { | ||
126 | - buf := bufPool.Get().(*bytes.Buffer) | ||
127 | - buf.Reset() // don't trust others | ||
128 | - defer bufPool.Put(buf) | ||
129 | - if err := huffmanDecode(buf, d.maxStrLen, p[:strLen]); err != nil { | ||
130 | - buf.Reset() | ||
131 | - return "", nil, err | ||
132 | - } | ||
133 | +type undecodedString struct { | ||
134 | + isHuff bool | ||
135 | + b []byte | ||
136 | +} | ||
137 | + | ||
138 | +func (d *Decoder) decodeString(u undecodedString) (string, error) { | ||
139 | + if !u.isHuff { | ||
140 | + return string(u.b), nil | ||
141 | + } | ||
142 | + buf := bufPool.Get().(*bytes.Buffer) | ||
143 | + buf.Reset() // don't trust others | ||
144 | + var s string | ||
145 | + err := huffmanDecode(buf, d.maxStrLen, u.b) | ||
146 | + if err == nil { | ||
147 | s = buf.String() | ||
148 | - buf.Reset() // be nice to GC | ||
149 | } | ||
150 | - return s, p[strLen:], nil | ||
151 | + buf.Reset() // be nice to GC | ||
152 | + bufPool.Put(buf) | ||
153 | + return s, err | ||
154 | } | ||
155 | -- | ||
156 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre1.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre1.patch new file mode 100644 index 0000000000..37ebc41947 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre1.patch | |||
@@ -0,0 +1,85 @@ | |||
1 | From 874b3132a84cf76da6a48978826c04c380a37a50 Mon Sep 17 00:00:00 2001 | ||
2 | From: avivklas <avivklas@gmail.com> | ||
3 | Date: Fri, 7 Aug 2020 21:50:12 +0300 | ||
4 | Subject: [PATCH] mime/multipart: return overflow errors in Reader.ReadForm | ||
5 | |||
6 | Updates Reader.ReadForm to check for overflow errors that may | ||
7 | result from a leeway addition of 10MiB to the input argument | ||
8 | maxMemory. | ||
9 | |||
10 | Fixes #40430 | ||
11 | |||
12 | Change-Id: I510b8966c95c51d04695ba9d08fcfe005fd11a5d | ||
13 | Reviewed-on: https://go-review.googlesource.com/c/go/+/247477 | ||
14 | Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com> | ||
15 | Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com> | ||
16 | Trust: Emmanuel Odeke <emm.odeke@gmail.com> | ||
17 | TryBot-Result: Go Bot <gobot@golang.org> | ||
18 | Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com> | ||
19 | |||
20 | Upstream-Status: Backport [https://github.com/golang/go/commit/874b3132a84cf76da6a48978826c04c380a37a50] | ||
21 | CVE: CVE-2022-41725 #Dependency Patch1 | ||
22 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
23 | --- | ||
24 | src/mime/multipart/formdata.go | 4 ++++ | ||
25 | src/mime/multipart/formdata_test.go | 18 ++++++++++++++++++ | ||
26 | 2 files changed, 22 insertions(+) | ||
27 | |||
28 | diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go | ||
29 | index 832d0ad693666..4eb31012941ac 100644 | ||
30 | --- a/src/mime/multipart/formdata.go | ||
31 | +++ b/src/mime/multipart/formdata.go | ||
32 | @@ -7,6 +7,7 @@ package multipart | ||
33 | import ( | ||
34 | "bytes" | ||
35 | "errors" | ||
36 | + "fmt" | ||
37 | "io" | ||
38 | "io/ioutil" | ||
39 | "net/textproto" | ||
40 | @@ -41,6 +42,9 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
41 | |||
42 | // Reserve an additional 10 MB for non-file parts. | ||
43 | maxValueBytes := maxMemory + int64(10<<20) | ||
44 | + if maxValueBytes <= 0 { | ||
45 | + return nil, fmt.Errorf("multipart: integer overflow from maxMemory(%d) + 10MiB for non-file parts", maxMemory) | ||
46 | + } | ||
47 | for { | ||
48 | p, err := r.NextPart() | ||
49 | if err == io.EOF { | ||
50 | diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go | ||
51 | index 7d756c8c244a0..7112e0d3727fe 100644 | ||
52 | --- a/src/mime/multipart/formdata_test.go | ||
53 | +++ b/src/mime/multipart/formdata_test.go | ||
54 | @@ -7,6 +7,7 @@ package multipart | ||
55 | import ( | ||
56 | "bytes" | ||
57 | "io" | ||
58 | + "math" | ||
59 | "os" | ||
60 | "strings" | ||
61 | "testing" | ||
62 | @@ -52,6 +53,23 @@ func TestReadFormWithNamelessFile(t *testing.T) { | ||
63 | } | ||
64 | } | ||
65 | |||
66 | +// Issue 40430: Ensure that we report integer overflows in additions of maxMemory, | ||
67 | +// instead of silently and subtly failing without indication. | ||
68 | +func TestReadFormMaxMemoryOverflow(t *testing.T) { | ||
69 | + b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n")) | ||
70 | + r := NewReader(b, boundary) | ||
71 | + f, err := r.ReadForm(math.MaxInt64) | ||
72 | + if err == nil { | ||
73 | + t.Fatal("Unexpected a non-nil error") | ||
74 | + } | ||
75 | + if f != nil { | ||
76 | + t.Fatalf("Unexpected returned a non-nil form: %v\n", f) | ||
77 | + } | ||
78 | + if g, w := err.Error(), "integer overflow from maxMemory"; !strings.Contains(g, w) { | ||
79 | + t.Errorf(`Error mismatch\n%q\ndid not contain\n%q`, g, w) | ||
80 | + } | ||
81 | +} | ||
82 | + | ||
83 | func TestReadFormWithTextContentType(t *testing.T) { | ||
84 | // From https://github.com/golang/go/issues/24041 | ||
85 | b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n")) | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre2.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre2.patch new file mode 100644 index 0000000000..b951ee893e --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre2.patch | |||
@@ -0,0 +1,97 @@ | |||
1 | From 4e5a313524da62600eb59dbf98624cfe946456f8 Mon Sep 17 00:00:00 2001 | ||
2 | From: Emmanuel T Odeke <emmanuel@orijtech.com> | ||
3 | Date: Tue, 20 Oct 2020 04:11:12 -0700 | ||
4 | Subject: [PATCH] net/http: test that ParseMultipartForm catches overflows | ||
5 | |||
6 | Tests that if the combination of: | ||
7 | * HTTP multipart file payload size | ||
8 | * ParseMultipartForm's maxMemory parameter | ||
9 | * the internal leeway buffer size of 10MiB | ||
10 | |||
11 | overflows, then we'll report an overflow instead of silently | ||
12 | passing. | ||
13 | |||
14 | Reapplies and fixes CL 254977, which was reverted in CL 263658. | ||
15 | |||
16 | The prior test lacked a res.Body.Close(), so fixed that and | ||
17 | added a leaked Transport check to verify correctness. | ||
18 | |||
19 | Updates 40430. | ||
20 | |||
21 | Change-Id: I3c0f7ef43d621f6eb00f07755f04f9f36c51f98f | ||
22 | Reviewed-on: https://go-review.googlesource.com/c/go/+/263817 | ||
23 | Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com> | ||
24 | TryBot-Result: Go Bot <gobot@golang.org> | ||
25 | Reviewed-by: Bryan C. Mills <bcmills@google.com> | ||
26 | Trust: Damien Neil <dneil@google.com> | ||
27 | |||
28 | Upstream-Status: Backport [https://github.com/golang/go/commit/4e5a313524da62600eb59dbf98624cfe946456f8] | ||
29 | CVE: CVE-2022-41725 #Dependency Patch2 | ||
30 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
31 | --- | ||
32 | src/net/http/request_test.go | 45 ++++++++++++++++++++++++++++++++++++ | ||
33 | 1 file changed, 45 insertions(+) | ||
34 | |||
35 | diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go | ||
36 | index b4ef472e71229..19526b9ad791a 100644 | ||
37 | --- a/src/net/http/request_test.go | ||
38 | +++ b/src/net/http/request_test.go | ||
39 | @@ -13,6 +13,7 @@ import ( | ||
40 | "fmt" | ||
41 | "io" | ||
42 | "io/ioutil" | ||
43 | + "math" | ||
44 | "mime/multipart" | ||
45 | . "net/http" | ||
46 | "net/http/httptest" | ||
47 | @@ -245,6 +246,50 @@ func TestParseMultipartForm(t *testing.T) { | ||
48 | } | ||
49 | } | ||
50 | |||
51 | +// Issue #40430: Test that if maxMemory for ParseMultipartForm when combined with | ||
52 | +// the payload size and the internal leeway buffer size of 10MiB overflows, that we | ||
53 | +// correctly return an error. | ||
54 | +func TestMaxInt64ForMultipartFormMaxMemoryOverflow(t *testing.T) { | ||
55 | + defer afterTest(t) | ||
56 | + | ||
57 | + payloadSize := 1 << 10 | ||
58 | + cst := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { | ||
59 | + // The combination of: | ||
60 | + // MaxInt64 + payloadSize + (internal spare of 10MiB) | ||
61 | + // triggers the overflow. See issue https://golang.org/issue/40430/ | ||
62 | + if err := req.ParseMultipartForm(math.MaxInt64); err != nil { | ||
63 | + Error(rw, err.Error(), StatusBadRequest) | ||
64 | + return | ||
65 | + } | ||
66 | + })) | ||
67 | + defer cst.Close() | ||
68 | + fBuf := new(bytes.Buffer) | ||
69 | + mw := multipart.NewWriter(fBuf) | ||
70 | + mf, err := mw.CreateFormFile("file", "myfile.txt") | ||
71 | + if err != nil { | ||
72 | + t.Fatal(err) | ||
73 | + } | ||
74 | + if _, err := mf.Write(bytes.Repeat([]byte("abc"), payloadSize)); err != nil { | ||
75 | + t.Fatal(err) | ||
76 | + } | ||
77 | + if err := mw.Close(); err != nil { | ||
78 | + t.Fatal(err) | ||
79 | + } | ||
80 | + req, err := NewRequest("POST", cst.URL, fBuf) | ||
81 | + if err != nil { | ||
82 | + t.Fatal(err) | ||
83 | + } | ||
84 | + req.Header.Set("Content-Type", mw.FormDataContentType()) | ||
85 | + res, err := cst.Client().Do(req) | ||
86 | + if err != nil { | ||
87 | + t.Fatal(err) | ||
88 | + } | ||
89 | + res.Body.Close() | ||
90 | + if g, w := res.StatusCode, StatusBadRequest; g != w { | ||
91 | + t.Fatalf("Status code mismatch: got %d, want %d", g, w) | ||
92 | + } | ||
93 | +} | ||
94 | + | ||
95 | func TestRedirect_h1(t *testing.T) { testRedirect(t, h1Mode) } | ||
96 | func TestRedirect_h2(t *testing.T) { testRedirect(t, h2Mode) } | ||
97 | func testRedirect(t *testing.T, h2 bool) { | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre3.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre3.patch new file mode 100644 index 0000000000..767225b888 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre3.patch | |||
@@ -0,0 +1,98 @@ | |||
1 | From 5246fa5e75b129a7dbd9722aa4de0cbaf7ceae43 Mon Sep 17 00:00:00 2001 | ||
2 | From: Russ Cox <rsc@golang.org> | ||
3 | Date: Thu, 3 Dec 2020 09:45:07 -0500 | ||
4 | Subject: [PATCH] mime/multipart: handle ReadForm(math.MaxInt64) better | ||
5 | |||
6 | Returning an error about integer overflow is needlessly pedantic. | ||
7 | The meaning of ReadForm(MaxInt64) is easily understood | ||
8 | (accept a lot of data) and can be implemented. | ||
9 | |||
10 | Fixes #40430. | ||
11 | |||
12 | Change-Id: I8a522033dd9a2f9ad31dd2ad82cf08d553736ab9 | ||
13 | Reviewed-on: https://go-review.googlesource.com/c/go/+/275112 | ||
14 | Trust: Russ Cox <rsc@golang.org> | ||
15 | Run-TryBot: Russ Cox <rsc@golang.org> | ||
16 | TryBot-Result: Go Bot <gobot@golang.org> | ||
17 | Reviewed-by: Ian Lance Taylor <iant@golang.org> | ||
18 | |||
19 | Upstream-Status: Backport [https://github.com/golang/go/commit/5246fa5e75b129a7dbd9722aa4de0cbaf7ceae43] | ||
20 | CVE: CVE-2022-41725 #Dependency Patch3 | ||
21 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
22 | --- | ||
23 | src/mime/multipart/formdata.go | 8 ++++++-- | ||
24 | src/mime/multipart/formdata_test.go | 14 +++++--------- | ||
25 | src/net/http/request_test.go | 2 +- | ||
26 | 3 files changed, 12 insertions(+), 12 deletions(-) | ||
27 | |||
28 | diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go | ||
29 | index 4eb31012941ac..9c42ea8c023b5 100644 | ||
30 | --- a/src/mime/multipart/formdata.go | ||
31 | +++ b/src/mime/multipart/formdata.go | ||
32 | @@ -7,9 +7,9 @@ package multipart | ||
33 | import ( | ||
34 | "bytes" | ||
35 | "errors" | ||
36 | - "fmt" | ||
37 | "io" | ||
38 | "io/ioutil" | ||
39 | + "math" | ||
40 | "net/textproto" | ||
41 | "os" | ||
42 | ) | ||
43 | @@ -43,7 +43,11 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
44 | // Reserve an additional 10 MB for non-file parts. | ||
45 | maxValueBytes := maxMemory + int64(10<<20) | ||
46 | if maxValueBytes <= 0 { | ||
47 | - return nil, fmt.Errorf("multipart: integer overflow from maxMemory(%d) + 10MiB for non-file parts", maxMemory) | ||
48 | + if maxMemory < 0 { | ||
49 | + maxValueBytes = 0 | ||
50 | + } else { | ||
51 | + maxValueBytes = math.MaxInt64 | ||
52 | + } | ||
53 | } | ||
54 | for { | ||
55 | p, err := r.NextPart() | ||
56 | diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go | ||
57 | index 7112e0d3727fe..e3a3a3eae8e15 100644 | ||
58 | --- a/src/mime/multipart/formdata_test.go | ||
59 | +++ b/src/mime/multipart/formdata_test.go | ||
60 | @@ -53,20 +53,16 @@ func TestReadFormWithNamelessFile(t *testing.T) { | ||
61 | } | ||
62 | } | ||
63 | |||
64 | -// Issue 40430: Ensure that we report integer overflows in additions of maxMemory, | ||
65 | -// instead of silently and subtly failing without indication. | ||
66 | +// Issue 40430: Handle ReadForm(math.MaxInt64) | ||
67 | func TestReadFormMaxMemoryOverflow(t *testing.T) { | ||
68 | b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n")) | ||
69 | r := NewReader(b, boundary) | ||
70 | f, err := r.ReadForm(math.MaxInt64) | ||
71 | - if err == nil { | ||
72 | - t.Fatal("Unexpected a non-nil error") | ||
73 | - } | ||
74 | - if f != nil { | ||
75 | - t.Fatalf("Unexpected returned a non-nil form: %v\n", f) | ||
76 | + if err != nil { | ||
77 | + t.Fatalf("ReadForm(MaxInt64): %v", err) | ||
78 | } | ||
79 | - if g, w := err.Error(), "integer overflow from maxMemory"; !strings.Contains(g, w) { | ||
80 | - t.Errorf(`Error mismatch\n%q\ndid not contain\n%q`, g, w) | ||
81 | + if f == nil { | ||
82 | + t.Fatal("ReadForm(MaxInt64): missing form") | ||
83 | } | ||
84 | } | ||
85 | |||
86 | diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go | ||
87 | index 19526b9ad791a..689498e19d5dd 100644 | ||
88 | --- a/src/net/http/request_test.go | ||
89 | +++ b/src/net/http/request_test.go | ||
90 | @@ -285,7 +285,7 @@ func TestMaxInt64ForMultipartFormMaxMemoryOverflow(t *testing.T) { | ||
91 | t.Fatal(err) | ||
92 | } | ||
93 | res.Body.Close() | ||
94 | - if g, w := res.StatusCode, StatusBadRequest; g != w { | ||
95 | + if g, w := res.StatusCode, StatusOK; g != w { | ||
96 | t.Fatalf("Status code mismatch: got %d, want %d", g, w) | ||
97 | } | ||
98 | } | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2022-41725.patch b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725.patch new file mode 100644 index 0000000000..5f80c62b0b --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2022-41725.patch | |||
@@ -0,0 +1,660 @@ | |||
1 | From 5c55ac9bf1e5f779220294c843526536605f42ab Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Wed, 25 Jan 2023 09:27:01 -0800 | ||
4 | Subject: [PATCH] [release-branch.go1.19] mime/multipart: limit memory/inode consumption of ReadForm | ||
5 | |||
6 | Reader.ReadForm is documented as storing "up to maxMemory bytes + 10MB" | ||
7 | in memory. Parsed forms can consume substantially more memory than | ||
8 | this limit, since ReadForm does not account for map entry overhead | ||
9 | and MIME headers. | ||
10 | |||
11 | In addition, while the amount of disk memory consumed by ReadForm can | ||
12 | be constrained by limiting the size of the parsed input, ReadForm will | ||
13 | create one temporary file per form part stored on disk, potentially | ||
14 | consuming a large number of inodes. | ||
15 | |||
16 | Update ReadForm's memory accounting to include part names, | ||
17 | MIME headers, and map entry overhead. | ||
18 | |||
19 | Update ReadForm to store all on-disk file parts in a single | ||
20 | temporary file. | ||
21 | |||
22 | Files returned by FileHeader.Open are documented as having a concrete | ||
23 | type of *os.File when a file is stored on disk. The change to use a | ||
24 | single temporary file for all parts means that this is no longer the | ||
25 | case when a form contains more than a single file part stored on disk. | ||
26 | |||
27 | The previous behavior of storing each file part in a separate disk | ||
28 | file may be reenabled with GODEBUG=multipartfiles=distinct. | ||
29 | |||
30 | Update Reader.NextPart and Reader.NextRawPart to set a 10MiB cap | ||
31 | on the size of MIME headers. | ||
32 | |||
33 | Thanks to Jakob Ackermann (@das7pad) for reporting this issue. | ||
34 | |||
35 | Updates #58006 | ||
36 | Fixes #58362 | ||
37 | Fixes CVE-2022-41725 | ||
38 | |||
39 | Change-Id: Ibd780a6c4c83ac8bcfd3cbe344f042e9940f2eab | ||
40 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1714276 | ||
41 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
42 | TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> | ||
43 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
44 | Run-TryBot: Damien Neil <dneil@google.com> | ||
45 | (cherry picked from commit ed4664330edcd91b24914c9371c377c132dbce8c) | ||
46 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1728949 | ||
47 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
48 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
49 | Reviewed-by: Damien Neil <dneil@google.com> | ||
50 | Reviewed-on: https://go-review.googlesource.com/c/go/+/468116 | ||
51 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
52 | Reviewed-by: Than McIntosh <thanm@google.com> | ||
53 | Run-TryBot: Michael Pratt <mpratt@google.com> | ||
54 | Auto-Submit: Michael Pratt <mpratt@google.com> | ||
55 | |||
56 | Upstream-Status: Backport [https://github.com/golang/go/commit/5c55ac9bf1e5f779220294c843526536605f42ab] | ||
57 | CVE: CVE-2022-41725 | ||
58 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
59 | --- | ||
60 | src/mime/multipart/formdata.go | 132 ++++++++++++++++++++----- | ||
61 | src/mime/multipart/formdata_test.go | 140 ++++++++++++++++++++++++++- | ||
62 | src/mime/multipart/multipart.go | 25 +++-- | ||
63 | src/mime/multipart/readmimeheader.go | 14 +++ | ||
64 | src/net/http/request_test.go | 2 +- | ||
65 | src/net/textproto/reader.go | 27 ++++++ | ||
66 | 6 files changed, 303 insertions(+), 37 deletions(-) | ||
67 | create mode 100644 src/mime/multipart/readmimeheader.go | ||
68 | |||
69 | diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go | ||
70 | index 9c42ea8..1eeb340 100644 | ||
71 | --- a/src/mime/multipart/formdata.go | ||
72 | +++ b/src/mime/multipart/formdata.go | ||
73 | @@ -7,6 +7,7 @@ package multipart | ||
74 | import ( | ||
75 | "bytes" | ||
76 | "errors" | ||
77 | + "internal/godebug" | ||
78 | "io" | ||
79 | "io/ioutil" | ||
80 | "math" | ||
81 | @@ -34,23 +35,58 @@ func (r *Reader) ReadForm(maxMemory int64) (*Form, error) { | ||
82 | |||
83 | func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
84 | form := &Form{make(map[string][]string), make(map[string][]*FileHeader)} | ||
85 | + var ( | ||
86 | + file *os.File | ||
87 | + fileOff int64 | ||
88 | + ) | ||
89 | + numDiskFiles := 0 | ||
90 | + multipartFiles := godebug.Get("multipartfiles") | ||
91 | + combineFiles := multipartFiles != "distinct" | ||
92 | defer func() { | ||
93 | + if file != nil { | ||
94 | + if cerr := file.Close(); err == nil { | ||
95 | + err = cerr | ||
96 | + } | ||
97 | + } | ||
98 | + if combineFiles && numDiskFiles > 1 { | ||
99 | + for _, fhs := range form.File { | ||
100 | + for _, fh := range fhs { | ||
101 | + fh.tmpshared = true | ||
102 | + } | ||
103 | + } | ||
104 | + } | ||
105 | if err != nil { | ||
106 | form.RemoveAll() | ||
107 | + if file != nil { | ||
108 | + os.Remove(file.Name()) | ||
109 | + } | ||
110 | } | ||
111 | }() | ||
112 | |||
113 | - // Reserve an additional 10 MB for non-file parts. | ||
114 | - maxValueBytes := maxMemory + int64(10<<20) | ||
115 | - if maxValueBytes <= 0 { | ||
116 | + // maxFileMemoryBytes is the maximum bytes of file data we will store in memory. | ||
117 | + // Data past this limit is written to disk. | ||
118 | + // This limit strictly applies to content, not metadata (filenames, MIME headers, etc.), | ||
119 | + // since metadata is always stored in memory, not disk. | ||
120 | + // | ||
121 | + // maxMemoryBytes is the maximum bytes we will store in memory, including file content, | ||
122 | + // non-file part values, metdata, and map entry overhead. | ||
123 | + // | ||
124 | + // We reserve an additional 10 MB in maxMemoryBytes for non-file data. | ||
125 | + // | ||
126 | + // The relationship between these parameters, as well as the overly-large and | ||
127 | + // unconfigurable 10 MB added on to maxMemory, is unfortunate but difficult to change | ||
128 | + // within the constraints of the API as documented. | ||
129 | + maxFileMemoryBytes := maxMemory | ||
130 | + maxMemoryBytes := maxMemory + int64(10<<20) | ||
131 | + if maxMemoryBytes <= 0 { | ||
132 | if maxMemory < 0 { | ||
133 | - maxValueBytes = 0 | ||
134 | + maxMemoryBytes = 0 | ||
135 | } else { | ||
136 | - maxValueBytes = math.MaxInt64 | ||
137 | + maxMemoryBytes = math.MaxInt64 | ||
138 | } | ||
139 | } | ||
140 | for { | ||
141 | - p, err := r.NextPart() | ||
142 | + p, err := r.nextPart(false, maxMemoryBytes) | ||
143 | if err == io.EOF { | ||
144 | break | ||
145 | } | ||
146 | @@ -64,16 +100,27 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
147 | } | ||
148 | filename := p.FileName() | ||
149 | |||
150 | + // Multiple values for the same key (one map entry, longer slice) are cheaper | ||
151 | + // than the same number of values for different keys (many map entries), but | ||
152 | + // using a consistent per-value cost for overhead is simpler. | ||
153 | + maxMemoryBytes -= int64(len(name)) | ||
154 | + maxMemoryBytes -= 100 // map overhead | ||
155 | + if maxMemoryBytes < 0 { | ||
156 | + // We can't actually take this path, since nextPart would already have | ||
157 | + // rejected the MIME headers for being too large. Check anyway. | ||
158 | + return nil, ErrMessageTooLarge | ||
159 | + } | ||
160 | + | ||
161 | var b bytes.Buffer | ||
162 | |||
163 | if filename == "" { | ||
164 | // value, store as string in memory | ||
165 | - n, err := io.CopyN(&b, p, maxValueBytes+1) | ||
166 | + n, err := io.CopyN(&b, p, maxMemoryBytes+1) | ||
167 | if err != nil && err != io.EOF { | ||
168 | return nil, err | ||
169 | } | ||
170 | - maxValueBytes -= n | ||
171 | - if maxValueBytes < 0 { | ||
172 | + maxMemoryBytes -= n | ||
173 | + if maxMemoryBytes < 0 { | ||
174 | return nil, ErrMessageTooLarge | ||
175 | } | ||
176 | form.Value[name] = append(form.Value[name], b.String()) | ||
177 | @@ -81,35 +128,45 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
178 | } | ||
179 | |||
180 | // file, store in memory or on disk | ||
181 | + maxMemoryBytes -= mimeHeaderSize(p.Header) | ||
182 | + if maxMemoryBytes < 0 { | ||
183 | + return nil, ErrMessageTooLarge | ||
184 | + } | ||
185 | fh := &FileHeader{ | ||
186 | Filename: filename, | ||
187 | Header: p.Header, | ||
188 | } | ||
189 | - n, err := io.CopyN(&b, p, maxMemory+1) | ||
190 | + n, err := io.CopyN(&b, p, maxFileMemoryBytes+1) | ||
191 | if err != nil && err != io.EOF { | ||
192 | return nil, err | ||
193 | } | ||
194 | - if n > maxMemory { | ||
195 | - // too big, write to disk and flush buffer | ||
196 | - file, err := ioutil.TempFile("", "multipart-") | ||
197 | - if err != nil { | ||
198 | - return nil, err | ||
199 | + if n > maxFileMemoryBytes { | ||
200 | + if file == nil { | ||
201 | + file, err = ioutil.TempFile(r.tempDir, "multipart-") | ||
202 | + if err != nil { | ||
203 | + return nil, err | ||
204 | + } | ||
205 | } | ||
206 | + numDiskFiles++ | ||
207 | size, err := io.Copy(file, io.MultiReader(&b, p)) | ||
208 | - if cerr := file.Close(); err == nil { | ||
209 | - err = cerr | ||
210 | - } | ||
211 | if err != nil { | ||
212 | - os.Remove(file.Name()) | ||
213 | return nil, err | ||
214 | } | ||
215 | fh.tmpfile = file.Name() | ||
216 | fh.Size = size | ||
217 | + fh.tmpoff = fileOff | ||
218 | + fileOff += size | ||
219 | + if !combineFiles { | ||
220 | + if err := file.Close(); err != nil { | ||
221 | + return nil, err | ||
222 | + } | ||
223 | + file = nil | ||
224 | + } | ||
225 | } else { | ||
226 | fh.content = b.Bytes() | ||
227 | fh.Size = int64(len(fh.content)) | ||
228 | - maxMemory -= n | ||
229 | - maxValueBytes -= n | ||
230 | + maxFileMemoryBytes -= n | ||
231 | + maxMemoryBytes -= n | ||
232 | } | ||
233 | form.File[name] = append(form.File[name], fh) | ||
234 | } | ||
235 | @@ -117,6 +174,17 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
236 | return form, nil | ||
237 | } | ||
238 | |||
239 | +func mimeHeaderSize(h textproto.MIMEHeader) (size int64) { | ||
240 | + for k, vs := range h { | ||
241 | + size += int64(len(k)) | ||
242 | + size += 100 // map entry overhead | ||
243 | + for _, v := range vs { | ||
244 | + size += int64(len(v)) | ||
245 | + } | ||
246 | + } | ||
247 | + return size | ||
248 | +} | ||
249 | + | ||
250 | // Form is a parsed multipart form. | ||
251 | // Its File parts are stored either in memory or on disk, | ||
252 | // and are accessible via the *FileHeader's Open method. | ||
253 | @@ -134,7 +202,7 @@ func (f *Form) RemoveAll() error { | ||
254 | for _, fh := range fhs { | ||
255 | if fh.tmpfile != "" { | ||
256 | e := os.Remove(fh.tmpfile) | ||
257 | - if e != nil && err == nil { | ||
258 | + if e != nil && !errors.Is(e, os.ErrNotExist) && err == nil { | ||
259 | err = e | ||
260 | } | ||
261 | } | ||
262 | @@ -149,15 +217,25 @@ type FileHeader struct { | ||
263 | Header textproto.MIMEHeader | ||
264 | Size int64 | ||
265 | |||
266 | - content []byte | ||
267 | - tmpfile string | ||
268 | + content []byte | ||
269 | + tmpfile string | ||
270 | + tmpoff int64 | ||
271 | + tmpshared bool | ||
272 | } | ||
273 | |||
274 | // Open opens and returns the FileHeader's associated File. | ||
275 | func (fh *FileHeader) Open() (File, error) { | ||
276 | if b := fh.content; b != nil { | ||
277 | r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))) | ||
278 | - return sectionReadCloser{r}, nil | ||
279 | + return sectionReadCloser{r, nil}, nil | ||
280 | + } | ||
281 | + if fh.tmpshared { | ||
282 | + f, err := os.Open(fh.tmpfile) | ||
283 | + if err != nil { | ||
284 | + return nil, err | ||
285 | + } | ||
286 | + r := io.NewSectionReader(f, fh.tmpoff, fh.Size) | ||
287 | + return sectionReadCloser{r, f}, nil | ||
288 | } | ||
289 | return os.Open(fh.tmpfile) | ||
290 | } | ||
291 | @@ -176,8 +254,12 @@ type File interface { | ||
292 | |||
293 | type sectionReadCloser struct { | ||
294 | *io.SectionReader | ||
295 | + io.Closer | ||
296 | } | ||
297 | |||
298 | func (rc sectionReadCloser) Close() error { | ||
299 | + if rc.Closer != nil { | ||
300 | + return rc.Closer.Close() | ||
301 | + } | ||
302 | return nil | ||
303 | } | ||
304 | diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go | ||
305 | index e3a3a3e..5cded71 100644 | ||
306 | --- a/src/mime/multipart/formdata_test.go | ||
307 | +++ b/src/mime/multipart/formdata_test.go | ||
308 | @@ -6,8 +6,10 @@ package multipart | ||
309 | |||
310 | import ( | ||
311 | "bytes" | ||
312 | + "fmt" | ||
313 | "io" | ||
314 | "math" | ||
315 | + "net/textproto" | ||
316 | "os" | ||
317 | "strings" | ||
318 | "testing" | ||
319 | @@ -208,8 +210,8 @@ Content-Disposition: form-data; name="largetext" | ||
320 | maxMemory int64 | ||
321 | err error | ||
322 | }{ | ||
323 | - {"smaller", 50, nil}, | ||
324 | - {"exact-fit", 25, nil}, | ||
325 | + {"smaller", 50 + int64(len("largetext")) + 100, nil}, | ||
326 | + {"exact-fit", 25 + int64(len("largetext")) + 100, nil}, | ||
327 | {"too-large", 0, ErrMessageTooLarge}, | ||
328 | } | ||
329 | for _, tc := range testCases { | ||
330 | @@ -224,7 +226,7 @@ Content-Disposition: form-data; name="largetext" | ||
331 | defer f.RemoveAll() | ||
332 | } | ||
333 | if tc.err != err { | ||
334 | - t.Fatalf("ReadForm error - got: %v; expected: %v", tc.err, err) | ||
335 | + t.Fatalf("ReadForm error - got: %v; expected: %v", err, tc.err) | ||
336 | } | ||
337 | if err == nil { | ||
338 | if g := f.Value["largetext"][0]; g != largeTextValue { | ||
339 | @@ -234,3 +236,135 @@ Content-Disposition: form-data; name="largetext" | ||
340 | }) | ||
341 | } | ||
342 | } | ||
343 | + | ||
344 | +// TestReadForm_MetadataTooLarge verifies that we account for the size of field names, | ||
345 | +// MIME headers, and map entry overhead while limiting the memory consumption of parsed forms. | ||
346 | +func TestReadForm_MetadataTooLarge(t *testing.T) { | ||
347 | + for _, test := range []struct { | ||
348 | + name string | ||
349 | + f func(*Writer) | ||
350 | + }{{ | ||
351 | + name: "large name", | ||
352 | + f: func(fw *Writer) { | ||
353 | + name := strings.Repeat("a", 10<<20) | ||
354 | + w, _ := fw.CreateFormField(name) | ||
355 | + w.Write([]byte("value")) | ||
356 | + }, | ||
357 | + }, { | ||
358 | + name: "large MIME header", | ||
359 | + f: func(fw *Writer) { | ||
360 | + h := make(textproto.MIMEHeader) | ||
361 | + h.Set("Content-Disposition", `form-data; name="a"`) | ||
362 | + h.Set("X-Foo", strings.Repeat("a", 10<<20)) | ||
363 | + w, _ := fw.CreatePart(h) | ||
364 | + w.Write([]byte("value")) | ||
365 | + }, | ||
366 | + }, { | ||
367 | + name: "many parts", | ||
368 | + f: func(fw *Writer) { | ||
369 | + for i := 0; i < 110000; i++ { | ||
370 | + w, _ := fw.CreateFormField("f") | ||
371 | + w.Write([]byte("v")) | ||
372 | + } | ||
373 | + }, | ||
374 | + }} { | ||
375 | + t.Run(test.name, func(t *testing.T) { | ||
376 | + var buf bytes.Buffer | ||
377 | + fw := NewWriter(&buf) | ||
378 | + test.f(fw) | ||
379 | + if err := fw.Close(); err != nil { | ||
380 | + t.Fatal(err) | ||
381 | + } | ||
382 | + fr := NewReader(&buf, fw.Boundary()) | ||
383 | + _, err := fr.ReadForm(0) | ||
384 | + if err != ErrMessageTooLarge { | ||
385 | + t.Errorf("fr.ReadForm() = %v, want ErrMessageTooLarge", err) | ||
386 | + } | ||
387 | + }) | ||
388 | + } | ||
389 | +} | ||
390 | + | ||
391 | +// TestReadForm_ManyFiles_Combined tests that a multipart form containing many files only | ||
392 | +// results in a single on-disk file. | ||
393 | +func TestReadForm_ManyFiles_Combined(t *testing.T) { | ||
394 | + const distinct = false | ||
395 | + testReadFormManyFiles(t, distinct) | ||
396 | +} | ||
397 | + | ||
398 | +// TestReadForm_ManyFiles_Distinct tests that setting GODEBUG=multipartfiles=distinct | ||
399 | +// results in every file in a multipart form being placed in a distinct on-disk file. | ||
400 | +func TestReadForm_ManyFiles_Distinct(t *testing.T) { | ||
401 | + t.Setenv("GODEBUG", "multipartfiles=distinct") | ||
402 | + const distinct = true | ||
403 | + testReadFormManyFiles(t, distinct) | ||
404 | +} | ||
405 | + | ||
406 | +func testReadFormManyFiles(t *testing.T, distinct bool) { | ||
407 | + var buf bytes.Buffer | ||
408 | + fw := NewWriter(&buf) | ||
409 | + const numFiles = 10 | ||
410 | + for i := 0; i < numFiles; i++ { | ||
411 | + name := fmt.Sprint(i) | ||
412 | + w, err := fw.CreateFormFile(name, name) | ||
413 | + if err != nil { | ||
414 | + t.Fatal(err) | ||
415 | + } | ||
416 | + w.Write([]byte(name)) | ||
417 | + } | ||
418 | + if err := fw.Close(); err != nil { | ||
419 | + t.Fatal(err) | ||
420 | + } | ||
421 | + fr := NewReader(&buf, fw.Boundary()) | ||
422 | + fr.tempDir = t.TempDir() | ||
423 | + form, err := fr.ReadForm(0) | ||
424 | + if err != nil { | ||
425 | + t.Fatal(err) | ||
426 | + } | ||
427 | + for i := 0; i < numFiles; i++ { | ||
428 | + name := fmt.Sprint(i) | ||
429 | + if got := len(form.File[name]); got != 1 { | ||
430 | + t.Fatalf("form.File[%q] has %v entries, want 1", name, got) | ||
431 | + } | ||
432 | + fh := form.File[name][0] | ||
433 | + file, err := fh.Open() | ||
434 | + if err != nil { | ||
435 | + t.Fatalf("form.File[%q].Open() = %v", name, err) | ||
436 | + } | ||
437 | + if distinct { | ||
438 | + if _, ok := file.(*os.File); !ok { | ||
439 | + t.Fatalf("form.File[%q].Open: %T, want *os.File", name, file) | ||
440 | + } | ||
441 | + } | ||
442 | + got, err := io.ReadAll(file) | ||
443 | + file.Close() | ||
444 | + if string(got) != name || err != nil { | ||
445 | + t.Fatalf("read form.File[%q]: %q, %v; want %q, nil", name, string(got), err, name) | ||
446 | + } | ||
447 | + } | ||
448 | + dir, err := os.Open(fr.tempDir) | ||
449 | + if err != nil { | ||
450 | + t.Fatal(err) | ||
451 | + } | ||
452 | + defer dir.Close() | ||
453 | + names, err := dir.Readdirnames(0) | ||
454 | + if err != nil { | ||
455 | + t.Fatal(err) | ||
456 | + } | ||
457 | + wantNames := 1 | ||
458 | + if distinct { | ||
459 | + wantNames = numFiles | ||
460 | + } | ||
461 | + if len(names) != wantNames { | ||
462 | + t.Fatalf("temp dir contains %v files; want 1", len(names)) | ||
463 | + } | ||
464 | + if err := form.RemoveAll(); err != nil { | ||
465 | + t.Fatalf("form.RemoveAll() = %v", err) | ||
466 | + } | ||
467 | + names, err = dir.Readdirnames(0) | ||
468 | + if err != nil { | ||
469 | + t.Fatal(err) | ||
470 | + } | ||
471 | + if len(names) != 0 { | ||
472 | + t.Fatalf("temp dir contains %v files; want 0", len(names)) | ||
473 | + } | ||
474 | +} | ||
475 | diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go | ||
476 | index 1750300..958cef8 100644 | ||
477 | --- a/src/mime/multipart/multipart.go | ||
478 | +++ b/src/mime/multipart/multipart.go | ||
479 | @@ -121,12 +121,12 @@ func (r *stickyErrorReader) Read(p []byte) (n int, _ error) { | ||
480 | return n, r.err | ||
481 | } | ||
482 | |||
483 | -func newPart(mr *Reader, rawPart bool) (*Part, error) { | ||
484 | +func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { | ||
485 | bp := &Part{ | ||
486 | Header: make(map[string][]string), | ||
487 | mr: mr, | ||
488 | } | ||
489 | - if err := bp.populateHeaders(); err != nil { | ||
490 | + if err := bp.populateHeaders(maxMIMEHeaderSize); err != nil { | ||
491 | return nil, err | ||
492 | } | ||
493 | bp.r = partReader{bp} | ||
494 | @@ -142,12 +142,16 @@ func newPart(mr *Reader, rawPart bool) (*Part, error) { | ||
495 | return bp, nil | ||
496 | } | ||
497 | |||
498 | -func (bp *Part) populateHeaders() error { | ||
499 | +func (bp *Part) populateHeaders(maxMIMEHeaderSize int64) error { | ||
500 | r := textproto.NewReader(bp.mr.bufReader) | ||
501 | - header, err := r.ReadMIMEHeader() | ||
502 | + header, err := readMIMEHeader(r, maxMIMEHeaderSize) | ||
503 | if err == nil { | ||
504 | bp.Header = header | ||
505 | } | ||
506 | + // TODO: Add a distinguishable error to net/textproto. | ||
507 | + if err != nil && err.Error() == "message too large" { | ||
508 | + err = ErrMessageTooLarge | ||
509 | + } | ||
510 | return err | ||
511 | } | ||
512 | |||
513 | @@ -287,6 +291,7 @@ func (p *Part) Close() error { | ||
514 | // isn't supported. | ||
515 | type Reader struct { | ||
516 | bufReader *bufio.Reader | ||
517 | + tempDir string // used in tests | ||
518 | |||
519 | currentPart *Part | ||
520 | partsRead int | ||
521 | @@ -297,6 +302,10 @@ type Reader struct { | ||
522 | dashBoundary []byte // "--boundary" | ||
523 | } | ||
524 | |||
525 | +// maxMIMEHeaderSize is the maximum size of a MIME header we will parse, | ||
526 | +// including header keys, values, and map overhead. | ||
527 | +const maxMIMEHeaderSize = 10 << 20 | ||
528 | + | ||
529 | // NextPart returns the next part in the multipart or an error. | ||
530 | // When there are no more parts, the error io.EOF is returned. | ||
531 | // | ||
532 | @@ -304,7 +313,7 @@ type Reader struct { | ||
533 | // has a value of "quoted-printable", that header is instead | ||
534 | // hidden and the body is transparently decoded during Read calls. | ||
535 | func (r *Reader) NextPart() (*Part, error) { | ||
536 | - return r.nextPart(false) | ||
537 | + return r.nextPart(false, maxMIMEHeaderSize) | ||
538 | } | ||
539 | |||
540 | // NextRawPart returns the next part in the multipart or an error. | ||
541 | @@ -313,10 +322,10 @@ func (r *Reader) NextPart() (*Part, error) { | ||
542 | // Unlike NextPart, it does not have special handling for | ||
543 | // "Content-Transfer-Encoding: quoted-printable". | ||
544 | func (r *Reader) NextRawPart() (*Part, error) { | ||
545 | - return r.nextPart(true) | ||
546 | + return r.nextPart(true, maxMIMEHeaderSize) | ||
547 | } | ||
548 | |||
549 | -func (r *Reader) nextPart(rawPart bool) (*Part, error) { | ||
550 | +func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { | ||
551 | if r.currentPart != nil { | ||
552 | r.currentPart.Close() | ||
553 | } | ||
554 | @@ -341,7 +350,7 @@ func (r *Reader) nextPart(rawPart bool) (*Part, error) { | ||
555 | |||
556 | if r.isBoundaryDelimiterLine(line) { | ||
557 | r.partsRead++ | ||
558 | - bp, err := newPart(r, rawPart) | ||
559 | + bp, err := newPart(r, rawPart, maxMIMEHeaderSize) | ||
560 | if err != nil { | ||
561 | return nil, err | ||
562 | } | ||
563 | diff --git a/src/mime/multipart/readmimeheader.go b/src/mime/multipart/readmimeheader.go | ||
564 | new file mode 100644 | ||
565 | index 0000000..6836928 | ||
566 | --- /dev/null | ||
567 | +++ b/src/mime/multipart/readmimeheader.go | ||
568 | @@ -0,0 +1,14 @@ | ||
569 | +// Copyright 2023 The Go Authors. All rights reserved. | ||
570 | +// Use of this source code is governed by a BSD-style | ||
571 | +// license that can be found in the LICENSE file. | ||
572 | +package multipart | ||
573 | + | ||
574 | +import ( | ||
575 | + "net/textproto" | ||
576 | + _ "unsafe" // for go:linkname | ||
577 | +) | ||
578 | + | ||
579 | +// readMIMEHeader is defined in package net/textproto. | ||
580 | +// | ||
581 | +//go:linkname readMIMEHeader net/textproto.readMIMEHeader | ||
582 | +func readMIMEHeader(r *textproto.Reader, lim int64) (textproto.MIMEHeader, error) | ||
583 | diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go | ||
584 | index 94133ee..170d3f5 100644 | ||
585 | --- a/src/net/http/request_test.go | ||
586 | +++ b/src/net/http/request_test.go | ||
587 | @@ -962,7 +962,7 @@ func testMissingFile(t *testing.T, req *Request) { | ||
588 | t.Errorf("FormFile file = %v, want nil", f) | ||
589 | } | ||
590 | if fh != nil { | ||
591 | - t.Errorf("FormFile file header = %q, want nil", fh) | ||
592 | + t.Errorf("FormFile file header = %v, want nil", fh) | ||
593 | } | ||
594 | if err != ErrMissingFile { | ||
595 | t.Errorf("FormFile err = %q, want ErrMissingFile", err) | ||
596 | diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go | ||
597 | index f63f5ec..96553fb 100644 | ||
598 | --- a/src/net/textproto/reader.go | ||
599 | +++ b/src/net/textproto/reader.go | ||
600 | @@ -7,9 +7,11 @@ package textproto | ||
601 | import ( | ||
602 | "bufio" | ||
603 | "bytes" | ||
604 | + "errors" | ||
605 | "fmt" | ||
606 | "io" | ||
607 | "io/ioutil" | ||
608 | + "math" | ||
609 | "strconv" | ||
610 | "strings" | ||
611 | "sync" | ||
612 | @@ -482,6 +484,12 @@ func (r *Reader) ReadDotLines() ([]string, error) { | ||
613 | // } | ||
614 | // | ||
615 | func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { | ||
616 | + return readMIMEHeader(r, math.MaxInt64) | ||
617 | +} | ||
618 | + | ||
619 | +// readMIMEHeader is a version of ReadMIMEHeader which takes a limit on the header size. | ||
620 | +// It is called by the mime/multipart package. | ||
621 | +func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { | ||
622 | // Avoid lots of small slice allocations later by allocating one | ||
623 | // large one ahead of time which we'll cut up into smaller | ||
624 | // slices. If this isn't big enough later, we allocate small ones. | ||
625 | @@ -525,6 +533,15 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { | ||
626 | continue | ||
627 | } | ||
628 | |||
629 | + // backport 5c55ac9bf1e5f779220294c843526536605f42ab | ||
630 | + // | ||
631 | + // value is computed as | ||
632 | + // value := string(bytes.TrimLeft(v, " \t")) | ||
633 | + // | ||
634 | + // in the original patch from 1.19. This relies on | ||
635 | + // 'v' which does not exist in 1.14. We leave the | ||
636 | + // 1.14 method unchanged. | ||
637 | + | ||
638 | // Skip initial spaces in value. | ||
639 | i++ // skip colon | ||
640 | for i < len(kv) && (kv[i] == ' ' || kv[i] == '\t') { | ||
641 | @@ -533,6 +550,16 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { | ||
642 | value := string(kv[i:]) | ||
643 | |||
644 | vv := m[key] | ||
645 | + if vv == nil { | ||
646 | + lim -= int64(len(key)) | ||
647 | + lim -= 100 // map entry overhead | ||
648 | + } | ||
649 | + lim -= int64(len(value)) | ||
650 | + if lim < 0 { | ||
651 | + // TODO: This should be a distinguishable error (ErrMessageTooLarge) | ||
652 | + // to allow mime/multipart to detect it. | ||
653 | + return m, errors.New("message too large") | ||
654 | + } | ||
655 | if vv == nil && len(strs) > 0 { | ||
656 | // More than likely this will be a single-element key. | ||
657 | // Most headers aren't multi-valued. | ||
658 | -- | ||
659 | 2.25.1 | ||
660 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24534.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24534.patch new file mode 100644 index 0000000000..d50db04bed --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24534.patch | |||
@@ -0,0 +1,200 @@ | |||
1 | From d6759e7a059f4208f07aa781402841d7ddaaef96 Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Fri, 10 Mar 2023 14:21:05 -0800 | ||
4 | Subject: [PATCH] [release-branch.go1.19] net/textproto: avoid overpredicting | ||
5 | the number of MIME header keys | ||
6 | |||
7 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802452 | ||
8 | Run-TryBot: Damien Neil <dneil@google.com> | ||
9 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
10 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
11 | (cherry picked from commit f739f080a72fd5b06d35c8e244165159645e2ed6) | ||
12 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802393 | ||
13 | Reviewed-by: Damien Neil <dneil@google.com> | ||
14 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
15 | Change-Id: I675451438d619a9130360c56daf529559004903f | ||
16 | Reviewed-on: https://go-review.googlesource.com/c/go/+/481982 | ||
17 | Run-TryBot: Michael Knyszek <mknyszek@google.com> | ||
18 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
19 | Reviewed-by: Matthew Dempsky <mdempsky@google.com> | ||
20 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
21 | |||
22 | Upstream-Status: Backport [https://github.com/golang/go/commit/d6759e7a059f4208f07aa781402841d7ddaaef96] | ||
23 | CVE: CVE-2023-24534 | ||
24 | Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com> | ||
25 | --- | ||
26 | src/bytes/bytes.go | 13 +++++++ | ||
27 | src/net/textproto/reader.go | 31 +++++++++++------ | ||
28 | src/net/textproto/reader_test.go | 59 ++++++++++++++++++++++++++++++++ | ||
29 | 3 files changed, 92 insertions(+), 11 deletions(-) | ||
30 | |||
31 | diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go | ||
32 | index e872cc2..1f0d760 100644 | ||
33 | --- a/src/bytes/bytes.go | ||
34 | +++ b/src/bytes/bytes.go | ||
35 | @@ -1078,6 +1078,19 @@ func Index(s, sep []byte) int { | ||
36 | return -1 | ||
37 | } | ||
38 | |||
39 | +// Cut slices s around the first instance of sep, | ||
40 | +// returning the text before and after sep. | ||
41 | +// The found result reports whether sep appears in s. | ||
42 | +// If sep does not appear in s, cut returns s, nil, false. | ||
43 | +// | ||
44 | +// Cut returns slices of the original slice s, not copies. | ||
45 | +func Cut(s, sep []byte) (before, after []byte, found bool) { | ||
46 | + if i := Index(s, sep); i >= 0 { | ||
47 | + return s[:i], s[i+len(sep):], true | ||
48 | + } | ||
49 | + return s, nil, false | ||
50 | +} | ||
51 | + | ||
52 | func indexRabinKarp(s, sep []byte) int { | ||
53 | // Rabin-Karp search | ||
54 | hashsep, pow := hashStr(sep) | ||
55 | diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go | ||
56 | index a505da9..8d547fe 100644 | ||
57 | --- a/src/net/textproto/reader.go | ||
58 | +++ b/src/net/textproto/reader.go | ||
59 | @@ -486,8 +487,11 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { | ||
60 | // large one ahead of time which we'll cut up into smaller | ||
61 | // slices. If this isn't big enough later, we allocate small ones. | ||
62 | var strs []string | ||
63 | - hint := r.upcomingHeaderNewlines() | ||
64 | + hint := r.upcomingHeaderKeys() | ||
65 | if hint > 0 { | ||
66 | + if hint > 1000 { | ||
67 | + hint = 1000 // set a cap to avoid overallocation | ||
68 | + } | ||
69 | strs = make([]string, hint) | ||
70 | } | ||
71 | |||
72 | @@ -562,9 +566,11 @@ func mustHaveFieldNameColon(line []byte) error { | ||
73 | return nil | ||
74 | } | ||
75 | |||
76 | -// upcomingHeaderNewlines returns an approximation of the number of newlines | ||
77 | +var nl = []byte("\n") | ||
78 | + | ||
79 | +// upcomingHeaderKeys returns an approximation of the number of keys | ||
80 | // that will be in this header. If it gets confused, it returns 0. | ||
81 | -func (r *Reader) upcomingHeaderNewlines() (n int) { | ||
82 | +func (r *Reader) upcomingHeaderKeys() (n int) { | ||
83 | // Try to determine the 'hint' size. | ||
84 | r.R.Peek(1) // force a buffer load if empty | ||
85 | s := r.R.Buffered() | ||
86 | @@ -572,17 +578,20 @@ func (r *Reader) upcomingHeaderNewlines() (n int) { | ||
87 | return | ||
88 | } | ||
89 | peek, _ := r.R.Peek(s) | ||
90 | - for len(peek) > 0 { | ||
91 | - i := bytes.IndexByte(peek, '\n') | ||
92 | - if i < 3 { | ||
93 | - // Not present (-1) or found within the next few bytes, | ||
94 | - // implying we're at the end ("\r\n\r\n" or "\n\n") | ||
95 | - return | ||
96 | + for len(peek) > 0 && n < 1000 { | ||
97 | + var line []byte | ||
98 | + line, peek, _ = bytes.Cut(peek, nl) | ||
99 | + if len(line) == 0 || (len(line) == 1 && line[0] == '\r') { | ||
100 | + // Blank line separating headers from the body. | ||
101 | + break | ||
102 | + } | ||
103 | + if line[0] == ' ' || line[0] == '\t' { | ||
104 | + // Folded continuation of the previous line. | ||
105 | + continue | ||
106 | } | ||
107 | n++ | ||
108 | - peek = peek[i+1:] | ||
109 | } | ||
110 | - return | ||
111 | + return n | ||
112 | } | ||
113 | |||
114 | // CanonicalMIMEHeaderKey returns the canonical format of the | ||
115 | diff --git a/src/net/textproto/reader_test.go b/src/net/textproto/reader_test.go | ||
116 | index 3124d43..3ae0de1 100644 | ||
117 | --- a/src/net/textproto/reader_test.go | ||
118 | +++ b/src/net/textproto/reader_test.go | ||
119 | @@ -9,6 +9,7 @@ import ( | ||
120 | "bytes" | ||
121 | "io" | ||
122 | "reflect" | ||
123 | + "runtime" | ||
124 | "strings" | ||
125 | "testing" | ||
126 | ) | ||
127 | @@ -127,6 +128,42 @@ func TestReadMIMEHeaderSingle(t *testing.T) { | ||
128 | } | ||
129 | } | ||
130 | |||
131 | +// TestReaderUpcomingHeaderKeys is testing an internal function, but it's very | ||
132 | +// difficult to test well via the external API. | ||
133 | +func TestReaderUpcomingHeaderKeys(t *testing.T) { | ||
134 | + for _, test := range []struct { | ||
135 | + input string | ||
136 | + want int | ||
137 | + }{{ | ||
138 | + input: "", | ||
139 | + want: 0, | ||
140 | + }, { | ||
141 | + input: "A: v", | ||
142 | + want: 1, | ||
143 | + }, { | ||
144 | + input: "A: v\r\nB: v\r\n", | ||
145 | + want: 2, | ||
146 | + }, { | ||
147 | + input: "A: v\nB: v\n", | ||
148 | + want: 2, | ||
149 | + }, { | ||
150 | + input: "A: v\r\n continued\r\n still continued\r\nB: v\r\n\r\n", | ||
151 | + want: 2, | ||
152 | + }, { | ||
153 | + input: "A: v\r\n\r\nB: v\r\nC: v\r\n", | ||
154 | + want: 1, | ||
155 | + }, { | ||
156 | + input: "A: v" + strings.Repeat("\n", 1000), | ||
157 | + want: 1, | ||
158 | + }} { | ||
159 | + r := reader(test.input) | ||
160 | + got := r.upcomingHeaderKeys() | ||
161 | + if test.want != got { | ||
162 | + t.Fatalf("upcomingHeaderKeys(%q): %v; want %v", test.input, got, test.want) | ||
163 | + } | ||
164 | + } | ||
165 | +} | ||
166 | + | ||
167 | func TestReadMIMEHeaderNoKey(t *testing.T) { | ||
168 | r := reader(": bar\ntest-1: 1\n\n") | ||
169 | m, err := r.ReadMIMEHeader() | ||
170 | @@ -223,6 +260,28 @@ func TestReadMIMEHeaderTrimContinued(t *testing.T) { | ||
171 | } | ||
172 | } | ||
173 | |||
174 | +// Test that reading a header doesn't overallocate. Issue 58975. | ||
175 | +func TestReadMIMEHeaderAllocations(t *testing.T) { | ||
176 | + var totalAlloc uint64 | ||
177 | + const count = 200 | ||
178 | + for i := 0; i < count; i++ { | ||
179 | + r := reader("A: b\r\n\r\n" + strings.Repeat("\n", 4096)) | ||
180 | + var m1, m2 runtime.MemStats | ||
181 | + runtime.ReadMemStats(&m1) | ||
182 | + _, err := r.ReadMIMEHeader() | ||
183 | + if err != nil { | ||
184 | + t.Fatalf("ReadMIMEHeader: %v", err) | ||
185 | + } | ||
186 | + runtime.ReadMemStats(&m2) | ||
187 | + totalAlloc += m2.TotalAlloc - m1.TotalAlloc | ||
188 | + } | ||
189 | + // 32k is large and we actually allocate substantially less, | ||
190 | + // but prior to the fix for #58975 we allocated ~400k in this case. | ||
191 | + if got, want := totalAlloc/count, uint64(32768); got > want { | ||
192 | + t.Fatalf("ReadMIMEHeader allocated %v bytes, want < %v", got, want) | ||
193 | + } | ||
194 | +} | ||
195 | + | ||
196 | type readResponseTest struct { | ||
197 | in string | ||
198 | inCode int | ||
199 | -- | ||
200 | 2.25.1 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_1.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_1.patch new file mode 100644 index 0000000000..39e1304fbd --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_1.patch | |||
@@ -0,0 +1,134 @@ | |||
1 | From ef41a4e2face45e580c5836eaebd51629fc23f15 Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Thu, 16 Mar 2023 14:18:04 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.19] mime/multipart: avoid excessive copy | ||
5 | buffer allocations in ReadForm | ||
6 | |||
7 | When copying form data to disk with io.Copy, | ||
8 | allocate only one copy buffer and reuse it rather than | ||
9 | creating two buffers per file (one from io.multiReader.WriteTo, | ||
10 | and a second one from os.File.ReadFrom). | ||
11 | |||
12 | Thanks to Jakob Ackermann (@das7pad) for reporting this issue. | ||
13 | |||
14 | For CVE-2023-24536 | ||
15 | For #59153 | ||
16 | For #59269 | ||
17 | |||
18 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802453 | ||
19 | Run-TryBot: Damien Neil <dneil@google.com> | ||
20 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
21 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
22 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802395 | ||
23 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
24 | Reviewed-by: Damien Neil <dneil@google.com> | ||
25 | Change-Id: Ie405470c92abffed3356913b37d813e982c96c8b | ||
26 | Reviewed-on: https://go-review.googlesource.com/c/go/+/481983 | ||
27 | Run-TryBot: Michael Knyszek <mknyszek@google.com> | ||
28 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
29 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
30 | Reviewed-by: Matthew Dempsky <mdempsky@google.com> | ||
31 | |||
32 | Upstream-Status: Backport [https://github.com/golang/go/commit/ef41a4e2face45e580c5836eaebd51629fc23f15] | ||
33 | CVE: CVE-2023-24536 | ||
34 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
35 | --- | ||
36 | src/mime/multipart/formdata.go | 15 +++++++-- | ||
37 | src/mime/multipart/formdata_test.go | 49 +++++++++++++++++++++++++++++ | ||
38 | 2 files changed, 61 insertions(+), 3 deletions(-) | ||
39 | |||
40 | diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go | ||
41 | index a7d4ca97f0484..975dcb6b26db4 100644 | ||
42 | --- a/src/mime/multipart/formdata.go | ||
43 | +++ b/src/mime/multipart/formdata.go | ||
44 | @@ -84,6 +84,7 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
45 | maxMemoryBytes = math.MaxInt64 | ||
46 | } | ||
47 | } | ||
48 | + var copyBuf []byte | ||
49 | for { | ||
50 | p, err := r.nextPart(false, maxMemoryBytes) | ||
51 | if err == io.EOF { | ||
52 | @@ -147,14 +148,22 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
53 | } | ||
54 | } | ||
55 | numDiskFiles++ | ||
56 | - size, err := io.Copy(file, io.MultiReader(&b, p)) | ||
57 | + if _, err := file.Write(b.Bytes()); err != nil { | ||
58 | + return nil, err | ||
59 | + } | ||
60 | + if copyBuf == nil { | ||
61 | + copyBuf = make([]byte, 32*1024) // same buffer size as io.Copy uses | ||
62 | + } | ||
63 | + // os.File.ReadFrom will allocate its own copy buffer if we let io.Copy use it. | ||
64 | + type writerOnly struct{ io.Writer } | ||
65 | + remainingSize, err := io.CopyBuffer(writerOnly{file}, p, copyBuf) | ||
66 | if err != nil { | ||
67 | return nil, err | ||
68 | } | ||
69 | fh.tmpfile = file.Name() | ||
70 | - fh.Size = size | ||
71 | + fh.Size = int64(b.Len()) + remainingSize | ||
72 | fh.tmpoff = fileOff | ||
73 | - fileOff += size | ||
74 | + fileOff += fh.Size | ||
75 | if !combineFiles { | ||
76 | if err := file.Close(); err != nil { | ||
77 | return nil, err | ||
78 | diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go | ||
79 | index 5cded7170c6b8..f5b56083b2377 100644 | ||
80 | --- a/src/mime/multipart/formdata_test.go | ||
81 | +++ b/src/mime/multipart/formdata_test.go | ||
82 | @@ -368,3 +368,52 @@ func testReadFormManyFiles(t *testing.T, distinct bool) { | ||
83 | t.Fatalf("temp dir contains %v files; want 0", len(names)) | ||
84 | } | ||
85 | } | ||
86 | + | ||
87 | +func BenchmarkReadForm(b *testing.B) { | ||
88 | + for _, test := range []struct { | ||
89 | + name string | ||
90 | + form func(fw *Writer, count int) | ||
91 | + }{{ | ||
92 | + name: "fields", | ||
93 | + form: func(fw *Writer, count int) { | ||
94 | + for i := 0; i < count; i++ { | ||
95 | + w, _ := fw.CreateFormField(fmt.Sprintf("field%v", i)) | ||
96 | + fmt.Fprintf(w, "value %v", i) | ||
97 | + } | ||
98 | + }, | ||
99 | + }, { | ||
100 | + name: "files", | ||
101 | + form: func(fw *Writer, count int) { | ||
102 | + for i := 0; i < count; i++ { | ||
103 | + w, _ := fw.CreateFormFile(fmt.Sprintf("field%v", i), fmt.Sprintf("file%v", i)) | ||
104 | + fmt.Fprintf(w, "value %v", i) | ||
105 | + } | ||
106 | + }, | ||
107 | + }} { | ||
108 | + b.Run(test.name, func(b *testing.B) { | ||
109 | + for _, maxMemory := range []int64{ | ||
110 | + 0, | ||
111 | + 1 << 20, | ||
112 | + } { | ||
113 | + var buf bytes.Buffer | ||
114 | + fw := NewWriter(&buf) | ||
115 | + test.form(fw, 10) | ||
116 | + if err := fw.Close(); err != nil { | ||
117 | + b.Fatal(err) | ||
118 | + } | ||
119 | + b.Run(fmt.Sprintf("maxMemory=%v", maxMemory), func(b *testing.B) { | ||
120 | + b.ReportAllocs() | ||
121 | + for i := 0; i < b.N; i++ { | ||
122 | + fr := NewReader(bytes.NewReader(buf.Bytes()), fw.Boundary()) | ||
123 | + form, err := fr.ReadForm(maxMemory) | ||
124 | + if err != nil { | ||
125 | + b.Fatal(err) | ||
126 | + } | ||
127 | + form.RemoveAll() | ||
128 | + } | ||
129 | + | ||
130 | + }) | ||
131 | + } | ||
132 | + }) | ||
133 | + } | ||
134 | +} | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_2.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_2.patch new file mode 100644 index 0000000000..9ba5114c82 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_2.patch | |||
@@ -0,0 +1,184 @@ | |||
1 | From 7a359a651c7ebdb29e0a1c03102fce793e9f58f0 Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Thu, 16 Mar 2023 16:56:12 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.19] net/textproto, mime/multipart: | ||
5 | improve accounting of non-file data | ||
6 | |||
7 | For requests containing large numbers of small parts, | ||
8 | memory consumption of a parsed form could be about 250% | ||
9 | over the estimated size. | ||
10 | |||
11 | When considering the size of parsed forms, account for the size of | ||
12 | FileHeader structs and increase the estimate of memory consumed by | ||
13 | map entries. | ||
14 | |||
15 | Thanks to Jakob Ackermann (@das7pad) for reporting this issue. | ||
16 | |||
17 | For CVE-2023-24536 | ||
18 | For #59153 | ||
19 | For #59269 | ||
20 | |||
21 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802454 | ||
22 | Run-TryBot: Damien Neil <dneil@google.com> | ||
23 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
24 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
25 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802396 | ||
26 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
27 | Reviewed-by: Damien Neil <dneil@google.com> | ||
28 | Change-Id: I31bc50e9346b4eee6fbe51a18c3c57230cc066db | ||
29 | Reviewed-on: https://go-review.googlesource.com/c/go/+/481984 | ||
30 | Reviewed-by: Matthew Dempsky <mdempsky@google.com> | ||
31 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
32 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
33 | Run-TryBot: Michael Knyszek <mknyszek@google.com> | ||
34 | |||
35 | Upstream-Status: Backport [https://github.com/golang/go/commit/7a359a651c7ebdb29e0a1c03102fce793e9f58f0] | ||
36 | CVE: CVE-2023-24536 | ||
37 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
38 | --- | ||
39 | src/mime/multipart/formdata.go | 9 +++-- | ||
40 | src/mime/multipart/formdata_test.go | 55 ++++++++++++----------------- | ||
41 | src/net/textproto/reader.go | 8 ++++- | ||
42 | 3 files changed, 37 insertions(+), 35 deletions(-) | ||
43 | |||
44 | diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go | ||
45 | index 975dcb6b26db4..3f6ff697ca608 100644 | ||
46 | --- a/src/mime/multipart/formdata.go | ||
47 | +++ b/src/mime/multipart/formdata.go | ||
48 | @@ -103,8 +103,9 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
49 | // Multiple values for the same key (one map entry, longer slice) are cheaper | ||
50 | // than the same number of values for different keys (many map entries), but | ||
51 | // using a consistent per-value cost for overhead is simpler. | ||
52 | + const mapEntryOverhead = 200 | ||
53 | maxMemoryBytes -= int64(len(name)) | ||
54 | - maxMemoryBytes -= 100 // map overhead | ||
55 | + maxMemoryBytes -= mapEntryOverhead | ||
56 | if maxMemoryBytes < 0 { | ||
57 | // We can't actually take this path, since nextPart would already have | ||
58 | // rejected the MIME headers for being too large. Check anyway. | ||
59 | @@ -128,7 +129,10 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
60 | } | ||
61 | |||
62 | // file, store in memory or on disk | ||
63 | + const fileHeaderSize = 100 | ||
64 | maxMemoryBytes -= mimeHeaderSize(p.Header) | ||
65 | + maxMemoryBytes -= mapEntryOverhead | ||
66 | + maxMemoryBytes -= fileHeaderSize | ||
67 | if maxMemoryBytes < 0 { | ||
68 | return nil, ErrMessageTooLarge | ||
69 | } | ||
70 | @@ -183,9 +187,10 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
71 | } | ||
72 | |||
73 | func mimeHeaderSize(h textproto.MIMEHeader) (size int64) { | ||
74 | + size = 400 | ||
75 | for k, vs := range h { | ||
76 | size += int64(len(k)) | ||
77 | - size += 100 // map entry overhead | ||
78 | + size += 200 // map entry overhead | ||
79 | for _, v := range vs { | ||
80 | size += int64(len(v)) | ||
81 | } | ||
82 | diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go | ||
83 | index f5b56083b2377..8ed26e0c34081 100644 | ||
84 | --- a/src/mime/multipart/formdata_test.go | ||
85 | +++ b/src/mime/multipart/formdata_test.go | ||
86 | @@ -192,10 +192,10 @@ func (r *failOnReadAfterErrorReader) Read(p []byte) (n int, err error) { | ||
87 | // TestReadForm_NonFileMaxMemory asserts that the ReadForm maxMemory limit is applied | ||
88 | // while processing non-file form data as well as file form data. | ||
89 | func TestReadForm_NonFileMaxMemory(t *testing.T) { | ||
90 | - n := 10<<20 + 25 | ||
91 | if testing.Short() { | ||
92 | - n = 10<<10 + 25 | ||
93 | + t.Skip("skipping in -short mode") | ||
94 | } | ||
95 | + n := 10 << 20 | ||
96 | largeTextValue := strings.Repeat("1", n) | ||
97 | message := `--MyBoundary | ||
98 | Content-Disposition: form-data; name="largetext" | ||
99 | @@ -203,38 +203,29 @@ Content-Disposition: form-data; name="largetext" | ||
100 | ` + largeTextValue + ` | ||
101 | --MyBoundary-- | ||
102 | ` | ||
103 | - | ||
104 | testBody := strings.ReplaceAll(message, "\n", "\r\n") | ||
105 | - testCases := []struct { | ||
106 | - name string | ||
107 | - maxMemory int64 | ||
108 | - err error | ||
109 | - }{ | ||
110 | - {"smaller", 50 + int64(len("largetext")) + 100, nil}, | ||
111 | - {"exact-fit", 25 + int64(len("largetext")) + 100, nil}, | ||
112 | - {"too-large", 0, ErrMessageTooLarge}, | ||
113 | - } | ||
114 | - for _, tc := range testCases { | ||
115 | - t.Run(tc.name, func(t *testing.T) { | ||
116 | - if tc.maxMemory == 0 && testing.Short() { | ||
117 | - t.Skip("skipping in -short mode") | ||
118 | - } | ||
119 | - b := strings.NewReader(testBody) | ||
120 | - r := NewReader(b, boundary) | ||
121 | - f, err := r.ReadForm(tc.maxMemory) | ||
122 | - if err == nil { | ||
123 | - defer f.RemoveAll() | ||
124 | - } | ||
125 | - if tc.err != err { | ||
126 | - t.Fatalf("ReadForm error - got: %v; expected: %v", err, tc.err) | ||
127 | - } | ||
128 | - if err == nil { | ||
129 | - if g := f.Value["largetext"][0]; g != largeTextValue { | ||
130 | - t.Errorf("largetext mismatch: got size: %v, expected size: %v", len(g), len(largeTextValue)) | ||
131 | - } | ||
132 | - } | ||
133 | - }) | ||
134 | + // Try parsing the form with increasing maxMemory values. | ||
135 | + // Changes in how we account for non-file form data may cause the exact point | ||
136 | + // where we change from rejecting the form as too large to accepting it to vary, | ||
137 | + // but we should see both successes and failures. | ||
138 | + const failWhenMaxMemoryLessThan = 128 | ||
139 | + for maxMemory := int64(0); maxMemory < failWhenMaxMemoryLessThan*2; maxMemory += 16 { | ||
140 | + b := strings.NewReader(testBody) | ||
141 | + r := NewReader(b, boundary) | ||
142 | + f, err := r.ReadForm(maxMemory) | ||
143 | + if err != nil { | ||
144 | + continue | ||
145 | + } | ||
146 | + if g := f.Value["largetext"][0]; g != largeTextValue { | ||
147 | + t.Errorf("largetext mismatch: got size: %v, expected size: %v", len(g), len(largeTextValue)) | ||
148 | + } | ||
149 | + f.RemoveAll() | ||
150 | + if maxMemory < failWhenMaxMemoryLessThan { | ||
151 | + t.Errorf("ReadForm(%v): no error, expect to hit memory limit when maxMemory < %v", maxMemory, failWhenMaxMemoryLessThan) | ||
152 | + } | ||
153 | + return | ||
154 | } | ||
155 | + t.Errorf("ReadForm(x) failed for x < 1024, expect success") | ||
156 | } | ||
157 | |||
158 | // TestReadForm_MetadataTooLarge verifies that we account for the size of field names, | ||
159 | diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go | ||
160 | index 9a21777df8be0..c1284fde25eb7 100644 | ||
161 | --- a/src/net/textproto/reader.go | ||
162 | +++ b/src/net/textproto/reader.go | ||
163 | @@ -503,6 +503,12 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { | ||
164 | |||
165 | m := make(MIMEHeader, hint) | ||
166 | |||
167 | + // Account for 400 bytes of overhead for the MIMEHeader, plus 200 bytes per entry. | ||
168 | + // Benchmarking map creation as of go1.20, a one-entry MIMEHeader is 416 bytes and large | ||
169 | + // MIMEHeaders average about 200 bytes per entry. | ||
170 | + lim -= 400 | ||
171 | + const mapEntryOverhead = 200 | ||
172 | + | ||
173 | // The first line cannot start with a leading space. | ||
174 | if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') { | ||
175 | line, err := r.readLineSlice() | ||
176 | @@ -538,7 +544,7 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { | ||
177 | vv := m[key] | ||
178 | if vv == nil { | ||
179 | lim -= int64(len(key)) | ||
180 | - lim -= 100 // map entry overhead | ||
181 | + lim -= mapEntryOverhead | ||
182 | } | ||
183 | lim -= int64(len(value)) | ||
184 | if lim < 0 { | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_3.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_3.patch new file mode 100644 index 0000000000..58c0a484ee --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24536_3.patch | |||
@@ -0,0 +1,349 @@ | |||
1 | From 7917b5f31204528ea72e0629f0b7d52b35b27538 Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Mon, 20 Mar 2023 10:43:19 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.19] mime/multipart: limit parsed mime message sizes | ||
5 | |||
6 | The parsed forms of MIME headers and multipart forms can consume | ||
7 | substantially more memory than the size of the input data. | ||
8 | A malicious input containing a very large number of headers or | ||
9 | form parts can cause excessively large memory allocations. | ||
10 | |||
11 | Set limits on the size of MIME data: | ||
12 | |||
13 | Reader.NextPart and Reader.NextRawPart limit the the number | ||
14 | of headers in a part to 10000. | ||
15 | |||
16 | Reader.ReadForm limits the total number of headers in all | ||
17 | FileHeaders to 10000. | ||
18 | |||
19 | Both of these limits may be set with with | ||
20 | GODEBUG=multipartmaxheaders=<values>. | ||
21 | |||
22 | Reader.ReadForm limits the number of parts in a form to 1000. | ||
23 | This limit may be set with GODEBUG=multipartmaxparts=<value>. | ||
24 | |||
25 | Thanks for Jakob Ackermann (@das7pad) for reporting this issue. | ||
26 | |||
27 | For CVE-2023-24536 | ||
28 | For #59153 | ||
29 | For #59269 | ||
30 | |||
31 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802455 | ||
32 | Run-TryBot: Damien Neil <dneil@google.com> | ||
33 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
34 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
35 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1801087 | ||
36 | Reviewed-by: Damien Neil <dneil@google.com> | ||
37 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
38 | Change-Id: If134890d75f0d95c681d67234daf191ba08e6424 | ||
39 | Reviewed-on: https://go-review.googlesource.com/c/go/+/481985 | ||
40 | Run-TryBot: Michael Knyszek <mknyszek@google.com> | ||
41 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
42 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
43 | Reviewed-by: Matthew Dempsky <mdempsky@google.com> | ||
44 | |||
45 | Upstream-Status: Backport [https://github.com/golang/go/commit/7917b5f31204528ea72e0629f0b7d52b35b27538] | ||
46 | CVE: CVE-2023-24536 | ||
47 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
48 | --- | ||
49 | src/mime/multipart/formdata.go | 19 ++++++++- | ||
50 | src/mime/multipart/formdata_test.go | 61 ++++++++++++++++++++++++++++ | ||
51 | src/mime/multipart/multipart.go | 31 ++++++++++---- | ||
52 | src/mime/multipart/readmimeheader.go | 2 +- | ||
53 | src/net/textproto/reader.go | 19 +++++---- | ||
54 | 5 files changed, 115 insertions(+), 17 deletions(-) | ||
55 | |||
56 | diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go | ||
57 | index 216cccb..0b508ae 100644 | ||
58 | --- a/src/mime/multipart/formdata.go | ||
59 | +++ b/src/mime/multipart/formdata.go | ||
60 | @@ -13,6 +13,7 @@ import ( | ||
61 | "math" | ||
62 | "net/textproto" | ||
63 | "os" | ||
64 | + "strconv" | ||
65 | ) | ||
66 | |||
67 | // ErrMessageTooLarge is returned by ReadForm if the message form | ||
68 | @@ -42,6 +43,15 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
69 | numDiskFiles := 0 | ||
70 | multipartFiles := godebug.Get("multipartfiles") | ||
71 | combineFiles := multipartFiles != "distinct" | ||
72 | + maxParts := 1000 | ||
73 | + multipartMaxParts := godebug.Get("multipartmaxparts") | ||
74 | + if multipartMaxParts != "" { | ||
75 | + if v, err := strconv.Atoi(multipartMaxParts); err == nil && v >= 0 { | ||
76 | + maxParts = v | ||
77 | + } | ||
78 | + } | ||
79 | + maxHeaders := maxMIMEHeaders() | ||
80 | + | ||
81 | defer func() { | ||
82 | if file != nil { | ||
83 | if cerr := file.Close(); err == nil { | ||
84 | @@ -87,13 +97,17 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
85 | } | ||
86 | var copyBuf []byte | ||
87 | for { | ||
88 | - p, err := r.nextPart(false, maxMemoryBytes) | ||
89 | + p, err := r.nextPart(false, maxMemoryBytes, maxHeaders) | ||
90 | if err == io.EOF { | ||
91 | break | ||
92 | } | ||
93 | if err != nil { | ||
94 | return nil, err | ||
95 | } | ||
96 | + if maxParts <= 0 { | ||
97 | + return nil, ErrMessageTooLarge | ||
98 | + } | ||
99 | + maxParts-- | ||
100 | |||
101 | name := p.FormName() | ||
102 | if name == "" { | ||
103 | @@ -137,6 +151,9 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) { | ||
104 | if maxMemoryBytes < 0 { | ||
105 | return nil, ErrMessageTooLarge | ||
106 | } | ||
107 | + for _, v := range p.Header { | ||
108 | + maxHeaders -= int64(len(v)) | ||
109 | + } | ||
110 | fh := &FileHeader{ | ||
111 | Filename: filename, | ||
112 | Header: p.Header, | ||
113 | diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go | ||
114 | index 8ed26e0..c78eeb7 100644 | ||
115 | --- a/src/mime/multipart/formdata_test.go | ||
116 | +++ b/src/mime/multipart/formdata_test.go | ||
117 | @@ -360,6 +360,67 @@ func testReadFormManyFiles(t *testing.T, distinct bool) { | ||
118 | } | ||
119 | } | ||
120 | |||
121 | +func TestReadFormLimits(t *testing.T) { | ||
122 | + for _, test := range []struct { | ||
123 | + values int | ||
124 | + files int | ||
125 | + extraKeysPerFile int | ||
126 | + wantErr error | ||
127 | + godebug string | ||
128 | + }{ | ||
129 | + {values: 1000}, | ||
130 | + {values: 1001, wantErr: ErrMessageTooLarge}, | ||
131 | + {values: 500, files: 500}, | ||
132 | + {values: 501, files: 500, wantErr: ErrMessageTooLarge}, | ||
133 | + {files: 1000}, | ||
134 | + {files: 1001, wantErr: ErrMessageTooLarge}, | ||
135 | + {files: 1, extraKeysPerFile: 9998}, // plus Content-Disposition and Content-Type | ||
136 | + {files: 1, extraKeysPerFile: 10000, wantErr: ErrMessageTooLarge}, | ||
137 | + {godebug: "multipartmaxparts=100", values: 100}, | ||
138 | + {godebug: "multipartmaxparts=100", values: 101, wantErr: ErrMessageTooLarge}, | ||
139 | + {godebug: "multipartmaxheaders=100", files: 2, extraKeysPerFile: 48}, | ||
140 | + {godebug: "multipartmaxheaders=100", files: 2, extraKeysPerFile: 50, wantErr: ErrMessageTooLarge}, | ||
141 | + } { | ||
142 | + name := fmt.Sprintf("values=%v/files=%v/extraKeysPerFile=%v", test.values, test.files, test.extraKeysPerFile) | ||
143 | + if test.godebug != "" { | ||
144 | + name += fmt.Sprintf("/godebug=%v", test.godebug) | ||
145 | + } | ||
146 | + t.Run(name, func(t *testing.T) { | ||
147 | + if test.godebug != "" { | ||
148 | + t.Setenv("GODEBUG", test.godebug) | ||
149 | + } | ||
150 | + var buf bytes.Buffer | ||
151 | + fw := NewWriter(&buf) | ||
152 | + for i := 0; i < test.values; i++ { | ||
153 | + w, _ := fw.CreateFormField(fmt.Sprintf("field%v", i)) | ||
154 | + fmt.Fprintf(w, "value %v", i) | ||
155 | + } | ||
156 | + for i := 0; i < test.files; i++ { | ||
157 | + h := make(textproto.MIMEHeader) | ||
158 | + h.Set("Content-Disposition", | ||
159 | + fmt.Sprintf(`form-data; name="file%v"; filename="file%v"`, i, i)) | ||
160 | + h.Set("Content-Type", "application/octet-stream") | ||
161 | + for j := 0; j < test.extraKeysPerFile; j++ { | ||
162 | + h.Set(fmt.Sprintf("k%v", j), "v") | ||
163 | + } | ||
164 | + w, _ := fw.CreatePart(h) | ||
165 | + fmt.Fprintf(w, "value %v", i) | ||
166 | + } | ||
167 | + if err := fw.Close(); err != nil { | ||
168 | + t.Fatal(err) | ||
169 | + } | ||
170 | + fr := NewReader(bytes.NewReader(buf.Bytes()), fw.Boundary()) | ||
171 | + form, err := fr.ReadForm(1 << 10) | ||
172 | + if err == nil { | ||
173 | + defer form.RemoveAll() | ||
174 | + } | ||
175 | + if err != test.wantErr { | ||
176 | + t.Errorf("ReadForm = %v, want %v", err, test.wantErr) | ||
177 | + } | ||
178 | + }) | ||
179 | + } | ||
180 | +} | ||
181 | + | ||
182 | func BenchmarkReadForm(b *testing.B) { | ||
183 | for _, test := range []struct { | ||
184 | name string | ||
185 | diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go | ||
186 | index 958cef8..94464a8 100644 | ||
187 | --- a/src/mime/multipart/multipart.go | ||
188 | +++ b/src/mime/multipart/multipart.go | ||
189 | @@ -16,11 +16,13 @@ import ( | ||
190 | "bufio" | ||
191 | "bytes" | ||
192 | "fmt" | ||
193 | + "internal/godebug" | ||
194 | "io" | ||
195 | "io/ioutil" | ||
196 | "mime" | ||
197 | "mime/quotedprintable" | ||
198 | "net/textproto" | ||
199 | + "strconv" | ||
200 | "strings" | ||
201 | ) | ||
202 | |||
203 | @@ -121,12 +123,12 @@ func (r *stickyErrorReader) Read(p []byte) (n int, _ error) { | ||
204 | return n, r.err | ||
205 | } | ||
206 | |||
207 | -func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { | ||
208 | +func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize, maxMIMEHeaders int64) (*Part, error) { | ||
209 | bp := &Part{ | ||
210 | Header: make(map[string][]string), | ||
211 | mr: mr, | ||
212 | } | ||
213 | - if err := bp.populateHeaders(maxMIMEHeaderSize); err != nil { | ||
214 | + if err := bp.populateHeaders(maxMIMEHeaderSize, maxMIMEHeaders); err != nil { | ||
215 | return nil, err | ||
216 | } | ||
217 | bp.r = partReader{bp} | ||
218 | @@ -142,9 +144,9 @@ func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { | ||
219 | return bp, nil | ||
220 | } | ||
221 | |||
222 | -func (bp *Part) populateHeaders(maxMIMEHeaderSize int64) error { | ||
223 | +func (bp *Part) populateHeaders(maxMIMEHeaderSize, maxMIMEHeaders int64) error { | ||
224 | r := textproto.NewReader(bp.mr.bufReader) | ||
225 | - header, err := readMIMEHeader(r, maxMIMEHeaderSize) | ||
226 | + header, err := readMIMEHeader(r, maxMIMEHeaderSize, maxMIMEHeaders) | ||
227 | if err == nil { | ||
228 | bp.Header = header | ||
229 | } | ||
230 | @@ -306,6 +308,19 @@ type Reader struct { | ||
231 | // including header keys, values, and map overhead. | ||
232 | const maxMIMEHeaderSize = 10 << 20 | ||
233 | |||
234 | +func maxMIMEHeaders() int64 { | ||
235 | + // multipartMaxHeaders is the maximum number of header entries NextPart will return, | ||
236 | + // as well as the maximum combined total of header entries Reader.ReadForm will return | ||
237 | + // in FileHeaders. | ||
238 | + multipartMaxHeaders := godebug.Get("multipartmaxheaders") | ||
239 | + if multipartMaxHeaders != "" { | ||
240 | + if v, err := strconv.ParseInt(multipartMaxHeaders, 10, 64); err == nil && v >= 0 { | ||
241 | + return v | ||
242 | + } | ||
243 | + } | ||
244 | + return 10000 | ||
245 | +} | ||
246 | + | ||
247 | // NextPart returns the next part in the multipart or an error. | ||
248 | // When there are no more parts, the error io.EOF is returned. | ||
249 | // | ||
250 | @@ -313,7 +328,7 @@ const maxMIMEHeaderSize = 10 << 20 | ||
251 | // has a value of "quoted-printable", that header is instead | ||
252 | // hidden and the body is transparently decoded during Read calls. | ||
253 | func (r *Reader) NextPart() (*Part, error) { | ||
254 | - return r.nextPart(false, maxMIMEHeaderSize) | ||
255 | + return r.nextPart(false, maxMIMEHeaderSize, maxMIMEHeaders()) | ||
256 | } | ||
257 | |||
258 | // NextRawPart returns the next part in the multipart or an error. | ||
259 | @@ -322,10 +337,10 @@ func (r *Reader) NextPart() (*Part, error) { | ||
260 | // Unlike NextPart, it does not have special handling for | ||
261 | // "Content-Transfer-Encoding: quoted-printable". | ||
262 | func (r *Reader) NextRawPart() (*Part, error) { | ||
263 | - return r.nextPart(true, maxMIMEHeaderSize) | ||
264 | + return r.nextPart(true, maxMIMEHeaderSize, maxMIMEHeaders()) | ||
265 | } | ||
266 | |||
267 | -func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize int64) (*Part, error) { | ||
268 | +func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize, maxMIMEHeaders int64) (*Part, error) { | ||
269 | if r.currentPart != nil { | ||
270 | r.currentPart.Close() | ||
271 | } | ||
272 | @@ -350,7 +365,7 @@ func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize int64) (*Part, error) | ||
273 | |||
274 | if r.isBoundaryDelimiterLine(line) { | ||
275 | r.partsRead++ | ||
276 | - bp, err := newPart(r, rawPart, maxMIMEHeaderSize) | ||
277 | + bp, err := newPart(r, rawPart, maxMIMEHeaderSize, maxMIMEHeaders) | ||
278 | if err != nil { | ||
279 | return nil, err | ||
280 | } | ||
281 | diff --git a/src/mime/multipart/readmimeheader.go b/src/mime/multipart/readmimeheader.go | ||
282 | index 6836928..25aa6e2 100644 | ||
283 | --- a/src/mime/multipart/readmimeheader.go | ||
284 | +++ b/src/mime/multipart/readmimeheader.go | ||
285 | @@ -11,4 +11,4 @@ import ( | ||
286 | // readMIMEHeader is defined in package net/textproto. | ||
287 | // | ||
288 | //go:linkname readMIMEHeader net/textproto.readMIMEHeader | ||
289 | -func readMIMEHeader(r *textproto.Reader, lim int64) (textproto.MIMEHeader, error) | ||
290 | +func readMIMEHeader(r *textproto.Reader, maxMemory, maxHeaders int64) (textproto.MIMEHeader, error) | ||
291 | diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go | ||
292 | index 1c79f0a..ad2d777 100644 | ||
293 | --- a/src/net/textproto/reader.go | ||
294 | +++ b/src/net/textproto/reader.go | ||
295 | @@ -484,12 +484,12 @@ func (r *Reader) ReadDotLines() ([]string, error) { | ||
296 | // } | ||
297 | // | ||
298 | func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { | ||
299 | - return readMIMEHeader(r, math.MaxInt64) | ||
300 | + return readMIMEHeader(r, math.MaxInt64, math.MaxInt64) | ||
301 | } | ||
302 | |||
303 | // readMIMEHeader is a version of ReadMIMEHeader which takes a limit on the header size. | ||
304 | // It is called by the mime/multipart package. | ||
305 | -func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { | ||
306 | +func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) { | ||
307 | // Avoid lots of small slice allocations later by allocating one | ||
308 | // large one ahead of time which we'll cut up into smaller | ||
309 | // slices. If this isn't big enough later, we allocate small ones. | ||
310 | @@ -507,7 +507,7 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { | ||
311 | // Account for 400 bytes of overhead for the MIMEHeader, plus 200 bytes per entry. | ||
312 | // Benchmarking map creation as of go1.20, a one-entry MIMEHeader is 416 bytes and large | ||
313 | // MIMEHeaders average about 200 bytes per entry. | ||
314 | - lim -= 400 | ||
315 | + maxMemory -= 400 | ||
316 | const mapEntryOverhead = 200 | ||
317 | |||
318 | // The first line cannot start with a leading space. | ||
319 | @@ -539,6 +539,11 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { | ||
320 | continue | ||
321 | } | ||
322 | |||
323 | + maxHeaders-- | ||
324 | + if maxHeaders < 0 { | ||
325 | + return nil, errors.New("message too large") | ||
326 | + } | ||
327 | + | ||
328 | // backport 5c55ac9bf1e5f779220294c843526536605f42ab | ||
329 | // | ||
330 | // value is computed as | ||
331 | @@ -557,11 +562,11 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) { | ||
332 | |||
333 | vv := m[key] | ||
334 | if vv == nil { | ||
335 | - lim -= int64(len(key)) | ||
336 | - lim -= mapEntryOverhead | ||
337 | + maxMemory -= int64(len(key)) | ||
338 | + maxMemory -= mapEntryOverhead | ||
339 | } | ||
340 | - lim -= int64(len(value)) | ||
341 | - if lim < 0 { | ||
342 | + maxMemory -= int64(len(value)) | ||
343 | + if maxMemory < 0 { | ||
344 | // TODO: This should be a distinguishable error (ErrMessageTooLarge) | ||
345 | // to allow mime/multipart to detect it. | ||
346 | return m, errors.New("message too large") | ||
347 | -- | ||
348 | 2.25.1 | ||
349 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24537.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24537.patch new file mode 100644 index 0000000000..e04b717fc1 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24537.patch | |||
@@ -0,0 +1,76 @@ | |||
1 | From bf8c7c575c8a552d9d79deb29e80854dc88528d0 Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Mon, 20 Mar 2023 10:43:19 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.20] mime/multipart: limit parsed mime | ||
5 | message sizes | ||
6 | |||
7 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802456 | ||
8 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
9 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
10 | Run-TryBot: Damien Neil <dneil@google.com> | ||
11 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802611 | ||
12 | Reviewed-by: Damien Neil <dneil@google.com> | ||
13 | Change-Id: Ifdfa192d54f722d781a4d8c5f35b5fb72d122168 | ||
14 | Reviewed-on: https://go-review.googlesource.com/c/go/+/481986 | ||
15 | Reviewed-by: Matthew Dempsky <mdempsky@google.com> | ||
16 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
17 | Run-TryBot: Michael Knyszek <mknyszek@google.com> | ||
18 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
19 | |||
20 | Upstream-Status: Backport [https://github.com/golang/go/commit/126a1d02da82f93ede7ce0bd8d3c51ef627f2104] | ||
21 | CVE: CVE-2023-24537 | ||
22 | Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com> | ||
23 | --- | ||
24 | src/go/parser/parser_test.go | 16 ++++++++++++++++ | ||
25 | src/go/scanner/scanner.go | 5 ++++- | ||
26 | 2 files changed, 20 insertions(+), 1 deletion(-) | ||
27 | |||
28 | diff --git a/src/go/parser/parser_test.go b/src/go/parser/parser_test.go | ||
29 | index 37a6a2b..714557c 100644 | ||
30 | --- a/src/go/parser/parser_test.go | ||
31 | +++ b/src/go/parser/parser_test.go | ||
32 | @@ -738,3 +738,19 @@ func TestScopeDepthLimit(t *testing.T) { | ||
33 | } | ||
34 | } | ||
35 | } | ||
36 | + | ||
37 | +// TestIssue59180 tests that line number overflow doesn't cause an infinite loop. | ||
38 | +func TestIssue59180(t *testing.T) { | ||
39 | + testcases := []string{ | ||
40 | + "package p\n//line :9223372036854775806\n\n//", | ||
41 | + "package p\n//line :1:9223372036854775806\n\n//", | ||
42 | + "package p\n//line file:9223372036854775806\n\n//", | ||
43 | + } | ||
44 | + | ||
45 | + for _, src := range testcases { | ||
46 | + _, err := ParseFile(token.NewFileSet(), "", src, ParseComments) | ||
47 | + if err == nil { | ||
48 | + t.Errorf("ParseFile(%s) succeeded unexpectedly", src) | ||
49 | + } | ||
50 | + } | ||
51 | +} | ||
52 | diff --git a/src/go/scanner/scanner.go b/src/go/scanner/scanner.go | ||
53 | index 00fe2dc..3159d25 100644 | ||
54 | --- a/src/go/scanner/scanner.go | ||
55 | +++ b/src/go/scanner/scanner.go | ||
56 | @@ -246,13 +246,16 @@ func (s *Scanner) updateLineInfo(next, offs int, text []byte) { | ||
57 | return | ||
58 | } | ||
59 | |||
60 | + // Put a cap on the maximum size of line and column numbers. | ||
61 | + // 30 bits allows for some additional space before wrapping an int32. | ||
62 | + const maxLineCol = 1<<30 - 1 | ||
63 | var line, col int | ||
64 | i2, n2, ok2 := trailingDigits(text[:i-1]) | ||
65 | if ok2 { | ||
66 | //line filename:line:col | ||
67 | i, i2 = i2, i | ||
68 | line, col = n2, n | ||
69 | - if col == 0 { | ||
70 | + if col == 0 || col > maxLineCol { | ||
71 | s.error(offs+i2, "invalid column number: "+string(text[i2:])) | ||
72 | return | ||
73 | } | ||
74 | -- | ||
75 | 2.25.1 | ||
76 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch new file mode 100644 index 0000000000..23c5075e41 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-1.patch | |||
@@ -0,0 +1,125 @@ | |||
1 | From 8acd01094d9ee17f6e763a61e49a8a808b3a9ddb Mon Sep 17 00:00:00 2001 | ||
2 | From: Brad Fitzpatrick <bradfitz@golang.org> | ||
3 | Date: Mon, 2 Aug 2021 14:55:51 -0700 | ||
4 | Subject: [PATCH 1/6] net/netip: add new IP address package | ||
5 | |||
6 | Co-authored-by: Alex Willmer <alex@moreati.org.uk> (GitHub @moreati) | ||
7 | Co-authored-by: Alexander Yastrebov <yastrebov.alex@gmail.com> | ||
8 | Co-authored-by: David Anderson <dave@natulte.net> (Tailscale CLA) | ||
9 | Co-authored-by: David Crawshaw <crawshaw@tailscale.com> (Tailscale CLA) | ||
10 | Co-authored-by: Dmytro Shynkevych <dmytro@tailscale.com> (Tailscale CLA) | ||
11 | Co-authored-by: Elias Naur <mail@eliasnaur.com> | ||
12 | Co-authored-by: Joe Tsai <joetsai@digital-static.net> (Tailscale CLA) | ||
13 | Co-authored-by: Jonathan Yu <jawnsy@cpan.org> (GitHub @jawnsy) | ||
14 | Co-authored-by: Josh Bleecher Snyder <josharian@gmail.com> (Tailscale CLA) | ||
15 | Co-authored-by: Maisem Ali <maisem@tailscale.com> (Tailscale CLA) | ||
16 | Co-authored-by: Manuel Mendez (Go AUTHORS mmendez534@...) | ||
17 | Co-authored-by: Matt Layher <mdlayher@gmail.com> | ||
18 | Co-authored-by: Noah Treuhaft <noah.treuhaft@gmail.com> (GitHub @nwt) | ||
19 | Co-authored-by: Stefan Majer <stefan.majer@gmail.com> | ||
20 | Co-authored-by: Terin Stock <terinjokes@gmail.com> (Cloudflare CLA) | ||
21 | Co-authored-by: Tobias Klauser <tklauser@distanz.ch> | ||
22 | |||
23 | Fixes #46518 | ||
24 | |||
25 | Change-Id: I0041f9e1115d61fa6e95fcf32b01d9faee708712 | ||
26 | Reviewed-on: https://go-review.googlesource.com/c/go/+/339309 | ||
27 | Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> | ||
28 | TryBot-Result: Go Bot <gobot@golang.org> | ||
29 | Reviewed-by: Russ Cox <rsc@golang.org> | ||
30 | Trust: Brad Fitzpatrick <bradfitz@golang.org> | ||
31 | |||
32 | Dependency Patch #1 | ||
33 | |||
34 | Upstream-Status: Backport from https://github.com/golang/go/commit/a59e33224e42d60a97fa720a45e1b74eb6aaa3d0 | ||
35 | CVE: CVE-2023-24538 | ||
36 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
37 | --- | ||
38 | src/internal/godebug/godebug.go | 34 ++++++++++++++++++++++++++++++++++ | ||
39 | src/internal/godebug/godebug_test.go | 34 ++++++++++++++++++++++++++++++++++ | ||
40 | 2 files changed, 68 insertions(+) | ||
41 | create mode 100644 src/internal/godebug/godebug.go | ||
42 | create mode 100644 src/internal/godebug/godebug_test.go | ||
43 | |||
44 | diff --git a/src/internal/godebug/godebug.go b/src/internal/godebug/godebug.go | ||
45 | new file mode 100644 | ||
46 | index 0000000..ac434e5 | ||
47 | --- /dev/null | ||
48 | +++ b/src/internal/godebug/godebug.go | ||
49 | @@ -0,0 +1,34 @@ | ||
50 | +// Copyright 2021 The Go Authors. All rights reserved. | ||
51 | +// Use of this source code is governed by a BSD-style | ||
52 | +// license that can be found in the LICENSE file. | ||
53 | + | ||
54 | +// Package godebug parses the GODEBUG environment variable. | ||
55 | +package godebug | ||
56 | + | ||
57 | +import "os" | ||
58 | + | ||
59 | +// Get returns the value for the provided GODEBUG key. | ||
60 | +func Get(key string) string { | ||
61 | + return get(os.Getenv("GODEBUG"), key) | ||
62 | +} | ||
63 | + | ||
64 | +// get returns the value part of key=value in s (a GODEBUG value). | ||
65 | +func get(s, key string) string { | ||
66 | + for i := 0; i < len(s)-len(key)-1; i++ { | ||
67 | + if i > 0 && s[i-1] != ',' { | ||
68 | + continue | ||
69 | + } | ||
70 | + afterKey := s[i+len(key):] | ||
71 | + if afterKey[0] != '=' || s[i:i+len(key)] != key { | ||
72 | + continue | ||
73 | + } | ||
74 | + val := afterKey[1:] | ||
75 | + for i, b := range val { | ||
76 | + if b == ',' { | ||
77 | + return val[:i] | ||
78 | + } | ||
79 | + } | ||
80 | + return val | ||
81 | + } | ||
82 | + return "" | ||
83 | +} | ||
84 | diff --git a/src/internal/godebug/godebug_test.go b/src/internal/godebug/godebug_test.go | ||
85 | new file mode 100644 | ||
86 | index 0000000..41b9117 | ||
87 | --- /dev/null | ||
88 | +++ b/src/internal/godebug/godebug_test.go | ||
89 | @@ -0,0 +1,34 @@ | ||
90 | +// Copyright 2021 The Go Authors. All rights reserved. | ||
91 | +// Use of this source code is governed by a BSD-style | ||
92 | +// license that can be found in the LICENSE file. | ||
93 | + | ||
94 | +package godebug | ||
95 | + | ||
96 | +import "testing" | ||
97 | + | ||
98 | +func TestGet(t *testing.T) { | ||
99 | + tests := []struct { | ||
100 | + godebug string | ||
101 | + key string | ||
102 | + want string | ||
103 | + }{ | ||
104 | + {"", "", ""}, | ||
105 | + {"", "foo", ""}, | ||
106 | + {"foo=bar", "foo", "bar"}, | ||
107 | + {"foo=bar,after=x", "foo", "bar"}, | ||
108 | + {"before=x,foo=bar,after=x", "foo", "bar"}, | ||
109 | + {"before=x,foo=bar", "foo", "bar"}, | ||
110 | + {",,,foo=bar,,,", "foo", "bar"}, | ||
111 | + {"foodecoy=wrong,foo=bar", "foo", "bar"}, | ||
112 | + {"foo=", "foo", ""}, | ||
113 | + {"foo", "foo", ""}, | ||
114 | + {",foo", "foo", ""}, | ||
115 | + {"foo=bar,baz", "loooooooong", ""}, | ||
116 | + } | ||
117 | + for _, tt := range tests { | ||
118 | + got := get(tt.godebug, tt.key) | ||
119 | + if got != tt.want { | ||
120 | + t.Errorf("get(%q, %q) = %q; want %q", tt.godebug, tt.key, got, tt.want) | ||
121 | + } | ||
122 | + } | ||
123 | +} | ||
124 | -- | ||
125 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-2.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-2.patch new file mode 100644 index 0000000000..f200c41e16 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538-2.patch | |||
@@ -0,0 +1,635 @@ | |||
1 | From 6fc21505614f36178df0dad7034b6b8e3f7588d5 Mon Sep 17 00:00:00 2001 | ||
2 | From: empijei <robclap8@gmail.com> | ||
3 | Date: Fri, 27 Mar 2020 19:27:55 +0100 | ||
4 | Subject: [PATCH 2/6] html/template,text/template: switch to Unicode escapes | ||
5 | for JSON compatibility | ||
6 | MIME-Version: 1.0 | ||
7 | Content-Type: text/plain; charset=UTF-8 | ||
8 | Content-Transfer-Encoding: 8bit | ||
9 | |||
10 | The existing implementation is not compatible with JSON | ||
11 | escape as it uses hex escaping. | ||
12 | Unicode escape, instead, is valid for both JSON and JS. | ||
13 | This fix avoids creating a separate escaping context for | ||
14 | scripts of type "application/ld+json" and it is more | ||
15 | future-proof in case more JSON+JS contexts get added | ||
16 | to the platform (e.g. import maps). | ||
17 | |||
18 | Fixes #33671 | ||
19 | Fixes #37634 | ||
20 | |||
21 | Change-Id: Id6f6524b4abc52e81d9d744d46bbe5bf2e081543 | ||
22 | Reviewed-on: https://go-review.googlesource.com/c/go/+/226097 | ||
23 | Reviewed-by: Carl Johnson <me@carlmjohnson.net> | ||
24 | Reviewed-by: Daniel Martí <mvdan@mvdan.cc> | ||
25 | Run-TryBot: Daniel Martí <mvdan@mvdan.cc> | ||
26 | TryBot-Result: Gobot Gobot <gobot@golang.org> | ||
27 | |||
28 | Dependency Patch #2 | ||
29 | |||
30 | Upstream-Status: Backport from https://github.com/golang/go/commit/d4d298040d072ddacea0e0d6b55fb148fff18070 | ||
31 | CVE: CVE-2023-24538 | ||
32 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
33 | --- | ||
34 | src/html/template/content_test.go | 70 +++++++++++++++++++------------------- | ||
35 | src/html/template/escape_test.go | 6 ++-- | ||
36 | src/html/template/example_test.go | 6 ++-- | ||
37 | src/html/template/js.go | 70 +++++++++++++++++++++++--------------- | ||
38 | src/html/template/js_test.go | 68 ++++++++++++++++++------------------ | ||
39 | src/html/template/template_test.go | 39 +++++++++++++++++++++ | ||
40 | src/text/template/exec_test.go | 6 ++-- | ||
41 | src/text/template/funcs.go | 8 ++--- | ||
42 | 8 files changed, 163 insertions(+), 110 deletions(-) | ||
43 | |||
44 | diff --git a/src/html/template/content_test.go b/src/html/template/content_test.go | ||
45 | index 72d56f5..bd86527 100644 | ||
46 | --- a/src/html/template/content_test.go | ||
47 | +++ b/src/html/template/content_test.go | ||
48 | @@ -18,7 +18,7 @@ func TestTypedContent(t *testing.T) { | ||
49 | HTML(`Hello, <b>World</b> &tc!`), | ||
50 | HTMLAttr(` dir="ltr"`), | ||
51 | JS(`c && alert("Hello, World!");`), | ||
52 | - JSStr(`Hello, World & O'Reilly\x21`), | ||
53 | + JSStr(`Hello, World & O'Reilly\u0021`), | ||
54 | URL(`greeting=H%69,&addressee=(World)`), | ||
55 | Srcset(`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`), | ||
56 | URL(`,foo/,`), | ||
57 | @@ -70,7 +70,7 @@ func TestTypedContent(t *testing.T) { | ||
58 | `Hello, <b>World</b> &tc!`, | ||
59 | ` dir="ltr"`, | ||
60 | `c && alert("Hello, World!");`, | ||
61 | - `Hello, World & O'Reilly\x21`, | ||
62 | + `Hello, World & O'Reilly\u0021`, | ||
63 | `greeting=H%69,&addressee=(World)`, | ||
64 | `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | ||
65 | `,foo/,`, | ||
66 | @@ -100,7 +100,7 @@ func TestTypedContent(t *testing.T) { | ||
67 | `Hello, World &tc!`, | ||
68 | ` dir="ltr"`, | ||
69 | `c && alert("Hello, World!");`, | ||
70 | - `Hello, World & O'Reilly\x21`, | ||
71 | + `Hello, World & O'Reilly\u0021`, | ||
72 | `greeting=H%69,&addressee=(World)`, | ||
73 | `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | ||
74 | `,foo/,`, | ||
75 | @@ -115,7 +115,7 @@ func TestTypedContent(t *testing.T) { | ||
76 | `Hello, World &tc!`, | ||
77 | ` dir="ltr"`, | ||
78 | `c && alert("Hello, World!");`, | ||
79 | - `Hello, World & O'Reilly\x21`, | ||
80 | + `Hello, World & O'Reilly\u0021`, | ||
81 | `greeting=H%69,&addressee=(World)`, | ||
82 | `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | ||
83 | `,foo/,`, | ||
84 | @@ -130,7 +130,7 @@ func TestTypedContent(t *testing.T) { | ||
85 | `Hello, <b>World</b> &tc!`, | ||
86 | ` dir="ltr"`, | ||
87 | `c && alert("Hello, World!");`, | ||
88 | - `Hello, World & O'Reilly\x21`, | ||
89 | + `Hello, World & O'Reilly\u0021`, | ||
90 | `greeting=H%69,&addressee=(World)`, | ||
91 | `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | ||
92 | `,foo/,`, | ||
93 | @@ -146,7 +146,7 @@ func TestTypedContent(t *testing.T) { | ||
94 | // Not escaped. | ||
95 | `c && alert("Hello, World!");`, | ||
96 | // Escape sequence not over-escaped. | ||
97 | - `"Hello, World & O'Reilly\x21"`, | ||
98 | + `"Hello, World & O'Reilly\u0021"`, | ||
99 | `"greeting=H%69,\u0026addressee=(World)"`, | ||
100 | `"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`, | ||
101 | `",foo/,"`, | ||
102 | @@ -162,7 +162,7 @@ func TestTypedContent(t *testing.T) { | ||
103 | // Not JS escaped but HTML escaped. | ||
104 | `c && alert("Hello, World!");`, | ||
105 | // Escape sequence not over-escaped. | ||
106 | - `"Hello, World & O'Reilly\x21"`, | ||
107 | + `"Hello, World & O'Reilly\u0021"`, | ||
108 | `"greeting=H%69,\u0026addressee=(World)"`, | ||
109 | `"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`, | ||
110 | `",foo/,"`, | ||
111 | @@ -171,30 +171,30 @@ func TestTypedContent(t *testing.T) { | ||
112 | { | ||
113 | `<script>alert("{{.}}")</script>`, | ||
114 | []string{ | ||
115 | - `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`, | ||
116 | - `a[href =~ \x22\/\/example.com\x22]#foo`, | ||
117 | - `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`, | ||
118 | - ` dir=\x22ltr\x22`, | ||
119 | - `c \x26\x26 alert(\x22Hello, World!\x22);`, | ||
120 | + `\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`, | ||
121 | + `a[href =~ \u0022\/\/example.com\u0022]#foo`, | ||
122 | + `Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`, | ||
123 | + ` dir=\u0022ltr\u0022`, | ||
124 | + `c \u0026\u0026 alert(\u0022Hello, World!\u0022);`, | ||
125 | // Escape sequence not over-escaped. | ||
126 | - `Hello, World \x26 O\x27Reilly\x21`, | ||
127 | - `greeting=H%69,\x26addressee=(World)`, | ||
128 | - `greeting=H%69,\x26addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`, | ||
129 | + `Hello, World \u0026 O\u0027Reilly\u0021`, | ||
130 | + `greeting=H%69,\u0026addressee=(World)`, | ||
131 | + `greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`, | ||
132 | `,foo\/,`, | ||
133 | }, | ||
134 | }, | ||
135 | { | ||
136 | `<script type="text/javascript">alert("{{.}}")</script>`, | ||
137 | []string{ | ||
138 | - `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`, | ||
139 | - `a[href =~ \x22\/\/example.com\x22]#foo`, | ||
140 | - `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`, | ||
141 | - ` dir=\x22ltr\x22`, | ||
142 | - `c \x26\x26 alert(\x22Hello, World!\x22);`, | ||
143 | + `\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`, | ||
144 | + `a[href =~ \u0022\/\/example.com\u0022]#foo`, | ||
145 | + `Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`, | ||
146 | + ` dir=\u0022ltr\u0022`, | ||
147 | + `c \u0026\u0026 alert(\u0022Hello, World!\u0022);`, | ||
148 | // Escape sequence not over-escaped. | ||
149 | - `Hello, World \x26 O\x27Reilly\x21`, | ||
150 | - `greeting=H%69,\x26addressee=(World)`, | ||
151 | - `greeting=H%69,\x26addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`, | ||
152 | + `Hello, World \u0026 O\u0027Reilly\u0021`, | ||
153 | + `greeting=H%69,\u0026addressee=(World)`, | ||
154 | + `greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`, | ||
155 | `,foo\/,`, | ||
156 | }, | ||
157 | }, | ||
158 | @@ -208,7 +208,7 @@ func TestTypedContent(t *testing.T) { | ||
159 | // Not escaped. | ||
160 | `c && alert("Hello, World!");`, | ||
161 | // Escape sequence not over-escaped. | ||
162 | - `"Hello, World & O'Reilly\x21"`, | ||
163 | + `"Hello, World & O'Reilly\u0021"`, | ||
164 | `"greeting=H%69,\u0026addressee=(World)"`, | ||
165 | `"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`, | ||
166 | `",foo/,"`, | ||
167 | @@ -224,7 +224,7 @@ func TestTypedContent(t *testing.T) { | ||
168 | `Hello, <b>World</b> &tc!`, | ||
169 | ` dir="ltr"`, | ||
170 | `c && alert("Hello, World!");`, | ||
171 | - `Hello, World & O'Reilly\x21`, | ||
172 | + `Hello, World & O'Reilly\u0021`, | ||
173 | `greeting=H%69,&addressee=(World)`, | ||
174 | `greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | ||
175 | `,foo/,`, | ||
176 | @@ -233,15 +233,15 @@ func TestTypedContent(t *testing.T) { | ||
177 | { | ||
178 | `<button onclick='alert("{{.}}")'>`, | ||
179 | []string{ | ||
180 | - `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`, | ||
181 | - `a[href =~ \x22\/\/example.com\x22]#foo`, | ||
182 | - `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`, | ||
183 | - ` dir=\x22ltr\x22`, | ||
184 | - `c \x26\x26 alert(\x22Hello, World!\x22);`, | ||
185 | + `\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`, | ||
186 | + `a[href =~ \u0022\/\/example.com\u0022]#foo`, | ||
187 | + `Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`, | ||
188 | + ` dir=\u0022ltr\u0022`, | ||
189 | + `c \u0026\u0026 alert(\u0022Hello, World!\u0022);`, | ||
190 | // Escape sequence not over-escaped. | ||
191 | - `Hello, World \x26 O\x27Reilly\x21`, | ||
192 | - `greeting=H%69,\x26addressee=(World)`, | ||
193 | - `greeting=H%69,\x26addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`, | ||
194 | + `Hello, World \u0026 O\u0027Reilly\u0021`, | ||
195 | + `greeting=H%69,\u0026addressee=(World)`, | ||
196 | + `greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`, | ||
197 | `,foo\/,`, | ||
198 | }, | ||
199 | }, | ||
200 | @@ -253,7 +253,7 @@ func TestTypedContent(t *testing.T) { | ||
201 | `Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`, | ||
202 | `%20dir%3d%22ltr%22`, | ||
203 | `c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`, | ||
204 | - `Hello%2c%20World%20%26%20O%27Reilly%5cx21`, | ||
205 | + `Hello%2c%20World%20%26%20O%27Reilly%5cu0021`, | ||
206 | // Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done. | ||
207 | `greeting=H%69,&addressee=%28World%29`, | ||
208 | `greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`, | ||
209 | @@ -268,7 +268,7 @@ func TestTypedContent(t *testing.T) { | ||
210 | `Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`, | ||
211 | `%20dir%3d%22ltr%22`, | ||
212 | `c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`, | ||
213 | - `Hello%2c%20World%20%26%20O%27Reilly%5cx21`, | ||
214 | + `Hello%2c%20World%20%26%20O%27Reilly%5cu0021`, | ||
215 | // Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is not done. | ||
216 | `greeting=H%69,&addressee=%28World%29`, | ||
217 | `greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`, | ||
218 | diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go | ||
219 | index e72a9ba..c709660 100644 | ||
220 | --- a/src/html/template/escape_test.go | ||
221 | +++ b/src/html/template/escape_test.go | ||
222 | @@ -238,7 +238,7 @@ func TestEscape(t *testing.T) { | ||
223 | { | ||
224 | "jsStr", | ||
225 | "<button onclick='alert("{{.H}}")'>", | ||
226 | - `<button onclick='alert("\x3cHello\x3e")'>`, | ||
227 | + `<button onclick='alert("\u003cHello\u003e")'>`, | ||
228 | }, | ||
229 | { | ||
230 | "badMarshaler", | ||
231 | @@ -259,7 +259,7 @@ func TestEscape(t *testing.T) { | ||
232 | { | ||
233 | "jsRe", | ||
234 | `<button onclick='alert(/{{"foo+bar"}}/.test(""))'>`, | ||
235 | - `<button onclick='alert(/foo\x2bbar/.test(""))'>`, | ||
236 | + `<button onclick='alert(/foo\u002bbar/.test(""))'>`, | ||
237 | }, | ||
238 | { | ||
239 | "jsReBlank", | ||
240 | @@ -825,7 +825,7 @@ func TestEscapeSet(t *testing.T) { | ||
241 | "main": `<button onclick="title='{{template "helper"}}'; ...">{{template "helper"}}</button>`, | ||
242 | "helper": `{{11}} of {{"<100>"}}`, | ||
243 | }, | ||
244 | - `<button onclick="title='11 of \x3c100\x3e'; ...">11 of <100></button>`, | ||
245 | + `<button onclick="title='11 of \u003c100\u003e'; ...">11 of <100></button>`, | ||
246 | }, | ||
247 | // A non-recursive template that ends in a different context. | ||
248 | // helper starts in jsCtxRegexp and ends in jsCtxDivOp. | ||
249 | diff --git a/src/html/template/example_test.go b/src/html/template/example_test.go | ||
250 | index 9d965f1..6cf936f 100644 | ||
251 | --- a/src/html/template/example_test.go | ||
252 | +++ b/src/html/template/example_test.go | ||
253 | @@ -116,9 +116,9 @@ func Example_escape() { | ||
254 | // "Fran & Freddie's Diner" <tasty@example.com> | ||
255 | // "Fran & Freddie's Diner" <tasty@example.com> | ||
256 | // "Fran & Freddie's Diner"32<tasty@example.com> | ||
257 | - // \"Fran \x26 Freddie\'s Diner\" \x3Ctasty@example.com\x3E | ||
258 | - // \"Fran \x26 Freddie\'s Diner\" \x3Ctasty@example.com\x3E | ||
259 | - // \"Fran \x26 Freddie\'s Diner\"32\x3Ctasty@example.com\x3E | ||
260 | + // \"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E | ||
261 | + // \"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E | ||
262 | + // \"Fran \u0026 Freddie\'s Diner\"32\u003Ctasty@example.com\u003E | ||
263 | // %22Fran+%26+Freddie%27s+Diner%2232%3Ctasty%40example.com%3E | ||
264 | |||
265 | } | ||
266 | diff --git a/src/html/template/js.go b/src/html/template/js.go | ||
267 | index 0e91458..ea9c183 100644 | ||
268 | --- a/src/html/template/js.go | ||
269 | +++ b/src/html/template/js.go | ||
270 | @@ -163,7 +163,6 @@ func jsValEscaper(args ...interface{}) string { | ||
271 | } | ||
272 | // TODO: detect cycles before calling Marshal which loops infinitely on | ||
273 | // cyclic data. This may be an unacceptable DoS risk. | ||
274 | - | ||
275 | b, err := json.Marshal(a) | ||
276 | if err != nil { | ||
277 | // Put a space before comment so that if it is flush against | ||
278 | @@ -178,8 +177,8 @@ func jsValEscaper(args ...interface{}) string { | ||
279 | // TODO: maybe post-process output to prevent it from containing | ||
280 | // "<!--", "-->", "<![CDATA[", "]]>", or "</script" | ||
281 | // in case custom marshalers produce output containing those. | ||
282 | - | ||
283 | - // TODO: Maybe abbreviate \u00ab to \xab to produce more compact output. | ||
284 | + // Note: Do not use \x escaping to save bytes because it is not JSON compatible and this escaper | ||
285 | + // supports ld+json content-type. | ||
286 | if len(b) == 0 { | ||
287 | // In, `x=y/{{.}}*z` a json.Marshaler that produces "" should | ||
288 | // not cause the output `x=y/*z`. | ||
289 | @@ -260,6 +259,8 @@ func replace(s string, replacementTable []string) string { | ||
290 | r, w = utf8.DecodeRuneInString(s[i:]) | ||
291 | var repl string | ||
292 | switch { | ||
293 | + case int(r) < len(lowUnicodeReplacementTable): | ||
294 | + repl = lowUnicodeReplacementTable[r] | ||
295 | case int(r) < len(replacementTable) && replacementTable[r] != "": | ||
296 | repl = replacementTable[r] | ||
297 | case r == '\u2028': | ||
298 | @@ -283,67 +284,80 @@ func replace(s string, replacementTable []string) string { | ||
299 | return b.String() | ||
300 | } | ||
301 | |||
302 | +var lowUnicodeReplacementTable = []string{ | ||
303 | + 0: `\u0000`, 1: `\u0001`, 2: `\u0002`, 3: `\u0003`, 4: `\u0004`, 5: `\u0005`, 6: `\u0006`, | ||
304 | + '\a': `\u0007`, | ||
305 | + '\b': `\u0008`, | ||
306 | + '\t': `\t`, | ||
307 | + '\n': `\n`, | ||
308 | + '\v': `\u000b`, // "\v" == "v" on IE 6. | ||
309 | + '\f': `\f`, | ||
310 | + '\r': `\r`, | ||
311 | + 0xe: `\u000e`, 0xf: `\u000f`, 0x10: `\u0010`, 0x11: `\u0011`, 0x12: `\u0012`, 0x13: `\u0013`, | ||
312 | + 0x14: `\u0014`, 0x15: `\u0015`, 0x16: `\u0016`, 0x17: `\u0017`, 0x18: `\u0018`, 0x19: `\u0019`, | ||
313 | + 0x1a: `\u001a`, 0x1b: `\u001b`, 0x1c: `\u001c`, 0x1d: `\u001d`, 0x1e: `\u001e`, 0x1f: `\u001f`, | ||
314 | +} | ||
315 | + | ||
316 | var jsStrReplacementTable = []string{ | ||
317 | - 0: `\0`, | ||
318 | + 0: `\u0000`, | ||
319 | '\t': `\t`, | ||
320 | '\n': `\n`, | ||
321 | - '\v': `\x0b`, // "\v" == "v" on IE 6. | ||
322 | + '\v': `\u000b`, // "\v" == "v" on IE 6. | ||
323 | '\f': `\f`, | ||
324 | '\r': `\r`, | ||
325 | // Encode HTML specials as hex so the output can be embedded | ||
326 | // in HTML attributes without further encoding. | ||
327 | - '"': `\x22`, | ||
328 | - '&': `\x26`, | ||
329 | - '\'': `\x27`, | ||
330 | - '+': `\x2b`, | ||
331 | + '"': `\u0022`, | ||
332 | + '&': `\u0026`, | ||
333 | + '\'': `\u0027`, | ||
334 | + '+': `\u002b`, | ||
335 | '/': `\/`, | ||
336 | - '<': `\x3c`, | ||
337 | - '>': `\x3e`, | ||
338 | + '<': `\u003c`, | ||
339 | + '>': `\u003e`, | ||
340 | '\\': `\\`, | ||
341 | } | ||
342 | |||
343 | // jsStrNormReplacementTable is like jsStrReplacementTable but does not | ||
344 | // overencode existing escapes since this table has no entry for `\`. | ||
345 | var jsStrNormReplacementTable = []string{ | ||
346 | - 0: `\0`, | ||
347 | + 0: `\u0000`, | ||
348 | '\t': `\t`, | ||
349 | '\n': `\n`, | ||
350 | - '\v': `\x0b`, // "\v" == "v" on IE 6. | ||
351 | + '\v': `\u000b`, // "\v" == "v" on IE 6. | ||
352 | '\f': `\f`, | ||
353 | '\r': `\r`, | ||
354 | // Encode HTML specials as hex so the output can be embedded | ||
355 | // in HTML attributes without further encoding. | ||
356 | - '"': `\x22`, | ||
357 | - '&': `\x26`, | ||
358 | - '\'': `\x27`, | ||
359 | - '+': `\x2b`, | ||
360 | + '"': `\u0022`, | ||
361 | + '&': `\u0026`, | ||
362 | + '\'': `\u0027`, | ||
363 | + '+': `\u002b`, | ||
364 | '/': `\/`, | ||
365 | - '<': `\x3c`, | ||
366 | - '>': `\x3e`, | ||
367 | + '<': `\u003c`, | ||
368 | + '>': `\u003e`, | ||
369 | } | ||
370 | - | ||
371 | var jsRegexpReplacementTable = []string{ | ||
372 | - 0: `\0`, | ||
373 | + 0: `\u0000`, | ||
374 | '\t': `\t`, | ||
375 | '\n': `\n`, | ||
376 | - '\v': `\x0b`, // "\v" == "v" on IE 6. | ||
377 | + '\v': `\u000b`, // "\v" == "v" on IE 6. | ||
378 | '\f': `\f`, | ||
379 | '\r': `\r`, | ||
380 | // Encode HTML specials as hex so the output can be embedded | ||
381 | // in HTML attributes without further encoding. | ||
382 | - '"': `\x22`, | ||
383 | + '"': `\u0022`, | ||
384 | '$': `\$`, | ||
385 | - '&': `\x26`, | ||
386 | - '\'': `\x27`, | ||
387 | + '&': `\u0026`, | ||
388 | + '\'': `\u0027`, | ||
389 | '(': `\(`, | ||
390 | ')': `\)`, | ||
391 | '*': `\*`, | ||
392 | - '+': `\x2b`, | ||
393 | + '+': `\u002b`, | ||
394 | '-': `\-`, | ||
395 | '.': `\.`, | ||
396 | '/': `\/`, | ||
397 | - '<': `\x3c`, | ||
398 | - '>': `\x3e`, | ||
399 | + '<': `\u003c`, | ||
400 | + '>': `\u003e`, | ||
401 | '?': `\?`, | ||
402 | '[': `\[`, | ||
403 | '\\': `\\`, | ||
404 | diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go | ||
405 | index 075adaa..d7ee47b 100644 | ||
406 | --- a/src/html/template/js_test.go | ||
407 | +++ b/src/html/template/js_test.go | ||
408 | @@ -137,7 +137,7 @@ func TestJSValEscaper(t *testing.T) { | ||
409 | {"foo", `"foo"`}, | ||
410 | // Newlines. | ||
411 | {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`}, | ||
412 | - // "\v" == "v" on IE 6 so use "\x0b" instead. | ||
413 | + // "\v" == "v" on IE 6 so use "\u000b" instead. | ||
414 | {"\t\x0b", `"\t\u000b"`}, | ||
415 | {struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`}, | ||
416 | {[]interface{}{}, "[]"}, | ||
417 | @@ -173,7 +173,7 @@ func TestJSStrEscaper(t *testing.T) { | ||
418 | }{ | ||
419 | {"", ``}, | ||
420 | {"foo", `foo`}, | ||
421 | - {"\u0000", `\0`}, | ||
422 | + {"\u0000", `\u0000`}, | ||
423 | {"\t", `\t`}, | ||
424 | {"\n", `\n`}, | ||
425 | {"\r", `\r`}, | ||
426 | @@ -183,14 +183,14 @@ func TestJSStrEscaper(t *testing.T) { | ||
427 | {"\\n", `\\n`}, | ||
428 | {"foo\r\nbar", `foo\r\nbar`}, | ||
429 | // Preserve attribute boundaries. | ||
430 | - {`"`, `\x22`}, | ||
431 | - {`'`, `\x27`}, | ||
432 | + {`"`, `\u0022`}, | ||
433 | + {`'`, `\u0027`}, | ||
434 | // Allow embedding in HTML without further escaping. | ||
435 | - {`&`, `\x26amp;`}, | ||
436 | + {`&`, `\u0026amp;`}, | ||
437 | // Prevent breaking out of text node and element boundaries. | ||
438 | - {"</script>", `\x3c\/script\x3e`}, | ||
439 | - {"<![CDATA[", `\x3c![CDATA[`}, | ||
440 | - {"]]>", `]]\x3e`}, | ||
441 | + {"</script>", `\u003c\/script\u003e`}, | ||
442 | + {"<![CDATA[", `\u003c![CDATA[`}, | ||
443 | + {"]]>", `]]\u003e`}, | ||
444 | // https://dev.w3.org/html5/markup/aria/syntax.html#escaping-text-span | ||
445 | // "The text in style, script, title, and textarea elements | ||
446 | // must not have an escaping text span start that is not | ||
447 | @@ -201,11 +201,11 @@ func TestJSStrEscaper(t *testing.T) { | ||
448 | // allow regular text content to be interpreted as script | ||
449 | // allowing script execution via a combination of a JS string | ||
450 | // injection followed by an HTML text injection. | ||
451 | - {"<!--", `\x3c!--`}, | ||
452 | - {"-->", `--\x3e`}, | ||
453 | + {"<!--", `\u003c!--`}, | ||
454 | + {"-->", `--\u003e`}, | ||
455 | // From https://code.google.com/p/doctype/wiki/ArticleUtf7 | ||
456 | {"+ADw-script+AD4-alert(1)+ADw-/script+AD4-", | ||
457 | - `\x2bADw-script\x2bAD4-alert(1)\x2bADw-\/script\x2bAD4-`, | ||
458 | + `\u002bADw-script\u002bAD4-alert(1)\u002bADw-\/script\u002bAD4-`, | ||
459 | }, | ||
460 | // Invalid UTF-8 sequence | ||
461 | {"foo\xA0bar", "foo\xA0bar"}, | ||
462 | @@ -228,7 +228,7 @@ func TestJSRegexpEscaper(t *testing.T) { | ||
463 | }{ | ||
464 | {"", `(?:)`}, | ||
465 | {"foo", `foo`}, | ||
466 | - {"\u0000", `\0`}, | ||
467 | + {"\u0000", `\u0000`}, | ||
468 | {"\t", `\t`}, | ||
469 | {"\n", `\n`}, | ||
470 | {"\r", `\r`}, | ||
471 | @@ -238,19 +238,19 @@ func TestJSRegexpEscaper(t *testing.T) { | ||
472 | {"\\n", `\\n`}, | ||
473 | {"foo\r\nbar", `foo\r\nbar`}, | ||
474 | // Preserve attribute boundaries. | ||
475 | - {`"`, `\x22`}, | ||
476 | - {`'`, `\x27`}, | ||
477 | + {`"`, `\u0022`}, | ||
478 | + {`'`, `\u0027`}, | ||
479 | // Allow embedding in HTML without further escaping. | ||
480 | - {`&`, `\x26amp;`}, | ||
481 | + {`&`, `\u0026amp;`}, | ||
482 | // Prevent breaking out of text node and element boundaries. | ||
483 | - {"</script>", `\x3c\/script\x3e`}, | ||
484 | - {"<![CDATA[", `\x3c!\[CDATA\[`}, | ||
485 | - {"]]>", `\]\]\x3e`}, | ||
486 | + {"</script>", `\u003c\/script\u003e`}, | ||
487 | + {"<![CDATA[", `\u003c!\[CDATA\[`}, | ||
488 | + {"]]>", `\]\]\u003e`}, | ||
489 | // Escaping text spans. | ||
490 | - {"<!--", `\x3c!\-\-`}, | ||
491 | - {"-->", `\-\-\x3e`}, | ||
492 | + {"<!--", `\u003c!\-\-`}, | ||
493 | + {"-->", `\-\-\u003e`}, | ||
494 | {"*", `\*`}, | ||
495 | - {"+", `\x2b`}, | ||
496 | + {"+", `\u002b`}, | ||
497 | {"?", `\?`}, | ||
498 | {"[](){}", `\[\]\(\)\{\}`}, | ||
499 | {"$foo|x.y", `\$foo\|x\.y`}, | ||
500 | @@ -284,27 +284,27 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) { | ||
501 | { | ||
502 | "jsStrEscaper", | ||
503 | jsStrEscaper, | ||
504 | - "\\0\x01\x02\x03\x04\x05\x06\x07" + | ||
505 | - "\x08\\t\\n\\x0b\\f\\r\x0E\x0F" + | ||
506 | - "\x10\x11\x12\x13\x14\x15\x16\x17" + | ||
507 | - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + | ||
508 | - ` !\x22#$%\x26\x27()*\x2b,-.\/` + | ||
509 | - `0123456789:;\x3c=\x3e?` + | ||
510 | + `\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007` + | ||
511 | + `\u0008\t\n\u000b\f\r\u000e\u000f` + | ||
512 | + `\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017` + | ||
513 | + `\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f` + | ||
514 | + ` !\u0022#$%\u0026\u0027()*\u002b,-.\/` + | ||
515 | + `0123456789:;\u003c=\u003e?` + | ||
516 | `@ABCDEFGHIJKLMNO` + | ||
517 | `PQRSTUVWXYZ[\\]^_` + | ||
518 | "`abcdefghijklmno" + | ||
519 | - "pqrstuvwxyz{|}~\x7f" + | ||
520 | + "pqrstuvwxyz{|}~\u007f" + | ||
521 | "\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E", | ||
522 | }, | ||
523 | { | ||
524 | "jsRegexpEscaper", | ||
525 | jsRegexpEscaper, | ||
526 | - "\\0\x01\x02\x03\x04\x05\x06\x07" + | ||
527 | - "\x08\\t\\n\\x0b\\f\\r\x0E\x0F" + | ||
528 | - "\x10\x11\x12\x13\x14\x15\x16\x17" + | ||
529 | - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + | ||
530 | - ` !\x22#\$%\x26\x27\(\)\*\x2b,\-\.\/` + | ||
531 | - `0123456789:;\x3c=\x3e\?` + | ||
532 | + `\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007` + | ||
533 | + `\u0008\t\n\u000b\f\r\u000e\u000f` + | ||
534 | + `\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017` + | ||
535 | + `\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f` + | ||
536 | + ` !\u0022#\$%\u0026\u0027\(\)\*\u002b,\-\.\/` + | ||
537 | + `0123456789:;\u003c=\u003e\?` + | ||
538 | `@ABCDEFGHIJKLMNO` + | ||
539 | `PQRSTUVWXYZ\[\\\]\^_` + | ||
540 | "`abcdefghijklmno" + | ||
541 | diff --git a/src/html/template/template_test.go b/src/html/template/template_test.go | ||
542 | index 13e6ba4..86bd4db 100644 | ||
543 | --- a/src/html/template/template_test.go | ||
544 | +++ b/src/html/template/template_test.go | ||
545 | @@ -6,6 +6,7 @@ package template_test | ||
546 | |||
547 | import ( | ||
548 | "bytes" | ||
549 | + "encoding/json" | ||
550 | . "html/template" | ||
551 | "strings" | ||
552 | "testing" | ||
553 | @@ -121,6 +122,44 @@ func TestNumbers(t *testing.T) { | ||
554 | c.mustExecute(c.root, nil, "12.34 7.5") | ||
555 | } | ||
556 | |||
557 | +func TestStringsInScriptsWithJsonContentTypeAreCorrectlyEscaped(t *testing.T) { | ||
558 | + // See #33671 and #37634 for more context on this. | ||
559 | + tests := []struct{ name, in string }{ | ||
560 | + {"empty", ""}, | ||
561 | + {"invalid", string(rune(-1))}, | ||
562 | + {"null", "\u0000"}, | ||
563 | + {"unit separator", "\u001F"}, | ||
564 | + {"tab", "\t"}, | ||
565 | + {"gt and lt", "<>"}, | ||
566 | + {"quotes", `'"`}, | ||
567 | + {"ASCII letters", "ASCII letters"}, | ||
568 | + {"Unicode", "ʕ⊙ϖ⊙ʔ"}, | ||
569 | + {"Pizza", "P"}, | ||
570 | + } | ||
571 | + const ( | ||
572 | + prefix = `<script type="application/ld+json">` | ||
573 | + suffix = `</script>` | ||
574 | + templ = prefix + `"{{.}}"` + suffix | ||
575 | + ) | ||
576 | + tpl := Must(New("JS string is JSON string").Parse(templ)) | ||
577 | + for _, tt := range tests { | ||
578 | + t.Run(tt.name, func(t *testing.T) { | ||
579 | + var buf bytes.Buffer | ||
580 | + if err := tpl.Execute(&buf, tt.in); err != nil { | ||
581 | + t.Fatalf("Cannot render template: %v", err) | ||
582 | + } | ||
583 | + trimmed := bytes.TrimSuffix(bytes.TrimPrefix(buf.Bytes(), []byte(prefix)), []byte(suffix)) | ||
584 | + var got string | ||
585 | + if err := json.Unmarshal(trimmed, &got); err != nil { | ||
586 | + t.Fatalf("Cannot parse JS string %q as JSON: %v", trimmed[1:len(trimmed)-1], err) | ||
587 | + } | ||
588 | + if got != tt.in { | ||
589 | + t.Errorf("Serialization changed the string value: got %q want %q", got, tt.in) | ||
590 | + } | ||
591 | + }) | ||
592 | + } | ||
593 | +} | ||
594 | + | ||
595 | type testCase struct { | ||
596 | t *testing.T | ||
597 | root *Template | ||
598 | diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go | ||
599 | index 77294ed..b8a809e 100644 | ||
600 | --- a/src/text/template/exec_test.go | ||
601 | +++ b/src/text/template/exec_test.go | ||
602 | @@ -911,9 +911,9 @@ func TestJSEscaping(t *testing.T) { | ||
603 | {`Go "jump" \`, `Go \"jump\" \\`}, | ||
604 | {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`}, | ||
605 | {"unprintable \uFDFF", `unprintable \uFDFF`}, | ||
606 | - {`<html>`, `\x3Chtml\x3E`}, | ||
607 | - {`no = in attributes`, `no \x3D in attributes`}, | ||
608 | - {`' does not become HTML entity`, `\x26#x27; does not become HTML entity`}, | ||
609 | + {`<html>`, `\u003Chtml\u003E`}, | ||
610 | + {`no = in attributes`, `no \u003D in attributes`}, | ||
611 | + {`' does not become HTML entity`, `\u0026#x27; does not become HTML entity`}, | ||
612 | } | ||
613 | for _, tc := range testCases { | ||
614 | s := JSEscapeString(tc.in) | ||
615 | diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go | ||
616 | index 46125bc..f3de9fb 100644 | ||
617 | --- a/src/text/template/funcs.go | ||
618 | +++ b/src/text/template/funcs.go | ||
619 | @@ -640,10 +640,10 @@ var ( | ||
620 | jsBackslash = []byte(`\\`) | ||
621 | jsApos = []byte(`\'`) | ||
622 | jsQuot = []byte(`\"`) | ||
623 | - jsLt = []byte(`\x3C`) | ||
624 | - jsGt = []byte(`\x3E`) | ||
625 | - jsAmp = []byte(`\x26`) | ||
626 | - jsEq = []byte(`\x3D`) | ||
627 | + jsLt = []byte(`\u003C`) | ||
628 | + jsGt = []byte(`\u003E`) | ||
629 | + jsAmp = []byte(`\u0026`) | ||
630 | + jsEq = []byte(`\u003D`) | ||
631 | ) | ||
632 | |||
633 | // JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. | ||
634 | -- | ||
635 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_3.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_3.patch new file mode 100644 index 0000000000..cd7dd0957c --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_3.patch | |||
@@ -0,0 +1,393 @@ | |||
1 | From 7ddce23c7d5b728acf8482f5006497c7b9915f8a Mon Sep 17 00:00:00 2001 | ||
2 | From: Ariel Mashraki <ariel@mashraki.co.il> | ||
3 | Date: Wed, 22 Apr 2020 22:17:56 +0300 | ||
4 | Subject: [PATCH 3/6] text/template: add CommentNode to template parse tree | ||
5 | MIME-Version: 1.0 | ||
6 | Content-Type: text/plain; charset=UTF-8 | ||
7 | Content-Transfer-Encoding: 8bit | ||
8 | |||
9 | Fixes #34652 | ||
10 | |||
11 | Change-Id: Icf6e3eda593fed826736f34f95a9d66f5450cc98 | ||
12 | Reviewed-on: https://go-review.googlesource.com/c/go/+/229398 | ||
13 | Reviewed-by: Daniel Martí <mvdan@mvdan.cc> | ||
14 | Run-TryBot: Daniel Martí <mvdan@mvdan.cc> | ||
15 | TryBot-Result: Gobot Gobot <gobot@golang.org> | ||
16 | |||
17 | Dependency Patch #3 | ||
18 | |||
19 | Upstream-Status: Backport from https://github.com/golang/go/commit/c8ea03828b0645b1fd5725888e44873b75fcfbb6 | ||
20 | CVE: CVE-2023-24538 | ||
21 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
22 | --- | ||
23 | api/next.txt | 19 +++++++++++++++++++ | ||
24 | src/html/template/escape.go | 2 ++ | ||
25 | src/html/template/template_test.go | 16 ++++++++++++++++ | ||
26 | src/text/template/exec.go | 1 + | ||
27 | src/text/template/parse/lex.go | 8 +++++++- | ||
28 | src/text/template/parse/lex_test.go | 7 +++++-- | ||
29 | src/text/template/parse/node.go | 33 +++++++++++++++++++++++++++++++++ | ||
30 | src/text/template/parse/parse.go | 22 +++++++++++++++++++--- | ||
31 | src/text/template/parse/parse_test.go | 25 +++++++++++++++++++++++++ | ||
32 | 9 files changed, 127 insertions(+), 6 deletions(-) | ||
33 | |||
34 | diff --git a/api/next.txt b/api/next.txt | ||
35 | index e69de29..076f39e 100644 | ||
36 | --- a/api/next.txt | ||
37 | +++ b/api/next.txt | ||
38 | @@ -0,0 +1,19 @@ | ||
39 | +pkg unicode, const Version = "13.0.0" | ||
40 | +pkg unicode, var Chorasmian *RangeTable | ||
41 | +pkg unicode, var Dives_Akuru *RangeTable | ||
42 | +pkg unicode, var Khitan_Small_Script *RangeTable | ||
43 | +pkg unicode, var Yezidi *RangeTable | ||
44 | +pkg text/template/parse, const NodeComment = 20 | ||
45 | +pkg text/template/parse, const NodeComment NodeType | ||
46 | +pkg text/template/parse, const ParseComments = 1 | ||
47 | +pkg text/template/parse, const ParseComments Mode | ||
48 | +pkg text/template/parse, method (*CommentNode) Copy() Node | ||
49 | +pkg text/template/parse, method (*CommentNode) String() string | ||
50 | +pkg text/template/parse, method (CommentNode) Position() Pos | ||
51 | +pkg text/template/parse, method (CommentNode) Type() NodeType | ||
52 | +pkg text/template/parse, type CommentNode struct | ||
53 | +pkg text/template/parse, type CommentNode struct, Text string | ||
54 | +pkg text/template/parse, type CommentNode struct, embedded NodeType | ||
55 | +pkg text/template/parse, type CommentNode struct, embedded Pos | ||
56 | +pkg text/template/parse, type Mode uint | ||
57 | +pkg text/template/parse, type Tree struct, Mode Mode | ||
58 | diff --git a/src/html/template/escape.go b/src/html/template/escape.go | ||
59 | index f12dafa..8739735 100644 | ||
60 | --- a/src/html/template/escape.go | ||
61 | +++ b/src/html/template/escape.go | ||
62 | @@ -124,6 +124,8 @@ func (e *escaper) escape(c context, n parse.Node) context { | ||
63 | switch n := n.(type) { | ||
64 | case *parse.ActionNode: | ||
65 | return e.escapeAction(c, n) | ||
66 | + case *parse.CommentNode: | ||
67 | + return c | ||
68 | case *parse.IfNode: | ||
69 | return e.escapeBranch(c, &n.BranchNode, "if") | ||
70 | case *parse.ListNode: | ||
71 | diff --git a/src/html/template/template_test.go b/src/html/template/template_test.go | ||
72 | index 86bd4db..1f2c888 100644 | ||
73 | --- a/src/html/template/template_test.go | ||
74 | +++ b/src/html/template/template_test.go | ||
75 | @@ -10,6 +10,7 @@ import ( | ||
76 | . "html/template" | ||
77 | "strings" | ||
78 | "testing" | ||
79 | + "text/template/parse" | ||
80 | ) | ||
81 | |||
82 | func TestTemplateClone(t *testing.T) { | ||
83 | @@ -160,6 +161,21 @@ func TestStringsInScriptsWithJsonContentTypeAreCorrectlyEscaped(t *testing.T) { | ||
84 | } | ||
85 | } | ||
86 | |||
87 | +func TestSkipEscapeComments(t *testing.T) { | ||
88 | + c := newTestCase(t) | ||
89 | + tr := parse.New("root") | ||
90 | + tr.Mode = parse.ParseComments | ||
91 | + newT, err := tr.Parse("{{/* A comment */}}{{ 1 }}{{/* Another comment */}}", "", "", make(map[string]*parse.Tree)) | ||
92 | + if err != nil { | ||
93 | + t.Fatalf("Cannot parse template text: %v", err) | ||
94 | + } | ||
95 | + c.root, err = c.root.AddParseTree("root", newT) | ||
96 | + if err != nil { | ||
97 | + t.Fatalf("Cannot add parse tree to template: %v", err) | ||
98 | + } | ||
99 | + c.mustExecute(c.root, nil, "1") | ||
100 | +} | ||
101 | + | ||
102 | type testCase struct { | ||
103 | t *testing.T | ||
104 | root *Template | ||
105 | diff --git a/src/text/template/exec.go b/src/text/template/exec.go | ||
106 | index ac3e741..7ac5175 100644 | ||
107 | --- a/src/text/template/exec.go | ||
108 | +++ b/src/text/template/exec.go | ||
109 | @@ -256,6 +256,7 @@ func (s *state) walk(dot reflect.Value, node parse.Node) { | ||
110 | if len(node.Pipe.Decl) == 0 { | ||
111 | s.printValue(node, val) | ||
112 | } | ||
113 | + case *parse.CommentNode: | ||
114 | case *parse.IfNode: | ||
115 | s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList) | ||
116 | case *parse.ListNode: | ||
117 | diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go | ||
118 | index 30371f2..e41373a 100644 | ||
119 | --- a/src/text/template/parse/lex.go | ||
120 | +++ b/src/text/template/parse/lex.go | ||
121 | @@ -41,6 +41,7 @@ const ( | ||
122 | itemBool // boolean constant | ||
123 | itemChar // printable ASCII character; grab bag for comma etc. | ||
124 | itemCharConstant // character constant | ||
125 | + itemComment // comment text | ||
126 | itemComplex // complex constant (1+2i); imaginary is just a number | ||
127 | itemAssign // equals ('=') introducing an assignment | ||
128 | itemDeclare // colon-equals (':=') introducing a declaration | ||
129 | @@ -112,6 +113,7 @@ type lexer struct { | ||
130 | leftDelim string // start of action | ||
131 | rightDelim string // end of action | ||
132 | trimRightDelim string // end of action with trim marker | ||
133 | + emitComment bool // emit itemComment tokens. | ||
134 | pos Pos // current position in the input | ||
135 | start Pos // start position of this item | ||
136 | width Pos // width of last rune read from input | ||
137 | @@ -203,7 +205,7 @@ func (l *lexer) drain() { | ||
138 | } | ||
139 | |||
140 | // lex creates a new scanner for the input string. | ||
141 | -func lex(name, input, left, right string) *lexer { | ||
142 | +func lex(name, input, left, right string, emitComment bool) *lexer { | ||
143 | if left == "" { | ||
144 | left = leftDelim | ||
145 | } | ||
146 | @@ -216,6 +218,7 @@ func lex(name, input, left, right string) *lexer { | ||
147 | leftDelim: left, | ||
148 | rightDelim: right, | ||
149 | trimRightDelim: rightTrimMarker + right, | ||
150 | + emitComment: emitComment, | ||
151 | items: make(chan item), | ||
152 | line: 1, | ||
153 | startLine: 1, | ||
154 | @@ -323,6 +326,9 @@ func lexComment(l *lexer) stateFn { | ||
155 | if !delim { | ||
156 | return l.errorf("comment ends before closing delimiter") | ||
157 | } | ||
158 | + if l.emitComment { | ||
159 | + l.emit(itemComment) | ||
160 | + } | ||
161 | if trimSpace { | ||
162 | l.pos += trimMarkerLen | ||
163 | } | ||
164 | diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go | ||
165 | index 563c4fc..f6d5f28 100644 | ||
166 | --- a/src/text/template/parse/lex_test.go | ||
167 | +++ b/src/text/template/parse/lex_test.go | ||
168 | @@ -15,6 +15,7 @@ var itemName = map[itemType]string{ | ||
169 | itemBool: "bool", | ||
170 | itemChar: "char", | ||
171 | itemCharConstant: "charconst", | ||
172 | + itemComment: "comment", | ||
173 | itemComplex: "complex", | ||
174 | itemDeclare: ":=", | ||
175 | itemEOF: "EOF", | ||
176 | @@ -90,6 +91,7 @@ var lexTests = []lexTest{ | ||
177 | {"text", `now is the time`, []item{mkItem(itemText, "now is the time"), tEOF}}, | ||
178 | {"text with comment", "hello-{{/* this is a comment */}}-world", []item{ | ||
179 | mkItem(itemText, "hello-"), | ||
180 | + mkItem(itemComment, "/* this is a comment */"), | ||
181 | mkItem(itemText, "-world"), | ||
182 | tEOF, | ||
183 | }}, | ||
184 | @@ -311,6 +313,7 @@ var lexTests = []lexTest{ | ||
185 | }}, | ||
186 | {"trimming spaces before and after comment", "hello- {{- /* hello */ -}} -world", []item{ | ||
187 | mkItem(itemText, "hello-"), | ||
188 | + mkItem(itemComment, "/* hello */"), | ||
189 | mkItem(itemText, "-world"), | ||
190 | tEOF, | ||
191 | }}, | ||
192 | @@ -389,7 +392,7 @@ var lexTests = []lexTest{ | ||
193 | |||
194 | // collect gathers the emitted items into a slice. | ||
195 | func collect(t *lexTest, left, right string) (items []item) { | ||
196 | - l := lex(t.name, t.input, left, right) | ||
197 | + l := lex(t.name, t.input, left, right, true) | ||
198 | for { | ||
199 | item := l.nextItem() | ||
200 | items = append(items, item) | ||
201 | @@ -529,7 +532,7 @@ func TestPos(t *testing.T) { | ||
202 | func TestShutdown(t *testing.T) { | ||
203 | // We need to duplicate template.Parse here to hold on to the lexer. | ||
204 | const text = "erroneous{{define}}{{else}}1234" | ||
205 | - lexer := lex("foo", text, "{{", "}}") | ||
206 | + lexer := lex("foo", text, "{{", "}}", false) | ||
207 | _, err := New("root").parseLexer(lexer) | ||
208 | if err == nil { | ||
209 | t.Fatalf("expected error") | ||
210 | diff --git a/src/text/template/parse/node.go b/src/text/template/parse/node.go | ||
211 | index 1c116ea..a9dad5e 100644 | ||
212 | --- a/src/text/template/parse/node.go | ||
213 | +++ b/src/text/template/parse/node.go | ||
214 | @@ -70,6 +70,7 @@ const ( | ||
215 | NodeTemplate // A template invocation action. | ||
216 | NodeVariable // A $ variable. | ||
217 | NodeWith // A with action. | ||
218 | + NodeComment // A comment. | ||
219 | ) | ||
220 | |||
221 | // Nodes. | ||
222 | @@ -149,6 +150,38 @@ func (t *TextNode) Copy() Node { | ||
223 | return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)} | ||
224 | } | ||
225 | |||
226 | +// CommentNode holds a comment. | ||
227 | +type CommentNode struct { | ||
228 | + NodeType | ||
229 | + Pos | ||
230 | + tr *Tree | ||
231 | + Text string // Comment text. | ||
232 | +} | ||
233 | + | ||
234 | +func (t *Tree) newComment(pos Pos, text string) *CommentNode { | ||
235 | + return &CommentNode{tr: t, NodeType: NodeComment, Pos: pos, Text: text} | ||
236 | +} | ||
237 | + | ||
238 | +func (c *CommentNode) String() string { | ||
239 | + var sb strings.Builder | ||
240 | + c.writeTo(&sb) | ||
241 | + return sb.String() | ||
242 | +} | ||
243 | + | ||
244 | +func (c *CommentNode) writeTo(sb *strings.Builder) { | ||
245 | + sb.WriteString("{{") | ||
246 | + sb.WriteString(c.Text) | ||
247 | + sb.WriteString("}}") | ||
248 | +} | ||
249 | + | ||
250 | +func (c *CommentNode) tree() *Tree { | ||
251 | + return c.tr | ||
252 | +} | ||
253 | + | ||
254 | +func (c *CommentNode) Copy() Node { | ||
255 | + return &CommentNode{tr: c.tr, NodeType: NodeComment, Pos: c.Pos, Text: c.Text} | ||
256 | +} | ||
257 | + | ||
258 | // PipeNode holds a pipeline with optional declaration | ||
259 | type PipeNode struct { | ||
260 | NodeType | ||
261 | diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go | ||
262 | index c9b80f4..496d8bf 100644 | ||
263 | --- a/src/text/template/parse/parse.go | ||
264 | +++ b/src/text/template/parse/parse.go | ||
265 | @@ -21,6 +21,7 @@ type Tree struct { | ||
266 | Name string // name of the template represented by the tree. | ||
267 | ParseName string // name of the top-level template during parsing, for error messages. | ||
268 | Root *ListNode // top-level root of the tree. | ||
269 | + Mode Mode // parsing mode. | ||
270 | text string // text parsed to create the template (or its parent) | ||
271 | // Parsing only; cleared after parse. | ||
272 | funcs []map[string]interface{} | ||
273 | @@ -29,8 +30,16 @@ type Tree struct { | ||
274 | peekCount int | ||
275 | vars []string // variables defined at the moment. | ||
276 | treeSet map[string]*Tree | ||
277 | + mode Mode | ||
278 | } | ||
279 | |||
280 | +// A mode value is a set of flags (or 0). Modes control parser behavior. | ||
281 | +type Mode uint | ||
282 | + | ||
283 | +const ( | ||
284 | + ParseComments Mode = 1 << iota // parse comments and add them to AST | ||
285 | +) | ||
286 | + | ||
287 | // Copy returns a copy of the Tree. Any parsing state is discarded. | ||
288 | func (t *Tree) Copy() *Tree { | ||
289 | if t == nil { | ||
290 | @@ -220,7 +229,8 @@ func (t *Tree) stopParse() { | ||
291 | func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) { | ||
292 | defer t.recover(&err) | ||
293 | t.ParseName = t.Name | ||
294 | - t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim), treeSet) | ||
295 | + emitComment := t.Mode&ParseComments != 0 | ||
296 | + t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim, emitComment), treeSet) | ||
297 | t.text = text | ||
298 | t.parse() | ||
299 | t.add() | ||
300 | @@ -240,12 +250,14 @@ func (t *Tree) add() { | ||
301 | } | ||
302 | } | ||
303 | |||
304 | -// IsEmptyTree reports whether this tree (node) is empty of everything but space. | ||
305 | +// IsEmptyTree reports whether this tree (node) is empty of everything but space or comments. | ||
306 | func IsEmptyTree(n Node) bool { | ||
307 | switch n := n.(type) { | ||
308 | case nil: | ||
309 | return true | ||
310 | case *ActionNode: | ||
311 | + case *CommentNode: | ||
312 | + return true | ||
313 | case *IfNode: | ||
314 | case *ListNode: | ||
315 | for _, node := range n.Nodes { | ||
316 | @@ -276,6 +288,7 @@ func (t *Tree) parse() { | ||
317 | if t.nextNonSpace().typ == itemDefine { | ||
318 | newT := New("definition") // name will be updated once we know it. | ||
319 | newT.text = t.text | ||
320 | + newT.Mode = t.Mode | ||
321 | newT.ParseName = t.ParseName | ||
322 | newT.startParse(t.funcs, t.lex, t.treeSet) | ||
323 | newT.parseDefinition() | ||
324 | @@ -331,13 +344,15 @@ func (t *Tree) itemList() (list *ListNode, next Node) { | ||
325 | } | ||
326 | |||
327 | // textOrAction: | ||
328 | -// text | action | ||
329 | +// text | comment | action | ||
330 | func (t *Tree) textOrAction() Node { | ||
331 | switch token := t.nextNonSpace(); token.typ { | ||
332 | case itemText: | ||
333 | return t.newText(token.pos, token.val) | ||
334 | case itemLeftDelim: | ||
335 | return t.action() | ||
336 | + case itemComment: | ||
337 | + return t.newComment(token.pos, token.val) | ||
338 | default: | ||
339 | t.unexpected(token, "input") | ||
340 | } | ||
341 | @@ -539,6 +554,7 @@ func (t *Tree) blockControl() Node { | ||
342 | |||
343 | block := New(name) // name will be updated once we know it. | ||
344 | block.text = t.text | ||
345 | + block.Mode = t.Mode | ||
346 | block.ParseName = t.ParseName | ||
347 | block.startParse(t.funcs, t.lex, t.treeSet) | ||
348 | var end Node | ||
349 | diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go | ||
350 | index 4e09a78..d9c13c5 100644 | ||
351 | --- a/src/text/template/parse/parse_test.go | ||
352 | +++ b/src/text/template/parse/parse_test.go | ||
353 | @@ -348,6 +348,30 @@ func TestParseCopy(t *testing.T) { | ||
354 | testParse(true, t) | ||
355 | } | ||
356 | |||
357 | +func TestParseWithComments(t *testing.T) { | ||
358 | + textFormat = "%q" | ||
359 | + defer func() { textFormat = "%s" }() | ||
360 | + tests := [...]parseTest{ | ||
361 | + {"comment", "{{/*\n\n\n*/}}", noError, "{{/*\n\n\n*/}}"}, | ||
362 | + {"comment trim left", "x \r\n\t{{- /* hi */}}", noError, `"x"{{/* hi */}}`}, | ||
363 | + {"comment trim right", "{{/* hi */ -}}\n\n\ty", noError, `{{/* hi */}}"y"`}, | ||
364 | + {"comment trim left and right", "x \r\n\t{{- /* */ -}}\n\n\ty", noError, `"x"{{/* */}}"y"`}, | ||
365 | + } | ||
366 | + for _, test := range tests { | ||
367 | + t.Run(test.name, func(t *testing.T) { | ||
368 | + tr := New(test.name) | ||
369 | + tr.Mode = ParseComments | ||
370 | + tmpl, err := tr.Parse(test.input, "", "", make(map[string]*Tree)) | ||
371 | + if err != nil { | ||
372 | + t.Errorf("%q: expected error; got none", test.name) | ||
373 | + } | ||
374 | + if result := tmpl.Root.String(); result != test.result { | ||
375 | + t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result) | ||
376 | + } | ||
377 | + }) | ||
378 | + } | ||
379 | +} | ||
380 | + | ||
381 | type isEmptyTest struct { | ||
382 | name string | ||
383 | input string | ||
384 | @@ -358,6 +382,7 @@ var isEmptyTests = []isEmptyTest{ | ||
385 | {"empty", ``, true}, | ||
386 | {"nonempty", `hello`, false}, | ||
387 | {"spaces only", " \t\n \t\n", true}, | ||
388 | + {"comment only", "{{/* comment */}}", true}, | ||
389 | {"definition", `{{define "x"}}something{{end}}`, true}, | ||
390 | {"definitions and space", "{{define `x`}}something{{end}}\n\n{{define `y`}}something{{end}}\n\n", true}, | ||
391 | {"definitions and text", "{{define `x`}}something{{end}}\nx\n{{define `y`}}something{{end}}\ny\n", false}, | ||
392 | -- | ||
393 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_4.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_4.patch new file mode 100644 index 0000000000..d5e2eb6684 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_4.patch | |||
@@ -0,0 +1,497 @@ | |||
1 | From 760d88497091fb5d6d231a18e6f4e06ecb9af9b2 Mon Sep 17 00:00:00 2001 | ||
2 | From: Russ Cox <rsc@golang.org> | ||
3 | Date: Thu, 10 Sep 2020 18:53:26 -0400 | ||
4 | Subject: [PATCH 4/6] text/template: allow newlines inside action delimiters | ||
5 | |||
6 | This allows multiline constructs like: | ||
7 | |||
8 | {{"hello" | | ||
9 | printf}} | ||
10 | |||
11 | Now that unclosed actions can span multiple lines, | ||
12 | track and report the start of the action when reporting errors. | ||
13 | |||
14 | Also clean up a few "unexpected <error message>" to be just "<error message>". | ||
15 | |||
16 | Fixes #29770. | ||
17 | |||
18 | Change-Id: I54c6c016029a8328b7902a4b6d85eab713ec3285 | ||
19 | Reviewed-on: https://go-review.googlesource.com/c/go/+/254257 | ||
20 | Trust: Russ Cox <rsc@golang.org> | ||
21 | Run-TryBot: Russ Cox <rsc@golang.org> | ||
22 | TryBot-Result: Go Bot <gobot@golang.org> | ||
23 | Reviewed-by: Rob Pike <r@golang.org> | ||
24 | |||
25 | Dependency Patch #4 | ||
26 | |||
27 | Upstream-Status: Backport from https://github.com/golang/go/commit/9384d34c58099657bb1b133beaf3ff37ada9b017 | ||
28 | CVE: CVE-2023-24538 | ||
29 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
30 | --- | ||
31 | src/text/template/doc.go | 21 ++++----- | ||
32 | src/text/template/exec_test.go | 2 +- | ||
33 | src/text/template/parse/lex.go | 84 +++++++++++++++++------------------ | ||
34 | src/text/template/parse/lex_test.go | 2 +- | ||
35 | src/text/template/parse/parse.go | 59 +++++++++++++----------- | ||
36 | src/text/template/parse/parse_test.go | 36 ++++++++++++--- | ||
37 | 6 files changed, 117 insertions(+), 87 deletions(-) | ||
38 | |||
39 | diff --git a/src/text/template/doc.go b/src/text/template/doc.go | ||
40 | index 4b0efd2..7b30294 100644 | ||
41 | --- a/src/text/template/doc.go | ||
42 | +++ b/src/text/template/doc.go | ||
43 | @@ -40,16 +40,17 @@ More intricate examples appear below. | ||
44 | Text and spaces | ||
45 | |||
46 | By default, all text between actions is copied verbatim when the template is | ||
47 | -executed. For example, the string " items are made of " in the example above appears | ||
48 | -on standard output when the program is run. | ||
49 | - | ||
50 | -However, to aid in formatting template source code, if an action's left delimiter | ||
51 | -(by default "{{") is followed immediately by a minus sign and ASCII space character | ||
52 | -("{{- "), all trailing white space is trimmed from the immediately preceding text. | ||
53 | -Similarly, if the right delimiter ("}}") is preceded by a space and minus sign | ||
54 | -(" -}}"), all leading white space is trimmed from the immediately following text. | ||
55 | -In these trim markers, the ASCII space must be present; "{{-3}}" parses as an | ||
56 | -action containing the number -3. | ||
57 | +executed. For example, the string " items are made of " in the example above | ||
58 | +appears on standard output when the program is run. | ||
59 | + | ||
60 | +However, to aid in formatting template source code, if an action's left | ||
61 | +delimiter (by default "{{") is followed immediately by a minus sign and white | ||
62 | +space, all trailing white space is trimmed from the immediately preceding text. | ||
63 | +Similarly, if the right delimiter ("}}") is preceded by white space and a minus | ||
64 | +sign, all leading white space is trimmed from the immediately following text. | ||
65 | +In these trim markers, the white space must be present: | ||
66 | +"{{- 3}}" is like "{{3}}" but trims the immediately preceding text, while | ||
67 | +"{{-3}}" parses as an action containing the number -3. | ||
68 | |||
69 | For instance, when executing the template whose source is | ||
70 | |||
71 | diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go | ||
72 | index b8a809e..3309b33 100644 | ||
73 | --- a/src/text/template/exec_test.go | ||
74 | +++ b/src/text/template/exec_test.go | ||
75 | @@ -1295,7 +1295,7 @@ func TestUnterminatedStringError(t *testing.T) { | ||
76 | t.Fatal("expected error") | ||
77 | } | ||
78 | str := err.Error() | ||
79 | - if !strings.Contains(str, "X:3: unexpected unterminated raw quoted string") { | ||
80 | + if !strings.Contains(str, "X:3: unterminated raw quoted string") { | ||
81 | t.Fatalf("unexpected error: %s", str) | ||
82 | } | ||
83 | } | ||
84 | diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go | ||
85 | index e41373a..6784071 100644 | ||
86 | --- a/src/text/template/parse/lex.go | ||
87 | +++ b/src/text/template/parse/lex.go | ||
88 | @@ -92,15 +92,14 @@ const eof = -1 | ||
89 | // If the action begins "{{- " rather than "{{", then all space/tab/newlines | ||
90 | // preceding the action are trimmed; conversely if it ends " -}}" the | ||
91 | // leading spaces are trimmed. This is done entirely in the lexer; the | ||
92 | -// parser never sees it happen. We require an ASCII space to be | ||
93 | -// present to avoid ambiguity with things like "{{-3}}". It reads | ||
94 | +// parser never sees it happen. We require an ASCII space (' ', \t, \r, \n) | ||
95 | +// to be present to avoid ambiguity with things like "{{-3}}". It reads | ||
96 | // better with the space present anyway. For simplicity, only ASCII | ||
97 | -// space does the job. | ||
98 | +// does the job. | ||
99 | const ( | ||
100 | - spaceChars = " \t\r\n" // These are the space characters defined by Go itself. | ||
101 | - leftTrimMarker = "- " // Attached to left delimiter, trims trailing spaces from preceding text. | ||
102 | - rightTrimMarker = " -" // Attached to right delimiter, trims leading spaces from following text. | ||
103 | - trimMarkerLen = Pos(len(leftTrimMarker)) | ||
104 | + spaceChars = " \t\r\n" // These are the space characters defined by Go itself. | ||
105 | + trimMarker = '-' // Attached to left/right delimiter, trims trailing spaces from preceding/following text. | ||
106 | + trimMarkerLen = Pos(1 + 1) // marker plus space before or after | ||
107 | ) | ||
108 | |||
109 | // stateFn represents the state of the scanner as a function that returns the next state. | ||
110 | @@ -108,19 +107,18 @@ type stateFn func(*lexer) stateFn | ||
111 | |||
112 | // lexer holds the state of the scanner. | ||
113 | type lexer struct { | ||
114 | - name string // the name of the input; used only for error reports | ||
115 | - input string // the string being scanned | ||
116 | - leftDelim string // start of action | ||
117 | - rightDelim string // end of action | ||
118 | - trimRightDelim string // end of action with trim marker | ||
119 | - emitComment bool // emit itemComment tokens. | ||
120 | - pos Pos // current position in the input | ||
121 | - start Pos // start position of this item | ||
122 | - width Pos // width of last rune read from input | ||
123 | - items chan item // channel of scanned items | ||
124 | - parenDepth int // nesting depth of ( ) exprs | ||
125 | - line int // 1+number of newlines seen | ||
126 | - startLine int // start line of this item | ||
127 | + name string // the name of the input; used only for error reports | ||
128 | + input string // the string being scanned | ||
129 | + leftDelim string // start of action | ||
130 | + rightDelim string // end of action | ||
131 | + emitComment bool // emit itemComment tokens. | ||
132 | + pos Pos // current position in the input | ||
133 | + start Pos // start position of this item | ||
134 | + width Pos // width of last rune read from input | ||
135 | + items chan item // channel of scanned items | ||
136 | + parenDepth int // nesting depth of ( ) exprs | ||
137 | + line int // 1+number of newlines seen | ||
138 | + startLine int // start line of this item | ||
139 | } | ||
140 | |||
141 | // next returns the next rune in the input. | ||
142 | @@ -213,15 +211,14 @@ func lex(name, input, left, right string, emitComment bool) *lexer { | ||
143 | right = rightDelim | ||
144 | } | ||
145 | l := &lexer{ | ||
146 | - name: name, | ||
147 | - input: input, | ||
148 | - leftDelim: left, | ||
149 | - rightDelim: right, | ||
150 | - trimRightDelim: rightTrimMarker + right, | ||
151 | - emitComment: emitComment, | ||
152 | - items: make(chan item), | ||
153 | - line: 1, | ||
154 | - startLine: 1, | ||
155 | + name: name, | ||
156 | + input: input, | ||
157 | + leftDelim: left, | ||
158 | + rightDelim: right, | ||
159 | + emitComment: emitComment, | ||
160 | + items: make(chan item), | ||
161 | + line: 1, | ||
162 | + startLine: 1, | ||
163 | } | ||
164 | go l.run() | ||
165 | return l | ||
166 | @@ -251,7 +248,7 @@ func lexText(l *lexer) stateFn { | ||
167 | ldn := Pos(len(l.leftDelim)) | ||
168 | l.pos += Pos(x) | ||
169 | trimLength := Pos(0) | ||
170 | - if strings.HasPrefix(l.input[l.pos+ldn:], leftTrimMarker) { | ||
171 | + if hasLeftTrimMarker(l.input[l.pos+ldn:]) { | ||
172 | trimLength = rightTrimLength(l.input[l.start:l.pos]) | ||
173 | } | ||
174 | l.pos -= trimLength | ||
175 | @@ -280,7 +277,7 @@ func rightTrimLength(s string) Pos { | ||
176 | |||
177 | // atRightDelim reports whether the lexer is at a right delimiter, possibly preceded by a trim marker. | ||
178 | func (l *lexer) atRightDelim() (delim, trimSpaces bool) { | ||
179 | - if strings.HasPrefix(l.input[l.pos:], l.trimRightDelim) { // With trim marker. | ||
180 | + if hasRightTrimMarker(l.input[l.pos:]) && strings.HasPrefix(l.input[l.pos+trimMarkerLen:], l.rightDelim) { // With trim marker. | ||
181 | return true, true | ||
182 | } | ||
183 | if strings.HasPrefix(l.input[l.pos:], l.rightDelim) { // Without trim marker. | ||
184 | @@ -297,7 +294,7 @@ func leftTrimLength(s string) Pos { | ||
185 | // lexLeftDelim scans the left delimiter, which is known to be present, possibly with a trim marker. | ||
186 | func lexLeftDelim(l *lexer) stateFn { | ||
187 | l.pos += Pos(len(l.leftDelim)) | ||
188 | - trimSpace := strings.HasPrefix(l.input[l.pos:], leftTrimMarker) | ||
189 | + trimSpace := hasLeftTrimMarker(l.input[l.pos:]) | ||
190 | afterMarker := Pos(0) | ||
191 | if trimSpace { | ||
192 | afterMarker = trimMarkerLen | ||
193 | @@ -342,7 +339,7 @@ func lexComment(l *lexer) stateFn { | ||
194 | |||
195 | // lexRightDelim scans the right delimiter, which is known to be present, possibly with a trim marker. | ||
196 | func lexRightDelim(l *lexer) stateFn { | ||
197 | - trimSpace := strings.HasPrefix(l.input[l.pos:], rightTrimMarker) | ||
198 | + trimSpace := hasRightTrimMarker(l.input[l.pos:]) | ||
199 | if trimSpace { | ||
200 | l.pos += trimMarkerLen | ||
201 | l.ignore() | ||
202 | @@ -369,7 +366,7 @@ func lexInsideAction(l *lexer) stateFn { | ||
203 | return l.errorf("unclosed left paren") | ||
204 | } | ||
205 | switch r := l.next(); { | ||
206 | - case r == eof || isEndOfLine(r): | ||
207 | + case r == eof: | ||
208 | return l.errorf("unclosed action") | ||
209 | case isSpace(r): | ||
210 | l.backup() // Put space back in case we have " -}}". | ||
211 | @@ -439,7 +436,7 @@ func lexSpace(l *lexer) stateFn { | ||
212 | } | ||
213 | // Be careful about a trim-marked closing delimiter, which has a minus | ||
214 | // after a space. We know there is a space, so check for the '-' that might follow. | ||
215 | - if strings.HasPrefix(l.input[l.pos-1:], l.trimRightDelim) { | ||
216 | + if hasRightTrimMarker(l.input[l.pos-1:]) && strings.HasPrefix(l.input[l.pos-1+trimMarkerLen:], l.rightDelim) { | ||
217 | l.backup() // Before the space. | ||
218 | if numSpaces == 1 { | ||
219 | return lexRightDelim // On the delim, so go right to that. | ||
220 | @@ -526,7 +523,7 @@ func lexFieldOrVariable(l *lexer, typ itemType) stateFn { | ||
221 | // day to implement arithmetic. | ||
222 | func (l *lexer) atTerminator() bool { | ||
223 | r := l.peek() | ||
224 | - if isSpace(r) || isEndOfLine(r) { | ||
225 | + if isSpace(r) { | ||
226 | return true | ||
227 | } | ||
228 | switch r { | ||
229 | @@ -657,15 +654,18 @@ Loop: | ||
230 | |||
231 | // isSpace reports whether r is a space character. | ||
232 | func isSpace(r rune) bool { | ||
233 | - return r == ' ' || r == '\t' | ||
234 | -} | ||
235 | - | ||
236 | -// isEndOfLine reports whether r is an end-of-line character. | ||
237 | -func isEndOfLine(r rune) bool { | ||
238 | - return r == '\r' || r == '\n' | ||
239 | + return r == ' ' || r == '\t' || r == '\r' || r == '\n' | ||
240 | } | ||
241 | |||
242 | // isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. | ||
243 | func isAlphaNumeric(r rune) bool { | ||
244 | return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) | ||
245 | } | ||
246 | + | ||
247 | +func hasLeftTrimMarker(s string) bool { | ||
248 | + return len(s) >= 2 && s[0] == trimMarker && isSpace(rune(s[1])) | ||
249 | +} | ||
250 | + | ||
251 | +func hasRightTrimMarker(s string) bool { | ||
252 | + return len(s) >= 2 && isSpace(rune(s[0])) && s[1] == trimMarker | ||
253 | +} | ||
254 | diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go | ||
255 | index f6d5f28..6510eed 100644 | ||
256 | --- a/src/text/template/parse/lex_test.go | ||
257 | +++ b/src/text/template/parse/lex_test.go | ||
258 | @@ -323,7 +323,7 @@ var lexTests = []lexTest{ | ||
259 | tLeft, | ||
260 | mkItem(itemError, "unrecognized character in action: U+0001"), | ||
261 | }}, | ||
262 | - {"unclosed action", "{{\n}}", []item{ | ||
263 | + {"unclosed action", "{{", []item{ | ||
264 | tLeft, | ||
265 | mkItem(itemError, "unclosed action"), | ||
266 | }}, | ||
267 | diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go | ||
268 | index 496d8bf..5e6e512 100644 | ||
269 | --- a/src/text/template/parse/parse.go | ||
270 | +++ b/src/text/template/parse/parse.go | ||
271 | @@ -24,13 +24,14 @@ type Tree struct { | ||
272 | Mode Mode // parsing mode. | ||
273 | text string // text parsed to create the template (or its parent) | ||
274 | // Parsing only; cleared after parse. | ||
275 | - funcs []map[string]interface{} | ||
276 | - lex *lexer | ||
277 | - token [3]item // three-token lookahead for parser. | ||
278 | - peekCount int | ||
279 | - vars []string // variables defined at the moment. | ||
280 | - treeSet map[string]*Tree | ||
281 | - mode Mode | ||
282 | + funcs []map[string]interface{} | ||
283 | + lex *lexer | ||
284 | + token [3]item // three-token lookahead for parser. | ||
285 | + peekCount int | ||
286 | + vars []string // variables defined at the moment. | ||
287 | + treeSet map[string]*Tree | ||
288 | + actionLine int // line of left delim starting action | ||
289 | + mode Mode | ||
290 | } | ||
291 | |||
292 | // A mode value is a set of flags (or 0). Modes control parser behavior. | ||
293 | @@ -187,6 +188,16 @@ func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item { | ||
294 | |||
295 | // unexpected complains about the token and terminates processing. | ||
296 | func (t *Tree) unexpected(token item, context string) { | ||
297 | + if token.typ == itemError { | ||
298 | + extra := "" | ||
299 | + if t.actionLine != 0 && t.actionLine != token.line { | ||
300 | + extra = fmt.Sprintf(" in action started at %s:%d", t.ParseName, t.actionLine) | ||
301 | + if strings.HasSuffix(token.val, " action") { | ||
302 | + extra = extra[len(" in action"):] // avoid "action in action" | ||
303 | + } | ||
304 | + } | ||
305 | + t.errorf("%s%s", token, extra) | ||
306 | + } | ||
307 | t.errorf("unexpected %s in %s", token, context) | ||
308 | } | ||
309 | |||
310 | @@ -350,6 +361,8 @@ func (t *Tree) textOrAction() Node { | ||
311 | case itemText: | ||
312 | return t.newText(token.pos, token.val) | ||
313 | case itemLeftDelim: | ||
314 | + t.actionLine = token.line | ||
315 | + defer t.clearActionLine() | ||
316 | return t.action() | ||
317 | case itemComment: | ||
318 | return t.newComment(token.pos, token.val) | ||
319 | @@ -359,6 +372,10 @@ func (t *Tree) textOrAction() Node { | ||
320 | return nil | ||
321 | } | ||
322 | |||
323 | +func (t *Tree) clearActionLine() { | ||
324 | + t.actionLine = 0 | ||
325 | +} | ||
326 | + | ||
327 | // Action: | ||
328 | // control | ||
329 | // command ("|" command)* | ||
330 | @@ -384,12 +401,12 @@ func (t *Tree) action() (n Node) { | ||
331 | t.backup() | ||
332 | token := t.peek() | ||
333 | // Do not pop variables; they persist until "end". | ||
334 | - return t.newAction(token.pos, token.line, t.pipeline("command")) | ||
335 | + return t.newAction(token.pos, token.line, t.pipeline("command", itemRightDelim)) | ||
336 | } | ||
337 | |||
338 | // Pipeline: | ||
339 | // declarations? command ('|' command)* | ||
340 | -func (t *Tree) pipeline(context string) (pipe *PipeNode) { | ||
341 | +func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) { | ||
342 | token := t.peekNonSpace() | ||
343 | pipe = t.newPipeline(token.pos, token.line, nil) | ||
344 | // Are there declarations or assignments? | ||
345 | @@ -430,12 +447,9 @@ decls: | ||
346 | } | ||
347 | for { | ||
348 | switch token := t.nextNonSpace(); token.typ { | ||
349 | - case itemRightDelim, itemRightParen: | ||
350 | + case end: | ||
351 | // At this point, the pipeline is complete | ||
352 | t.checkPipeline(pipe, context) | ||
353 | - if token.typ == itemRightParen { | ||
354 | - t.backup() | ||
355 | - } | ||
356 | return | ||
357 | case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier, | ||
358 | itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen: | ||
359 | @@ -464,7 +478,7 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) { | ||
360 | |||
361 | func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) { | ||
362 | defer t.popVars(len(t.vars)) | ||
363 | - pipe = t.pipeline(context) | ||
364 | + pipe = t.pipeline(context, itemRightDelim) | ||
365 | var next Node | ||
366 | list, next = t.itemList() | ||
367 | switch next.Type() { | ||
368 | @@ -550,7 +564,7 @@ func (t *Tree) blockControl() Node { | ||
369 | |||
370 | token := t.nextNonSpace() | ||
371 | name := t.parseTemplateName(token, context) | ||
372 | - pipe := t.pipeline(context) | ||
373 | + pipe := t.pipeline(context, itemRightDelim) | ||
374 | |||
375 | block := New(name) // name will be updated once we know it. | ||
376 | block.text = t.text | ||
377 | @@ -580,7 +594,7 @@ func (t *Tree) templateControl() Node { | ||
378 | if t.nextNonSpace().typ != itemRightDelim { | ||
379 | t.backup() | ||
380 | // Do not pop variables; they persist until "end". | ||
381 | - pipe = t.pipeline(context) | ||
382 | + pipe = t.pipeline(context, itemRightDelim) | ||
383 | } | ||
384 | return t.newTemplate(token.pos, token.line, name, pipe) | ||
385 | } | ||
386 | @@ -614,13 +628,12 @@ func (t *Tree) command() *CommandNode { | ||
387 | switch token := t.next(); token.typ { | ||
388 | case itemSpace: | ||
389 | continue | ||
390 | - case itemError: | ||
391 | - t.errorf("%s", token.val) | ||
392 | case itemRightDelim, itemRightParen: | ||
393 | t.backup() | ||
394 | case itemPipe: | ||
395 | + // nothing here; break loop below | ||
396 | default: | ||
397 | - t.errorf("unexpected %s in operand", token) | ||
398 | + t.unexpected(token, "operand") | ||
399 | } | ||
400 | break | ||
401 | } | ||
402 | @@ -675,8 +688,6 @@ func (t *Tree) operand() Node { | ||
403 | // A nil return means the next item is not a term. | ||
404 | func (t *Tree) term() Node { | ||
405 | switch token := t.nextNonSpace(); token.typ { | ||
406 | - case itemError: | ||
407 | - t.errorf("%s", token.val) | ||
408 | case itemIdentifier: | ||
409 | if !t.hasFunction(token.val) { | ||
410 | t.errorf("function %q not defined", token.val) | ||
411 | @@ -699,11 +710,7 @@ func (t *Tree) term() Node { | ||
412 | } | ||
413 | return number | ||
414 | case itemLeftParen: | ||
415 | - pipe := t.pipeline("parenthesized pipeline") | ||
416 | - if token := t.next(); token.typ != itemRightParen { | ||
417 | - t.errorf("unclosed right paren: unexpected %s", token) | ||
418 | - } | ||
419 | - return pipe | ||
420 | + return t.pipeline("parenthesized pipeline", itemRightParen) | ||
421 | case itemString, itemRawString: | ||
422 | s, err := strconv.Unquote(token.val) | ||
423 | if err != nil { | ||
424 | diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go | ||
425 | index d9c13c5..220f984 100644 | ||
426 | --- a/src/text/template/parse/parse_test.go | ||
427 | +++ b/src/text/template/parse/parse_test.go | ||
428 | @@ -250,6 +250,13 @@ var parseTests = []parseTest{ | ||
429 | {"comment trim left and right", "x \r\n\t{{- /* */ -}}\n\n\ty", noError, `"x""y"`}, | ||
430 | {"block definition", `{{block "foo" .}}hello{{end}}`, noError, | ||
431 | `{{template "foo" .}}`}, | ||
432 | + | ||
433 | + {"newline in assignment", "{{ $x \n := \n 1 \n }}", noError, "{{$x := 1}}"}, | ||
434 | + {"newline in empty action", "{{\n}}", hasError, "{{\n}}"}, | ||
435 | + {"newline in pipeline", "{{\n\"x\"\n|\nprintf\n}}", noError, `{{"x" | printf}}`}, | ||
436 | + {"newline in comment", "{{/*\nhello\n*/}}", noError, ""}, | ||
437 | + {"newline in comment", "{{-\n/*\nhello\n*/\n-}}", noError, ""}, | ||
438 | + | ||
439 | // Errors. | ||
440 | {"unclosed action", "hello{{range", hasError, ""}, | ||
441 | {"unmatched end", "{{end}}", hasError, ""}, | ||
442 | @@ -426,23 +433,38 @@ var errorTests = []parseTest{ | ||
443 | // Check line numbers are accurate. | ||
444 | {"unclosed1", | ||
445 | "line1\n{{", | ||
446 | - hasError, `unclosed1:2: unexpected unclosed action in command`}, | ||
447 | + hasError, `unclosed1:2: unclosed action`}, | ||
448 | {"unclosed2", | ||
449 | "line1\n{{define `x`}}line2\n{{", | ||
450 | - hasError, `unclosed2:3: unexpected unclosed action in command`}, | ||
451 | + hasError, `unclosed2:3: unclosed action`}, | ||
452 | + {"unclosed3", | ||
453 | + "line1\n{{\"x\"\n\"y\"\n", | ||
454 | + hasError, `unclosed3:4: unclosed action started at unclosed3:2`}, | ||
455 | + {"unclosed4", | ||
456 | + "{{\n\n\n\n\n", | ||
457 | + hasError, `unclosed4:6: unclosed action started at unclosed4:1`}, | ||
458 | + {"var1", | ||
459 | + "line1\n{{\nx\n}}", | ||
460 | + hasError, `var1:3: function "x" not defined`}, | ||
461 | // Specific errors. | ||
462 | {"function", | ||
463 | "{{foo}}", | ||
464 | hasError, `function "foo" not defined`}, | ||
465 | - {"comment", | ||
466 | + {"comment1", | ||
467 | "{{/*}}", | ||
468 | - hasError, `unclosed comment`}, | ||
469 | + hasError, `comment1:1: unclosed comment`}, | ||
470 | + {"comment2", | ||
471 | + "{{/*\nhello\n}}", | ||
472 | + hasError, `comment2:1: unclosed comment`}, | ||
473 | {"lparen", | ||
474 | "{{.X (1 2 3}}", | ||
475 | hasError, `unclosed left paren`}, | ||
476 | {"rparen", | ||
477 | - "{{.X 1 2 3)}}", | ||
478 | - hasError, `unexpected ")"`}, | ||
479 | + "{{.X 1 2 3 ) }}", | ||
480 | + hasError, `unexpected ")" in command`}, | ||
481 | + {"rparen2", | ||
482 | + "{{(.X 1 2 3", | ||
483 | + hasError, `unclosed action`}, | ||
484 | {"space", | ||
485 | "{{`x`3}}", | ||
486 | hasError, `in operand`}, | ||
487 | @@ -488,7 +510,7 @@ var errorTests = []parseTest{ | ||
488 | hasError, `missing value for parenthesized pipeline`}, | ||
489 | {"multilinerawstring", | ||
490 | "{{ $v := `\n` }} {{", | ||
491 | - hasError, `multilinerawstring:2: unexpected unclosed action`}, | ||
492 | + hasError, `multilinerawstring:2: unclosed action`}, | ||
493 | {"rangeundefvar", | ||
494 | "{{range $k}}{{end}}", | ||
495 | hasError, `undefined variable`}, | ||
496 | -- | ||
497 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_5.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_5.patch new file mode 100644 index 0000000000..fc38929648 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_5.patch | |||
@@ -0,0 +1,585 @@ | |||
1 | From e0e6bca6ddc0e6d9fa3a5b644af9b446924fbf83 Mon Sep 17 00:00:00 2001 | ||
2 | From: Russ Cox <rsc@golang.org> | ||
3 | Date: Thu, 20 May 2021 12:46:33 -0400 | ||
4 | Subject: [PATCH 5/6] html/template, text/template: implement break and | ||
5 | continue for range loops | ||
6 | |||
7 | Break and continue for range loops was accepted as a proposal in June 2017. | ||
8 | It was implemented in CL 66410 (Oct 2017) | ||
9 | but then rolled back in CL 92155 (Feb 2018) | ||
10 | because html/template changes had not been implemented. | ||
11 | |||
12 | This CL reimplements break and continue in text/template | ||
13 | and then adds support for them in html/template as well. | ||
14 | |||
15 | Fixes #20531. | ||
16 | |||
17 | Change-Id: I05330482a976f1c078b4b49c2287bd9031bb7616 | ||
18 | Reviewed-on: https://go-review.googlesource.com/c/go/+/321491 | ||
19 | Trust: Russ Cox <rsc@golang.org> | ||
20 | Run-TryBot: Russ Cox <rsc@golang.org> | ||
21 | TryBot-Result: Go Bot <gobot@golang.org> | ||
22 | Reviewed-by: Rob Pike <r@golang.org> | ||
23 | |||
24 | Dependency Patch #5 | ||
25 | |||
26 | Upstream-Status: Backport from https://github.com/golang/go/commit/d0dd26a88c019d54f22463daae81e785f5867565 | ||
27 | CVE: CVE-2023-24538 | ||
28 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
29 | --- | ||
30 | src/html/template/context.go | 4 ++ | ||
31 | src/html/template/escape.go | 71 ++++++++++++++++++++++++++++++++++- | ||
32 | src/html/template/escape_test.go | 24 ++++++++++++ | ||
33 | src/text/template/doc.go | 8 ++++ | ||
34 | src/text/template/exec.go | 24 +++++++++++- | ||
35 | src/text/template/exec_test.go | 2 + | ||
36 | src/text/template/parse/lex.go | 13 ++++++- | ||
37 | src/text/template/parse/lex_test.go | 2 + | ||
38 | src/text/template/parse/node.go | 36 ++++++++++++++++++ | ||
39 | src/text/template/parse/parse.go | 42 ++++++++++++++++++++- | ||
40 | src/text/template/parse/parse_test.go | 8 ++++ | ||
41 | 11 files changed, 230 insertions(+), 4 deletions(-) | ||
42 | |||
43 | diff --git a/src/html/template/context.go b/src/html/template/context.go | ||
44 | index f7d4849..aaa7d08 100644 | ||
45 | --- a/src/html/template/context.go | ||
46 | +++ b/src/html/template/context.go | ||
47 | @@ -6,6 +6,7 @@ package template | ||
48 | |||
49 | import ( | ||
50 | "fmt" | ||
51 | + "text/template/parse" | ||
52 | ) | ||
53 | |||
54 | // context describes the state an HTML parser must be in when it reaches the | ||
55 | @@ -22,6 +23,7 @@ type context struct { | ||
56 | jsCtx jsCtx | ||
57 | attr attr | ||
58 | element element | ||
59 | + n parse.Node // for range break/continue | ||
60 | err *Error | ||
61 | } | ||
62 | |||
63 | @@ -141,6 +143,8 @@ const ( | ||
64 | // stateError is an infectious error state outside any valid | ||
65 | // HTML/CSS/JS construct. | ||
66 | stateError | ||
67 | + // stateDead marks unreachable code after a {{break}} or {{continue}}. | ||
68 | + stateDead | ||
69 | ) | ||
70 | |||
71 | // isComment is true for any state that contains content meant for template | ||
72 | diff --git a/src/html/template/escape.go b/src/html/template/escape.go | ||
73 | index 8739735..6dea79c 100644 | ||
74 | --- a/src/html/template/escape.go | ||
75 | +++ b/src/html/template/escape.go | ||
76 | @@ -97,6 +97,15 @@ type escaper struct { | ||
77 | actionNodeEdits map[*parse.ActionNode][]string | ||
78 | templateNodeEdits map[*parse.TemplateNode]string | ||
79 | textNodeEdits map[*parse.TextNode][]byte | ||
80 | + // rangeContext holds context about the current range loop. | ||
81 | + rangeContext *rangeContext | ||
82 | +} | ||
83 | + | ||
84 | +// rangeContext holds information about the current range loop. | ||
85 | +type rangeContext struct { | ||
86 | + outer *rangeContext // outer loop | ||
87 | + breaks []context // context at each break action | ||
88 | + continues []context // context at each continue action | ||
89 | } | ||
90 | |||
91 | // makeEscaper creates a blank escaper for the given set. | ||
92 | @@ -109,6 +118,7 @@ func makeEscaper(n *nameSpace) escaper { | ||
93 | map[*parse.ActionNode][]string{}, | ||
94 | map[*parse.TemplateNode]string{}, | ||
95 | map[*parse.TextNode][]byte{}, | ||
96 | + nil, | ||
97 | } | ||
98 | } | ||
99 | |||
100 | @@ -124,8 +134,16 @@ func (e *escaper) escape(c context, n parse.Node) context { | ||
101 | switch n := n.(type) { | ||
102 | case *parse.ActionNode: | ||
103 | return e.escapeAction(c, n) | ||
104 | + case *parse.BreakNode: | ||
105 | + c.n = n | ||
106 | + e.rangeContext.breaks = append(e.rangeContext.breaks, c) | ||
107 | + return context{state: stateDead} | ||
108 | case *parse.CommentNode: | ||
109 | return c | ||
110 | + case *parse.ContinueNode: | ||
111 | + c.n = n | ||
112 | + e.rangeContext.continues = append(e.rangeContext.breaks, c) | ||
113 | + return context{state: stateDead} | ||
114 | case *parse.IfNode: | ||
115 | return e.escapeBranch(c, &n.BranchNode, "if") | ||
116 | case *parse.ListNode: | ||
117 | @@ -427,6 +445,12 @@ func join(a, b context, node parse.Node, nodeName string) context { | ||
118 | if b.state == stateError { | ||
119 | return b | ||
120 | } | ||
121 | + if a.state == stateDead { | ||
122 | + return b | ||
123 | + } | ||
124 | + if b.state == stateDead { | ||
125 | + return a | ||
126 | + } | ||
127 | if a.eq(b) { | ||
128 | return a | ||
129 | } | ||
130 | @@ -466,14 +490,27 @@ func join(a, b context, node parse.Node, nodeName string) context { | ||
131 | |||
132 | // escapeBranch escapes a branch template node: "if", "range" and "with". | ||
133 | func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) context { | ||
134 | + if nodeName == "range" { | ||
135 | + e.rangeContext = &rangeContext{outer: e.rangeContext} | ||
136 | + } | ||
137 | c0 := e.escapeList(c, n.List) | ||
138 | - if nodeName == "range" && c0.state != stateError { | ||
139 | + if nodeName == "range" { | ||
140 | + if c0.state != stateError { | ||
141 | + c0 = joinRange(c0, e.rangeContext) | ||
142 | + } | ||
143 | + e.rangeContext = e.rangeContext.outer | ||
144 | + if c0.state == stateError { | ||
145 | + return c0 | ||
146 | + } | ||
147 | + | ||
148 | // The "true" branch of a "range" node can execute multiple times. | ||
149 | // We check that executing n.List once results in the same context | ||
150 | // as executing n.List twice. | ||
151 | + e.rangeContext = &rangeContext{outer: e.rangeContext} | ||
152 | c1, _ := e.escapeListConditionally(c0, n.List, nil) | ||
153 | c0 = join(c0, c1, n, nodeName) | ||
154 | if c0.state == stateError { | ||
155 | + e.rangeContext = e.rangeContext.outer | ||
156 | // Make clear that this is a problem on loop re-entry | ||
157 | // since developers tend to overlook that branch when | ||
158 | // debugging templates. | ||
159 | @@ -481,11 +518,39 @@ func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) | ||
160 | c0.err.Description = "on range loop re-entry: " + c0.err.Description | ||
161 | return c0 | ||
162 | } | ||
163 | + c0 = joinRange(c0, e.rangeContext) | ||
164 | + e.rangeContext = e.rangeContext.outer | ||
165 | + if c0.state == stateError { | ||
166 | + return c0 | ||
167 | + } | ||
168 | } | ||
169 | c1 := e.escapeList(c, n.ElseList) | ||
170 | return join(c0, c1, n, nodeName) | ||
171 | } | ||
172 | |||
173 | +func joinRange(c0 context, rc *rangeContext) context { | ||
174 | + // Merge contexts at break and continue statements into overall body context. | ||
175 | + // In theory we could treat breaks differently from continues, but for now it is | ||
176 | + // enough to treat them both as going back to the start of the loop (which may then stop). | ||
177 | + for _, c := range rc.breaks { | ||
178 | + c0 = join(c0, c, c.n, "range") | ||
179 | + if c0.state == stateError { | ||
180 | + c0.err.Line = c.n.(*parse.BreakNode).Line | ||
181 | + c0.err.Description = "at range loop break: " + c0.err.Description | ||
182 | + return c0 | ||
183 | + } | ||
184 | + } | ||
185 | + for _, c := range rc.continues { | ||
186 | + c0 = join(c0, c, c.n, "range") | ||
187 | + if c0.state == stateError { | ||
188 | + c0.err.Line = c.n.(*parse.ContinueNode).Line | ||
189 | + c0.err.Description = "at range loop continue: " + c0.err.Description | ||
190 | + return c0 | ||
191 | + } | ||
192 | + } | ||
193 | + return c0 | ||
194 | +} | ||
195 | + | ||
196 | // escapeList escapes a list template node. | ||
197 | func (e *escaper) escapeList(c context, n *parse.ListNode) context { | ||
198 | if n == nil { | ||
199 | @@ -493,6 +558,9 @@ func (e *escaper) escapeList(c context, n *parse.ListNode) context { | ||
200 | } | ||
201 | for _, m := range n.Nodes { | ||
202 | c = e.escape(c, m) | ||
203 | + if c.state == stateDead { | ||
204 | + break | ||
205 | + } | ||
206 | } | ||
207 | return c | ||
208 | } | ||
209 | @@ -503,6 +571,7 @@ func (e *escaper) escapeList(c context, n *parse.ListNode) context { | ||
210 | // which is the same as whether e was updated. | ||
211 | func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter func(*escaper, context) bool) (context, bool) { | ||
212 | e1 := makeEscaper(e.ns) | ||
213 | + e1.rangeContext = e.rangeContext | ||
214 | // Make type inferences available to f. | ||
215 | for k, v := range e.output { | ||
216 | e1.output[k] = v | ||
217 | diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go | ||
218 | index c709660..fa2b84a 100644 | ||
219 | --- a/src/html/template/escape_test.go | ||
220 | +++ b/src/html/template/escape_test.go | ||
221 | @@ -920,6 +920,22 @@ func TestErrors(t *testing.T) { | ||
222 | "<a href='/foo?{{range .Items}}&{{.K}}={{.V}}{{end}}'>", | ||
223 | "", | ||
224 | }, | ||
225 | + { | ||
226 | + "{{range .Items}}<a{{if .X}}{{end}}>{{end}}", | ||
227 | + "", | ||
228 | + }, | ||
229 | + { | ||
230 | + "{{range .Items}}<a{{if .X}}{{end}}>{{continue}}{{end}}", | ||
231 | + "", | ||
232 | + }, | ||
233 | + { | ||
234 | + "{{range .Items}}<a{{if .X}}{{end}}>{{break}}{{end}}", | ||
235 | + "", | ||
236 | + }, | ||
237 | + { | ||
238 | + "{{range .Items}}<a{{if .X}}{{end}}>{{if .X}}{{break}}{{end}}{{end}}", | ||
239 | + "", | ||
240 | + }, | ||
241 | // Error cases. | ||
242 | { | ||
243 | "{{if .Cond}}<a{{end}}", | ||
244 | @@ -956,6 +972,14 @@ func TestErrors(t *testing.T) { | ||
245 | "z:2:8: on range loop re-entry: {{range}} branches", | ||
246 | }, | ||
247 | { | ||
248 | + "{{range .Items}}<a{{if .X}}{{break}}{{end}}>{{end}}", | ||
249 | + "z:1:29: at range loop break: {{range}} branches end in different contexts", | ||
250 | + }, | ||
251 | + { | ||
252 | + "{{range .Items}}<a{{if .X}}{{continue}}{{end}}>{{end}}", | ||
253 | + "z:1:29: at range loop continue: {{range}} branches end in different contexts", | ||
254 | + }, | ||
255 | + { | ||
256 | "<a b=1 c={{.H}}", | ||
257 | "z: ends in a non-text context: {stateAttr delimSpaceOrTagEnd", | ||
258 | }, | ||
259 | diff --git a/src/text/template/doc.go b/src/text/template/doc.go | ||
260 | index 7b30294..0228b15 100644 | ||
261 | --- a/src/text/template/doc.go | ||
262 | +++ b/src/text/template/doc.go | ||
263 | @@ -112,6 +112,14 @@ data, defined in detail in the corresponding sections that follow. | ||
264 | T0 is executed; otherwise, dot is set to the successive elements | ||
265 | of the array, slice, or map and T1 is executed. | ||
266 | |||
267 | + {{break}} | ||
268 | + The innermost {{range pipeline}} loop is ended early, stopping the | ||
269 | + current iteration and bypassing all remaining iterations. | ||
270 | + | ||
271 | + {{continue}} | ||
272 | + The current iteration of the innermost {{range pipeline}} loop is | ||
273 | + stopped, and the loop starts the next iteration. | ||
274 | + | ||
275 | {{template "name"}} | ||
276 | The template with the specified name is executed with nil data. | ||
277 | |||
278 | diff --git a/src/text/template/exec.go b/src/text/template/exec.go | ||
279 | index 7ac5175..6cb140a 100644 | ||
280 | --- a/src/text/template/exec.go | ||
281 | +++ b/src/text/template/exec.go | ||
282 | @@ -5,6 +5,7 @@ | ||
283 | package template | ||
284 | |||
285 | import ( | ||
286 | + "errors" | ||
287 | "fmt" | ||
288 | "internal/fmtsort" | ||
289 | "io" | ||
290 | @@ -244,6 +245,12 @@ func (t *Template) DefinedTemplates() string { | ||
291 | return b.String() | ||
292 | } | ||
293 | |||
294 | +// Sentinel errors for use with panic to signal early exits from range loops. | ||
295 | +var ( | ||
296 | + walkBreak = errors.New("break") | ||
297 | + walkContinue = errors.New("continue") | ||
298 | +) | ||
299 | + | ||
300 | // Walk functions step through the major pieces of the template structure, | ||
301 | // generating output as they go. | ||
302 | func (s *state) walk(dot reflect.Value, node parse.Node) { | ||
303 | @@ -256,7 +263,11 @@ func (s *state) walk(dot reflect.Value, node parse.Node) { | ||
304 | if len(node.Pipe.Decl) == 0 { | ||
305 | s.printValue(node, val) | ||
306 | } | ||
307 | + case *parse.BreakNode: | ||
308 | + panic(walkBreak) | ||
309 | case *parse.CommentNode: | ||
310 | + case *parse.ContinueNode: | ||
311 | + panic(walkContinue) | ||
312 | case *parse.IfNode: | ||
313 | s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList) | ||
314 | case *parse.ListNode: | ||
315 | @@ -335,6 +346,11 @@ func isTrue(val reflect.Value) (truth, ok bool) { | ||
316 | |||
317 | func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { | ||
318 | s.at(r) | ||
319 | + defer func() { | ||
320 | + if r := recover(); r != nil && r != walkBreak { | ||
321 | + panic(r) | ||
322 | + } | ||
323 | + }() | ||
324 | defer s.pop(s.mark()) | ||
325 | val, _ := indirect(s.evalPipeline(dot, r.Pipe)) | ||
326 | // mark top of stack before any variables in the body are pushed. | ||
327 | @@ -348,8 +364,14 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { | ||
328 | if len(r.Pipe.Decl) > 1 { | ||
329 | s.setTopVar(2, index) | ||
330 | } | ||
331 | + defer s.pop(mark) | ||
332 | + defer func() { | ||
333 | + // Consume panic(walkContinue) | ||
334 | + if r := recover(); r != nil && r != walkContinue { | ||
335 | + panic(r) | ||
336 | + } | ||
337 | + }() | ||
338 | s.walk(elem, r.List) | ||
339 | - s.pop(mark) | ||
340 | } | ||
341 | switch val.Kind() { | ||
342 | case reflect.Array, reflect.Slice: | ||
343 | diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go | ||
344 | index 3309b33..a639f44 100644 | ||
345 | --- a/src/text/template/exec_test.go | ||
346 | +++ b/src/text/template/exec_test.go | ||
347 | @@ -563,6 +563,8 @@ var execTests = []execTest{ | ||
348 | {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true}, | ||
349 | {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, | ||
350 | {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, | ||
351 | + {"range []int break else", "{{range .SI}}-{{.}}-{{break}}NOTREACHED{{else}}EMPTY{{end}}", "-3-", tVal, true}, | ||
352 | + {"range []int continue else", "{{range .SI}}-{{.}}-{{continue}}NOTREACHED{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, | ||
353 | {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true}, | ||
354 | {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true}, | ||
355 | {"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true}, | ||
356 | diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go | ||
357 | index 6784071..95e3377 100644 | ||
358 | --- a/src/text/template/parse/lex.go | ||
359 | +++ b/src/text/template/parse/lex.go | ||
360 | @@ -62,6 +62,8 @@ const ( | ||
361 | // Keywords appear after all the rest. | ||
362 | itemKeyword // used only to delimit the keywords | ||
363 | itemBlock // block keyword | ||
364 | + itemBreak // break keyword | ||
365 | + itemContinue // continue keyword | ||
366 | itemDot // the cursor, spelled '.' | ||
367 | itemDefine // define keyword | ||
368 | itemElse // else keyword | ||
369 | @@ -76,6 +78,8 @@ const ( | ||
370 | var key = map[string]itemType{ | ||
371 | ".": itemDot, | ||
372 | "block": itemBlock, | ||
373 | + "break": itemBreak, | ||
374 | + "continue": itemContinue, | ||
375 | "define": itemDefine, | ||
376 | "else": itemElse, | ||
377 | "end": itemEnd, | ||
378 | @@ -119,6 +123,8 @@ type lexer struct { | ||
379 | parenDepth int // nesting depth of ( ) exprs | ||
380 | line int // 1+number of newlines seen | ||
381 | startLine int // start line of this item | ||
382 | + breakOK bool // break keyword allowed | ||
383 | + continueOK bool // continue keyword allowed | ||
384 | } | ||
385 | |||
386 | // next returns the next rune in the input. | ||
387 | @@ -461,7 +467,12 @@ Loop: | ||
388 | } | ||
389 | switch { | ||
390 | case key[word] > itemKeyword: | ||
391 | - l.emit(key[word]) | ||
392 | + item := key[word] | ||
393 | + if item == itemBreak && !l.breakOK || item == itemContinue && !l.continueOK { | ||
394 | + l.emit(itemIdentifier) | ||
395 | + } else { | ||
396 | + l.emit(item) | ||
397 | + } | ||
398 | case word[0] == '.': | ||
399 | l.emit(itemField) | ||
400 | case word == "true", word == "false": | ||
401 | diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go | ||
402 | index 6510eed..df6aabf 100644 | ||
403 | --- a/src/text/template/parse/lex_test.go | ||
404 | +++ b/src/text/template/parse/lex_test.go | ||
405 | @@ -35,6 +35,8 @@ var itemName = map[itemType]string{ | ||
406 | // keywords | ||
407 | itemDot: ".", | ||
408 | itemBlock: "block", | ||
409 | + itemBreak: "break", | ||
410 | + itemContinue: "continue", | ||
411 | itemDefine: "define", | ||
412 | itemElse: "else", | ||
413 | itemIf: "if", | ||
414 | diff --git a/src/text/template/parse/node.go b/src/text/template/parse/node.go | ||
415 | index a9dad5e..c398da0 100644 | ||
416 | --- a/src/text/template/parse/node.go | ||
417 | +++ b/src/text/template/parse/node.go | ||
418 | @@ -71,6 +71,8 @@ const ( | ||
419 | NodeVariable // A $ variable. | ||
420 | NodeWith // A with action. | ||
421 | NodeComment // A comment. | ||
422 | + NodeBreak // A break action. | ||
423 | + NodeContinue // A continue action. | ||
424 | ) | ||
425 | |||
426 | // Nodes. | ||
427 | @@ -907,6 +909,40 @@ func (i *IfNode) Copy() Node { | ||
428 | return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList()) | ||
429 | } | ||
430 | |||
431 | +// BreakNode represents a {{break}} action. | ||
432 | +type BreakNode struct { | ||
433 | + tr *Tree | ||
434 | + NodeType | ||
435 | + Pos | ||
436 | + Line int | ||
437 | +} | ||
438 | + | ||
439 | +func (t *Tree) newBreak(pos Pos, line int) *BreakNode { | ||
440 | + return &BreakNode{tr: t, NodeType: NodeBreak, Pos: pos, Line: line} | ||
441 | +} | ||
442 | + | ||
443 | +func (b *BreakNode) Copy() Node { return b.tr.newBreak(b.Pos, b.Line) } | ||
444 | +func (b *BreakNode) String() string { return "{{break}}" } | ||
445 | +func (b *BreakNode) tree() *Tree { return b.tr } | ||
446 | +func (b *BreakNode) writeTo(sb *strings.Builder) { sb.WriteString("{{break}}") } | ||
447 | + | ||
448 | +// ContinueNode represents a {{continue}} action. | ||
449 | +type ContinueNode struct { | ||
450 | + tr *Tree | ||
451 | + NodeType | ||
452 | + Pos | ||
453 | + Line int | ||
454 | +} | ||
455 | + | ||
456 | +func (t *Tree) newContinue(pos Pos, line int) *ContinueNode { | ||
457 | + return &ContinueNode{tr: t, NodeType: NodeContinue, Pos: pos, Line: line} | ||
458 | +} | ||
459 | + | ||
460 | +func (c *ContinueNode) Copy() Node { return c.tr.newContinue(c.Pos, c.Line) } | ||
461 | +func (c *ContinueNode) String() string { return "{{continue}}" } | ||
462 | +func (c *ContinueNode) tree() *Tree { return c.tr } | ||
463 | +func (c *ContinueNode) writeTo(sb *strings.Builder) { sb.WriteString("{{continue}}") } | ||
464 | + | ||
465 | // RangeNode represents a {{range}} action and its commands. | ||
466 | type RangeNode struct { | ||
467 | BranchNode | ||
468 | diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go | ||
469 | index 5e6e512..7f78b56 100644 | ||
470 | --- a/src/text/template/parse/parse.go | ||
471 | +++ b/src/text/template/parse/parse.go | ||
472 | @@ -31,6 +31,7 @@ type Tree struct { | ||
473 | vars []string // variables defined at the moment. | ||
474 | treeSet map[string]*Tree | ||
475 | actionLine int // line of left delim starting action | ||
476 | + rangeDepth int | ||
477 | mode Mode | ||
478 | } | ||
479 | |||
480 | @@ -223,6 +224,8 @@ func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer, treeSet ma | ||
481 | t.vars = []string{"$"} | ||
482 | t.funcs = funcs | ||
483 | t.treeSet = treeSet | ||
484 | + lex.breakOK = !t.hasFunction("break") | ||
485 | + lex.continueOK = !t.hasFunction("continue") | ||
486 | } | ||
487 | |||
488 | // stopParse terminates parsing. | ||
489 | @@ -385,6 +388,10 @@ func (t *Tree) action() (n Node) { | ||
490 | switch token := t.nextNonSpace(); token.typ { | ||
491 | case itemBlock: | ||
492 | return t.blockControl() | ||
493 | + case itemBreak: | ||
494 | + return t.breakControl(token.pos, token.line) | ||
495 | + case itemContinue: | ||
496 | + return t.continueControl(token.pos, token.line) | ||
497 | case itemElse: | ||
498 | return t.elseControl() | ||
499 | case itemEnd: | ||
500 | @@ -404,6 +411,32 @@ func (t *Tree) action() (n Node) { | ||
501 | return t.newAction(token.pos, token.line, t.pipeline("command", itemRightDelim)) | ||
502 | } | ||
503 | |||
504 | +// Break: | ||
505 | +// {{break}} | ||
506 | +// Break keyword is past. | ||
507 | +func (t *Tree) breakControl(pos Pos, line int) Node { | ||
508 | + if token := t.next(); token.typ != itemRightDelim { | ||
509 | + t.unexpected(token, "in {{break}}") | ||
510 | + } | ||
511 | + if t.rangeDepth == 0 { | ||
512 | + t.errorf("{{break}} outside {{range}}") | ||
513 | + } | ||
514 | + return t.newBreak(pos, line) | ||
515 | +} | ||
516 | + | ||
517 | +// Continue: | ||
518 | +// {{continue}} | ||
519 | +// Continue keyword is past. | ||
520 | +func (t *Tree) continueControl(pos Pos, line int) Node { | ||
521 | + if token := t.next(); token.typ != itemRightDelim { | ||
522 | + t.unexpected(token, "in {{continue}}") | ||
523 | + } | ||
524 | + if t.rangeDepth == 0 { | ||
525 | + t.errorf("{{continue}} outside {{range}}") | ||
526 | + } | ||
527 | + return t.newContinue(pos, line) | ||
528 | +} | ||
529 | + | ||
530 | // Pipeline: | ||
531 | // declarations? command ('|' command)* | ||
532 | func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) { | ||
533 | @@ -479,8 +512,14 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) { | ||
534 | func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) { | ||
535 | defer t.popVars(len(t.vars)) | ||
536 | pipe = t.pipeline(context, itemRightDelim) | ||
537 | + if context == "range" { | ||
538 | + t.rangeDepth++ | ||
539 | + } | ||
540 | var next Node | ||
541 | list, next = t.itemList() | ||
542 | + if context == "range" { | ||
543 | + t.rangeDepth-- | ||
544 | + } | ||
545 | switch next.Type() { | ||
546 | case nodeEnd: //done | ||
547 | case nodeElse: | ||
548 | @@ -522,7 +561,8 @@ func (t *Tree) ifControl() Node { | ||
549 | // {{range pipeline}} itemList {{else}} itemList {{end}} | ||
550 | // Range keyword is past. | ||
551 | func (t *Tree) rangeControl() Node { | ||
552 | - return t.newRange(t.parseControl(false, "range")) | ||
553 | + r := t.newRange(t.parseControl(false, "range")) | ||
554 | + return r | ||
555 | } | ||
556 | |||
557 | // With: | ||
558 | diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go | ||
559 | index 220f984..ba45636 100644 | ||
560 | --- a/src/text/template/parse/parse_test.go | ||
561 | +++ b/src/text/template/parse/parse_test.go | ||
562 | @@ -230,6 +230,10 @@ var parseTests = []parseTest{ | ||
563 | `{{range $x := .SI}}{{.}}{{end}}`}, | ||
564 | {"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError, | ||
565 | `{{range $x, $y := .SI}}{{.}}{{end}}`}, | ||
566 | + {"range with break", "{{range .SI}}{{.}}{{break}}{{end}}", noError, | ||
567 | + `{{range .SI}}{{.}}{{break}}{{end}}`}, | ||
568 | + {"range with continue", "{{range .SI}}{{.}}{{continue}}{{end}}", noError, | ||
569 | + `{{range .SI}}{{.}}{{continue}}{{end}}`}, | ||
570 | {"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError, | ||
571 | `{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`}, | ||
572 | {"template", "{{template `x`}}", noError, | ||
573 | @@ -279,6 +283,10 @@ var parseTests = []parseTest{ | ||
574 | {"adjacent args", "{{printf 3`x`}}", hasError, ""}, | ||
575 | {"adjacent args with .", "{{printf `x`.}}", hasError, ""}, | ||
576 | {"extra end after if", "{{if .X}}a{{else if .Y}}b{{end}}{{end}}", hasError, ""}, | ||
577 | + {"break outside range", "{{range .}}{{end}} {{break}}", hasError, ""}, | ||
578 | + {"continue outside range", "{{range .}}{{end}} {{continue}}", hasError, ""}, | ||
579 | + {"break in range else", "{{range .}}{{else}}{{break}}{{end}}", hasError, ""}, | ||
580 | + {"continue in range else", "{{range .}}{{else}}{{continue}}{{end}}", hasError, ""}, | ||
581 | // Other kinds of assignments and operators aren't available yet. | ||
582 | {"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"}, | ||
583 | {"bug0b", "{{$x += 1}}{{$x}}", hasError, ""}, | ||
584 | -- | ||
585 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_6.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_6.patch new file mode 100644 index 0000000000..baf400b891 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_6.patch | |||
@@ -0,0 +1,371 @@ | |||
1 | From 16f4882984569f179d73967c9eee679bb9b098c5 Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Mon, 20 Mar 2023 11:01:13 -0700 | ||
4 | Subject: [PATCH 6/6] html/template: disallow actions in JS template literals | ||
5 | |||
6 | ECMAScript 6 introduced template literals[0][1] which are delimited with | ||
7 | backticks. These need to be escaped in a similar fashion to the | ||
8 | delimiters for other string literals. Additionally template literals can | ||
9 | contain special syntax for string interpolation. | ||
10 | |||
11 | There is no clear way to allow safe insertion of actions within JS | ||
12 | template literals, as handling (JS) string interpolation inside of these | ||
13 | literals is rather complex. As such we've chosen to simply disallow | ||
14 | template actions within these template literals. | ||
15 | |||
16 | A new error code is added for this parsing failure case, errJsTmplLit, | ||
17 | but it is unexported as it is not backwards compatible with other minor | ||
18 | release versions to introduce an API change in a minor release. We will | ||
19 | export this code in the next major release. | ||
20 | |||
21 | The previous behavior (with the cavet that backticks are now escaped | ||
22 | properly) can be re-enabled with GODEBUG=jstmpllitinterp=1. | ||
23 | |||
24 | This change subsumes CL471455. | ||
25 | |||
26 | Thanks to Sohom Datta, Manipal Institute of Technology, for reporting | ||
27 | this issue. | ||
28 | |||
29 | Fixes CVE-2023-24538 | ||
30 | For #59234 | ||
31 | Fixes #59271 | ||
32 | |||
33 | [0] https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-template-literals | ||
34 | [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals | ||
35 | |||
36 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802457 | ||
37 | Reviewed-by: Damien Neil <dneil@google.com> | ||
38 | Run-TryBot: Damien Neil <dneil@google.com> | ||
39 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
40 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
41 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802612 | ||
42 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
43 | Change-Id: Ic7f10595615f2b2740d9c85ad7ef40dc0e78c04c | ||
44 | Reviewed-on: https://go-review.googlesource.com/c/go/+/481987 | ||
45 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
46 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
47 | Run-TryBot: Michael Knyszek <mknyszek@google.com> | ||
48 | Reviewed-by: Matthew Dempsky <mdempsky@google.com> | ||
49 | |||
50 | Upstream-Status: Backport from https://github.com/golang/go/commit/b1e3ecfa06b67014429a197ec5e134ce4303ad9b | ||
51 | CVE: CVE-2023-24538 | ||
52 | Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com> | ||
53 | --- | ||
54 | src/html/template/context.go | 2 ++ | ||
55 | src/html/template/error.go | 13 ++++++++ | ||
56 | src/html/template/escape.go | 11 +++++++ | ||
57 | src/html/template/escape_test.go | 66 ++++++++++++++++++++++----------------- | ||
58 | src/html/template/js.go | 2 ++ | ||
59 | src/html/template/js_test.go | 2 +- | ||
60 | src/html/template/jsctx_string.go | 9 ++++++ | ||
61 | src/html/template/state_string.go | 37 ++++++++++++++++++++-- | ||
62 | src/html/template/transition.go | 7 ++++- | ||
63 | 9 files changed, 116 insertions(+), 33 deletions(-) | ||
64 | |||
65 | diff --git a/src/html/template/context.go b/src/html/template/context.go | ||
66 | index f7d4849..0b65313 100644 | ||
67 | --- a/src/html/template/context.go | ||
68 | +++ b/src/html/template/context.go | ||
69 | @@ -116,6 +116,8 @@ const ( | ||
70 | stateJSDqStr | ||
71 | // stateJSSqStr occurs inside a JavaScript single quoted string. | ||
72 | stateJSSqStr | ||
73 | + // stateJSBqStr occurs inside a JavaScript back quoted string. | ||
74 | + stateJSBqStr | ||
75 | // stateJSRegexp occurs inside a JavaScript regexp literal. | ||
76 | stateJSRegexp | ||
77 | // stateJSBlockCmt occurs inside a JavaScript /* block comment */. | ||
78 | diff --git a/src/html/template/error.go b/src/html/template/error.go | ||
79 | index 0e52706..fd26b64 100644 | ||
80 | --- a/src/html/template/error.go | ||
81 | +++ b/src/html/template/error.go | ||
82 | @@ -211,6 +211,19 @@ const ( | ||
83 | // pipeline occurs in an unquoted attribute value context, "html" is | ||
84 | // disallowed. Avoid using "html" and "urlquery" entirely in new templates. | ||
85 | ErrPredefinedEscaper | ||
86 | + | ||
87 | + // errJSTmplLit: "... appears in a JS template literal" | ||
88 | + // Example: | ||
89 | + // <script>var tmpl = `{{.Interp}`</script> | ||
90 | + // Discussion: | ||
91 | + // Package html/template does not support actions inside of JS template | ||
92 | + // literals. | ||
93 | + // | ||
94 | + // TODO(rolandshoemaker): we cannot add this as an exported error in a minor | ||
95 | + // release, since it is backwards incompatible with the other minor | ||
96 | + // releases. As such we need to leave it unexported, and then we'll add it | ||
97 | + // in the next major release. | ||
98 | + errJSTmplLit | ||
99 | ) | ||
100 | |||
101 | func (e *Error) Error() string { | ||
102 | diff --git a/src/html/template/escape.go b/src/html/template/escape.go | ||
103 | index f12dafa..29ca5b3 100644 | ||
104 | --- a/src/html/template/escape.go | ||
105 | +++ b/src/html/template/escape.go | ||
106 | @@ -8,6 +8,7 @@ import ( | ||
107 | "bytes" | ||
108 | "fmt" | ||
109 | "html" | ||
110 | + "internal/godebug" | ||
111 | "io" | ||
112 | "text/template" | ||
113 | "text/template/parse" | ||
114 | @@ -203,6 +204,16 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context { | ||
115 | c.jsCtx = jsCtxDivOp | ||
116 | case stateJSDqStr, stateJSSqStr: | ||
117 | s = append(s, "_html_template_jsstrescaper") | ||
118 | + case stateJSBqStr: | ||
119 | + debugAllowActionJSTmpl := godebug.Get("jstmpllitinterp") | ||
120 | + if debugAllowActionJSTmpl == "1" { | ||
121 | + s = append(s, "_html_template_jsstrescaper") | ||
122 | + } else { | ||
123 | + return context{ | ||
124 | + state: stateError, | ||
125 | + err: errorf(errJSTmplLit, n, n.Line, "%s appears in a JS template literal", n), | ||
126 | + } | ||
127 | + } | ||
128 | case stateJSRegexp: | ||
129 | s = append(s, "_html_template_jsregexpescaper") | ||
130 | case stateCSS: | ||
131 | diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go | ||
132 | index fa2b84a..1b150e9 100644 | ||
133 | --- a/src/html/template/escape_test.go | ||
134 | +++ b/src/html/template/escape_test.go | ||
135 | @@ -681,35 +681,31 @@ func TestEscape(t *testing.T) { | ||
136 | } | ||
137 | |||
138 | for _, test := range tests { | ||
139 | - tmpl := New(test.name) | ||
140 | - tmpl = Must(tmpl.Parse(test.input)) | ||
141 | - // Check for bug 6459: Tree field was not set in Parse. | ||
142 | - if tmpl.Tree != tmpl.text.Tree { | ||
143 | - t.Errorf("%s: tree not set properly", test.name) | ||
144 | - continue | ||
145 | - } | ||
146 | - b := new(bytes.Buffer) | ||
147 | - if err := tmpl.Execute(b, data); err != nil { | ||
148 | - t.Errorf("%s: template execution failed: %s", test.name, err) | ||
149 | - continue | ||
150 | - } | ||
151 | - if w, g := test.output, b.String(); w != g { | ||
152 | - t.Errorf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g) | ||
153 | - continue | ||
154 | - } | ||
155 | - b.Reset() | ||
156 | - if err := tmpl.Execute(b, pdata); err != nil { | ||
157 | - t.Errorf("%s: template execution failed for pointer: %s", test.name, err) | ||
158 | - continue | ||
159 | - } | ||
160 | - if w, g := test.output, b.String(); w != g { | ||
161 | - t.Errorf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g) | ||
162 | - continue | ||
163 | - } | ||
164 | - if tmpl.Tree != tmpl.text.Tree { | ||
165 | - t.Errorf("%s: tree mismatch", test.name) | ||
166 | - continue | ||
167 | - } | ||
168 | + t.Run(test.name, func(t *testing.T) { | ||
169 | + tmpl := New(test.name) | ||
170 | + tmpl = Must(tmpl.Parse(test.input)) | ||
171 | + // Check for bug 6459: Tree field was not set in Parse. | ||
172 | + if tmpl.Tree != tmpl.text.Tree { | ||
173 | + t.Fatalf("%s: tree not set properly", test.name) | ||
174 | + } | ||
175 | + b := new(strings.Builder) | ||
176 | + if err := tmpl.Execute(b, data); err != nil { | ||
177 | + t.Fatalf("%s: template execution failed: %s", test.name, err) | ||
178 | + } | ||
179 | + if w, g := test.output, b.String(); w != g { | ||
180 | + t.Fatalf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g) | ||
181 | + } | ||
182 | + b.Reset() | ||
183 | + if err := tmpl.Execute(b, pdata); err != nil { | ||
184 | + t.Fatalf("%s: template execution failed for pointer: %s", test.name, err) | ||
185 | + } | ||
186 | + if w, g := test.output, b.String(); w != g { | ||
187 | + t.Fatalf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g) | ||
188 | + } | ||
189 | + if tmpl.Tree != tmpl.text.Tree { | ||
190 | + t.Fatalf("%s: tree mismatch", test.name) | ||
191 | + } | ||
192 | + }) | ||
193 | } | ||
194 | } | ||
195 | |||
196 | @@ -936,6 +932,10 @@ func TestErrors(t *testing.T) { | ||
197 | "{{range .Items}}<a{{if .X}}{{end}}>{{if .X}}{{break}}{{end}}{{end}}", | ||
198 | "", | ||
199 | }, | ||
200 | + { | ||
201 | + "<script>var a = `${a+b}`</script>`", | ||
202 | + "", | ||
203 | + }, | ||
204 | // Error cases. | ||
205 | { | ||
206 | "{{if .Cond}}<a{{end}}", | ||
207 | @@ -1082,6 +1082,10 @@ func TestErrors(t *testing.T) { | ||
208 | // html is allowed since it is the last command in the pipeline, but urlquery is not. | ||
209 | `predefined escaper "urlquery" disallowed in template`, | ||
210 | }, | ||
211 | + { | ||
212 | + "<script>var tmpl = `asd {{.}}`;</script>", | ||
213 | + `{{.}} appears in a JS template literal`, | ||
214 | + }, | ||
215 | } | ||
216 | for _, test := range tests { | ||
217 | buf := new(bytes.Buffer) | ||
218 | @@ -1304,6 +1308,10 @@ func TestEscapeText(t *testing.T) { | ||
219 | context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript}, | ||
220 | }, | ||
221 | { | ||
222 | + "<a onclick=\"`foo", | ||
223 | + context{state: stateJSBqStr, delim: delimDoubleQuote, attr: attrScript}, | ||
224 | + }, | ||
225 | + { | ||
226 | `<A ONCLICK="'`, | ||
227 | context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript}, | ||
228 | }, | ||
229 | diff --git a/src/html/template/js.go b/src/html/template/js.go | ||
230 | index ea9c183..b888eaf 100644 | ||
231 | --- a/src/html/template/js.go | ||
232 | +++ b/src/html/template/js.go | ||
233 | @@ -308,6 +308,7 @@ var jsStrReplacementTable = []string{ | ||
234 | // Encode HTML specials as hex so the output can be embedded | ||
235 | // in HTML attributes without further encoding. | ||
236 | '"': `\u0022`, | ||
237 | + '`': `\u0060`, | ||
238 | '&': `\u0026`, | ||
239 | '\'': `\u0027`, | ||
240 | '+': `\u002b`, | ||
241 | @@ -331,6 +332,7 @@ var jsStrNormReplacementTable = []string{ | ||
242 | '"': `\u0022`, | ||
243 | '&': `\u0026`, | ||
244 | '\'': `\u0027`, | ||
245 | + '`': `\u0060`, | ||
246 | '+': `\u002b`, | ||
247 | '/': `\/`, | ||
248 | '<': `\u003c`, | ||
249 | diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go | ||
250 | index d7ee47b..7d963ae 100644 | ||
251 | --- a/src/html/template/js_test.go | ||
252 | +++ b/src/html/template/js_test.go | ||
253 | @@ -292,7 +292,7 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) { | ||
254 | `0123456789:;\u003c=\u003e?` + | ||
255 | `@ABCDEFGHIJKLMNO` + | ||
256 | `PQRSTUVWXYZ[\\]^_` + | ||
257 | - "`abcdefghijklmno" + | ||
258 | + "\\u0060abcdefghijklmno" + | ||
259 | "pqrstuvwxyz{|}~\u007f" + | ||
260 | "\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E", | ||
261 | }, | ||
262 | diff --git a/src/html/template/jsctx_string.go b/src/html/template/jsctx_string.go | ||
263 | index dd1d87e..2394893 100644 | ||
264 | --- a/src/html/template/jsctx_string.go | ||
265 | +++ b/src/html/template/jsctx_string.go | ||
266 | @@ -4,6 +4,15 @@ package template | ||
267 | |||
268 | import "strconv" | ||
269 | |||
270 | +func _() { | ||
271 | + // An "invalid array index" compiler error signifies that the constant values have changed. | ||
272 | + // Re-run the stringer command to generate them again. | ||
273 | + var x [1]struct{} | ||
274 | + _ = x[jsCtxRegexp-0] | ||
275 | + _ = x[jsCtxDivOp-1] | ||
276 | + _ = x[jsCtxUnknown-2] | ||
277 | +} | ||
278 | + | ||
279 | const _jsCtx_name = "jsCtxRegexpjsCtxDivOpjsCtxUnknown" | ||
280 | |||
281 | var _jsCtx_index = [...]uint8{0, 11, 21, 33} | ||
282 | diff --git a/src/html/template/state_string.go b/src/html/template/state_string.go | ||
283 | index 05104be..6fb1a6e 100644 | ||
284 | --- a/src/html/template/state_string.go | ||
285 | +++ b/src/html/template/state_string.go | ||
286 | @@ -4,9 +4,42 @@ package template | ||
287 | |||
288 | import "strconv" | ||
289 | |||
290 | -const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateError" | ||
291 | +func _() { | ||
292 | + // An "invalid array index" compiler error signifies that the constant values have changed. | ||
293 | + // Re-run the stringer command to generate them again. | ||
294 | + var x [1]struct{} | ||
295 | + _ = x[stateText-0] | ||
296 | + _ = x[stateTag-1] | ||
297 | + _ = x[stateAttrName-2] | ||
298 | + _ = x[stateAfterName-3] | ||
299 | + _ = x[stateBeforeValue-4] | ||
300 | + _ = x[stateHTMLCmt-5] | ||
301 | + _ = x[stateRCDATA-6] | ||
302 | + _ = x[stateAttr-7] | ||
303 | + _ = x[stateURL-8] | ||
304 | + _ = x[stateSrcset-9] | ||
305 | + _ = x[stateJS-10] | ||
306 | + _ = x[stateJSDqStr-11] | ||
307 | + _ = x[stateJSSqStr-12] | ||
308 | + _ = x[stateJSBqStr-13] | ||
309 | + _ = x[stateJSRegexp-14] | ||
310 | + _ = x[stateJSBlockCmt-15] | ||
311 | + _ = x[stateJSLineCmt-16] | ||
312 | + _ = x[stateCSS-17] | ||
313 | + _ = x[stateCSSDqStr-18] | ||
314 | + _ = x[stateCSSSqStr-19] | ||
315 | + _ = x[stateCSSDqURL-20] | ||
316 | + _ = x[stateCSSSqURL-21] | ||
317 | + _ = x[stateCSSURL-22] | ||
318 | + _ = x[stateCSSBlockCmt-23] | ||
319 | + _ = x[stateCSSLineCmt-24] | ||
320 | + _ = x[stateError-25] | ||
321 | + _ = x[stateDead-26] | ||
322 | +} | ||
323 | + | ||
324 | +const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead" | ||
325 | |||
326 | -var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 155, 170, 184, 192, 205, 218, 231, 244, 255, 271, 286, 296} | ||
327 | +var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 154, 167, 182, 196, 204, 217, 230, 243, 256, 267, 283, 298, 308, 317} | ||
328 | |||
329 | func (i state) String() string { | ||
330 | if i >= state(len(_state_index)-1) { | ||
331 | diff --git a/src/html/template/transition.go b/src/html/template/transition.go | ||
332 | index 06df679..92eb351 100644 | ||
333 | --- a/src/html/template/transition.go | ||
334 | +++ b/src/html/template/transition.go | ||
335 | @@ -27,6 +27,7 @@ var transitionFunc = [...]func(context, []byte) (context, int){ | ||
336 | stateJS: tJS, | ||
337 | stateJSDqStr: tJSDelimited, | ||
338 | stateJSSqStr: tJSDelimited, | ||
339 | + stateJSBqStr: tJSDelimited, | ||
340 | stateJSRegexp: tJSDelimited, | ||
341 | stateJSBlockCmt: tBlockCmt, | ||
342 | stateJSLineCmt: tLineCmt, | ||
343 | @@ -262,7 +263,7 @@ func tURL(c context, s []byte) (context, int) { | ||
344 | |||
345 | // tJS is the context transition function for the JS state. | ||
346 | func tJS(c context, s []byte) (context, int) { | ||
347 | - i := bytes.IndexAny(s, `"'/`) | ||
348 | + i := bytes.IndexAny(s, "\"`'/") | ||
349 | if i == -1 { | ||
350 | // Entire input is non string, comment, regexp tokens. | ||
351 | c.jsCtx = nextJSCtx(s, c.jsCtx) | ||
352 | @@ -274,6 +275,8 @@ func tJS(c context, s []byte) (context, int) { | ||
353 | c.state, c.jsCtx = stateJSDqStr, jsCtxRegexp | ||
354 | case '\'': | ||
355 | c.state, c.jsCtx = stateJSSqStr, jsCtxRegexp | ||
356 | + case '`': | ||
357 | + c.state, c.jsCtx = stateJSBqStr, jsCtxRegexp | ||
358 | case '/': | ||
359 | switch { | ||
360 | case i+1 < len(s) && s[i+1] == '/': | ||
361 | @@ -303,6 +306,8 @@ func tJSDelimited(c context, s []byte) (context, int) { | ||
362 | switch c.state { | ||
363 | case stateJSSqStr: | ||
364 | specials = `\'` | ||
365 | + case stateJSBqStr: | ||
366 | + specials = "`\\" | ||
367 | case stateJSRegexp: | ||
368 | specials = `\/[]` | ||
369 | } | ||
370 | -- | ||
371 | 2.7.4 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24539.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24539.patch new file mode 100644 index 0000000000..281b6486a8 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24539.patch | |||
@@ -0,0 +1,60 @@ | |||
1 | From 8673ca81e5340b87709db2d9749c92a3bf925df1 Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Thu, 13 Apr 2023 15:40:44 -0700 | ||
4 | Subject: [PATCH] html/template: disallow angle brackets in CSS values | ||
5 | |||
6 | Angle brackets should not appear in CSS contexts, as they may affect | ||
7 | token boundaries (such as closing a <style> tag, resulting in | ||
8 | injection). Instead emit filterFailsafe, matching the behavior for other | ||
9 | dangerous characters. | ||
10 | |||
11 | Thanks to Juho Nurminen of Mattermost for reporting this issue. | ||
12 | |||
13 | Fixes #59720 | ||
14 | Fixes CVE-2023-24539 | ||
15 | |||
16 | Change-Id: Iccc659c9a18415992b0c05c178792228e3a7bae4 | ||
17 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1826636 | ||
18 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
19 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
20 | Reviewed-by: Damien Neil <dneil@google.com> | ||
21 | Reviewed-on: https://go-review.googlesource.com/c/go/+/491615 | ||
22 | Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> | ||
23 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
24 | Run-TryBot: Carlos Amedee <carlos@golang.org> | ||
25 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
26 | |||
27 | Upstream-Status: Backport from [https://github.com/golang/go/commit/8673ca81e5340b87709db2d9749c92a3bf925df1] | ||
28 | CVE: CVE-2023-24539 | ||
29 | Signed-off-by: Ashish Sharma <asharma@mvista.com> | ||
30 | --- | ||
31 | src/html/template/css.go | 2 +- | ||
32 | src/html/template/css_test.go | 2 ++ | ||
33 | 2 files changed, 3 insertions(+), 1 deletion(-) | ||
34 | |||
35 | diff --git a/src/html/template/css.go b/src/html/template/css.go | ||
36 | index 890a0c6b227fe..f650d8b3e843a 100644 | ||
37 | --- a/src/html/template/css.go | ||
38 | +++ b/src/html/template/css.go | ||
39 | @@ -238,7 +238,7 @@ func cssValueFilter(args ...any) string { | ||
40 | // inside a string that might embed JavaScript source. | ||
41 | for i, c := range b { | ||
42 | switch c { | ||
43 | - case 0, '"', '\'', '(', ')', '/', ';', '@', '[', '\\', ']', '`', '{', '}': | ||
44 | + case 0, '"', '\'', '(', ')', '/', ';', '@', '[', '\\', ']', '`', '{', '}', '<', '>': | ||
45 | return filterFailsafe | ||
46 | case '-': | ||
47 | // Disallow <!-- or -->. | ||
48 | diff --git a/src/html/template/css_test.go b/src/html/template/css_test.go | ||
49 | index a735638b0314f..2b76256a766e9 100644 | ||
50 | --- a/src/html/template/css_test.go | ||
51 | +++ b/src/html/template/css_test.go | ||
52 | @@ -231,6 +231,8 @@ func TestCSSValueFilter(t *testing.T) { | ||
53 | {`-exp\000052 ession(alert(1337))`, "ZgotmplZ"}, | ||
54 | {`-expre\0000073sion`, "-expre\x073sion"}, | ||
55 | {`@import url evil.css`, "ZgotmplZ"}, | ||
56 | + {"<", "ZgotmplZ"}, | ||
57 | + {">", "ZgotmplZ"}, | ||
58 | } | ||
59 | for _, test := range tests { | ||
60 | got := cssValueFilter(test.css) | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-24540.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-24540.patch new file mode 100644 index 0000000000..799a0dfcda --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-24540.patch | |||
@@ -0,0 +1,90 @@ | |||
1 | From ce7bd33345416e6d8cac901792060591cafc2797 Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Tue, 11 Apr 2023 16:27:43 +0100 | ||
4 | Subject: [PATCH] [release-branch.go1.19] html/template: handle all JS | ||
5 | whitespace characters | ||
6 | |||
7 | Rather than just a small set. Character class as defined by \s [0]. | ||
8 | |||
9 | Thanks to Juho Nurminen of Mattermost for reporting this. | ||
10 | |||
11 | For #59721 | ||
12 | Fixes #59813 | ||
13 | Fixes CVE-2023-24540 | ||
14 | |||
15 | [0] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes | ||
16 | |||
17 | Change-Id: I56d4fa1ef08125b417106ee7dbfb5b0923b901ba | ||
18 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1821459 | ||
19 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
20 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
21 | Reviewed-by: Damien Neil <dneil@google.com> | ||
22 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1851497 | ||
23 | Run-TryBot: Damien Neil <dneil@google.com> | ||
24 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
25 | Reviewed-on: https://go-review.googlesource.com/c/go/+/491355 | ||
26 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
27 | Reviewed-by: Carlos Amedee <carlos@golang.org> | ||
28 | TryBot-Bypass: Carlos Amedee <carlos@golang.org> | ||
29 | Run-TryBot: Carlos Amedee <carlos@golang.org> | ||
30 | |||
31 | Upstream-Status: Backport [https://github.com/golang/go/commit/ce7bd33345416e6d8cac901792060591cafc2797] | ||
32 | CVE: CVE-2023-24540 | ||
33 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
34 | --- | ||
35 | src/html/template/js.go | 8 +++++++- | ||
36 | src/html/template/js_test.go | 11 +++++++---- | ||
37 | 2 files changed, 14 insertions(+), 5 deletions(-) | ||
38 | |||
39 | diff --git a/src/html/template/js.go b/src/html/template/js.go | ||
40 | index fe7054efe5cd8..4e05c1455723f 100644 | ||
41 | --- a/src/html/template/js.go | ||
42 | +++ b/src/html/template/js.go | ||
43 | @@ -13,6 +13,11 @@ import ( | ||
44 | "unicode/utf8" | ||
45 | ) | ||
46 | |||
47 | +// jsWhitespace contains all of the JS whitespace characters, as defined | ||
48 | +// by the \s character class. | ||
49 | +// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions/Character_classes. | ||
50 | +const jsWhitespace = "\f\n\r\t\v\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\ufeff" | ||
51 | + | ||
52 | // nextJSCtx returns the context that determines whether a slash after the | ||
53 | // given run of tokens starts a regular expression instead of a division | ||
54 | // operator: / or /=. | ||
55 | @@ -26,7 +31,8 @@ import ( | ||
56 | // JavaScript 2.0 lexical grammar and requires one token of lookbehind: | ||
57 | // https://www.mozilla.org/js/language/js20-2000-07/rationale/syntax.html | ||
58 | func nextJSCtx(s []byte, preceding jsCtx) jsCtx { | ||
59 | - s = bytes.TrimRight(s, "\t\n\f\r \u2028\u2029") | ||
60 | + // Trim all JS whitespace characters | ||
61 | + s = bytes.TrimRight(s, jsWhitespace) | ||
62 | if len(s) == 0 { | ||
63 | return preceding | ||
64 | } | ||
65 | diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go | ||
66 | index e07c695f7a77d..e52180cc113b5 100644 | ||
67 | --- a/src/html/template/js_test.go | ||
68 | +++ b/src/html/template/js_test.go | ||
69 | @@ -81,14 +81,17 @@ func TestNextJsCtx(t *testing.T) { | ||
70 | {jsCtxDivOp, "0"}, | ||
71 | // Dots that are part of a number are div preceders. | ||
72 | {jsCtxDivOp, "0."}, | ||
73 | + // Some JS interpreters treat NBSP as a normal space, so | ||
74 | + // we must too in order to properly escape things. | ||
75 | + {jsCtxRegexp, "=\u00A0"}, | ||
76 | } | ||
77 | |||
78 | for _, test := range tests { | ||
79 | - if nextJSCtx([]byte(test.s), jsCtxRegexp) != test.jsCtx { | ||
80 | - t.Errorf("want %s got %q", test.jsCtx, test.s) | ||
81 | + if ctx := nextJSCtx([]byte(test.s), jsCtxRegexp); ctx != test.jsCtx { | ||
82 | + t.Errorf("%q: want %s got %s", test.s, test.jsCtx, ctx) | ||
83 | } | ||
84 | - if nextJSCtx([]byte(test.s), jsCtxDivOp) != test.jsCtx { | ||
85 | - t.Errorf("want %s got %q", test.jsCtx, test.s) | ||
86 | + if ctx := nextJSCtx([]byte(test.s), jsCtxDivOp); ctx != test.jsCtx { | ||
87 | + t.Errorf("%q: want %s got %s", test.s, test.jsCtx, ctx) | ||
88 | } | ||
89 | } | ||
90 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-29400.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-29400.patch new file mode 100644 index 0000000000..092c7aa0ff --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-29400.patch | |||
@@ -0,0 +1,94 @@ | |||
1 | From 0d347544cbca0f42b160424f6bc2458ebcc7b3fc Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Thu, 13 Apr 2023 14:01:50 -0700 | ||
4 | Subject: [PATCH] html/template: emit filterFailsafe for empty unquoted attr | ||
5 | value | ||
6 | |||
7 | An unquoted action used as an attribute value can result in unsafe | ||
8 | behavior if it is empty, as HTML normalization will result in unexpected | ||
9 | attributes, and may allow attribute injection. If executing a template | ||
10 | results in a empty unquoted attribute value, emit filterFailsafe | ||
11 | instead. | ||
12 | |||
13 | Thanks to Juho Nurminen of Mattermost for reporting this issue. | ||
14 | |||
15 | Fixes #59722 | ||
16 | Fixes CVE-2023-29400 | ||
17 | |||
18 | Change-Id: Ia38d1b536ae2b4af5323a6c6d861e3c057c2570a | ||
19 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1826631 | ||
20 | Reviewed-by: Julie Qiu <julieqiu@google.com> | ||
21 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
22 | Reviewed-by: Damien Neil <dneil@google.com> | ||
23 | Reviewed-on: https://go-review.googlesource.com/c/go/+/491617 | ||
24 | Run-TryBot: Carlos Amedee <carlos@golang.org> | ||
25 | Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> | ||
26 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
27 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
28 | |||
29 | Upstream-Status: Backport from [https://github.com/golang/go/commit/0d347544cbca0f42b160424f6bc2458ebcc7b3fc] | ||
30 | CVE: CVE-2023-29400 | ||
31 | Signed-off-by: Ashish Sharma <asharma@mvista.com> | ||
32 | --- | ||
33 | src/html/template/escape.go | 5 ++--- | ||
34 | src/html/template/escape_test.go | 15 +++++++++++++++ | ||
35 | src/html/template/html.go | 3 +++ | ||
36 | 3 files changed, 20 insertions(+), 3 deletions(-) | ||
37 | |||
38 | diff --git a/src/html/template/escape.go b/src/html/template/escape.go | ||
39 | index 4ba1d6b31897e..a62ef159f0dcd 100644 | ||
40 | --- a/src/html/template/escape.go | ||
41 | +++ b/src/html/template/escape.go | ||
42 | @@ -382,9 +382,8 @@ func normalizeEscFn(e string) string { | ||
43 | // for all x. | ||
44 | var redundantFuncs = map[string]map[string]bool{ | ||
45 | "_html_template_commentescaper": { | ||
46 | - "_html_template_attrescaper": true, | ||
47 | - "_html_template_nospaceescaper": true, | ||
48 | - "_html_template_htmlescaper": true, | ||
49 | + "_html_template_attrescaper": true, | ||
50 | + "_html_template_htmlescaper": true, | ||
51 | }, | ||
52 | "_html_template_cssescaper": { | ||
53 | "_html_template_attrescaper": true, | ||
54 | diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go | ||
55 | index 3dd212bac9406..f8b2b448f2dfa 100644 | ||
56 | --- a/src/html/template/escape_test.go | ||
57 | +++ b/src/html/template/escape_test.go | ||
58 | @@ -678,6 +678,21 @@ func TestEscape(t *testing.T) { | ||
59 | `<img srcset={{",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"}}>`, | ||
60 | `<img srcset=,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,>`, | ||
61 | }, | ||
62 | + { | ||
63 | + "unquoted empty attribute value (plaintext)", | ||
64 | + "<p name={{.U}}>", | ||
65 | + "<p name=ZgotmplZ>", | ||
66 | + }, | ||
67 | + { | ||
68 | + "unquoted empty attribute value (url)", | ||
69 | + "<p href={{.U}}>", | ||
70 | + "<p href=ZgotmplZ>", | ||
71 | + }, | ||
72 | + { | ||
73 | + "quoted empty attribute value", | ||
74 | + "<p name=\"{{.U}}\">", | ||
75 | + "<p name=\"\">", | ||
76 | + }, | ||
77 | } | ||
78 | |||
79 | for _, test := range tests { | ||
80 | diff --git a/src/html/template/html.go b/src/html/template/html.go | ||
81 | index bcca0b51a0ef9..a181699a5bda8 100644 | ||
82 | --- a/src/html/template/html.go | ||
83 | +++ b/src/html/template/html.go | ||
84 | @@ -14,6 +14,9 @@ import ( | ||
85 | // htmlNospaceEscaper escapes for inclusion in unquoted attribute values. | ||
86 | func htmlNospaceEscaper(args ...interface{}) string { | ||
87 | s, t := stringify(args...) | ||
88 | + if s == "" { | ||
89 | + return filterFailsafe | ||
90 | + } | ||
91 | if t == contentTypeHTML { | ||
92 | return htmlReplacer(stripTags(s), htmlNospaceNormReplacementTable, false) | ||
93 | } | ||
94 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-29402.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-29402.patch new file mode 100644 index 0000000000..01eed9fe1b --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-29402.patch | |||
@@ -0,0 +1,201 @@ | |||
1 | rom c160b49b6d328c86bd76ca2fff9009a71347333f Mon Sep 17 00:00:00 2001 | ||
2 | From: "Bryan C. Mills" <bcmills@google.com> | ||
3 | Date: Fri, 12 May 2023 14:15:16 -0400 | ||
4 | Subject: [PATCH] [release-branch.go1.19] cmd/go: disallow package directories | ||
5 | containing newlines | ||
6 | |||
7 | Directory or file paths containing newlines may cause tools (such as | ||
8 | cmd/cgo) that emit "//line" or "#line" -directives to write part of | ||
9 | the path into non-comment lines in generated source code. If those | ||
10 | lines contain valid Go code, it may be injected into the resulting | ||
11 | binary. | ||
12 | |||
13 | (Note that Go import paths and file paths within module zip files | ||
14 | already could not contain newlines.) | ||
15 | |||
16 | Thanks to Juho Nurminen of Mattermost for reporting this issue. | ||
17 | |||
18 | Updates #60167. | ||
19 | Fixes #60515. | ||
20 | Fixes CVE-2023-29402. | ||
21 | |||
22 | Change-Id: If55d0400c02beb7a5da5eceac60f1abeac99f064 | ||
23 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1882606 | ||
24 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
25 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
26 | Reviewed-by: Russ Cox <rsc@google.com> | ||
27 | Reviewed-by: Damien Neil <dneil@google.com> | ||
28 | (cherry picked from commit 41f9046495564fc728d6f98384ab7276450ac7e2) | ||
29 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1902229 | ||
30 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1904343 | ||
31 | Reviewed-by: Michael Knyszek <mknyszek@google.com> | ||
32 | Reviewed-by: Bryan Mills <bcmills@google.com> | ||
33 | Reviewed-on: https://go-review.googlesource.com/c/go/+/501218 | ||
34 | Run-TryBot: David Chase <drchase@google.com> | ||
35 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
36 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
37 | |||
38 | Upstream-Status: Backport [https://github.com/golang/go/commit/c160b49b6d328c86bd76ca2fff9009a71347333f] | ||
39 | CVE: CVE-2023-29402 | ||
40 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
41 | --- | ||
42 | src/cmd/go/internal/load/pkg.go | 4 + | ||
43 | src/cmd/go/internal/work/exec.go | 6 ++ | ||
44 | src/cmd/go/script_test.go | 1 + | ||
45 | .../go/testdata/script/build_cwd_newline.txt | 100 ++++++++++++++++++ | ||
46 | 4 files changed, 111 insertions(+) | ||
47 | create mode 100644 src/cmd/go/testdata/script/build_cwd_newline.txt | ||
48 | |||
49 | diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go | ||
50 | index 369a79b..d2b63b0 100644 | ||
51 | --- a/src/cmd/go/internal/load/pkg.go | ||
52 | +++ b/src/cmd/go/internal/load/pkg.go | ||
53 | @@ -1697,6 +1697,10 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) { | ||
54 | setError(ImportErrorf(p.ImportPath, "invalid import path %q", p.ImportPath)) | ||
55 | return | ||
56 | } | ||
57 | + if strings.ContainsAny(p.Dir, "\r\n") { | ||
58 | + setError(fmt.Errorf("invalid package directory %q", p.Dir)) | ||
59 | + return | ||
60 | + } | ||
61 | |||
62 | // Build list of imported packages and full dependency list. | ||
63 | imports := make([]*Package, 0, len(p.Imports)) | ||
64 | diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go | ||
65 | index 9a9650b..050b785 100644 | ||
66 | --- a/src/cmd/go/internal/work/exec.go | ||
67 | +++ b/src/cmd/go/internal/work/exec.go | ||
68 | @@ -458,6 +458,12 @@ func (b *Builder) build(a *Action) (err error) { | ||
69 | b.Print(a.Package.ImportPath + "\n") | ||
70 | } | ||
71 | |||
72 | + if p.Error != nil { | ||
73 | + // Don't try to build anything for packages with errors. There may be a | ||
74 | + // problem with the inputs that makes the package unsafe to build. | ||
75 | + return p.Error | ||
76 | + } | ||
77 | + | ||
78 | if a.Package.BinaryOnly { | ||
79 | p.Stale = true | ||
80 | p.StaleReason = "binary-only packages are no longer supported" | ||
81 | diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go | ||
82 | index ec498bb..a1398ad 100644 | ||
83 | --- a/src/cmd/go/script_test.go | ||
84 | +++ b/src/cmd/go/script_test.go | ||
85 | @@ -123,6 +123,7 @@ func (ts *testScript) setup() { | ||
86 | "devnull=" + os.DevNull, | ||
87 | "goversion=" + goVersion(ts), | ||
88 | ":=" + string(os.PathListSeparator), | ||
89 | + "newline=\n", | ||
90 | } | ||
91 | |||
92 | if runtime.GOOS == "plan9" { | ||
93 | diff --git a/src/cmd/go/testdata/script/build_cwd_newline.txt b/src/cmd/go/testdata/script/build_cwd_newline.txt | ||
94 | new file mode 100644 | ||
95 | index 0000000..61c6966 | ||
96 | --- /dev/null | ||
97 | +++ b/src/cmd/go/testdata/script/build_cwd_newline.txt | ||
98 | @@ -0,0 +1,100 @@ | ||
99 | +[windows] skip 'filesystem normalizes / to \' | ||
100 | +[plan9] skip 'filesystem disallows \n in paths' | ||
101 | + | ||
102 | +# If the directory path containing a package to be built includes a newline, | ||
103 | +# the go command should refuse to even try to build the package. | ||
104 | + | ||
105 | +env DIR=$WORK${/}${newline}'package main'${newline}'func main() { panic("uh-oh")'${newline}'/*' | ||
106 | + | ||
107 | +mkdir $DIR | ||
108 | +cd $DIR | ||
109 | +exec pwd | ||
110 | +cp $WORK/go.mod ./go.mod | ||
111 | +cp $WORK/main.go ./main.go | ||
112 | +cp $WORK/main_test.go ./main_test.go | ||
113 | + | ||
114 | +! go build -o $devnull . | ||
115 | +stderr 'package example: invalid package directory .*uh-oh' | ||
116 | + | ||
117 | +! go build -o $devnull main.go | ||
118 | +stderr 'package command-line-arguments: invalid package directory .*uh-oh' | ||
119 | + | ||
120 | +! go run . | ||
121 | +stderr 'package example: invalid package directory .*uh-oh' | ||
122 | + | ||
123 | +! go run main.go | ||
124 | +stderr 'package command-line-arguments: invalid package directory .*uh-oh' | ||
125 | + | ||
126 | +! go test . | ||
127 | +stderr 'package example: invalid package directory .*uh-oh' | ||
128 | + | ||
129 | +! go test -v main.go main_test.go | ||
130 | +stderr 'package command-line-arguments: invalid package directory .*uh-oh' | ||
131 | + | ||
132 | + | ||
133 | +# Since we do preserve $PWD (or set it appropriately) for commands, and we do | ||
134 | +# not resolve symlinks unnecessarily, referring to the contents of the unsafe | ||
135 | +# directory via a safe symlink should be ok, and should not inject the data from | ||
136 | +# the symlink target path. | ||
137 | + | ||
138 | +[!symlink] stop 'remainder of test checks symlink behavior' | ||
139 | +[short] stop 'links and runs binaries' | ||
140 | + | ||
141 | +symlink $WORK${/}link -> $DIR | ||
142 | + | ||
143 | +go run $WORK${/}link${/}main.go | ||
144 | +! stdout panic | ||
145 | +! stderr panic | ||
146 | +stderr '^ok$' | ||
147 | + | ||
148 | +go test -v $WORK${/}link${/}main.go $WORK${/}link${/}main_test.go | ||
149 | +! stdout panic | ||
150 | +! stderr panic | ||
151 | +stdout '^ok$' # 'go test' combines the test's stdout into stderr | ||
152 | + | ||
153 | +cd $WORK/link | ||
154 | + | ||
155 | +! go run $DIR${/}main.go | ||
156 | +stderr 'package command-line-arguments: invalid package directory .*uh-oh' | ||
157 | + | ||
158 | +go run . | ||
159 | +! stdout panic | ||
160 | +! stderr panic | ||
161 | +stderr '^ok$' | ||
162 | + | ||
163 | +go run main.go | ||
164 | +! stdout panic | ||
165 | +! stderr panic | ||
166 | +stderr '^ok$' | ||
167 | + | ||
168 | +go test -v | ||
169 | +! stdout panic | ||
170 | +! stderr panic | ||
171 | +stdout '^ok$' # 'go test' combines the test's stdout into stderr | ||
172 | + | ||
173 | +go test -v . | ||
174 | +! stdout panic | ||
175 | +! stderr panic | ||
176 | +stdout '^ok$' # 'go test' combines the test's stdout into stderr | ||
177 | + | ||
178 | + | ||
179 | +-- $WORK/go.mod -- | ||
180 | +module example | ||
181 | +go 1.19 | ||
182 | +-- $WORK/main.go -- | ||
183 | +package main | ||
184 | + | ||
185 | +import "C" | ||
186 | + | ||
187 | +func main() { | ||
188 | + /* nothing here */ | ||
189 | + println("ok") | ||
190 | +} | ||
191 | +-- $WORK/main_test.go -- | ||
192 | +package main | ||
193 | + | ||
194 | +import "testing" | ||
195 | + | ||
196 | +func TestMain(*testing.M) { | ||
197 | + main() | ||
198 | +} | ||
199 | -- | ||
200 | 2.25.1 | ||
201 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-29404.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-29404.patch new file mode 100644 index 0000000000..61336ee9ee --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-29404.patch | |||
@@ -0,0 +1,84 @@ | |||
1 | From bf3c8ce03e175e870763901a3850bca01381a828 Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Fri, 5 May 2023 13:10:34 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.19] cmd/go: enforce flags with | ||
5 | non-optional arguments | ||
6 | |||
7 | Enforce that linker flags which expect arguments get them, otherwise it | ||
8 | may be possible to smuggle unexpected flags through as the linker can | ||
9 | consume what looks like a flag as an argument to a preceding flag (i.e. | ||
10 | "-Wl,-O -Wl,-R,-bad-flag" is interpreted as "-O=-R -bad-flag"). Also be | ||
11 | somewhat more restrictive in the general format of some flags. | ||
12 | |||
13 | Thanks to Juho Nurminen of Mattermost for reporting this issue. | ||
14 | |||
15 | Updates #60305 | ||
16 | Fixes #60511 | ||
17 | Fixes CVE-2023-29404 | ||
18 | |||
19 | Change-Id: Icdffef2c0f644da50261cace6f43742783931cff | ||
20 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1876275 | ||
21 | Reviewed-by: Ian Lance Taylor <iant@google.com> | ||
22 | Reviewed-by: Damien Neil <dneil@google.com> | ||
23 | (cherry picked from commit 896779503cf754cbdac24b61d4cc953b50fe2dde) | ||
24 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1902225 | ||
25 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
26 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1904342 | ||
27 | Reviewed-by: Michael Knyszek <mknyszek@google.com> | ||
28 | Reviewed-on: https://go-review.googlesource.com/c/go/+/501217 | ||
29 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
30 | Run-TryBot: David Chase <drchase@google.com> | ||
31 | TryBot-Bypass: Michael Knyszek <mknyszek@google.com> | ||
32 | |||
33 | Upstream-Status: Backport [https://github.com/golang/go/commit/bf3c8ce03e175e870763901a3850bca01381a828] | ||
34 | CVE: CVE-2023-29404 | ||
35 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
36 | --- | ||
37 | src/cmd/go/internal/work/security.go | 6 +++--- | ||
38 | src/cmd/go/internal/work/security_test.go | 5 +++++ | ||
39 | 2 files changed, 8 insertions(+), 3 deletions(-) | ||
40 | |||
41 | diff --git a/src/cmd/go/internal/work/security.go b/src/cmd/go/internal/work/security.go | ||
42 | index a823b20..8acb6dc 100644 | ||
43 | --- a/src/cmd/go/internal/work/security.go | ||
44 | +++ b/src/cmd/go/internal/work/security.go | ||
45 | @@ -177,17 +177,17 @@ var validLinkerFlags = []*lazyregexp.Regexp{ | ||
46 | re(`-Wl,-Bdynamic`), | ||
47 | re(`-Wl,-berok`), | ||
48 | re(`-Wl,-Bstatic`), | ||
49 | - re(`-WL,-O([^@,\-][^,]*)?`), | ||
50 | + re(`-Wl,-O[0-9]+`), | ||
51 | re(`-Wl,-d[ny]`), | ||
52 | re(`-Wl,--disable-new-dtags`), | ||
53 | - re(`-Wl,-e[=,][a-zA-Z0-9]*`), | ||
54 | + re(`-Wl,-e[=,][a-zA-Z0-9]+`), | ||
55 | re(`-Wl,--enable-new-dtags`), | ||
56 | re(`-Wl,--end-group`), | ||
57 | re(`-Wl,--(no-)?export-dynamic`), | ||
58 | re(`-Wl,-framework,[^,@\-][^,]+`), | ||
59 | re(`-Wl,-headerpad_max_install_names`), | ||
60 | re(`-Wl,--no-undefined`), | ||
61 | - re(`-Wl,-R([^@\-][^,@]*$)`), | ||
62 | + re(`-Wl,-R,?([^@\-,][^,@]*$)`), | ||
63 | re(`-Wl,--just-symbols[=,]([^,@\-][^,@]+)`), | ||
64 | re(`-Wl,-rpath(-link)?[=,]([^,@\-][^,]+)`), | ||
65 | re(`-Wl,-s`), | ||
66 | diff --git a/src/cmd/go/internal/work/security_test.go b/src/cmd/go/internal/work/security_test.go | ||
67 | index bd707ff..7b0b7d3 100644 | ||
68 | --- a/src/cmd/go/internal/work/security_test.go | ||
69 | +++ b/src/cmd/go/internal/work/security_test.go | ||
70 | @@ -220,6 +220,11 @@ var badLinkerFlags = [][]string{ | ||
71 | {"-Wl,-R,@foo"}, | ||
72 | {"-Wl,--just-symbols,@foo"}, | ||
73 | {"../x.o"}, | ||
74 | + {"-Wl,-R,"}, | ||
75 | + {"-Wl,-O"}, | ||
76 | + {"-Wl,-e="}, | ||
77 | + {"-Wl,-e,"}, | ||
78 | + {"-Wl,-R,-flag"}, | ||
79 | } | ||
80 | |||
81 | func TestCheckLinkerFlags(t *testing.T) { | ||
82 | -- | ||
83 | 2.25.1 | ||
84 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-29405-1.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-29405-1.patch new file mode 100644 index 0000000000..70d50cc08a --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-29405-1.patch | |||
@@ -0,0 +1,112 @@ | |||
1 | From fa60c381ed06c12f9c27a7b50ca44c5f84f7f0f4 Mon Sep 17 00:00:00 2001 | ||
2 | From: Ian Lance Taylor <iant@golang.org> | ||
3 | Date: Thu, 4 May 2023 14:06:39 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.20] cmd/go,cmd/cgo: in _cgo_flags use one | ||
5 | line per flag | ||
6 | |||
7 | The flags that we recorded in _cgo_flags did not use any quoting, | ||
8 | so a flag containing embedded spaces was mishandled. | ||
9 | Change the _cgo_flags format to put each flag on a separate line. | ||
10 | That is a simple format that does not require any quoting. | ||
11 | |||
12 | As far as I can tell only cmd/go uses _cgo_flags, and it is only | ||
13 | used for gccgo. If this patch doesn't cause any trouble, then | ||
14 | in the next release we can change to only using _cgo_flags for gccgo. | ||
15 | |||
16 | Thanks to Juho Nurminen of Mattermost for reporting this issue. | ||
17 | |||
18 | Updates #60306 | ||
19 | Fixes #60514 | ||
20 | Fixes CVE-2023-29405 | ||
21 | |||
22 | Change-Id: I36b6e188a44c80d7b9573efa577c386770bd2ba3 | ||
23 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1875094 | ||
24 | Reviewed-by: Damien Neil <dneil@google.com> | ||
25 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
26 | (cherry picked from commit bcdfcadd5612212089d958bc352a6f6c90742dcc) | ||
27 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1902228 | ||
28 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
29 | TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> | ||
30 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1904345 | ||
31 | Reviewed-by: Michael Knyszek <mknyszek@google.com> | ||
32 | Reviewed-on: https://go-review.googlesource.com/c/go/+/501220 | ||
33 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
34 | Run-TryBot: David Chase <drchase@google.com> | ||
35 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
36 | --- | ||
37 | Upstream-Status: Backport [https://github.com/golang/go/commit/fa60c381ed06c12f9c27a7b50ca44c5f84f7f0f4] | ||
38 | CVE: CVE-2023-29405 | ||
39 | Signed-off-by: Ashish Sharma <asharma@mvista.com> | ||
40 | |||
41 | src/cmd/cgo/out.go | 4 +++- | ||
42 | src/cmd/go/internal/work/gccgo.go | 14 ++++++------- | ||
43 | .../go/testdata/script/gccgo_link_ldflags.txt | 20 +++++++++++++++++++ | ||
44 | 3 files changed, 29 insertions(+), 9 deletions(-) | ||
45 | create mode 100644 src/cmd/go/testdata/script/gccgo_link_ldflags.txt | ||
46 | |||
47 | diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go | ||
48 | index d26f9e76a374a..d0c6fe3d4c2c2 100644 | ||
49 | --- a/src/cmd/cgo/out.go | ||
50 | +++ b/src/cmd/cgo/out.go | ||
51 | @@ -47,7 +47,9 @@ func (p *Package) writeDefs() { | ||
52 | |||
53 | fflg := creat(*objDir + "_cgo_flags") | ||
54 | for k, v := range p.CgoFlags { | ||
55 | - fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, strings.Join(v, " ")) | ||
56 | + for _, arg := range v { | ||
57 | + fmt.Fprintf(fflg, "_CGO_%s=%s\n", arg) | ||
58 | + } | ||
59 | if k == "LDFLAGS" && !*gccgo { | ||
60 | for _, arg := range v { | ||
61 | fmt.Fprintf(fgo2, "//go:cgo_ldflag %q\n", arg) | ||
62 | diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go | ||
63 | index 08a4c2d8166c7..a048b7f4eecef 100644 | ||
64 | --- a/src/cmd/go/internal/work/gccgo.go | ||
65 | +++ b/src/cmd/go/internal/work/gccgo.go | ||
66 | @@ -280,14 +280,12 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string | ||
67 | const ldflagsPrefix = "_CGO_LDFLAGS=" | ||
68 | for _, line := range strings.Split(string(flags), "\n") { | ||
69 | if strings.HasPrefix(line, ldflagsPrefix) { | ||
70 | - newFlags := strings.Fields(line[len(ldflagsPrefix):]) | ||
71 | - for _, flag := range newFlags { | ||
72 | - // Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS | ||
73 | - // but they don't mean anything to the linker so filter | ||
74 | - // them out. | ||
75 | - if flag != "-g" && !strings.HasPrefix(flag, "-O") { | ||
76 | - cgoldflags = append(cgoldflags, flag) | ||
77 | - } | ||
78 | + flag := line[len(ldflagsPrefix):] | ||
79 | + // Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS | ||
80 | + // but they don't mean anything to the linker so filter | ||
81 | + // them out. | ||
82 | + if flag != "-g" && !strings.HasPrefix(flag, "-O") { | ||
83 | + cgoldflags = append(cgoldflags, flag) | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | diff --git a/src/cmd/go/testdata/script/gccgo_link_ldflags.txt b/src/cmd/go/testdata/script/gccgo_link_ldflags.txt | ||
88 | new file mode 100644 | ||
89 | index 0000000000000..4e91ae56505b6 | ||
90 | --- /dev/null | ||
91 | +++ b/src/cmd/go/testdata/script/gccgo_link_ldflags.txt | ||
92 | @@ -0,0 +1,20 @@ | ||
93 | +# Test that #cgo LDFLAGS are properly quoted. | ||
94 | +# The #cgo LDFLAGS below should pass a string with spaces to -L, | ||
95 | +# as though searching a directory with a space in its name. | ||
96 | +# It should not pass --nosuchoption to the external linker. | ||
97 | + | ||
98 | +[!cgo] skip | ||
99 | + | ||
100 | +go build | ||
101 | + | ||
102 | +[!exec:gccgo] skip | ||
103 | + | ||
104 | +go build -compiler gccgo | ||
105 | + | ||
106 | +-- go.mod -- | ||
107 | +module m | ||
108 | +-- cgo.go -- | ||
109 | +package main | ||
110 | +// #cgo LDFLAGS: -L "./ -Wl,--nosuchoption" | ||
111 | +import "C" | ||
112 | +func main() {} | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-29405-2.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-29405-2.patch new file mode 100644 index 0000000000..369eca581e --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-29405-2.patch | |||
@@ -0,0 +1,38 @@ | |||
1 | From 1008486a9ff979dbd21c7466eeb6abf378f9c637 Mon Sep 17 00:00:00 2001 | ||
2 | From: Ian Lance Taylor <iant@golang.org> | ||
3 | Date: Tue, 6 Jun 2023 12:51:17 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.20] cmd/cgo: correct _cgo_flags output | ||
5 | |||
6 | For #60306 | ||
7 | For #60514 | ||
8 | |||
9 | Change-Id: I3f5d14aee7d7195030e8872e42b1d97aa11d3582 | ||
10 | Reviewed-on: https://go-review.googlesource.com/c/go/+/501298 | ||
11 | Run-TryBot: Ian Lance Taylor <iant@golang.org> | ||
12 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
13 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
14 | Reviewed-by: David Chase <drchase@google.com> | ||
15 | Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> | ||
16 | --- | ||
17 | |||
18 | Upstream-Status: Backport [https://github.com/golang/go/commit/1008486a9ff979dbd21c7466eeb6abf378f9c637] | ||
19 | CVE: CVE-2023-29405 | ||
20 | Signed-off-by: Ashish Sharma <asharma@mvista.com> | ||
21 | |||
22 | |||
23 | src/cmd/cgo/out.go | 2 +- | ||
24 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
25 | |||
26 | diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go | ||
27 | index d0c6fe3d4c2c2..a48f52105628a 100644 | ||
28 | --- a/src/cmd/cgo/out.go | ||
29 | +++ b/src/cmd/cgo/out.go | ||
30 | @@ -48,7 +48,7 @@ func (p *Package) writeDefs() { | ||
31 | fflg := creat(*objDir + "_cgo_flags") | ||
32 | for k, v := range p.CgoFlags { | ||
33 | for _, arg := range v { | ||
34 | - fmt.Fprintf(fflg, "_CGO_%s=%s\n", arg) | ||
35 | + fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, arg) | ||
36 | } | ||
37 | if k == "LDFLAGS" && !*gccgo { | ||
38 | for _, arg := range v { | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-29406-1.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-29406-1.patch new file mode 100644 index 0000000000..080def4682 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-29406-1.patch | |||
@@ -0,0 +1,212 @@ | |||
1 | From 5fa6923b1ea891400153d04ddf1545e23b40041b Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Wed, 28 Jun 2023 13:20:08 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.19] net/http: validate Host header before | ||
5 | sending | ||
6 | |||
7 | Verify that the Host header we send is valid. | ||
8 | Avoids surprising behavior such as a Host of "go.dev\r\nX-Evil:oops" | ||
9 | adding an X-Evil header to HTTP/1 requests. | ||
10 | |||
11 | Add a test, skip the test for HTTP/2. HTTP/2 is not vulnerable to | ||
12 | header injection in the way HTTP/1 is, but x/net/http2 doesn't validate | ||
13 | the header and will go into a retry loop when the server rejects it. | ||
14 | CL 506995 adds the necessary validation to x/net/http2. | ||
15 | |||
16 | Updates #60374 | ||
17 | Fixes #61075 | ||
18 | For CVE-2023-29406 | ||
19 | |||
20 | Change-Id: I05cb6866a9bead043101954dfded199258c6dd04 | ||
21 | Reviewed-on: https://go-review.googlesource.com/c/go/+/506996 | ||
22 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
23 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
24 | Run-TryBot: Damien Neil <dneil@google.com> | ||
25 | (cherry picked from commit 499458f7ca04087958987a33c2703c3ef03e27e2) | ||
26 | Reviewed-on: https://go-review.googlesource.com/c/go/+/507358 | ||
27 | Run-TryBot: Tatiana Bradley <tatianabradley@google.com> | ||
28 | Reviewed-by: Roland Shoemaker <roland@golang.org> | ||
29 | |||
30 | Upstream-Status: Backport [https://github.com/golang/go/commit/5fa6923b1ea891400153d04ddf1545e23b40041b] | ||
31 | CVE: CVE-2023-29406 | ||
32 | Signed-off-by: Vivek Kumbhar <vkumbhar@mvista.com> | ||
33 | --- | ||
34 | src/net/http/http_test.go | 29 --------------------- | ||
35 | src/net/http/request.go | 47 ++++++++-------------------------- | ||
36 | src/net/http/request_test.go | 11 ++------ | ||
37 | src/net/http/transport_test.go | 18 +++++++++++++ | ||
38 | 4 files changed, 31 insertions(+), 74 deletions(-) | ||
39 | |||
40 | diff --git a/src/net/http/http_test.go b/src/net/http/http_test.go | ||
41 | index f4ea52d..ea38cb4 100644 | ||
42 | --- a/src/net/http/http_test.go | ||
43 | +++ b/src/net/http/http_test.go | ||
44 | @@ -49,35 +49,6 @@ func TestForeachHeaderElement(t *testing.T) { | ||
45 | } | ||
46 | } | ||
47 | |||
48 | -func TestCleanHost(t *testing.T) { | ||
49 | - tests := []struct { | ||
50 | - in, want string | ||
51 | - }{ | ||
52 | - {"www.google.com", "www.google.com"}, | ||
53 | - {"www.google.com foo", "www.google.com"}, | ||
54 | - {"www.google.com/foo", "www.google.com"}, | ||
55 | - {" first character is a space", ""}, | ||
56 | - {"[1::6]:8080", "[1::6]:8080"}, | ||
57 | - | ||
58 | - // Punycode: | ||
59 | - {"гофер.рф/foo", "xn--c1ae0ajs.xn--p1ai"}, | ||
60 | - {"bücher.de", "xn--bcher-kva.de"}, | ||
61 | - {"bücher.de:8080", "xn--bcher-kva.de:8080"}, | ||
62 | - // Verify we convert to lowercase before punycode: | ||
63 | - {"BÜCHER.de", "xn--bcher-kva.de"}, | ||
64 | - {"BÜCHER.de:8080", "xn--bcher-kva.de:8080"}, | ||
65 | - // Verify we normalize to NFC before punycode: | ||
66 | - {"gophér.nfc", "xn--gophr-esa.nfc"}, // NFC input; no work needed | ||
67 | - {"goph\u0065\u0301r.nfd", "xn--gophr-esa.nfd"}, // NFD input | ||
68 | - } | ||
69 | - for _, tt := range tests { | ||
70 | - got := cleanHost(tt.in) | ||
71 | - if tt.want != got { | ||
72 | - t.Errorf("cleanHost(%q) = %q, want %q", tt.in, got, tt.want) | ||
73 | - } | ||
74 | - } | ||
75 | -} | ||
76 | - | ||
77 | // Test that cmd/go doesn't link in the HTTP server. | ||
78 | // | ||
79 | // This catches accidental dependencies between the HTTP transport and | ||
80 | diff --git a/src/net/http/request.go b/src/net/http/request.go | ||
81 | index cb2edd2..2706300 100644 | ||
82 | --- a/src/net/http/request.go | ||
83 | +++ b/src/net/http/request.go | ||
84 | @@ -18,7 +18,6 @@ import ( | ||
85 | "io/ioutil" | ||
86 | "mime" | ||
87 | "mime/multipart" | ||
88 | - "net" | ||
89 | "net/http/httptrace" | ||
90 | "net/textproto" | ||
91 | "net/url" | ||
92 | @@ -26,7 +25,8 @@ import ( | ||
93 | "strconv" | ||
94 | "strings" | ||
95 | "sync" | ||
96 | - | ||
97 | + | ||
98 | + "golang.org/x/net/http/httpguts" | ||
99 | "golang.org/x/net/idna" | ||
100 | ) | ||
101 | |||
102 | @@ -557,12 +557,19 @@ func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitF | ||
103 | // is not given, use the host from the request URL. | ||
104 | // | ||
105 | // Clean the host, in case it arrives with unexpected stuff in it. | ||
106 | - host := cleanHost(r.Host) | ||
107 | + host := r.Host | ||
108 | if host == "" { | ||
109 | if r.URL == nil { | ||
110 | return errMissingHost | ||
111 | } | ||
112 | - host = cleanHost(r.URL.Host) | ||
113 | + host = r.URL.Host | ||
114 | + } | ||
115 | + host, err = httpguts.PunycodeHostPort(host) | ||
116 | + if err != nil { | ||
117 | + return err | ||
118 | + } | ||
119 | + if !httpguts.ValidHostHeader(host) { | ||
120 | + return errors.New("http: invalid Host header") | ||
121 | } | ||
122 | |||
123 | // According to RFC 6874, an HTTP client, proxy, or other | ||
124 | @@ -717,38 +724,6 @@ func idnaASCII(v string) (string, error) { | ||
125 | return idna.Lookup.ToASCII(v) | ||
126 | } | ||
127 | |||
128 | -// cleanHost cleans up the host sent in request's Host header. | ||
129 | -// | ||
130 | -// It both strips anything after '/' or ' ', and puts the value | ||
131 | -// into Punycode form, if necessary. | ||
132 | -// | ||
133 | -// Ideally we'd clean the Host header according to the spec: | ||
134 | -// https://tools.ietf.org/html/rfc7230#section-5.4 (Host = uri-host [ ":" port ]") | ||
135 | -// https://tools.ietf.org/html/rfc7230#section-2.7 (uri-host -> rfc3986's host) | ||
136 | -// https://tools.ietf.org/html/rfc3986#section-3.2.2 (definition of host) | ||
137 | -// But practically, what we are trying to avoid is the situation in | ||
138 | -// issue 11206, where a malformed Host header used in the proxy context | ||
139 | -// would create a bad request. So it is enough to just truncate at the | ||
140 | -// first offending character. | ||
141 | -func cleanHost(in string) string { | ||
142 | - if i := strings.IndexAny(in, " /"); i != -1 { | ||
143 | - in = in[:i] | ||
144 | - } | ||
145 | - host, port, err := net.SplitHostPort(in) | ||
146 | - if err != nil { // input was just a host | ||
147 | - a, err := idnaASCII(in) | ||
148 | - if err != nil { | ||
149 | - return in // garbage in, garbage out | ||
150 | - } | ||
151 | - return a | ||
152 | - } | ||
153 | - a, err := idnaASCII(host) | ||
154 | - if err != nil { | ||
155 | - return in // garbage in, garbage out | ||
156 | - } | ||
157 | - return net.JoinHostPort(a, port) | ||
158 | -} | ||
159 | - | ||
160 | // removeZone removes IPv6 zone identifier from host. | ||
161 | // E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080" | ||
162 | func removeZone(host string) string { | ||
163 | diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go | ||
164 | index 461d66e..0d417ff 100644 | ||
165 | --- a/src/net/http/request_test.go | ||
166 | +++ b/src/net/http/request_test.go | ||
167 | @@ -676,15 +676,8 @@ func TestRequestBadHost(t *testing.T) { | ||
168 | } | ||
169 | req.Host = "foo.com with spaces" | ||
170 | req.URL.Host = "foo.com with spaces" | ||
171 | - req.Write(logWrites{t, &got}) | ||
172 | - want := []string{ | ||
173 | - "GET /after HTTP/1.1\r\n", | ||
174 | - "Host: foo.com\r\n", | ||
175 | - "User-Agent: " + DefaultUserAgent + "\r\n", | ||
176 | - "\r\n", | ||
177 | - } | ||
178 | - if !reflect.DeepEqual(got, want) { | ||
179 | - t.Errorf("Writes = %q\n Want = %q", got, want) | ||
180 | + if err := req.Write(logWrites{t, &got}); err == nil { | ||
181 | + t.Errorf("Writing request with invalid Host: succeded, want error") | ||
182 | } | ||
183 | } | ||
184 | |||
185 | diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go | ||
186 | index fa0c370..0afb6b9 100644 | ||
187 | --- a/src/net/http/transport_test.go | ||
188 | +++ b/src/net/http/transport_test.go | ||
189 | @@ -6249,3 +6249,21 @@ func TestIssue32441(t *testing.T) { | ||
190 | t.Error(err) | ||
191 | } | ||
192 | } | ||
193 | + | ||
194 | +func TestRequestSanitization(t *testing.T) { | ||
195 | + setParallel(t) | ||
196 | + defer afterTest(t) | ||
197 | + | ||
198 | + ts := newClientServerTest(t, h1Mode, HandlerFunc(func(rw ResponseWriter, req *Request) { | ||
199 | + if h, ok := req.Header["X-Evil"]; ok { | ||
200 | + t.Errorf("request has X-Evil header: %q", h) | ||
201 | + } | ||
202 | + })).ts | ||
203 | + defer ts.Close() | ||
204 | + req, _ := NewRequest("GET", ts.URL, nil) | ||
205 | + req.Host = "go.dev\r\nX-Evil:evil" | ||
206 | + resp, _ := ts.Client().Do(req) | ||
207 | + if resp != nil { | ||
208 | + resp.Body.Close() | ||
209 | + } | ||
210 | +} | ||
211 | -- | ||
212 | 2.25.1 | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-29406-2.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-29406-2.patch new file mode 100644 index 0000000000..637f46a537 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-29406-2.patch | |||
@@ -0,0 +1,114 @@ | |||
1 | From c08a5fa413a34111c9a37fd9e545de27ab0978b1 Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Wed, 19 Jul 2023 10:30:46 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.19] net/http: permit requests with | ||
5 | invalid Host headers | ||
6 | |||
7 | Historically, the Transport has silently truncated invalid | ||
8 | Host headers at the first '/' or ' ' character. CL 506996 changed | ||
9 | this behavior to reject invalid Host headers entirely. | ||
10 | Unfortunately, Docker appears to rely on the previous behavior. | ||
11 | |||
12 | When sending a HTTP/1 request with an invalid Host, send an empty | ||
13 | Host header. This is safer than truncation: If you care about the | ||
14 | Host, then you should get the one you set; if you don't care, | ||
15 | then an empty Host should be fine. | ||
16 | |||
17 | Continue to fully validate Host headers sent to a proxy, | ||
18 | since proxies generally can't productively forward requests | ||
19 | without a Host. | ||
20 | |||
21 | For #60374 | ||
22 | Fixes #61431 | ||
23 | Fixes #61825 | ||
24 | |||
25 | Change-Id: If170c7dd860aa20eb58fe32990fc93af832742b6 | ||
26 | Reviewed-on: https://go-review.googlesource.com/c/go/+/511155 | ||
27 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
28 | Reviewed-by: Roland Shoemaker <roland@golang.org> | ||
29 | Run-TryBot: Damien Neil <dneil@google.com> | ||
30 | (cherry picked from commit b9153f6ef338baee5fe02a867c8fbc83a8b29dd1) | ||
31 | Reviewed-on: https://go-review.googlesource.com/c/go/+/518855 | ||
32 | Auto-Submit: Dmitri Shuralyov <dmitshur@google.com> | ||
33 | Run-TryBot: Roland Shoemaker <roland@golang.org> | ||
34 | Reviewed-by: Russ Cox <rsc@golang.org> | ||
35 | |||
36 | Upstream-Status: Backport [https://github.com/golang/go/commit/c08a5fa413a34111c9a37fd9e545de27ab0978b1] | ||
37 | CVE: CVE-2023-29406 | ||
38 | Signed-off-by: Ming Liu <liu.ming50@gmail.com> | ||
39 | --- | ||
40 | src/net/http/request.go | 23 ++++++++++++++++++++++- | ||
41 | src/net/http/request_test.go | 17 ++++++++++++----- | ||
42 | 2 files changed, 34 insertions(+), 6 deletions(-) | ||
43 | |||
44 | diff --git a/src/net/http/request.go b/src/net/http/request.go | ||
45 | index 3100037386..91cb8a66b9 100644 | ||
46 | --- a/src/net/http/request.go | ||
47 | +++ b/src/net/http/request.go | ||
48 | @@ -582,8 +582,29 @@ func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitF | ||
49 | if err != nil { | ||
50 | return err | ||
51 | } | ||
52 | + // Validate that the Host header is a valid header in general, | ||
53 | + // but don't validate the host itself. This is sufficient to avoid | ||
54 | + // header or request smuggling via the Host field. | ||
55 | + // The server can (and will, if it's a net/http server) reject | ||
56 | + // the request if it doesn't consider the host valid. | ||
57 | if !httpguts.ValidHostHeader(host) { | ||
58 | - return errors.New("http: invalid Host header") | ||
59 | + // Historically, we would truncate the Host header after '/' or ' '. | ||
60 | + // Some users have relied on this truncation to convert a network | ||
61 | + // address such as Unix domain socket path into a valid, ignored | ||
62 | + // Host header (see https://go.dev/issue/61431). | ||
63 | + // | ||
64 | + // We don't preserve the truncation, because sending an altered | ||
65 | + // header field opens a smuggling vector. Instead, zero out the | ||
66 | + // Host header entirely if it isn't valid. (An empty Host is valid; | ||
67 | + // see RFC 9112 Section 3.2.) | ||
68 | + // | ||
69 | + // Return an error if we're sending to a proxy, since the proxy | ||
70 | + // probably can't do anything useful with an empty Host header. | ||
71 | + if !usingProxy { | ||
72 | + host = "" | ||
73 | + } else { | ||
74 | + return errors.New("http: invalid Host header") | ||
75 | + } | ||
76 | } | ||
77 | |||
78 | // According to RFC 6874, an HTTP client, proxy, or other | ||
79 | diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go | ||
80 | index fddc85d6a9..dd1e2dc2a1 100644 | ||
81 | --- a/src/net/http/request_test.go | ||
82 | +++ b/src/net/http/request_test.go | ||
83 | @@ -770,16 +770,23 @@ func TestRequestWriteBufferedWriter(t *testing.T) { | ||
84 | } | ||
85 | } | ||
86 | |||
87 | -func TestRequestBadHost(t *testing.T) { | ||
88 | +func TestRequestBadHostHeader(t *testing.T) { | ||
89 | got := []string{} | ||
90 | req, err := NewRequest("GET", "http://foo/after", nil) | ||
91 | if err != nil { | ||
92 | t.Fatal(err) | ||
93 | } | ||
94 | - req.Host = "foo.com with spaces" | ||
95 | - req.URL.Host = "foo.com with spaces" | ||
96 | - if err := req.Write(logWrites{t, &got}); err == nil { | ||
97 | - t.Errorf("Writing request with invalid Host: succeded, want error") | ||
98 | + req.Host = "foo.com\nnewline" | ||
99 | + req.URL.Host = "foo.com\nnewline" | ||
100 | + req.Write(logWrites{t, &got}) | ||
101 | + want := []string{ | ||
102 | + "GET /after HTTP/1.1\r\n", | ||
103 | + "Host: \r\n", | ||
104 | + "User-Agent: " + DefaultUserAgent + "\r\n", | ||
105 | + "\r\n", | ||
106 | + } | ||
107 | + if !reflect.DeepEqual(got, want) { | ||
108 | + t.Errorf("Writes = %q\n Want = %q", got, want) | ||
109 | } | ||
110 | } | ||
111 | |||
112 | -- | ||
113 | 2.34.1 | ||
114 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-29409.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-29409.patch new file mode 100644 index 0000000000..00685cc180 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-29409.patch | |||
@@ -0,0 +1,175 @@ | |||
1 | From 2300f7ef07718f6be4d8aa8486c7de99836e233f Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Wed, 7 Jun 2023 15:27:13 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.19] crypto/tls: restrict RSA keys in | ||
5 | certificates to <= 8192 bits | ||
6 | |||
7 | Extremely large RSA keys in certificate chains can cause a client/server | ||
8 | to expend significant CPU time verifying signatures. Limit this by | ||
9 | restricting the size of RSA keys transmitted during handshakes to <= | ||
10 | 8192 bits. | ||
11 | |||
12 | Based on a survey of publicly trusted RSA keys, there are currently only | ||
13 | three certificates in circulation with keys larger than this, and all | ||
14 | three appear to be test certificates that are not actively deployed. It | ||
15 | is possible there are larger keys in use in private PKIs, but we target | ||
16 | the web PKI, so causing breakage here in the interests of increasing the | ||
17 | default safety of users of crypto/tls seems reasonable. | ||
18 | |||
19 | Thanks to Mateusz Poliwczak for reporting this issue. | ||
20 | |||
21 | Updates #61460 | ||
22 | Fixes #61579 | ||
23 | Fixes CVE-2023-29409 | ||
24 | |||
25 | Change-Id: Ie35038515a649199a36a12fc2c5df3af855dca6c | ||
26 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1912161 | ||
27 | Reviewed-by: Damien Neil <dneil@google.com> | ||
28 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
29 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
30 | (cherry picked from commit d865c715d92887361e4bd5596e19e513f27781b7) | ||
31 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1965487 | ||
32 | Reviewed-on: https://go-review.googlesource.com/c/go/+/514915 | ||
33 | Run-TryBot: David Chase <drchase@google.com> | ||
34 | Reviewed-by: Matthew Dempsky <mdempsky@google.com> | ||
35 | TryBot-Bypass: David Chase <drchase@google.com> | ||
36 | |||
37 | Upstream-Status: Backport [https://github.com/golang/go/commit/2300f7ef07718f6be4d8aa8486c7de99836e233f] | ||
38 | CVE: CVE-2023-29409 | ||
39 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
40 | --- | ||
41 | src/crypto/tls/handshake_client.go | 8 +++ | ||
42 | src/crypto/tls/handshake_client_test.go | 78 +++++++++++++++++++++++++ | ||
43 | src/crypto/tls/handshake_server.go | 4 ++ | ||
44 | 3 files changed, 90 insertions(+) | ||
45 | |||
46 | diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go | ||
47 | index 4fb528c..ba33ea1 100644 | ||
48 | --- a/src/crypto/tls/handshake_client.go | ||
49 | +++ b/src/crypto/tls/handshake_client.go | ||
50 | @@ -788,6 +788,10 @@ func (hs *clientHandshakeState) sendFinished(out []byte) error { | ||
51 | return nil | ||
52 | } | ||
53 | |||
54 | +// maxRSAKeySize is the maximum RSA key size in bits that we are willing | ||
55 | +// to verify the signatures of during a TLS handshake. | ||
56 | +const maxRSAKeySize = 8192 | ||
57 | + | ||
58 | // verifyServerCertificate parses and verifies the provided chain, setting | ||
59 | // c.verifiedChains and c.peerCertificates or sending the appropriate alert. | ||
60 | func (c *Conn) verifyServerCertificate(certificates [][]byte) error { | ||
61 | @@ -798,6 +802,10 @@ func (c *Conn) verifyServerCertificate(certificates [][]byte) error { | ||
62 | c.sendAlert(alertBadCertificate) | ||
63 | return errors.New("tls: failed to parse certificate from server: " + err.Error()) | ||
64 | } | ||
65 | + if cert.PublicKeyAlgorithm == x509.RSA && cert.PublicKey.(*rsa.PublicKey).N.BitLen() > maxRSAKeySize { | ||
66 | + c.sendAlert(alertBadCertificate) | ||
67 | + return fmt.Errorf("tls: server sent certificate containing RSA key larger than %d bits", maxRSAKeySize) | ||
68 | + } | ||
69 | certs[i] = cert | ||
70 | } | ||
71 | |||
72 | diff --git a/src/crypto/tls/handshake_client_test.go b/src/crypto/tls/handshake_client_test.go | ||
73 | index 6bd3c37..8d20b2b 100644 | ||
74 | --- a/src/crypto/tls/handshake_client_test.go | ||
75 | +++ b/src/crypto/tls/handshake_client_test.go | ||
76 | @@ -1984,3 +1984,81 @@ func TestCloseClientConnectionOnIdleServer(t *testing.T) { | ||
77 | t.Errorf("Error expected, but no error returned") | ||
78 | } | ||
79 | } | ||
80 | + | ||
81 | +// discardConn wraps a net.Conn but discards all writes, but reports that they happened. | ||
82 | +type discardConn struct { | ||
83 | + net.Conn | ||
84 | +} | ||
85 | + | ||
86 | +func (dc *discardConn) Write(data []byte) (int, error) { | ||
87 | + return len(data), nil | ||
88 | +} | ||
89 | + | ||
90 | +// largeRSAKeyCertPEM contains a 8193 bit RSA key | ||
91 | +const largeRSAKeyCertPEM = `-----BEGIN CERTIFICATE----- | ||
92 | +MIIInjCCBIWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDEwd0ZXN0 | ||
93 | +aW5nMB4XDTIzMDYwNzIxMjMzNloXDTIzMDYwNzIzMjMzNlowEjEQMA4GA1UEAxMH | ||
94 | +dGVzdGluZzCCBCIwDQYJKoZIhvcNAQEBBQADggQPADCCBAoCggQBAWdHsf6Rh2Ca | ||
95 | +n2SQwn4t4OQrOjbLLdGE1pM6TBKKrHUFy62uEL8atNjlcfXIsa4aEu3xNGiqxqur | ||
96 | +ZectlkZbm0FkaaQ1Wr9oikDY3KfjuaXdPdO/XC/h8AKNxlDOylyXwUSK/CuYb+1j | ||
97 | +gy8yF5QFvVfwW/xwTlHmhUeSkVSQPosfQ6yXNNsmMzkd+ZPWLrfq4R+wiNtwYGu0 | ||
98 | +WSBcI/M9o8/vrNLnIppoiBJJ13j9CR1ToEAzOFh9wwRWLY10oZhoh1ONN1KQURx4 | ||
99 | +qedzvvP2DSjZbUccdvl2rBGvZpzfOiFdm1FCnxB0c72Cqx+GTHXBFf8bsa7KHky9 | ||
100 | +sNO1GUanbq17WoDNgwbY6H51bfShqv0CErxatwWox3we4EcAmFHPVTCYL1oWVMGo | ||
101 | +a3Eth91NZj+b/nGhF9lhHKGzXSv9brmLLkfvM1jA6XhNhA7BQ5Vz67lj2j3XfXdh | ||
102 | +t/BU5pBXbL4Ut4mIhT1YnKXAjX2/LF5RHQTE8Vwkx5JAEKZyUEGOReD/B+7GOrLp | ||
103 | +HduMT9vZAc5aR2k9I8qq1zBAzsL69lyQNAPaDYd1BIAjUety9gAYaSQffCgAgpRO | ||
104 | +Gt+DYvxS+7AT/yEd5h74MU2AH7KrAkbXOtlwupiGwhMVTstncDJWXMJqbBhyHPF8 | ||
105 | +3UmZH0hbL4PYmzSj9LDWQQXI2tv6vrCpfts3Cqhqxz9vRpgY7t1Wu6l/r+KxYYz3 | ||
106 | +1pcGpPvRmPh0DJm7cPTiXqPnZcPt+ulSaSdlxmd19OnvG5awp0fXhxryZVwuiT8G | ||
107 | +VDkhyARrxYrdjlINsZJZbQjO0t8ketXAELJOnbFXXzeCOosyOHkLwsqOO96AVJA8 | ||
108 | +45ZVL5m95ClGy0RSrjVIkXsxTAMVG6SPAqKwk6vmTdRGuSPS4rhgckPVDHmccmuq | ||
109 | +dfnT2YkX+wB2/M3oCgU+s30fAHGkbGZ0pCdNbFYFZLiH0iiMbTDl/0L/z7IdK0nH | ||
110 | +GLHVE7apPraKC6xl6rPWsD2iSfrmtIPQa0+rqbIVvKP5JdfJ8J4alI+OxFw/znQe | ||
111 | +V0/Rez0j22Fe119LZFFSXhRv+ZSvcq20xDwh00mzcumPWpYuCVPozA18yIhC9tNn | ||
112 | +ALHndz0tDseIdy9vC71jQWy9iwri3ueN0DekMMF8JGzI1Z6BAFzgyAx3DkHtwHg7 | ||
113 | +B7qD0jPG5hJ5+yt323fYgJsuEAYoZ8/jzZ01pkX8bt+UsVN0DGnSGsI2ktnIIk3J | ||
114 | +l+8krjmUy6EaW79nITwoOqaeHOIp8m3UkjEcoKOYrzHRKqRy+A09rY+m/cAQaafW | ||
115 | +4xp0Zv7qZPLwnu0jsqB4jD8Ll9yPB02ndsoV6U5PeHzTkVhPml19jKUAwFfs7TJg | ||
116 | +kXy+/xFhYVUCAwEAATANBgkqhkiG9w0BAQsFAAOCBAIAAQnZY77pMNeypfpba2WK | ||
117 | +aDasT7dk2JqP0eukJCVPTN24Zca+xJNPdzuBATm/8SdZK9lddIbjSnWRsKvTnO2r | ||
118 | +/rYdlPf3jM5uuJtb8+Uwwe1s+gszelGS9G/lzzq+ehWicRIq2PFcs8o3iQMfENiv | ||
119 | +qILJ+xjcrvms5ZPDNahWkfRx3KCg8Q+/at2n5p7XYjMPYiLKHnDC+RE2b1qT20IZ | ||
120 | +FhuK/fTWLmKbfYFNNga6GC4qcaZJ7x0pbm4SDTYp0tkhzcHzwKhidfNB5J2vNz6l | ||
121 | +Ur6wiYwamFTLqcOwWo7rdvI+sSn05WQBv0QZlzFX+OAu0l7WQ7yU+noOxBhjvHds | ||
122 | +14+r9qcQZg2q9kG+evopYZqYXRUNNlZKo9MRBXhfrISulFAc5lRFQIXMXnglvAu+ | ||
123 | +Ipz2gomEAOcOPNNVldhKAU94GAMJd/KfN0ZP7gX3YvPzuYU6XDhag5RTohXLm18w | ||
124 | +5AF+ES3DOQ6ixu3DTf0D+6qrDuK+prdX8ivcdTQVNOQ+MIZeGSc6NWWOTaMGJ3lg | ||
125 | +aZIxJUGdo6E7GBGiC1YTjgFKFbHzek1LRTh/LX3vbSudxwaG0HQxwsU9T4DWiMqa | ||
126 | +Fkf2KteLEUA6HrR+0XlAZrhwoqAmrJ+8lCFX3V0gE9lpENfVHlFXDGyx10DpTB28 | ||
127 | +DdjnY3F7EPWNzwf9P3oNT69CKW3Bk6VVr3ROOJtDxVu1ioWo3TaXltQ0VOnap2Pu | ||
128 | +sa5wfrpfwBDuAS9JCDg4ttNp2nW3F7tgXC6xPqw5pvGwUppEw9XNrqV8TZrxduuv | ||
129 | +rQ3NyZ7KSzIpmFlD3UwV/fGfz3UQmHS6Ng1evrUID9DjfYNfRqSGIGjDfxGtYD+j | ||
130 | +Z1gLJZuhjJpNtwBkKRtlNtrCWCJK2hidK/foxwD7kwAPo2I9FjpltxCRywZUs07X | ||
131 | +KwXTfBR9v6ij1LV6K58hFS+8ezZyZ05CeVBFkMQdclTOSfuPxlMkQOtjp8QWDj+F | ||
132 | +j/MYziT5KBkHvcbrjdRtUJIAi4N7zCsPZtjik918AK1WBNRVqPbrgq/XSEXMfuvs | ||
133 | +6JbfK0B76vdBDRtJFC1JsvnIrGbUztxXzyQwFLaR/AjVJqpVlysLWzPKWVX6/+SJ | ||
134 | +u1NQOl2E8P6ycyBsuGnO89p0S4F8cMRcI2X1XQsZ7/q0NBrOMaEp5T3SrWo9GiQ3 | ||
135 | +o2SBdbs3Y6MBPBtTu977Z/0RO63J3M5i2tjUiDfrFy7+VRLKr7qQ7JibohyB8QaR | ||
136 | +9tedgjn2f+of7PnP/PEl1cCphUZeHM7QKUMPT8dbqwmKtlYY43EHXcvNOT5IBk3X | ||
137 | +9lwJoZk/B2i+ZMRNSP34ztAwtxmasPt6RAWGQpWCn9qmttAHAnMfDqe7F7jVR6rS | ||
138 | +u58= | ||
139 | +-----END CERTIFICATE-----` | ||
140 | + | ||
141 | +func TestHandshakeRSATooBig(t *testing.T) { | ||
142 | + testCert, _ := pem.Decode([]byte(largeRSAKeyCertPEM)) | ||
143 | + | ||
144 | + c := &Conn{conn: &discardConn{}, config: testConfig.Clone()} | ||
145 | + | ||
146 | + expectedErr := "tls: server sent certificate containing RSA key larger than 8192 bits" | ||
147 | + err := c.verifyServerCertificate([][]byte{testCert.Bytes}) | ||
148 | + if err == nil || err.Error() != expectedErr { | ||
149 | + t.Errorf("Conn.verifyServerCertificate unexpected error: want %q, got %q", expectedErr, err) | ||
150 | + } | ||
151 | + | ||
152 | + expectedErr = "tls: client sent certificate containing RSA key larger than 8192 bits" | ||
153 | + err = c.processCertsFromClient(Certificate{Certificate: [][]byte{testCert.Bytes}}) | ||
154 | + if err == nil || err.Error() != expectedErr { | ||
155 | + t.Errorf("Conn.processCertsFromClient unexpected error: want %q, got %q", expectedErr, err) | ||
156 | + } | ||
157 | +} | ||
158 | diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go | ||
159 | index b16415a..2e36840 100644 | ||
160 | --- a/src/crypto/tls/handshake_server.go | ||
161 | +++ b/src/crypto/tls/handshake_server.go | ||
162 | @@ -738,6 +738,10 @@ func (c *Conn) processCertsFromClient(certificate Certificate) error { | ||
163 | c.sendAlert(alertBadCertificate) | ||
164 | return errors.New("tls: failed to parse client certificate: " + err.Error()) | ||
165 | } | ||
166 | + if certs[i].PublicKeyAlgorithm == x509.RSA && certs[i].PublicKey.(*rsa.PublicKey).N.BitLen() > maxRSAKeySize { | ||
167 | + c.sendAlert(alertBadCertificate) | ||
168 | + return fmt.Errorf("tls: client sent certificate containing RSA key larger than %d bits", maxRSAKeySize) | ||
169 | + } | ||
170 | } | ||
171 | |||
172 | if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) { | ||
173 | -- | ||
174 | 2.25.1 | ||
175 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-39318.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-39318.patch new file mode 100644 index 0000000000..00def8fcda --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-39318.patch | |||
@@ -0,0 +1,262 @@ | |||
1 | From 023b542edf38e2a1f87fcefb9f75ff2f99401b4c Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Thu, 3 Aug 2023 12:24:13 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.20] html/template: support HTML-like | ||
5 | comments in script contexts | ||
6 | |||
7 | Per Appendix B.1.1 of the ECMAScript specification, support HTML-like | ||
8 | comments in script contexts. Also per section 12.5, support hashbang | ||
9 | comments. This brings our parsing in-line with how browsers treat these | ||
10 | comment types. | ||
11 | |||
12 | Thanks to Takeshi Kaneko (GMO Cybersecurity by Ierae, Inc.) for | ||
13 | reporting this issue. | ||
14 | |||
15 | Fixes #62196 | ||
16 | Fixes #62395 | ||
17 | Fixes CVE-2023-39318 | ||
18 | |||
19 | Change-Id: Id512702c5de3ae46cf648e268cb10e1eb392a181 | ||
20 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1976593 | ||
21 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
22 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
23 | Reviewed-by: Damien Neil <dneil@google.com> | ||
24 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
25 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2014620 | ||
26 | Reviewed-on: https://go-review.googlesource.com/c/go/+/526098 | ||
27 | Run-TryBot: Cherry Mui <cherryyz@google.com> | ||
28 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
29 | |||
30 | Upstream-Status: Backport from [https://github.com/golang/go/commit/023b542edf38e2a1f87fcefb9f75ff2f99401b4c] | ||
31 | CVE: CVE-2023-39318 | ||
32 | Signed-off-by: Siddharth Doshi <sdoshi@mvista.com> | ||
33 | --- | ||
34 | src/html/template/context.go | 6 ++- | ||
35 | src/html/template/escape.go | 5 +- | ||
36 | src/html/template/escape_test.go | 10 ++++ | ||
37 | src/html/template/state_string.go | 26 +++++----- | ||
38 | src/html/template/transition.go | 80 ++++++++++++++++++++----------- | ||
39 | 5 files changed, 84 insertions(+), 43 deletions(-) | ||
40 | |||
41 | diff --git a/src/html/template/context.go b/src/html/template/context.go | ||
42 | index 0b65313..4eb7891 100644 | ||
43 | --- a/src/html/template/context.go | ||
44 | +++ b/src/html/template/context.go | ||
45 | @@ -124,6 +124,10 @@ const ( | ||
46 | stateJSBlockCmt | ||
47 | // stateJSLineCmt occurs inside a JavaScript // line comment. | ||
48 | stateJSLineCmt | ||
49 | + // stateJSHTMLOpenCmt occurs inside a JavaScript <!-- HTML-like comment. | ||
50 | + stateJSHTMLOpenCmt | ||
51 | + // stateJSHTMLCloseCmt occurs inside a JavaScript --> HTML-like comment. | ||
52 | + stateJSHTMLCloseCmt | ||
53 | // stateCSS occurs inside a <style> element or style attribute. | ||
54 | stateCSS | ||
55 | // stateCSSDqStr occurs inside a CSS double quoted string. | ||
56 | @@ -149,7 +153,7 @@ const ( | ||
57 | // authors & maintainers, not for end-users or machines. | ||
58 | func isComment(s state) bool { | ||
59 | switch s { | ||
60 | - case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateCSSBlockCmt, stateCSSLineCmt: | ||
61 | + case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateJSHTMLOpenCmt, stateJSHTMLCloseCmt, stateCSSBlockCmt, stateCSSLineCmt: | ||
62 | return true | ||
63 | } | ||
64 | return false | ||
65 | diff --git a/src/html/template/escape.go b/src/html/template/escape.go | ||
66 | index 435f912..ad2ec69 100644 | ||
67 | --- a/src/html/template/escape.go | ||
68 | +++ b/src/html/template/escape.go | ||
69 | @@ -698,9 +698,12 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context { | ||
70 | if c.state != c1.state && isComment(c1.state) && c1.delim == delimNone { | ||
71 | // Preserve the portion between written and the comment start. | ||
72 | cs := i1 - 2 | ||
73 | - if c1.state == stateHTMLCmt { | ||
74 | + if c1.state == stateHTMLCmt || c1.state == stateJSHTMLOpenCmt { | ||
75 | // "<!--" instead of "/*" or "//" | ||
76 | cs -= 2 | ||
77 | + } else if c1.state == stateJSHTMLCloseCmt { | ||
78 | + // "-->" instead of "/*" or "//" | ||
79 | + cs -= 1 | ||
80 | } | ||
81 | b.Write(s[written:cs]) | ||
82 | written = i1 | ||
83 | diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go | ||
84 | index f550691..5f41e52 100644 | ||
85 | --- a/src/html/template/escape_test.go | ||
86 | +++ b/src/html/template/escape_test.go | ||
87 | @@ -503,6 +503,16 @@ func TestEscape(t *testing.T) { | ||
88 | "<script>var a/*b*///c\nd</script>", | ||
89 | "<script>var a \nd</script>", | ||
90 | }, | ||
91 | + { | ||
92 | + "JS HTML-like comments", | ||
93 | + "<script>before <!-- beep\nbetween\nbefore-->boop\n</script>", | ||
94 | + "<script>before \nbetween\nbefore\n</script>", | ||
95 | + }, | ||
96 | + { | ||
97 | + "JS hashbang comment", | ||
98 | + "<script>#! beep\n</script>", | ||
99 | + "<script>\n</script>", | ||
100 | + }, | ||
101 | { | ||
102 | "CSS comments", | ||
103 | "<style>p// paragraph\n" + | ||
104 | diff --git a/src/html/template/state_string.go b/src/html/template/state_string.go | ||
105 | index 05104be..b5cfe70 100644 | ||
106 | --- a/src/html/template/state_string.go | ||
107 | +++ b/src/html/template/state_string.go | ||
108 | @@ -25,21 +25,23 @@ func _() { | ||
109 | _ = x[stateJSRegexp-14] | ||
110 | _ = x[stateJSBlockCmt-15] | ||
111 | _ = x[stateJSLineCmt-16] | ||
112 | - _ = x[stateCSS-17] | ||
113 | - _ = x[stateCSSDqStr-18] | ||
114 | - _ = x[stateCSSSqStr-19] | ||
115 | - _ = x[stateCSSDqURL-20] | ||
116 | - _ = x[stateCSSSqURL-21] | ||
117 | - _ = x[stateCSSURL-22] | ||
118 | - _ = x[stateCSSBlockCmt-23] | ||
119 | - _ = x[stateCSSLineCmt-24] | ||
120 | - _ = x[stateError-25] | ||
121 | - _ = x[stateDead-26] | ||
122 | + _ = x[stateJSHTMLOpenCmt-17] | ||
123 | + _ = x[stateJSHTMLCloseCmt-18] | ||
124 | + _ = x[stateCSS-19] | ||
125 | + _ = x[stateCSSDqStr-20] | ||
126 | + _ = x[stateCSSSqStr-21] | ||
127 | + _ = x[stateCSSDqURL-22] | ||
128 | + _ = x[stateCSSSqURL-23] | ||
129 | + _ = x[stateCSSURL-24] | ||
130 | + _ = x[stateCSSBlockCmt-25] | ||
131 | + _ = x[stateCSSLineCmt-26] | ||
132 | + _ = x[stateError-27] | ||
133 | + _ = x[stateDead-28] | ||
134 | } | ||
135 | |||
136 | -const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead" | ||
137 | +const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateJSHTMLOpenCmtstateJSHTMLCloseCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead" | ||
138 | |||
139 | -var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 154, 167, 182, 196, 204, 217, 230, 243, 256, 267, 283, 298, 308, 317} | ||
140 | +var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 154, 167, 182, 196, 214, 233, 241, 254, 267, 280, 293, 304, 320, 335, 345, 354} | ||
141 | |||
142 | func (i state) String() string { | ||
143 | if i >= state(len(_state_index)-1) { | ||
144 | diff --git a/src/html/template/transition.go b/src/html/template/transition.go | ||
145 | index 92eb351..12aa4c4 100644 | ||
146 | --- a/src/html/template/transition.go | ||
147 | +++ b/src/html/template/transition.go | ||
148 | @@ -14,32 +14,34 @@ import ( | ||
149 | // the updated context and the number of bytes consumed from the front of the | ||
150 | // input. | ||
151 | var transitionFunc = [...]func(context, []byte) (context, int){ | ||
152 | - stateText: tText, | ||
153 | - stateTag: tTag, | ||
154 | - stateAttrName: tAttrName, | ||
155 | - stateAfterName: tAfterName, | ||
156 | - stateBeforeValue: tBeforeValue, | ||
157 | - stateHTMLCmt: tHTMLCmt, | ||
158 | - stateRCDATA: tSpecialTagEnd, | ||
159 | - stateAttr: tAttr, | ||
160 | - stateURL: tURL, | ||
161 | - stateSrcset: tURL, | ||
162 | - stateJS: tJS, | ||
163 | - stateJSDqStr: tJSDelimited, | ||
164 | - stateJSSqStr: tJSDelimited, | ||
165 | - stateJSBqStr: tJSDelimited, | ||
166 | - stateJSRegexp: tJSDelimited, | ||
167 | - stateJSBlockCmt: tBlockCmt, | ||
168 | - stateJSLineCmt: tLineCmt, | ||
169 | - stateCSS: tCSS, | ||
170 | - stateCSSDqStr: tCSSStr, | ||
171 | - stateCSSSqStr: tCSSStr, | ||
172 | - stateCSSDqURL: tCSSStr, | ||
173 | - stateCSSSqURL: tCSSStr, | ||
174 | - stateCSSURL: tCSSStr, | ||
175 | - stateCSSBlockCmt: tBlockCmt, | ||
176 | - stateCSSLineCmt: tLineCmt, | ||
177 | - stateError: tError, | ||
178 | + stateText: tText, | ||
179 | + stateTag: tTag, | ||
180 | + stateAttrName: tAttrName, | ||
181 | + stateAfterName: tAfterName, | ||
182 | + stateBeforeValue: tBeforeValue, | ||
183 | + stateHTMLCmt: tHTMLCmt, | ||
184 | + stateRCDATA: tSpecialTagEnd, | ||
185 | + stateAttr: tAttr, | ||
186 | + stateURL: tURL, | ||
187 | + stateSrcset: tURL, | ||
188 | + stateJS: tJS, | ||
189 | + stateJSDqStr: tJSDelimited, | ||
190 | + stateJSSqStr: tJSDelimited, | ||
191 | + stateJSBqStr: tJSDelimited, | ||
192 | + stateJSRegexp: tJSDelimited, | ||
193 | + stateJSBlockCmt: tBlockCmt, | ||
194 | + stateJSLineCmt: tLineCmt, | ||
195 | + stateJSHTMLOpenCmt: tLineCmt, | ||
196 | + stateJSHTMLCloseCmt: tLineCmt, | ||
197 | + stateCSS: tCSS, | ||
198 | + stateCSSDqStr: tCSSStr, | ||
199 | + stateCSSSqStr: tCSSStr, | ||
200 | + stateCSSDqURL: tCSSStr, | ||
201 | + stateCSSSqURL: tCSSStr, | ||
202 | + stateCSSURL: tCSSStr, | ||
203 | + stateCSSBlockCmt: tBlockCmt, | ||
204 | + stateCSSLineCmt: tLineCmt, | ||
205 | + stateError: tError, | ||
206 | } | ||
207 | |||
208 | var commentStart = []byte("<!--") | ||
209 | @@ -263,7 +265,7 @@ func tURL(c context, s []byte) (context, int) { | ||
210 | |||
211 | // tJS is the context transition function for the JS state. | ||
212 | func tJS(c context, s []byte) (context, int) { | ||
213 | - i := bytes.IndexAny(s, "\"`'/") | ||
214 | + i := bytes.IndexAny(s, "\"`'/<-#") | ||
215 | if i == -1 { | ||
216 | // Entire input is non string, comment, regexp tokens. | ||
217 | c.jsCtx = nextJSCtx(s, c.jsCtx) | ||
218 | @@ -293,6 +295,26 @@ func tJS(c context, s []byte) (context, int) { | ||
219 | err: errorf(ErrSlashAmbig, nil, 0, "'/' could start a division or regexp: %.32q", s[i:]), | ||
220 | }, len(s) | ||
221 | } | ||
222 | + // ECMAScript supports HTML style comments for legacy reasons, see Appendix | ||
223 | + // B.1.1 "HTML-like Comments". The handling of these comments is somewhat | ||
224 | + // confusing. Multi-line comments are not supported, i.e. anything on lines | ||
225 | + // between the opening and closing tokens is not considered a comment, but | ||
226 | + // anything following the opening or closing token, on the same line, is | ||
227 | + // ignored. As such we simply treat any line prefixed with "<!--" or "-->" | ||
228 | + // as if it were actually prefixed with "//" and move on. | ||
229 | + case '<': | ||
230 | + if i+3 < len(s) && bytes.Equal(commentStart, s[i:i+4]) { | ||
231 | + c.state, i = stateJSHTMLOpenCmt, i+3 | ||
232 | + } | ||
233 | + case '-': | ||
234 | + if i+2 < len(s) && bytes.Equal(commentEnd, s[i:i+3]) { | ||
235 | + c.state, i = stateJSHTMLCloseCmt, i+2 | ||
236 | + } | ||
237 | + // ECMAScript also supports "hashbang" comment lines, see Section 12.5. | ||
238 | + case '#': | ||
239 | + if i+1 < len(s) && s[i+1] == '!' { | ||
240 | + c.state, i = stateJSLineCmt, i+1 | ||
241 | + } | ||
242 | default: | ||
243 | panic("unreachable") | ||
244 | } | ||
245 | @@ -372,12 +394,12 @@ func tBlockCmt(c context, s []byte) (context, int) { | ||
246 | return c, i + 2 | ||
247 | } | ||
248 | |||
249 | -// tLineCmt is the context transition function for //comment states. | ||
250 | +// tLineCmt is the context transition function for //comment states, and the JS HTML-like comment state. | ||
251 | func tLineCmt(c context, s []byte) (context, int) { | ||
252 | var lineTerminators string | ||
253 | var endState state | ||
254 | switch c.state { | ||
255 | - case stateJSLineCmt: | ||
256 | + case stateJSLineCmt, stateJSHTMLOpenCmt, stateJSHTMLCloseCmt: | ||
257 | lineTerminators, endState = "\n\r\u2028\u2029", stateJS | ||
258 | case stateCSSLineCmt: | ||
259 | lineTerminators, endState = "\n\f\r", stateCSS | ||
260 | -- | ||
261 | 2.24.4 | ||
262 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-39319.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-39319.patch new file mode 100644 index 0000000000..69106e3e05 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-39319.patch | |||
@@ -0,0 +1,230 @@ | |||
1 | From 2070531d2f53df88e312edace6c8dfc9686ab2f5 Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Thu, 3 Aug 2023 12:28:28 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.20] html/template: properly handle | ||
5 | special tags within the script context | ||
6 | |||
7 | The HTML specification has incredibly complex rules for how to handle | ||
8 | "<!--", "<script", and "</script" when they appear within literals in | ||
9 | the script context. Rather than attempting to apply these restrictions | ||
10 | (which require a significantly more complex state machine) we apply | ||
11 | the workaround suggested in section 4.12.1.3 of the HTML specification [1]. | ||
12 | |||
13 | More precisely, when "<!--", "<script", and "</script" appear within | ||
14 | literals (strings and regular expressions, ignoring comments since we | ||
15 | already elide their content) we replace the "<" with "\x3C". This avoids | ||
16 | the unintuitive behavior that using these tags within literals can cause, | ||
17 | by simply preventing the rendered content from triggering it. This may | ||
18 | break some correct usages of these tags, but on balance is more likely | ||
19 | to prevent XSS attacks where users are unknowingly either closing or not | ||
20 | closing the script blocks where they think they are. | ||
21 | |||
22 | Thanks to Takeshi Kaneko (GMO Cybersecurity by Ierae, Inc.) for | ||
23 | reporting this issue. | ||
24 | |||
25 | Fixes #62197 | ||
26 | Fixes #62397 | ||
27 | Fixes CVE-2023-39319 | ||
28 | |||
29 | [1] https://html.spec.whatwg.org/#restrictions-for-contents-of-script-elements | ||
30 | |||
31 | Change-Id: Iab57b0532694827e3eddf57a7497ba1fab1746dc | ||
32 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1976594 | ||
33 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
34 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
35 | Reviewed-by: Damien Neil <dneil@google.com> | ||
36 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
37 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2014621 | ||
38 | TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> | ||
39 | Reviewed-on: https://go-review.googlesource.com/c/go/+/526099 | ||
40 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
41 | Run-TryBot: Cherry Mui <cherryyz@google.com> | ||
42 | |||
43 | Upstream-Status: Backport from [https://github.com/golang/go/commit/2070531d2f53df88e312edace6c8dfc9686ab2f5] | ||
44 | CVE: CVE-2023-39319 | ||
45 | Signed-off-by: Siddharth Doshi <sdoshi@mvista.com> | ||
46 | --- | ||
47 | src/html/template/context.go | 14 ++++++++++ | ||
48 | src/html/template/escape.go | 26 ++++++++++++++++++ | ||
49 | src/html/template/escape_test.go | 47 +++++++++++++++++++++++++++++++- | ||
50 | src/html/template/transition.go | 15 ++++++++++ | ||
51 | 4 files changed, 101 insertions(+), 1 deletion(-) | ||
52 | |||
53 | diff --git a/src/html/template/context.go b/src/html/template/context.go | ||
54 | index 4eb7891..feb6517 100644 | ||
55 | --- a/src/html/template/context.go | ||
56 | +++ b/src/html/template/context.go | ||
57 | @@ -168,6 +168,20 @@ func isInTag(s state) bool { | ||
58 | return false | ||
59 | } | ||
60 | |||
61 | +// isInScriptLiteral returns true if s is one of the literal states within a | ||
62 | +// <script> tag, and as such occurances of "<!--", "<script", and "</script" | ||
63 | +// need to be treated specially. | ||
64 | +func isInScriptLiteral(s state) bool { | ||
65 | + // Ignore the comment states (stateJSBlockCmt, stateJSLineCmt, | ||
66 | + // stateJSHTMLOpenCmt, stateJSHTMLCloseCmt) because their content is already | ||
67 | + // omitted from the output. | ||
68 | + switch s { | ||
69 | + case stateJSDqStr, stateJSSqStr, stateJSBqStr, stateJSRegexp: | ||
70 | + return true | ||
71 | + } | ||
72 | + return false | ||
73 | +} | ||
74 | + | ||
75 | // delim is the delimiter that will end the current HTML attribute. | ||
76 | type delim uint8 | ||
77 | |||
78 | diff --git a/src/html/template/escape.go b/src/html/template/escape.go | ||
79 | index ad2ec69..de8cf6f 100644 | ||
80 | --- a/src/html/template/escape.go | ||
81 | +++ b/src/html/template/escape.go | ||
82 | @@ -10,6 +10,7 @@ import ( | ||
83 | "html" | ||
84 | "internal/godebug" | ||
85 | "io" | ||
86 | + "regexp" | ||
87 | "text/template" | ||
88 | "text/template/parse" | ||
89 | ) | ||
90 | @@ -650,6 +651,26 @@ var delimEnds = [...]string{ | ||
91 | delimSpaceOrTagEnd: " \t\n\f\r>", | ||
92 | } | ||
93 | |||
94 | +var ( | ||
95 | + // Per WHATWG HTML specification, section 4.12.1.3, there are extremely | ||
96 | + // complicated rules for how to handle the set of opening tags <!--, | ||
97 | + // <script, and </script when they appear in JS literals (i.e. strings, | ||
98 | + // regexs, and comments). The specification suggests a simple solution, | ||
99 | + // rather than implementing the arcane ABNF, which involves simply escaping | ||
100 | + // the opening bracket with \x3C. We use the below regex for this, since it | ||
101 | + // makes doing the case-insensitive find-replace much simpler. | ||
102 | + specialScriptTagRE = regexp.MustCompile("(?i)<(script|/script|!--)") | ||
103 | + specialScriptTagReplacement = []byte("\\x3C$1") | ||
104 | +) | ||
105 | + | ||
106 | +func containsSpecialScriptTag(s []byte) bool { | ||
107 | + return specialScriptTagRE.Match(s) | ||
108 | +} | ||
109 | + | ||
110 | +func escapeSpecialScriptTags(s []byte) []byte { | ||
111 | + return specialScriptTagRE.ReplaceAll(s, specialScriptTagReplacement) | ||
112 | +} | ||
113 | + | ||
114 | var doctypeBytes = []byte("<!DOCTYPE") | ||
115 | |||
116 | // escapeText escapes a text template node. | ||
117 | @@ -708,6 +729,11 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context { | ||
118 | b.Write(s[written:cs]) | ||
119 | written = i1 | ||
120 | } | ||
121 | + if isInScriptLiteral(c.state) && containsSpecialScriptTag(s[i:i1]) { | ||
122 | + b.Write(s[written:i]) | ||
123 | + b.Write(escapeSpecialScriptTags(s[i:i1])) | ||
124 | + written = i1 | ||
125 | + } | ||
126 | if i == i1 && c.state == c1.state { | ||
127 | panic(fmt.Sprintf("infinite loop from %v to %v on %q..%q", c, c1, s[:i], s[i:])) | ||
128 | } | ||
129 | diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go | ||
130 | index 5f41e52..0cacb20 100644 | ||
131 | --- a/src/html/template/escape_test.go | ||
132 | +++ b/src/html/template/escape_test.go | ||
133 | @@ -513,6 +513,21 @@ func TestEscape(t *testing.T) { | ||
134 | "<script>#! beep\n</script>", | ||
135 | "<script>\n</script>", | ||
136 | }, | ||
137 | + { | ||
138 | + "Special tags in <script> string literals", | ||
139 | + `<script>var a = "asd < 123 <!-- 456 < fgh <script jkl < 789 </script"</script>`, | ||
140 | + `<script>var a = "asd < 123 \x3C!-- 456 < fgh \x3Cscript jkl < 789 \x3C/script"</script>`, | ||
141 | + }, | ||
142 | + { | ||
143 | + "Special tags in <script> string literals (mixed case)", | ||
144 | + `<script>var a = "<!-- <ScripT </ScripT"</script>`, | ||
145 | + `<script>var a = "\x3C!-- \x3CScripT \x3C/ScripT"</script>`, | ||
146 | + }, | ||
147 | + { | ||
148 | + "Special tags in <script> regex literals (mixed case)", | ||
149 | + `<script>var a = /<!-- <ScripT </ScripT/</script>`, | ||
150 | + `<script>var a = /\x3C!-- \x3CScripT \x3C/ScripT/</script>`, | ||
151 | + }, | ||
152 | { | ||
153 | "CSS comments", | ||
154 | "<style>p// paragraph\n" + | ||
155 | @@ -1501,8 +1516,38 @@ func TestEscapeText(t *testing.T) { | ||
156 | context{state: stateJS, element: elementScript}, | ||
157 | }, | ||
158 | { | ||
159 | + // <script and </script tags are escaped, so </script> should not | ||
160 | + // cause us to exit the JS state. | ||
161 | `<script>document.write("<script>alert(1)</script>");`, | ||
162 | - context{state: stateText}, | ||
163 | + context{state: stateJS, element: elementScript}, | ||
164 | + }, | ||
165 | + { | ||
166 | + `<script>document.write("<script>`, | ||
167 | + context{state: stateJSDqStr, element: elementScript}, | ||
168 | + }, | ||
169 | + { | ||
170 | + `<script>document.write("<script>alert(1)</script>`, | ||
171 | + context{state: stateJSDqStr, element: elementScript}, | ||
172 | + }, | ||
173 | + { | ||
174 | + `<script>document.write("<script>alert(1)<!--`, | ||
175 | + context{state: stateJSDqStr, element: elementScript}, | ||
176 | + }, | ||
177 | + { | ||
178 | + `<script>document.write("<script>alert(1)</Script>");`, | ||
179 | + context{state: stateJS, element: elementScript}, | ||
180 | + }, | ||
181 | + { | ||
182 | + `<script>document.write("<!--");`, | ||
183 | + context{state: stateJS, element: elementScript}, | ||
184 | + }, | ||
185 | + { | ||
186 | + `<script>let a = /</script`, | ||
187 | + context{state: stateJSRegexp, element: elementScript}, | ||
188 | + }, | ||
189 | + { | ||
190 | + `<script>let a = /</script/`, | ||
191 | + context{state: stateJS, element: elementScript, jsCtx: jsCtxDivOp}, | ||
192 | }, | ||
193 | { | ||
194 | `<script type="text/template">`, | ||
195 | diff --git a/src/html/template/transition.go b/src/html/template/transition.go | ||
196 | index 12aa4c4..3d2a37c 100644 | ||
197 | --- a/src/html/template/transition.go | ||
198 | +++ b/src/html/template/transition.go | ||
199 | @@ -214,6 +214,11 @@ var ( | ||
200 | // element states. | ||
201 | func tSpecialTagEnd(c context, s []byte) (context, int) { | ||
202 | if c.element != elementNone { | ||
203 | + // script end tags ("</script") within script literals are ignored, so that | ||
204 | + // we can properly escape them. | ||
205 | + if c.element == elementScript && (isInScriptLiteral(c.state) || isComment(c.state)) { | ||
206 | + return c, len(s) | ||
207 | + } | ||
208 | if i := indexTagEnd(s, specialTagEndMarkers[c.element]); i != -1 { | ||
209 | return context{}, i | ||
210 | } | ||
211 | @@ -353,6 +358,16 @@ func tJSDelimited(c context, s []byte) (context, int) { | ||
212 | inCharset = true | ||
213 | case ']': | ||
214 | inCharset = false | ||
215 | + case '/': | ||
216 | + // If "</script" appears in a regex literal, the '/' should not | ||
217 | + // close the regex literal, and it will later be escaped to | ||
218 | + // "\x3C/script" in escapeText. | ||
219 | + if i > 0 && i+7 <= len(s) && bytes.Compare(bytes.ToLower(s[i-1:i+7]), []byte("</script")) == 0 { | ||
220 | + i++ | ||
221 | + } else if !inCharset { | ||
222 | + c.state, c.jsCtx = stateJS, jsCtxDivOp | ||
223 | + return c, i + 1 | ||
224 | + } | ||
225 | default: | ||
226 | // end delimiter | ||
227 | if !inCharset { | ||
228 | -- | ||
229 | 2.24.4 | ||
230 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-39326.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-39326.patch new file mode 100644 index 0000000000..998af361e8 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-39326.patch | |||
@@ -0,0 +1,181 @@ | |||
1 | From 6446af942e2e2b161c4ec1b60d9703a2b55dc4dd Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Tue, 7 Nov 2023 10:47:56 -0800 | ||
4 | Subject: [PATCH] [release-branch.go1.20] net/http: limit chunked data overhead | ||
5 | |||
6 | The chunked transfer encoding adds some overhead to | ||
7 | the content transferred. When writing one byte per | ||
8 | chunk, for example, there are five bytes of overhead | ||
9 | per byte of data transferred: "1\r\nX\r\n" to send "X". | ||
10 | |||
11 | Chunks may include "chunk extensions", | ||
12 | which we skip over and do not use. | ||
13 | For example: "1;chunk extension here\r\nX\r\n". | ||
14 | |||
15 | A malicious sender can use chunk extensions to add | ||
16 | about 4k of overhead per byte of data. | ||
17 | (The maximum chunk header line size we will accept.) | ||
18 | |||
19 | Track the amount of overhead read in chunked data, | ||
20 | and produce an error if it seems excessive. | ||
21 | |||
22 | Updates #64433 | ||
23 | Fixes #64434 | ||
24 | Fixes CVE-2023-39326 | ||
25 | |||
26 | Change-Id: I40f8d70eb6f9575fb43f506eb19132ccedafcf39 | ||
27 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2076135 | ||
28 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
29 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
30 | (cherry picked from commit 3473ae72ee66c60744665a24b2fde143e8964d4f) | ||
31 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2095407 | ||
32 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
33 | TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> | ||
34 | Reviewed-by: Damien Neil <dneil@google.com> | ||
35 | Reviewed-on: https://go-review.googlesource.com/c/go/+/547355 | ||
36 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
37 | LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> | ||
38 | |||
39 | Upstream-Status: Backport [https://github.com/golang/go/commit/6446af942e2e2b161c4ec1b60d9703a2b55dc4dd] | ||
40 | CVE: CVE-2023-39326 | ||
41 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
42 | --- | ||
43 | src/net/http/internal/chunked.go | 36 +++++++++++++--- | ||
44 | src/net/http/internal/chunked_test.go | 59 +++++++++++++++++++++++++++ | ||
45 | 2 files changed, 89 insertions(+), 6 deletions(-) | ||
46 | |||
47 | diff --git a/src/net/http/internal/chunked.go b/src/net/http/internal/chunked.go | ||
48 | index f06e572..ddbaacb 100644 | ||
49 | --- a/src/net/http/internal/chunked.go | ||
50 | +++ b/src/net/http/internal/chunked.go | ||
51 | @@ -39,7 +39,8 @@ type chunkedReader struct { | ||
52 | n uint64 // unread bytes in chunk | ||
53 | err error | ||
54 | buf [2]byte | ||
55 | - checkEnd bool // whether need to check for \r\n chunk footer | ||
56 | + checkEnd bool // whether need to check for \r\n chunk footer | ||
57 | + excess int64 // "excessive" chunk overhead, for malicious sender detection | ||
58 | } | ||
59 | |||
60 | func (cr *chunkedReader) beginChunk() { | ||
61 | @@ -49,10 +50,38 @@ func (cr *chunkedReader) beginChunk() { | ||
62 | if cr.err != nil { | ||
63 | return | ||
64 | } | ||
65 | + cr.excess += int64(len(line)) + 2 // header, plus \r\n after the chunk data | ||
66 | + line = trimTrailingWhitespace(line) | ||
67 | + line, cr.err = removeChunkExtension(line) | ||
68 | + if cr.err != nil { | ||
69 | + return | ||
70 | + } | ||
71 | cr.n, cr.err = parseHexUint(line) | ||
72 | if cr.err != nil { | ||
73 | return | ||
74 | } | ||
75 | + // A sender who sends one byte per chunk will send 5 bytes of overhead | ||
76 | + // for every byte of data. ("1\r\nX\r\n" to send "X".) | ||
77 | + // We want to allow this, since streaming a byte at a time can be legitimate. | ||
78 | + // | ||
79 | + // A sender can use chunk extensions to add arbitrary amounts of additional | ||
80 | + // data per byte read. ("1;very long extension\r\nX\r\n" to send "X".) | ||
81 | + // We don't want to disallow extensions (although we discard them), | ||
82 | + // but we also don't want to allow a sender to reduce the signal/noise ratio | ||
83 | + // arbitrarily. | ||
84 | + // | ||
85 | + // We track the amount of excess overhead read, | ||
86 | + // and produce an error if it grows too large. | ||
87 | + // | ||
88 | + // Currently, we say that we're willing to accept 16 bytes of overhead per chunk, | ||
89 | + // plus twice the amount of real data in the chunk. | ||
90 | + cr.excess -= 16 + (2 * int64(cr.n)) | ||
91 | + if cr.excess < 0 { | ||
92 | + cr.excess = 0 | ||
93 | + } | ||
94 | + if cr.excess > 16*1024 { | ||
95 | + cr.err = errors.New("chunked encoding contains too much non-data") | ||
96 | + } | ||
97 | if cr.n == 0 { | ||
98 | cr.err = io.EOF | ||
99 | } | ||
100 | @@ -133,11 +162,6 @@ func readChunkLine(b *bufio.Reader) ([]byte, error) { | ||
101 | if len(p) >= maxLineLength { | ||
102 | return nil, ErrLineTooLong | ||
103 | } | ||
104 | - p = trimTrailingWhitespace(p) | ||
105 | - p, err = removeChunkExtension(p) | ||
106 | - if err != nil { | ||
107 | - return nil, err | ||
108 | - } | ||
109 | return p, nil | ||
110 | } | ||
111 | |||
112 | diff --git a/src/net/http/internal/chunked_test.go b/src/net/http/internal/chunked_test.go | ||
113 | index d067165..b20747d 100644 | ||
114 | --- a/src/net/http/internal/chunked_test.go | ||
115 | +++ b/src/net/http/internal/chunked_test.go | ||
116 | @@ -212,3 +212,62 @@ func TestChunkReadPartial(t *testing.T) { | ||
117 | } | ||
118 | |||
119 | } | ||
120 | + | ||
121 | +func TestChunkReaderTooMuchOverhead(t *testing.T) { | ||
122 | + // If the sender is sending 100x as many chunk header bytes as chunk data, | ||
123 | + // we should reject the stream at some point. | ||
124 | + chunk := []byte("1;") | ||
125 | + for i := 0; i < 100; i++ { | ||
126 | + chunk = append(chunk, 'a') // chunk extension | ||
127 | + } | ||
128 | + chunk = append(chunk, "\r\nX\r\n"...) | ||
129 | + const bodylen = 1 << 20 | ||
130 | + r := NewChunkedReader(&funcReader{f: func(i int) ([]byte, error) { | ||
131 | + if i < bodylen { | ||
132 | + return chunk, nil | ||
133 | + } | ||
134 | + return []byte("0\r\n"), nil | ||
135 | + }}) | ||
136 | + _, err := io.ReadAll(r) | ||
137 | + if err == nil { | ||
138 | + t.Fatalf("successfully read body with excessive overhead; want error") | ||
139 | + } | ||
140 | +} | ||
141 | + | ||
142 | +func TestChunkReaderByteAtATime(t *testing.T) { | ||
143 | + // Sending one byte per chunk should not trip the excess-overhead detection. | ||
144 | + const bodylen = 1 << 20 | ||
145 | + r := NewChunkedReader(&funcReader{f: func(i int) ([]byte, error) { | ||
146 | + if i < bodylen { | ||
147 | + return []byte("1\r\nX\r\n"), nil | ||
148 | + } | ||
149 | + return []byte("0\r\n"), nil | ||
150 | + }}) | ||
151 | + got, err := io.ReadAll(r) | ||
152 | + if err != nil { | ||
153 | + t.Errorf("unexpected error: %v", err) | ||
154 | + } | ||
155 | + if len(got) != bodylen { | ||
156 | + t.Errorf("read %v bytes, want %v", len(got), bodylen) | ||
157 | + } | ||
158 | +} | ||
159 | + | ||
160 | +type funcReader struct { | ||
161 | + f func(iteration int) ([]byte, error) | ||
162 | + i int | ||
163 | + b []byte | ||
164 | + err error | ||
165 | +} | ||
166 | + | ||
167 | +func (r *funcReader) Read(p []byte) (n int, err error) { | ||
168 | + if len(r.b) == 0 && r.err == nil { | ||
169 | + r.b, r.err = r.f(r.i) | ||
170 | + r.i++ | ||
171 | + } | ||
172 | + n = copy(p, r.b) | ||
173 | + r.b = r.b[n:] | ||
174 | + if len(r.b) > 0 { | ||
175 | + return n, nil | ||
176 | + } | ||
177 | + return n, r.err | ||
178 | +} | ||
179 | -- | ||
180 | 2.25.1 | ||
181 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-45287-pre1.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-45287-pre1.patch new file mode 100644 index 0000000000..4d65180253 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-45287-pre1.patch | |||
@@ -0,0 +1,393 @@ | |||
1 | From 9baafabac9a84813a336f068862207d2bb06d255 Mon Sep 17 00:00:00 2001 | ||
2 | From: Filippo Valsorda <filippo@golang.org> | ||
3 | Date: Wed, 1 Apr 2020 17:25:40 -0400 | ||
4 | Subject: [PATCH] crypto/rsa: refactor RSA-PSS signing and verification | ||
5 | |||
6 | Cleaned up for readability and consistency. | ||
7 | |||
8 | There is one tiny behavioral change: when PSSSaltLengthEqualsHash is | ||
9 | used and both hash and opts.Hash were set, hash.Size() was used for the | ||
10 | salt length instead of opts.Hash.Size(). That's clearly wrong because | ||
11 | opts.Hash is documented to override hash. | ||
12 | |||
13 | Change-Id: I3e25dad933961eac827c6d2e3bbfe45fc5a6fb0e | ||
14 | Reviewed-on: https://go-review.googlesource.com/c/go/+/226937 | ||
15 | Run-TryBot: Filippo Valsorda <filippo@golang.org> | ||
16 | TryBot-Result: Gobot Gobot <gobot@golang.org> | ||
17 | Reviewed-by: Katie Hockman <katie@golang.org> | ||
18 | |||
19 | Upstream-Status: Backport [https://github.com/golang/go/commit/9baafabac9a84813a336f068862207d2bb06d255] | ||
20 | CVE: CVE-2023-45287 #Dependency Patch1 | ||
21 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
22 | --- | ||
23 | src/crypto/rsa/pss.go | 173 ++++++++++++++++++++++-------------------- | ||
24 | src/crypto/rsa/rsa.go | 9 ++- | ||
25 | 2 files changed, 96 insertions(+), 86 deletions(-) | ||
26 | |||
27 | diff --git a/src/crypto/rsa/pss.go b/src/crypto/rsa/pss.go | ||
28 | index 3ff0c2f4d0076..f9844d87329a8 100644 | ||
29 | --- a/src/crypto/rsa/pss.go | ||
30 | +++ b/src/crypto/rsa/pss.go | ||
31 | @@ -4,9 +4,7 @@ | ||
32 | |||
33 | package rsa | ||
34 | |||
35 | -// This file implements the PSS signature scheme [1]. | ||
36 | -// | ||
37 | -// [1] https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf | ||
38 | +// This file implements the RSASSA-PSS signature scheme according to RFC 8017. | ||
39 | |||
40 | import ( | ||
41 | "bytes" | ||
42 | @@ -17,8 +15,22 @@ import ( | ||
43 | "math/big" | ||
44 | ) | ||
45 | |||
46 | +// Per RFC 8017, Section 9.1 | ||
47 | +// | ||
48 | +// EM = MGF1 xor DB || H( 8*0x00 || mHash || salt ) || 0xbc | ||
49 | +// | ||
50 | +// where | ||
51 | +// | ||
52 | +// DB = PS || 0x01 || salt | ||
53 | +// | ||
54 | +// and PS can be empty so | ||
55 | +// | ||
56 | +// emLen = dbLen + hLen + 1 = psLen + sLen + hLen + 2 | ||
57 | +// | ||
58 | + | ||
59 | func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error) { | ||
60 | - // See [1], section 9.1.1 | ||
61 | + // See RFC 8017, Section 9.1.1. | ||
62 | + | ||
63 | hLen := hash.Size() | ||
64 | sLen := len(salt) | ||
65 | emLen := (emBits + 7) / 8 | ||
66 | @@ -30,7 +42,7 @@ func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byt | ||
67 | // 2. Let mHash = Hash(M), an octet string of length hLen. | ||
68 | |||
69 | if len(mHash) != hLen { | ||
70 | - return nil, errors.New("crypto/rsa: input must be hashed message") | ||
71 | + return nil, errors.New("crypto/rsa: input must be hashed with given hash") | ||
72 | } | ||
73 | |||
74 | // 3. If emLen < hLen + sLen + 2, output "encoding error" and stop. | ||
75 | @@ -40,8 +52,9 @@ func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byt | ||
76 | } | ||
77 | |||
78 | em := make([]byte, emLen) | ||
79 | - db := em[:emLen-sLen-hLen-2+1+sLen] | ||
80 | - h := em[emLen-sLen-hLen-2+1+sLen : emLen-1] | ||
81 | + psLen := emLen - sLen - hLen - 2 | ||
82 | + db := em[:psLen+1+sLen] | ||
83 | + h := em[psLen+1+sLen : emLen-1] | ||
84 | |||
85 | // 4. Generate a random octet string salt of length sLen; if sLen = 0, | ||
86 | // then salt is the empty string. | ||
87 | @@ -69,8 +82,8 @@ func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byt | ||
88 | // 8. Let DB = PS || 0x01 || salt; DB is an octet string of length | ||
89 | // emLen - hLen - 1. | ||
90 | |||
91 | - db[emLen-sLen-hLen-2] = 0x01 | ||
92 | - copy(db[emLen-sLen-hLen-1:], salt) | ||
93 | + db[psLen] = 0x01 | ||
94 | + copy(db[psLen+1:], salt) | ||
95 | |||
96 | // 9. Let dbMask = MGF(H, emLen - hLen - 1). | ||
97 | // | ||
98 | @@ -81,47 +94,57 @@ func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byt | ||
99 | // 11. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in | ||
100 | // maskedDB to zero. | ||
101 | |||
102 | - db[0] &= (0xFF >> uint(8*emLen-emBits)) | ||
103 | + db[0] &= 0xff >> (8*emLen - emBits) | ||
104 | |||
105 | // 12. Let EM = maskedDB || H || 0xbc. | ||
106 | - em[emLen-1] = 0xBC | ||
107 | + em[emLen-1] = 0xbc | ||
108 | |||
109 | // 13. Output EM. | ||
110 | return em, nil | ||
111 | } | ||
112 | |||
113 | func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error { | ||
114 | + // See RFC 8017, Section 9.1.2. | ||
115 | + | ||
116 | + hLen := hash.Size() | ||
117 | + if sLen == PSSSaltLengthEqualsHash { | ||
118 | + sLen = hLen | ||
119 | + } | ||
120 | + emLen := (emBits + 7) / 8 | ||
121 | + if emLen != len(em) { | ||
122 | + return errors.New("rsa: internal error: inconsistent length") | ||
123 | + } | ||
124 | + | ||
125 | // 1. If the length of M is greater than the input limitation for the | ||
126 | // hash function (2^61 - 1 octets for SHA-1), output "inconsistent" | ||
127 | // and stop. | ||
128 | // | ||
129 | // 2. Let mHash = Hash(M), an octet string of length hLen. | ||
130 | - hLen := hash.Size() | ||
131 | if hLen != len(mHash) { | ||
132 | return ErrVerification | ||
133 | } | ||
134 | |||
135 | // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop. | ||
136 | - emLen := (emBits + 7) / 8 | ||
137 | if emLen < hLen+sLen+2 { | ||
138 | return ErrVerification | ||
139 | } | ||
140 | |||
141 | // 4. If the rightmost octet of EM does not have hexadecimal value | ||
142 | // 0xbc, output "inconsistent" and stop. | ||
143 | - if em[len(em)-1] != 0xBC { | ||
144 | + if em[emLen-1] != 0xbc { | ||
145 | return ErrVerification | ||
146 | } | ||
147 | |||
148 | // 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and | ||
149 | // let H be the next hLen octets. | ||
150 | db := em[:emLen-hLen-1] | ||
151 | - h := em[emLen-hLen-1 : len(em)-1] | ||
152 | + h := em[emLen-hLen-1 : emLen-1] | ||
153 | |||
154 | // 6. If the leftmost 8 * emLen - emBits bits of the leftmost octet in | ||
155 | // maskedDB are not all equal to zero, output "inconsistent" and | ||
156 | // stop. | ||
157 | - if em[0]&(0xFF<<uint(8-(8*emLen-emBits))) != 0 { | ||
158 | + var bitMask byte = 0xff >> (8*emLen - emBits) | ||
159 | + if em[0] & ^bitMask != 0 { | ||
160 | return ErrVerification | ||
161 | } | ||
162 | |||
163 | @@ -132,37 +155,30 @@ func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error { | ||
164 | |||
165 | // 9. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB | ||
166 | // to zero. | ||
167 | - db[0] &= (0xFF >> uint(8*emLen-emBits)) | ||
168 | + db[0] &= bitMask | ||
169 | |||
170 | + // If we don't know the salt length, look for the 0x01 delimiter. | ||
171 | if sLen == PSSSaltLengthAuto { | ||
172 | - FindSaltLength: | ||
173 | - for sLen = emLen - (hLen + 2); sLen >= 0; sLen-- { | ||
174 | - switch db[emLen-hLen-sLen-2] { | ||
175 | - case 1: | ||
176 | - break FindSaltLength | ||
177 | - case 0: | ||
178 | - continue | ||
179 | - default: | ||
180 | - return ErrVerification | ||
181 | - } | ||
182 | - } | ||
183 | - if sLen < 0 { | ||
184 | + psLen := bytes.IndexByte(db, 0x01) | ||
185 | + if psLen < 0 { | ||
186 | return ErrVerification | ||
187 | } | ||
188 | - } else { | ||
189 | - // 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero | ||
190 | - // or if the octet at position emLen - hLen - sLen - 1 (the leftmost | ||
191 | - // position is "position 1") does not have hexadecimal value 0x01, | ||
192 | - // output "inconsistent" and stop. | ||
193 | - for _, e := range db[:emLen-hLen-sLen-2] { | ||
194 | - if e != 0x00 { | ||
195 | - return ErrVerification | ||
196 | - } | ||
197 | - } | ||
198 | - if db[emLen-hLen-sLen-2] != 0x01 { | ||
199 | + sLen = len(db) - psLen - 1 | ||
200 | + } | ||
201 | + | ||
202 | + // 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero | ||
203 | + // or if the octet at position emLen - hLen - sLen - 1 (the leftmost | ||
204 | + // position is "position 1") does not have hexadecimal value 0x01, | ||
205 | + // output "inconsistent" and stop. | ||
206 | + psLen := emLen - hLen - sLen - 2 | ||
207 | + for _, e := range db[:psLen] { | ||
208 | + if e != 0x00 { | ||
209 | return ErrVerification | ||
210 | } | ||
211 | } | ||
212 | + if db[psLen] != 0x01 { | ||
213 | + return ErrVerification | ||
214 | + } | ||
215 | |||
216 | // 11. Let salt be the last sLen octets of DB. | ||
217 | salt := db[len(db)-sLen:] | ||
218 | @@ -181,19 +197,19 @@ func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error { | ||
219 | h0 := hash.Sum(nil) | ||
220 | |||
221 | // 14. If H = H', output "consistent." Otherwise, output "inconsistent." | ||
222 | - if !bytes.Equal(h0, h) { | ||
223 | + if !bytes.Equal(h0, h) { // TODO: constant time? | ||
224 | return ErrVerification | ||
225 | } | ||
226 | return nil | ||
227 | } | ||
228 | |||
229 | -// signPSSWithSalt calculates the signature of hashed using PSS [1] with specified salt. | ||
230 | +// signPSSWithSalt calculates the signature of hashed using PSS with specified salt. | ||
231 | // Note that hashed must be the result of hashing the input message using the | ||
232 | // given hash function. salt is a random sequence of bytes whose length will be | ||
233 | // later used to verify the signature. | ||
234 | func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) (s []byte, err error) { | ||
235 | - nBits := priv.N.BitLen() | ||
236 | - em, err := emsaPSSEncode(hashed, nBits-1, salt, hash.New()) | ||
237 | + emBits := priv.N.BitLen() - 1 | ||
238 | + em, err := emsaPSSEncode(hashed, emBits, salt, hash.New()) | ||
239 | if err != nil { | ||
240 | return | ||
241 | } | ||
242 | @@ -202,7 +218,7 @@ func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, | ||
243 | if err != nil { | ||
244 | return | ||
245 | } | ||
246 | - s = make([]byte, (nBits+7)/8) | ||
247 | + s = make([]byte, priv.Size()) | ||
248 | copyWithLeftPad(s, c.Bytes()) | ||
249 | return | ||
250 | } | ||
251 | @@ -223,16 +239,15 @@ type PSSOptions struct { | ||
252 | // PSSSaltLength constants. | ||
253 | SaltLength int | ||
254 | |||
255 | - // Hash, if not zero, overrides the hash function passed to SignPSS. | ||
256 | - // This is the only way to specify the hash function when using the | ||
257 | - // crypto.Signer interface. | ||
258 | + // Hash is the hash function used to generate the message digest. If not | ||
259 | + // zero, it overrides the hash function passed to SignPSS. It's required | ||
260 | + // when using PrivateKey.Sign. | ||
261 | Hash crypto.Hash | ||
262 | } | ||
263 | |||
264 | -// HashFunc returns pssOpts.Hash so that PSSOptions implements | ||
265 | -// crypto.SignerOpts. | ||
266 | -func (pssOpts *PSSOptions) HashFunc() crypto.Hash { | ||
267 | - return pssOpts.Hash | ||
268 | +// HashFunc returns opts.Hash so that PSSOptions implements crypto.SignerOpts. | ||
269 | +func (opts *PSSOptions) HashFunc() crypto.Hash { | ||
270 | + return opts.Hash | ||
271 | } | ||
272 | |||
273 | func (opts *PSSOptions) saltLength() int { | ||
274 | @@ -242,56 +257,50 @@ func (opts *PSSOptions) saltLength() int { | ||
275 | return opts.SaltLength | ||
276 | } | ||
277 | |||
278 | -// SignPSS calculates the signature of hashed using RSASSA-PSS [1]. | ||
279 | -// Note that hashed must be the result of hashing the input message using the | ||
280 | -// given hash function. The opts argument may be nil, in which case sensible | ||
281 | -// defaults are used. | ||
282 | -func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte, opts *PSSOptions) ([]byte, error) { | ||
283 | +// SignPSS calculates the signature of digest using PSS. | ||
284 | +// | ||
285 | +// digest must be the result of hashing the input message using the given hash | ||
286 | +// function. The opts argument may be nil, in which case sensible defaults are | ||
287 | +// used. If opts.Hash is set, it overrides hash. | ||
288 | +func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, opts *PSSOptions) ([]byte, error) { | ||
289 | + if opts != nil && opts.Hash != 0 { | ||
290 | + hash = opts.Hash | ||
291 | + } | ||
292 | + | ||
293 | saltLength := opts.saltLength() | ||
294 | switch saltLength { | ||
295 | case PSSSaltLengthAuto: | ||
296 | - saltLength = (priv.N.BitLen()+7)/8 - 2 - hash.Size() | ||
297 | + saltLength = priv.Size() - 2 - hash.Size() | ||
298 | case PSSSaltLengthEqualsHash: | ||
299 | saltLength = hash.Size() | ||
300 | } | ||
301 | |||
302 | - if opts != nil && opts.Hash != 0 { | ||
303 | - hash = opts.Hash | ||
304 | - } | ||
305 | - | ||
306 | salt := make([]byte, saltLength) | ||
307 | if _, err := io.ReadFull(rand, salt); err != nil { | ||
308 | return nil, err | ||
309 | } | ||
310 | - return signPSSWithSalt(rand, priv, hash, hashed, salt) | ||
311 | + return signPSSWithSalt(rand, priv, hash, digest, salt) | ||
312 | } | ||
313 | |||
314 | // VerifyPSS verifies a PSS signature. | ||
315 | -// hashed is the result of hashing the input message using the given hash | ||
316 | -// function and sig is the signature. A valid signature is indicated by | ||
317 | -// returning a nil error. The opts argument may be nil, in which case sensible | ||
318 | -// defaults are used. | ||
319 | -func VerifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, opts *PSSOptions) error { | ||
320 | - return verifyPSS(pub, hash, hashed, sig, opts.saltLength()) | ||
321 | -} | ||
322 | - | ||
323 | -// verifyPSS verifies a PSS signature with the given salt length. | ||
324 | -func verifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, saltLen int) error { | ||
325 | - nBits := pub.N.BitLen() | ||
326 | - if len(sig) != (nBits+7)/8 { | ||
327 | +// | ||
328 | +// A valid signature is indicated by returning a nil error. digest must be the | ||
329 | +// result of hashing the input message using the given hash function. The opts | ||
330 | +// argument may be nil, in which case sensible defaults are used. opts.Hash is | ||
331 | +// ignored. | ||
332 | +func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts *PSSOptions) error { | ||
333 | + if len(sig) != pub.Size() { | ||
334 | return ErrVerification | ||
335 | } | ||
336 | s := new(big.Int).SetBytes(sig) | ||
337 | m := encrypt(new(big.Int), pub, s) | ||
338 | - emBits := nBits - 1 | ||
339 | + emBits := pub.N.BitLen() - 1 | ||
340 | emLen := (emBits + 7) / 8 | ||
341 | - if emLen < len(m.Bytes()) { | ||
342 | + emBytes := m.Bytes() | ||
343 | + if emLen < len(emBytes) { | ||
344 | return ErrVerification | ||
345 | } | ||
346 | em := make([]byte, emLen) | ||
347 | - copyWithLeftPad(em, m.Bytes()) | ||
348 | - if saltLen == PSSSaltLengthEqualsHash { | ||
349 | - saltLen = hash.Size() | ||
350 | - } | ||
351 | - return emsaPSSVerify(hashed, em, emBits, saltLen, hash.New()) | ||
352 | + copyWithLeftPad(em, emBytes) | ||
353 | + return emsaPSSVerify(digest, em, emBits, opts.saltLength(), hash.New()) | ||
354 | } | ||
355 | diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go | ||
356 | index 5a42990640164..b4bfa13defbdf 100644 | ||
357 | --- a/src/crypto/rsa/rsa.go | ||
358 | +++ b/src/crypto/rsa/rsa.go | ||
359 | @@ -2,7 +2,7 @@ | ||
360 | // Use of this source code is governed by a BSD-style | ||
361 | // license that can be found in the LICENSE file. | ||
362 | |||
363 | -// Package rsa implements RSA encryption as specified in PKCS#1. | ||
364 | +// Package rsa implements RSA encryption as specified in PKCS#1 and RFC 8017. | ||
365 | // | ||
366 | // RSA is a single, fundamental operation that is used in this package to | ||
367 | // implement either public-key encryption or public-key signatures. | ||
368 | @@ -10,13 +10,13 @@ | ||
369 | // The original specification for encryption and signatures with RSA is PKCS#1 | ||
370 | // and the terms "RSA encryption" and "RSA signatures" by default refer to | ||
371 | // PKCS#1 version 1.5. However, that specification has flaws and new designs | ||
372 | -// should use version two, usually called by just OAEP and PSS, where | ||
373 | +// should use version 2, usually called by just OAEP and PSS, where | ||
374 | // possible. | ||
375 | // | ||
376 | // Two sets of interfaces are included in this package. When a more abstract | ||
377 | // interface isn't necessary, there are functions for encrypting/decrypting | ||
378 | // with v1.5/OAEP and signing/verifying with v1.5/PSS. If one needs to abstract | ||
379 | -// over the public-key primitive, the PrivateKey struct implements the | ||
380 | +// over the public key primitive, the PrivateKey type implements the | ||
381 | // Decrypter and Signer interfaces from the crypto package. | ||
382 | // | ||
383 | // The RSA operations in this package are not implemented using constant-time algorithms. | ||
384 | @@ -111,7 +111,8 @@ func (priv *PrivateKey) Public() crypto.PublicKey { | ||
385 | |||
386 | // Sign signs digest with priv, reading randomness from rand. If opts is a | ||
387 | // *PSSOptions then the PSS algorithm will be used, otherwise PKCS#1 v1.5 will | ||
388 | -// be used. | ||
389 | +// be used. digest must be the result of hashing the input message using | ||
390 | +// opts.HashFunc(). | ||
391 | // | ||
392 | // This method implements crypto.Signer, which is an interface to support keys | ||
393 | // where the private part is kept in, for example, a hardware module. Common | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-45287-pre2.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-45287-pre2.patch new file mode 100644 index 0000000000..1327b44545 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-45287-pre2.patch | |||
@@ -0,0 +1,401 @@ | |||
1 | From c9d5f60eaa4450ccf1ce878d55b4c6a12843f2f3 Mon Sep 17 00:00:00 2001 | ||
2 | From: Filippo Valsorda <filippo@golang.org> | ||
3 | Date: Mon, 27 Apr 2020 21:52:38 -0400 | ||
4 | Subject: [PATCH] math/big: add (*Int).FillBytes | ||
5 | |||
6 | Replaced almost every use of Bytes with FillBytes. | ||
7 | |||
8 | Note that the approved proposal was for | ||
9 | |||
10 | func (*Int) FillBytes(buf []byte) | ||
11 | |||
12 | while this implements | ||
13 | |||
14 | func (*Int) FillBytes(buf []byte) []byte | ||
15 | |||
16 | because the latter was far nicer to use in all callsites. | ||
17 | |||
18 | Fixes #35833 | ||
19 | |||
20 | Change-Id: Ia912df123e5d79b763845312ea3d9a8051343c0a | ||
21 | Reviewed-on: https://go-review.googlesource.com/c/go/+/230397 | ||
22 | Reviewed-by: Robert Griesemer <gri@golang.org> | ||
23 | |||
24 | Upstream-Status: Backport [https://github.com/golang/go/commit/c9d5f60eaa4450ccf1ce878d55b4c6a12843f2f3] | ||
25 | CVE: CVE-2023-45287 #Dependency Patch2 | ||
26 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
27 | --- | ||
28 | src/crypto/elliptic/elliptic.go | 13 ++++---- | ||
29 | src/crypto/rsa/pkcs1v15.go | 20 +++--------- | ||
30 | src/crypto/rsa/pss.go | 17 +++++------ | ||
31 | src/crypto/rsa/rsa.go | 32 +++---------------- | ||
32 | src/crypto/tls/key_schedule.go | 7 ++--- | ||
33 | src/crypto/x509/sec1.go | 7 ++--- | ||
34 | src/math/big/int.go | 15 +++++++++ | ||
35 | src/math/big/int_test.go | 54 +++++++++++++++++++++++++++++++++ | ||
36 | src/math/big/nat.go | 15 ++++++--- | ||
37 | 9 files changed, 106 insertions(+), 74 deletions(-) | ||
38 | |||
39 | diff --git a/src/crypto/elliptic/elliptic.go b/src/crypto/elliptic/elliptic.go | ||
40 | index e2f71cdb63bab..bd5168c5fd842 100644 | ||
41 | --- a/src/crypto/elliptic/elliptic.go | ||
42 | +++ b/src/crypto/elliptic/elliptic.go | ||
43 | @@ -277,7 +277,7 @@ var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f} | ||
44 | func GenerateKey(curve Curve, rand io.Reader) (priv []byte, x, y *big.Int, err error) { | ||
45 | N := curve.Params().N | ||
46 | bitSize := N.BitLen() | ||
47 | - byteLen := (bitSize + 7) >> 3 | ||
48 | + byteLen := (bitSize + 7) / 8 | ||
49 | priv = make([]byte, byteLen) | ||
50 | |||
51 | for x == nil { | ||
52 | @@ -304,15 +304,14 @@ func GenerateKey(curve Curve, rand io.Reader) (priv []byte, x, y *big.Int, err e | ||
53 | |||
54 | // Marshal converts a point into the uncompressed form specified in section 4.3.6 of ANSI X9.62. | ||
55 | func Marshal(curve Curve, x, y *big.Int) []byte { | ||
56 | - byteLen := (curve.Params().BitSize + 7) >> 3 | ||
57 | + byteLen := (curve.Params().BitSize + 7) / 8 | ||
58 | |||
59 | ret := make([]byte, 1+2*byteLen) | ||
60 | ret[0] = 4 // uncompressed point | ||
61 | |||
62 | - xBytes := x.Bytes() | ||
63 | - copy(ret[1+byteLen-len(xBytes):], xBytes) | ||
64 | - yBytes := y.Bytes() | ||
65 | - copy(ret[1+2*byteLen-len(yBytes):], yBytes) | ||
66 | + x.FillBytes(ret[1 : 1+byteLen]) | ||
67 | + y.FillBytes(ret[1+byteLen : 1+2*byteLen]) | ||
68 | + | ||
69 | return ret | ||
70 | } | ||
71 | |||
72 | @@ -320,7 +319,7 @@ func Marshal(curve Curve, x, y *big.Int) []byte { | ||
73 | // It is an error if the point is not in uncompressed form or is not on the curve. | ||
74 | // On error, x = nil. | ||
75 | func Unmarshal(curve Curve, data []byte) (x, y *big.Int) { | ||
76 | - byteLen := (curve.Params().BitSize + 7) >> 3 | ||
77 | + byteLen := (curve.Params().BitSize + 7) / 8 | ||
78 | if len(data) != 1+2*byteLen { | ||
79 | return | ||
80 | } | ||
81 | diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go | ||
82 | index 499242ffc5b57..3208119ae1ff4 100644 | ||
83 | --- a/src/crypto/rsa/pkcs1v15.go | ||
84 | +++ b/src/crypto/rsa/pkcs1v15.go | ||
85 | @@ -61,8 +61,7 @@ func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error) | ||
86 | m := new(big.Int).SetBytes(em) | ||
87 | c := encrypt(new(big.Int), pub, m) | ||
88 | |||
89 | - copyWithLeftPad(em, c.Bytes()) | ||
90 | - return em, nil | ||
91 | + return c.FillBytes(em), nil | ||
92 | } | ||
93 | |||
94 | // DecryptPKCS1v15 decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5. | ||
95 | @@ -150,7 +149,7 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid | ||
96 | return | ||
97 | } | ||
98 | |||
99 | - em = leftPad(m.Bytes(), k) | ||
100 | + em = m.FillBytes(make([]byte, k)) | ||
101 | firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0) | ||
102 | secondByteIsTwo := subtle.ConstantTimeByteEq(em[1], 2) | ||
103 | |||
104 | @@ -256,8 +255,7 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b | ||
105 | return nil, err | ||
106 | } | ||
107 | |||
108 | - copyWithLeftPad(em, c.Bytes()) | ||
109 | - return em, nil | ||
110 | + return c.FillBytes(em), nil | ||
111 | } | ||
112 | |||
113 | // VerifyPKCS1v15 verifies an RSA PKCS#1 v1.5 signature. | ||
114 | @@ -286,7 +284,7 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) | ||
115 | |||
116 | c := new(big.Int).SetBytes(sig) | ||
117 | m := encrypt(new(big.Int), pub, c) | ||
118 | - em := leftPad(m.Bytes(), k) | ||
119 | + em := m.FillBytes(make([]byte, k)) | ||
120 | // EM = 0x00 || 0x01 || PS || 0x00 || T | ||
121 | |||
122 | ok := subtle.ConstantTimeByteEq(em[0], 0) | ||
123 | @@ -323,13 +321,3 @@ func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, | ||
124 | } | ||
125 | return | ||
126 | } | ||
127 | - | ||
128 | -// copyWithLeftPad copies src to the end of dest, padding with zero bytes as | ||
129 | -// needed. | ||
130 | -func copyWithLeftPad(dest, src []byte) { | ||
131 | - numPaddingBytes := len(dest) - len(src) | ||
132 | - for i := 0; i < numPaddingBytes; i++ { | ||
133 | - dest[i] = 0 | ||
134 | - } | ||
135 | - copy(dest[numPaddingBytes:], src) | ||
136 | -} | ||
137 | diff --git a/src/crypto/rsa/pss.go b/src/crypto/rsa/pss.go | ||
138 | index f9844d87329a8..b2adbedb28fa8 100644 | ||
139 | --- a/src/crypto/rsa/pss.go | ||
140 | +++ b/src/crypto/rsa/pss.go | ||
141 | @@ -207,20 +207,19 @@ func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error { | ||
142 | // Note that hashed must be the result of hashing the input message using the | ||
143 | // given hash function. salt is a random sequence of bytes whose length will be | ||
144 | // later used to verify the signature. | ||
145 | -func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) (s []byte, err error) { | ||
146 | +func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) ([]byte, error) { | ||
147 | emBits := priv.N.BitLen() - 1 | ||
148 | em, err := emsaPSSEncode(hashed, emBits, salt, hash.New()) | ||
149 | if err != nil { | ||
150 | - return | ||
151 | + return nil, err | ||
152 | } | ||
153 | m := new(big.Int).SetBytes(em) | ||
154 | c, err := decryptAndCheck(rand, priv, m) | ||
155 | if err != nil { | ||
156 | - return | ||
157 | + return nil, err | ||
158 | } | ||
159 | - s = make([]byte, priv.Size()) | ||
160 | - copyWithLeftPad(s, c.Bytes()) | ||
161 | - return | ||
162 | + s := make([]byte, priv.Size()) | ||
163 | + return c.FillBytes(s), nil | ||
164 | } | ||
165 | |||
166 | const ( | ||
167 | @@ -296,11 +295,9 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts | ||
168 | m := encrypt(new(big.Int), pub, s) | ||
169 | emBits := pub.N.BitLen() - 1 | ||
170 | emLen := (emBits + 7) / 8 | ||
171 | - emBytes := m.Bytes() | ||
172 | - if emLen < len(emBytes) { | ||
173 | + if m.BitLen() > emLen*8 { | ||
174 | return ErrVerification | ||
175 | } | ||
176 | - em := make([]byte, emLen) | ||
177 | - copyWithLeftPad(em, emBytes) | ||
178 | + em := m.FillBytes(make([]byte, emLen)) | ||
179 | return emsaPSSVerify(digest, em, emBits, opts.saltLength(), hash.New()) | ||
180 | } | ||
181 | diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go | ||
182 | index b4bfa13defbdf..28eb5926c1a54 100644 | ||
183 | --- a/src/crypto/rsa/rsa.go | ||
184 | +++ b/src/crypto/rsa/rsa.go | ||
185 | @@ -416,16 +416,9 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l | ||
186 | m := new(big.Int) | ||
187 | m.SetBytes(em) | ||
188 | c := encrypt(new(big.Int), pub, m) | ||
189 | - out := c.Bytes() | ||
190 | |||
191 | - if len(out) < k { | ||
192 | - // If the output is too small, we need to left-pad with zeros. | ||
193 | - t := make([]byte, k) | ||
194 | - copy(t[k-len(out):], out) | ||
195 | - out = t | ||
196 | - } | ||
197 | - | ||
198 | - return out, nil | ||
199 | + out := make([]byte, k) | ||
200 | + return c.FillBytes(out), nil | ||
201 | } | ||
202 | |||
203 | // ErrDecryption represents a failure to decrypt a message. | ||
204 | @@ -597,12 +590,9 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext | ||
205 | lHash := hash.Sum(nil) | ||
206 | hash.Reset() | ||
207 | |||
208 | - // Converting the plaintext number to bytes will strip any | ||
209 | - // leading zeros so we may have to left pad. We do this unconditionally | ||
210 | - // to avoid leaking timing information. (Although we still probably | ||
211 | - // leak the number of leading zeros. It's not clear that we can do | ||
212 | - // anything about this.) | ||
213 | - em := leftPad(m.Bytes(), k) | ||
214 | + // We probably leak the number of leading zeros. | ||
215 | + // It's not clear that we can do anything about this. | ||
216 | + em := m.FillBytes(make([]byte, k)) | ||
217 | |||
218 | firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0) | ||
219 | |||
220 | @@ -643,15 +633,3 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext | ||
221 | |||
222 | return rest[index+1:], nil | ||
223 | } | ||
224 | - | ||
225 | -// leftPad returns a new slice of length size. The contents of input are right | ||
226 | -// aligned in the new slice. | ||
227 | -func leftPad(input []byte, size int) (out []byte) { | ||
228 | - n := len(input) | ||
229 | - if n > size { | ||
230 | - n = size | ||
231 | - } | ||
232 | - out = make([]byte, size) | ||
233 | - copy(out[len(out)-n:], input) | ||
234 | - return | ||
235 | -} | ||
236 | diff --git a/src/crypto/tls/key_schedule.go b/src/crypto/tls/key_schedule.go | ||
237 | index 2aab323202f7d..314016979afb8 100644 | ||
238 | --- a/src/crypto/tls/key_schedule.go | ||
239 | +++ b/src/crypto/tls/key_schedule.go | ||
240 | @@ -173,11 +173,8 @@ func (p *nistParameters) SharedKey(peerPublicKey []byte) []byte { | ||
241 | } | ||
242 | |||
243 | xShared, _ := curve.ScalarMult(x, y, p.privateKey) | ||
244 | - sharedKey := make([]byte, (curve.Params().BitSize+7)>>3) | ||
245 | - xBytes := xShared.Bytes() | ||
246 | - copy(sharedKey[len(sharedKey)-len(xBytes):], xBytes) | ||
247 | - | ||
248 | - return sharedKey | ||
249 | + sharedKey := make([]byte, (curve.Params().BitSize+7)/8) | ||
250 | + return xShared.FillBytes(sharedKey) | ||
251 | } | ||
252 | |||
253 | type x25519Parameters struct { | ||
254 | diff --git a/src/crypto/x509/sec1.go b/src/crypto/x509/sec1.go | ||
255 | index 0bfb90cd5464a..52c108ff1d624 100644 | ||
256 | --- a/src/crypto/x509/sec1.go | ||
257 | +++ b/src/crypto/x509/sec1.go | ||
258 | @@ -52,13 +52,10 @@ func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) { | ||
259 | // marshalECPrivateKey marshals an EC private key into ASN.1, DER format and | ||
260 | // sets the curve ID to the given OID, or omits it if OID is nil. | ||
261 | func marshalECPrivateKeyWithOID(key *ecdsa.PrivateKey, oid asn1.ObjectIdentifier) ([]byte, error) { | ||
262 | - privateKeyBytes := key.D.Bytes() | ||
263 | - paddedPrivateKey := make([]byte, (key.Curve.Params().N.BitLen()+7)/8) | ||
264 | - copy(paddedPrivateKey[len(paddedPrivateKey)-len(privateKeyBytes):], privateKeyBytes) | ||
265 | - | ||
266 | + privateKey := make([]byte, (key.Curve.Params().N.BitLen()+7)/8) | ||
267 | return asn1.Marshal(ecPrivateKey{ | ||
268 | Version: 1, | ||
269 | - PrivateKey: paddedPrivateKey, | ||
270 | + PrivateKey: key.D.FillBytes(privateKey), | ||
271 | NamedCurveOID: oid, | ||
272 | PublicKey: asn1.BitString{Bytes: elliptic.Marshal(key.Curve, key.X, key.Y)}, | ||
273 | }) | ||
274 | diff --git a/src/math/big/int.go b/src/math/big/int.go | ||
275 | index 8816cf5266cc4..65f32487b58c0 100644 | ||
276 | --- a/src/math/big/int.go | ||
277 | +++ b/src/math/big/int.go | ||
278 | @@ -447,11 +447,26 @@ func (z *Int) SetBytes(buf []byte) *Int { | ||
279 | } | ||
280 | |||
281 | // Bytes returns the absolute value of x as a big-endian byte slice. | ||
282 | +// | ||
283 | +// To use a fixed length slice, or a preallocated one, use FillBytes. | ||
284 | func (x *Int) Bytes() []byte { | ||
285 | buf := make([]byte, len(x.abs)*_S) | ||
286 | return buf[x.abs.bytes(buf):] | ||
287 | } | ||
288 | |||
289 | +// FillBytes sets buf to the absolute value of x, storing it as a zero-extended | ||
290 | +// big-endian byte slice, and returns buf. | ||
291 | +// | ||
292 | +// If the absolute value of x doesn't fit in buf, FillBytes will panic. | ||
293 | +func (x *Int) FillBytes(buf []byte) []byte { | ||
294 | + // Clear whole buffer. (This gets optimized into a memclr.) | ||
295 | + for i := range buf { | ||
296 | + buf[i] = 0 | ||
297 | + } | ||
298 | + x.abs.bytes(buf) | ||
299 | + return buf | ||
300 | +} | ||
301 | + | ||
302 | // BitLen returns the length of the absolute value of x in bits. | ||
303 | // The bit length of 0 is 0. | ||
304 | func (x *Int) BitLen() int { | ||
305 | diff --git a/src/math/big/int_test.go b/src/math/big/int_test.go | ||
306 | index e3a1587b3f0ad..3c8557323a032 100644 | ||
307 | --- a/src/math/big/int_test.go | ||
308 | +++ b/src/math/big/int_test.go | ||
309 | @@ -1840,3 +1840,57 @@ func BenchmarkDiv(b *testing.B) { | ||
310 | }) | ||
311 | } | ||
312 | } | ||
313 | + | ||
314 | +func TestFillBytes(t *testing.T) { | ||
315 | + checkResult := func(t *testing.T, buf []byte, want *Int) { | ||
316 | + t.Helper() | ||
317 | + got := new(Int).SetBytes(buf) | ||
318 | + if got.CmpAbs(want) != 0 { | ||
319 | + t.Errorf("got 0x%x, want 0x%x: %x", got, want, buf) | ||
320 | + } | ||
321 | + } | ||
322 | + panics := func(f func()) (panic bool) { | ||
323 | + defer func() { panic = recover() != nil }() | ||
324 | + f() | ||
325 | + return | ||
326 | + } | ||
327 | + | ||
328 | + for _, n := range []string{ | ||
329 | + "0", | ||
330 | + "1000", | ||
331 | + "0xffffffff", | ||
332 | + "-0xffffffff", | ||
333 | + "0xffffffffffffffff", | ||
334 | + "0x10000000000000000", | ||
335 | + "0xabababababababababababababababababababababababababa", | ||
336 | + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", | ||
337 | + } { | ||
338 | + t.Run(n, func(t *testing.T) { | ||
339 | + t.Logf(n) | ||
340 | + x, ok := new(Int).SetString(n, 0) | ||
341 | + if !ok { | ||
342 | + panic("invalid test entry") | ||
343 | + } | ||
344 | + | ||
345 | + // Perfectly sized buffer. | ||
346 | + byteLen := (x.BitLen() + 7) / 8 | ||
347 | + buf := make([]byte, byteLen) | ||
348 | + checkResult(t, x.FillBytes(buf), x) | ||
349 | + | ||
350 | + // Way larger, checking all bytes get zeroed. | ||
351 | + buf = make([]byte, 100) | ||
352 | + for i := range buf { | ||
353 | + buf[i] = 0xff | ||
354 | + } | ||
355 | + checkResult(t, x.FillBytes(buf), x) | ||
356 | + | ||
357 | + // Too small. | ||
358 | + if byteLen > 0 { | ||
359 | + buf = make([]byte, byteLen-1) | ||
360 | + if !panics(func() { x.FillBytes(buf) }) { | ||
361 | + t.Errorf("expected panic for small buffer and value %x", x) | ||
362 | + } | ||
363 | + } | ||
364 | + }) | ||
365 | + } | ||
366 | +} | ||
367 | diff --git a/src/math/big/nat.go b/src/math/big/nat.go | ||
368 | index c31ec5156b81d..6a3989bf9d82b 100644 | ||
369 | --- a/src/math/big/nat.go | ||
370 | +++ b/src/math/big/nat.go | ||
371 | @@ -1476,19 +1476,26 @@ func (z nat) expNNMontgomery(x, y, m nat) nat { | ||
372 | } | ||
373 | |||
374 | // bytes writes the value of z into buf using big-endian encoding. | ||
375 | -// len(buf) must be >= len(z)*_S. The value of z is encoded in the | ||
376 | -// slice buf[i:]. The number i of unused bytes at the beginning of | ||
377 | -// buf is returned as result. | ||
378 | +// The value of z is encoded in the slice buf[i:]. If the value of z | ||
379 | +// cannot be represented in buf, bytes panics. The number i of unused | ||
380 | +// bytes at the beginning of buf is returned as result. | ||
381 | func (z nat) bytes(buf []byte) (i int) { | ||
382 | i = len(buf) | ||
383 | for _, d := range z { | ||
384 | for j := 0; j < _S; j++ { | ||
385 | i-- | ||
386 | - buf[i] = byte(d) | ||
387 | + if i >= 0 { | ||
388 | + buf[i] = byte(d) | ||
389 | + } else if byte(d) != 0 { | ||
390 | + panic("math/big: buffer too small to fit value") | ||
391 | + } | ||
392 | d >>= 8 | ||
393 | } | ||
394 | } | ||
395 | |||
396 | + if i < 0 { | ||
397 | + i = 0 | ||
398 | + } | ||
399 | for i < len(buf) && buf[i] == 0 { | ||
400 | i++ | ||
401 | } | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-45287-pre3.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-45287-pre3.patch new file mode 100644 index 0000000000..ae9fcc170c --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-45287-pre3.patch | |||
@@ -0,0 +1,86 @@ | |||
1 | From 8f676144ad7b7c91adb0c6e1ec89aaa6283c6807 Mon Sep 17 00:00:00 2001 | ||
2 | From: Himanshu Kishna Srivastava <28himanshu@gmail.com> | ||
3 | Date: Tue, 16 Mar 2021 22:37:46 +0530 | ||
4 | Subject: [PATCH] crypto/rsa: fix salt length calculation with | ||
5 | PSSSaltLengthAuto | ||
6 | |||
7 | When PSSSaltLength is set, the maximum salt length must equal: | ||
8 | |||
9 | (modulus_key_size - 1 + 7)/8 - hash_length - 2 | ||
10 | and for example, with a 4096 bit modulus key, and a SHA-1 hash, | ||
11 | it should be: | ||
12 | |||
13 | (4096 -1 + 7)/8 - 20 - 2 = 490 | ||
14 | Previously we'd encounter this error: | ||
15 | |||
16 | crypto/rsa: key size too small for PSS signature | ||
17 | |||
18 | Fixes #42741 | ||
19 | |||
20 | Change-Id: I18bb82c41c511d564b3f4c443f4b3a38ab010ac5 | ||
21 | Reviewed-on: https://go-review.googlesource.com/c/go/+/302230 | ||
22 | Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com> | ||
23 | Reviewed-by: Filippo Valsorda <filippo@golang.org> | ||
24 | Trust: Emmanuel Odeke <emmanuel@orijtech.com> | ||
25 | Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com> | ||
26 | TryBot-Result: Go Bot <gobot@golang.org> | ||
27 | |||
28 | Upstream-Status: Backport [https://github.com/golang/go/commit/8f676144ad7b7c91adb0c6e1ec89aaa6283c6807] | ||
29 | CVE: CVE-2023-45287 #Dependency Patch3 | ||
30 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
31 | --- | ||
32 | src/crypto/rsa/pss.go | 2 +- | ||
33 | src/crypto/rsa/pss_test.go | 20 +++++++++++++++++++- | ||
34 | 2 files changed, 20 insertions(+), 2 deletions(-) | ||
35 | |||
36 | diff --git a/src/crypto/rsa/pss.go b/src/crypto/rsa/pss.go | ||
37 | index b2adbedb28fa8..814522de8181f 100644 | ||
38 | --- a/src/crypto/rsa/pss.go | ||
39 | +++ b/src/crypto/rsa/pss.go | ||
40 | @@ -269,7 +269,7 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, | ||
41 | saltLength := opts.saltLength() | ||
42 | switch saltLength { | ||
43 | case PSSSaltLengthAuto: | ||
44 | - saltLength = priv.Size() - 2 - hash.Size() | ||
45 | + saltLength = (priv.N.BitLen()-1+7)/8 - 2 - hash.Size() | ||
46 | case PSSSaltLengthEqualsHash: | ||
47 | saltLength = hash.Size() | ||
48 | } | ||
49 | diff --git a/src/crypto/rsa/pss_test.go b/src/crypto/rsa/pss_test.go | ||
50 | index dfa8d8bb5ad02..c3a6d468497cd 100644 | ||
51 | --- a/src/crypto/rsa/pss_test.go | ||
52 | +++ b/src/crypto/rsa/pss_test.go | ||
53 | @@ -12,7 +12,7 @@ import ( | ||
54 | _ "crypto/md5" | ||
55 | "crypto/rand" | ||
56 | "crypto/sha1" | ||
57 | - _ "crypto/sha256" | ||
58 | + "crypto/sha256" | ||
59 | "encoding/hex" | ||
60 | "math/big" | ||
61 | "os" | ||
62 | @@ -233,6 +233,24 @@ func TestPSSSigning(t *testing.T) { | ||
63 | } | ||
64 | } | ||
65 | |||
66 | +func TestSignWithPSSSaltLengthAuto(t *testing.T) { | ||
67 | + key, err := GenerateKey(rand.Reader, 513) | ||
68 | + if err != nil { | ||
69 | + t.Fatal(err) | ||
70 | + } | ||
71 | + digest := sha256.Sum256([]byte("message")) | ||
72 | + signature, err := key.Sign(rand.Reader, digest[:], &PSSOptions{ | ||
73 | + SaltLength: PSSSaltLengthAuto, | ||
74 | + Hash: crypto.SHA256, | ||
75 | + }) | ||
76 | + if err != nil { | ||
77 | + t.Fatal(err) | ||
78 | + } | ||
79 | + if len(signature) == 0 { | ||
80 | + t.Fatal("empty signature returned") | ||
81 | + } | ||
82 | +} | ||
83 | + | ||
84 | func bigFromHex(hex string) *big.Int { | ||
85 | n, ok := new(big.Int).SetString(hex, 16) | ||
86 | if !ok { | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-45287.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-45287.patch new file mode 100644 index 0000000000..90a74255db --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-45287.patch | |||
@@ -0,0 +1,1697 @@ | |||
1 | From 8a81fdf165facdcefa06531de5af98a4db343035 Mon Sep 17 00:00:00 2001 | ||
2 | From: =?UTF-8?q?L=C3=BAc=C3=A1s=20Meier?= <cronokirby@gmail.com> | ||
3 | Date: Tue, 8 Jun 2021 21:36:06 +0200 | ||
4 | Subject: [PATCH] crypto/rsa: replace big.Int for encryption and decryption | ||
5 | |||
6 | Infamously, big.Int does not provide constant-time arithmetic, making | ||
7 | its use in cryptographic code quite tricky. RSA uses big.Int | ||
8 | pervasively, in its public API, for key generation, precomputation, and | ||
9 | for encryption and decryption. This is a known problem. One mitigation, | ||
10 | blinding, is already in place during decryption. This helps mitigate the | ||
11 | very leaky exponentiation operation. Because big.Int is fundamentally | ||
12 | not constant-time, it's unfortunately difficult to guarantee that | ||
13 | mitigations like these are completely effective. | ||
14 | |||
15 | This patch removes the use of big.Int for encryption and decryption, | ||
16 | replacing it with an internal nat type instead. Signing and verification | ||
17 | are also affected, because they depend on encryption and decryption. | ||
18 | |||
19 | Overall, this patch degrades performance by 55% for private key | ||
20 | operations, and 4-5x for (much faster) public key operations. | ||
21 | (Signatures do both, so the slowdown is worse than decryption.) | ||
22 | |||
23 | name old time/op new time/op delta | ||
24 | DecryptPKCS1v15/2048-8 1.50ms ± 0% 2.34ms ± 0% +56.44% (p=0.000 n=8+10) | ||
25 | DecryptPKCS1v15/3072-8 4.40ms ± 0% 6.79ms ± 0% +54.33% (p=0.000 n=10+9) | ||
26 | DecryptPKCS1v15/4096-8 9.31ms ± 0% 15.14ms ± 0% +62.60% (p=0.000 n=10+10) | ||
27 | EncryptPKCS1v15/2048-8 8.16µs ± 0% 355.58µs ± 0% +4258.90% (p=0.000 n=10+9) | ||
28 | DecryptOAEP/2048-8 1.50ms ± 0% 2.34ms ± 0% +55.68% (p=0.000 n=10+9) | ||
29 | EncryptOAEP/2048-8 8.51µs ± 0% 355.95µs ± 0% +4082.75% (p=0.000 n=10+9) | ||
30 | SignPKCS1v15/2048-8 1.51ms ± 0% 2.69ms ± 0% +77.94% (p=0.000 n=10+10) | ||
31 | VerifyPKCS1v15/2048-8 7.25µs ± 0% 354.34µs ± 0% +4789.52% (p=0.000 n=9+9) | ||
32 | SignPSS/2048-8 1.51ms ± 0% 2.70ms ± 0% +78.80% (p=0.000 n=9+10) | ||
33 | VerifyPSS/2048-8 8.27µs ± 1% 355.65µs ± 0% +4199.39% (p=0.000 n=10+10) | ||
34 | |||
35 | Keep in mind that this is without any assembly at all, and that further | ||
36 | improvements are likely possible. I think having a review of the logic | ||
37 | and the cryptography would be a good idea at this stage, before we | ||
38 | complicate the code too much through optimization. | ||
39 | |||
40 | The bulk of the work is in nat.go. This introduces two new types: nat, | ||
41 | representing natural numbers, and modulus, representing moduli used in | ||
42 | modular arithmetic. | ||
43 | |||
44 | A nat has an "announced size", which may be larger than its "true size", | ||
45 | the number of bits needed to represent this number. Operations on a nat | ||
46 | will only ever leak its announced size, never its true size, or other | ||
47 | information about its value. The size of a nat is always clear based on | ||
48 | how its value is set. For example, x.mod(y, m) will make the announced | ||
49 | size of x match that of m, since x is reduced modulo m. | ||
50 | |||
51 | Operations assume that the announced size of the operands match what's | ||
52 | expected (with a few exceptions). For example, x.modAdd(y, m) assumes | ||
53 | that x and y have the same announced size as m, and that they're reduced | ||
54 | modulo m. | ||
55 | |||
56 | Nats are represented over unsatured bits.UintSize - 1 bit limbs. This | ||
57 | means that we can't reuse the assembly routines for big.Int, which use | ||
58 | saturated bits.UintSize limbs. The advantage of unsaturated limbs is | ||
59 | that it makes Montgomery multiplication faster, by needing fewer | ||
60 | registers in a hot loop. This makes exponentiation faster, which | ||
61 | consists of many Montgomery multiplications. | ||
62 | |||
63 | Moduli use nat internally. Unlike nat, the true size of a modulus always | ||
64 | matches its announced size. When creating a modulus, any zero padding is | ||
65 | removed. Moduli will also precompute constants when created, which is | ||
66 | another reason why having a separate type is desirable. | ||
67 | |||
68 | Updates #20654 | ||
69 | |||
70 | Co-authored-by: Filippo Valsorda <filippo@golang.org> | ||
71 | Change-Id: I73b61f87d58ab912e80a9644e255d552cbadcced | ||
72 | Reviewed-on: https://go-review.googlesource.com/c/go/+/326012 | ||
73 | Run-TryBot: Filippo Valsorda <filippo@golang.org> | ||
74 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
75 | Reviewed-by: Roland Shoemaker <roland@golang.org> | ||
76 | Reviewed-by: Joedian Reid <joedian@golang.org> | ||
77 | |||
78 | Upstream-Status: Backport [https://github.com/golang/go/commit/8a81fdf165facdcefa06531de5af98a4db343035] | ||
79 | CVE: CVE-2023-45287 | ||
80 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
81 | --- | ||
82 | src/crypto/rsa/example_test.go | 21 +- | ||
83 | src/crypto/rsa/nat.go | 626 +++++++++++++++++++++++++++++++++ | ||
84 | src/crypto/rsa/nat_test.go | 384 ++++++++++++++++++++ | ||
85 | src/crypto/rsa/pkcs1v15.go | 47 +-- | ||
86 | src/crypto/rsa/pss.go | 50 ++- | ||
87 | src/crypto/rsa/pss_test.go | 10 +- | ||
88 | src/crypto/rsa/rsa.go | 174 ++++----- | ||
89 | 7 files changed, 1143 insertions(+), 169 deletions(-) | ||
90 | create mode 100644 src/crypto/rsa/nat.go | ||
91 | create mode 100644 src/crypto/rsa/nat_test.go | ||
92 | |||
93 | diff --git a/src/crypto/rsa/example_test.go b/src/crypto/rsa/example_test.go | ||
94 | index 1435b70..1963609 100644 | ||
95 | --- a/src/crypto/rsa/example_test.go | ||
96 | +++ b/src/crypto/rsa/example_test.go | ||
97 | @@ -12,7 +12,6 @@ import ( | ||
98 | "crypto/sha256" | ||
99 | "encoding/hex" | ||
100 | "fmt" | ||
101 | - "io" | ||
102 | "os" | ||
103 | ) | ||
104 | |||
105 | @@ -36,21 +35,17 @@ import ( | ||
106 | // a buffer that contains a random key. Thus, if the RSA result isn't | ||
107 | // well-formed, the implementation uses a random key in constant time. | ||
108 | func ExampleDecryptPKCS1v15SessionKey() { | ||
109 | - // crypto/rand.Reader is a good source of entropy for blinding the RSA | ||
110 | - // operation. | ||
111 | - rng := rand.Reader | ||
112 | - | ||
113 | // The hybrid scheme should use at least a 16-byte symmetric key. Here | ||
114 | // we read the random key that will be used if the RSA decryption isn't | ||
115 | // well-formed. | ||
116 | key := make([]byte, 32) | ||
117 | - if _, err := io.ReadFull(rng, key); err != nil { | ||
118 | + if _, err := rand.Read(key); err != nil { | ||
119 | panic("RNG failure") | ||
120 | } | ||
121 | |||
122 | rsaCiphertext, _ := hex.DecodeString("aabbccddeeff") | ||
123 | |||
124 | - if err := DecryptPKCS1v15SessionKey(rng, rsaPrivateKey, rsaCiphertext, key); err != nil { | ||
125 | + if err := DecryptPKCS1v15SessionKey(nil, rsaPrivateKey, rsaCiphertext, key); err != nil { | ||
126 | // Any errors that result will be “public” – meaning that they | ||
127 | // can be determined without any secret information. (For | ||
128 | // instance, if the length of key is impossible given the RSA | ||
129 | @@ -86,10 +81,6 @@ func ExampleDecryptPKCS1v15SessionKey() { | ||
130 | } | ||
131 | |||
132 | func ExampleSignPKCS1v15() { | ||
133 | - // crypto/rand.Reader is a good source of entropy for blinding the RSA | ||
134 | - // operation. | ||
135 | - rng := rand.Reader | ||
136 | - | ||
137 | message := []byte("message to be signed") | ||
138 | |||
139 | // Only small messages can be signed directly; thus the hash of a | ||
140 | @@ -99,7 +90,7 @@ func ExampleSignPKCS1v15() { | ||
141 | // of writing (2016). | ||
142 | hashed := sha256.Sum256(message) | ||
143 | |||
144 | - signature, err := SignPKCS1v15(rng, rsaPrivateKey, crypto.SHA256, hashed[:]) | ||
145 | + signature, err := SignPKCS1v15(nil, rsaPrivateKey, crypto.SHA256, hashed[:]) | ||
146 | if err != nil { | ||
147 | fmt.Fprintf(os.Stderr, "Error from signing: %s\n", err) | ||
148 | return | ||
149 | @@ -151,11 +142,7 @@ func ExampleDecryptOAEP() { | ||
150 | ciphertext, _ := hex.DecodeString("4d1ee10e8f286390258c51a5e80802844c3e6358ad6690b7285218a7c7ed7fc3a4c7b950fbd04d4b0239cc060dcc7065ca6f84c1756deb71ca5685cadbb82be025e16449b905c568a19c088a1abfad54bf7ecc67a7df39943ec511091a34c0f2348d04e058fcff4d55644de3cd1d580791d4524b92f3e91695582e6e340a1c50b6c6d78e80b4e42c5b4d45e479b492de42bbd39cc642ebb80226bb5200020d501b24a37bcc2ec7f34e596b4fd6b063de4858dbf5a4e3dd18e262eda0ec2d19dbd8e890d672b63d368768360b20c0b6b8592a438fa275e5fa7f60bef0dd39673fd3989cc54d2cb80c08fcd19dacbc265ee1c6014616b0e04ea0328c2a04e73460") | ||
151 | label := []byte("orders") | ||
152 | |||
153 | - // crypto/rand.Reader is a good source of entropy for blinding the RSA | ||
154 | - // operation. | ||
155 | - rng := rand.Reader | ||
156 | - | ||
157 | - plaintext, err := DecryptOAEP(sha256.New(), rng, test2048Key, ciphertext, label) | ||
158 | + plaintext, err := DecryptOAEP(sha256.New(), nil, test2048Key, ciphertext, label) | ||
159 | if err != nil { | ||
160 | fmt.Fprintf(os.Stderr, "Error from decryption: %s\n", err) | ||
161 | return | ||
162 | diff --git a/src/crypto/rsa/nat.go b/src/crypto/rsa/nat.go | ||
163 | new file mode 100644 | ||
164 | index 0000000..da521c2 | ||
165 | --- /dev/null | ||
166 | +++ b/src/crypto/rsa/nat.go | ||
167 | @@ -0,0 +1,626 @@ | ||
168 | +// Copyright 2021 The Go Authors. All rights reserved. | ||
169 | +// Use of this source code is governed by a BSD-style | ||
170 | +// license that can be found in the LICENSE file. | ||
171 | + | ||
172 | +package rsa | ||
173 | + | ||
174 | +import ( | ||
175 | + "math/big" | ||
176 | + "math/bits" | ||
177 | +) | ||
178 | + | ||
179 | +const ( | ||
180 | + // _W is the number of bits we use for our limbs. | ||
181 | + _W = bits.UintSize - 1 | ||
182 | + // _MASK selects _W bits from a full machine word. | ||
183 | + _MASK = (1 << _W) - 1 | ||
184 | +) | ||
185 | + | ||
186 | +// choice represents a constant-time boolean. The value of choice is always | ||
187 | +// either 1 or 0. We use an int instead of bool in order to make decisions in | ||
188 | +// constant time by turning it into a mask. | ||
189 | +type choice uint | ||
190 | + | ||
191 | +func not(c choice) choice { return 1 ^ c } | ||
192 | + | ||
193 | +const yes = choice(1) | ||
194 | +const no = choice(0) | ||
195 | + | ||
196 | +// ctSelect returns x if on == 1, and y if on == 0. The execution time of this | ||
197 | +// function does not depend on its inputs. If on is any value besides 1 or 0, | ||
198 | +// the result is undefined. | ||
199 | +func ctSelect(on choice, x, y uint) uint { | ||
200 | + // When on == 1, mask is 0b111..., otherwise mask is 0b000... | ||
201 | + mask := -uint(on) | ||
202 | + // When mask is all zeros, we just have y, otherwise, y cancels with itself. | ||
203 | + return y ^ (mask & (y ^ x)) | ||
204 | +} | ||
205 | + | ||
206 | +// ctEq returns 1 if x == y, and 0 otherwise. The execution time of this | ||
207 | +// function does not depend on its inputs. | ||
208 | +func ctEq(x, y uint) choice { | ||
209 | + // If x != y, then either x - y or y - x will generate a carry. | ||
210 | + _, c1 := bits.Sub(x, y, 0) | ||
211 | + _, c2 := bits.Sub(y, x, 0) | ||
212 | + return not(choice(c1 | c2)) | ||
213 | +} | ||
214 | + | ||
215 | +// ctGeq returns 1 if x >= y, and 0 otherwise. The execution time of this | ||
216 | +// function does not depend on its inputs. | ||
217 | +func ctGeq(x, y uint) choice { | ||
218 | + // If x < y, then x - y generates a carry. | ||
219 | + _, carry := bits.Sub(x, y, 0) | ||
220 | + return not(choice(carry)) | ||
221 | +} | ||
222 | + | ||
223 | +// nat represents an arbitrary natural number | ||
224 | +// | ||
225 | +// Each nat has an announced length, which is the number of limbs it has stored. | ||
226 | +// Operations on this number are allowed to leak this length, but will not leak | ||
227 | +// any information about the values contained in those limbs. | ||
228 | +type nat struct { | ||
229 | + // limbs is a little-endian representation in base 2^W with | ||
230 | + // W = bits.UintSize - 1. The top bit is always unset between operations. | ||
231 | + // | ||
232 | + // The top bit is left unset to optimize Montgomery multiplication, in the | ||
233 | + // inner loop of exponentiation. Using fully saturated limbs would leave us | ||
234 | + // working with 129-bit numbers on 64-bit platforms, wasting a lot of space, | ||
235 | + // and thus time. | ||
236 | + limbs []uint | ||
237 | +} | ||
238 | + | ||
239 | +// expand expands x to n limbs, leaving its value unchanged. | ||
240 | +func (x *nat) expand(n int) *nat { | ||
241 | + for len(x.limbs) > n { | ||
242 | + if x.limbs[len(x.limbs)-1] != 0 { | ||
243 | + panic("rsa: internal error: shrinking nat") | ||
244 | + } | ||
245 | + x.limbs = x.limbs[:len(x.limbs)-1] | ||
246 | + } | ||
247 | + if cap(x.limbs) < n { | ||
248 | + newLimbs := make([]uint, n) | ||
249 | + copy(newLimbs, x.limbs) | ||
250 | + x.limbs = newLimbs | ||
251 | + return x | ||
252 | + } | ||
253 | + extraLimbs := x.limbs[len(x.limbs):n] | ||
254 | + for i := range extraLimbs { | ||
255 | + extraLimbs[i] = 0 | ||
256 | + } | ||
257 | + x.limbs = x.limbs[:n] | ||
258 | + return x | ||
259 | +} | ||
260 | + | ||
261 | +// reset returns a zero nat of n limbs, reusing x's storage if n <= cap(x.limbs). | ||
262 | +func (x *nat) reset(n int) *nat { | ||
263 | + if cap(x.limbs) < n { | ||
264 | + x.limbs = make([]uint, n) | ||
265 | + return x | ||
266 | + } | ||
267 | + for i := range x.limbs { | ||
268 | + x.limbs[i] = 0 | ||
269 | + } | ||
270 | + x.limbs = x.limbs[:n] | ||
271 | + return x | ||
272 | +} | ||
273 | + | ||
274 | +// clone returns a new nat, with the same value and announced length as x. | ||
275 | +func (x *nat) clone() *nat { | ||
276 | + out := &nat{make([]uint, len(x.limbs))} | ||
277 | + copy(out.limbs, x.limbs) | ||
278 | + return out | ||
279 | +} | ||
280 | + | ||
281 | +// natFromBig creates a new natural number from a big.Int. | ||
282 | +// | ||
283 | +// The announced length of the resulting nat is based on the actual bit size of | ||
284 | +// the input, ignoring leading zeroes. | ||
285 | +func natFromBig(x *big.Int) *nat { | ||
286 | + xLimbs := x.Bits() | ||
287 | + bitSize := bigBitLen(x) | ||
288 | + requiredLimbs := (bitSize + _W - 1) / _W | ||
289 | + | ||
290 | + out := &nat{make([]uint, requiredLimbs)} | ||
291 | + outI := 0 | ||
292 | + shift := 0 | ||
293 | + for i := range xLimbs { | ||
294 | + xi := uint(xLimbs[i]) | ||
295 | + out.limbs[outI] |= (xi << shift) & _MASK | ||
296 | + outI++ | ||
297 | + if outI == requiredLimbs { | ||
298 | + return out | ||
299 | + } | ||
300 | + out.limbs[outI] = xi >> (_W - shift) | ||
301 | + shift++ // this assumes bits.UintSize - _W = 1 | ||
302 | + if shift == _W { | ||
303 | + shift = 0 | ||
304 | + outI++ | ||
305 | + } | ||
306 | + } | ||
307 | + return out | ||
308 | +} | ||
309 | + | ||
310 | +// fillBytes sets bytes to x as a zero-extended big-endian byte slice. | ||
311 | +// | ||
312 | +// If bytes is not long enough to contain the number or at least len(x.limbs)-1 | ||
313 | +// limbs, or has zero length, fillBytes will panic. | ||
314 | +func (x *nat) fillBytes(bytes []byte) []byte { | ||
315 | + if len(bytes) == 0 { | ||
316 | + panic("nat: fillBytes invoked with too small buffer") | ||
317 | + } | ||
318 | + for i := range bytes { | ||
319 | + bytes[i] = 0 | ||
320 | + } | ||
321 | + shift := 0 | ||
322 | + outI := len(bytes) - 1 | ||
323 | + for i, limb := range x.limbs { | ||
324 | + remainingBits := _W | ||
325 | + for remainingBits >= 8 { | ||
326 | + bytes[outI] |= byte(limb) << shift | ||
327 | + consumed := 8 - shift | ||
328 | + limb >>= consumed | ||
329 | + remainingBits -= consumed | ||
330 | + shift = 0 | ||
331 | + outI-- | ||
332 | + if outI < 0 { | ||
333 | + if limb != 0 || i < len(x.limbs)-1 { | ||
334 | + panic("nat: fillBytes invoked with too small buffer") | ||
335 | + } | ||
336 | + return bytes | ||
337 | + } | ||
338 | + } | ||
339 | + bytes[outI] = byte(limb) | ||
340 | + shift = remainingBits | ||
341 | + } | ||
342 | + return bytes | ||
343 | +} | ||
344 | + | ||
345 | +// natFromBytes converts a slice of big-endian bytes into a nat. | ||
346 | +// | ||
347 | +// The announced length of the output depends on the length of bytes. Unlike | ||
348 | +// big.Int, creating a nat will not remove leading zeros. | ||
349 | +func natFromBytes(bytes []byte) *nat { | ||
350 | + bitSize := len(bytes) * 8 | ||
351 | + requiredLimbs := (bitSize + _W - 1) / _W | ||
352 | + | ||
353 | + out := &nat{make([]uint, requiredLimbs)} | ||
354 | + outI := 0 | ||
355 | + shift := 0 | ||
356 | + for i := len(bytes) - 1; i >= 0; i-- { | ||
357 | + bi := bytes[i] | ||
358 | + out.limbs[outI] |= uint(bi) << shift | ||
359 | + shift += 8 | ||
360 | + if shift >= _W { | ||
361 | + shift -= _W | ||
362 | + out.limbs[outI] &= _MASK | ||
363 | + outI++ | ||
364 | + if shift > 0 { | ||
365 | + out.limbs[outI] = uint(bi) >> (8 - shift) | ||
366 | + } | ||
367 | + } | ||
368 | + } | ||
369 | + return out | ||
370 | +} | ||
371 | + | ||
372 | +// cmpEq returns 1 if x == y, and 0 otherwise. | ||
373 | +// | ||
374 | +// Both operands must have the same announced length. | ||
375 | +func (x *nat) cmpEq(y *nat) choice { | ||
376 | + // Eliminate bounds checks in the loop. | ||
377 | + size := len(x.limbs) | ||
378 | + xLimbs := x.limbs[:size] | ||
379 | + yLimbs := y.limbs[:size] | ||
380 | + | ||
381 | + equal := yes | ||
382 | + for i := 0; i < size; i++ { | ||
383 | + equal &= ctEq(xLimbs[i], yLimbs[i]) | ||
384 | + } | ||
385 | + return equal | ||
386 | +} | ||
387 | + | ||
388 | +// cmpGeq returns 1 if x >= y, and 0 otherwise. | ||
389 | +// | ||
390 | +// Both operands must have the same announced length. | ||
391 | +func (x *nat) cmpGeq(y *nat) choice { | ||
392 | + // Eliminate bounds checks in the loop. | ||
393 | + size := len(x.limbs) | ||
394 | + xLimbs := x.limbs[:size] | ||
395 | + yLimbs := y.limbs[:size] | ||
396 | + | ||
397 | + var c uint | ||
398 | + for i := 0; i < size; i++ { | ||
399 | + c = (xLimbs[i] - yLimbs[i] - c) >> _W | ||
400 | + } | ||
401 | + // If there was a carry, then subtracting y underflowed, so | ||
402 | + // x is not greater than or equal to y. | ||
403 | + return not(choice(c)) | ||
404 | +} | ||
405 | + | ||
406 | +// assign sets x <- y if on == 1, and does nothing otherwise. | ||
407 | +// | ||
408 | +// Both operands must have the same announced length. | ||
409 | +func (x *nat) assign(on choice, y *nat) *nat { | ||
410 | + // Eliminate bounds checks in the loop. | ||
411 | + size := len(x.limbs) | ||
412 | + xLimbs := x.limbs[:size] | ||
413 | + yLimbs := y.limbs[:size] | ||
414 | + | ||
415 | + for i := 0; i < size; i++ { | ||
416 | + xLimbs[i] = ctSelect(on, yLimbs[i], xLimbs[i]) | ||
417 | + } | ||
418 | + return x | ||
419 | +} | ||
420 | + | ||
421 | +// add computes x += y if on == 1, and does nothing otherwise. It returns the | ||
422 | +// carry of the addition regardless of on. | ||
423 | +// | ||
424 | +// Both operands must have the same announced length. | ||
425 | +func (x *nat) add(on choice, y *nat) (c uint) { | ||
426 | + // Eliminate bounds checks in the loop. | ||
427 | + size := len(x.limbs) | ||
428 | + xLimbs := x.limbs[:size] | ||
429 | + yLimbs := y.limbs[:size] | ||
430 | + | ||
431 | + for i := 0; i < size; i++ { | ||
432 | + res := xLimbs[i] + yLimbs[i] + c | ||
433 | + xLimbs[i] = ctSelect(on, res&_MASK, xLimbs[i]) | ||
434 | + c = res >> _W | ||
435 | + } | ||
436 | + return | ||
437 | +} | ||
438 | + | ||
439 | +// sub computes x -= y if on == 1, and does nothing otherwise. It returns the | ||
440 | +// borrow of the subtraction regardless of on. | ||
441 | +// | ||
442 | +// Both operands must have the same announced length. | ||
443 | +func (x *nat) sub(on choice, y *nat) (c uint) { | ||
444 | + // Eliminate bounds checks in the loop. | ||
445 | + size := len(x.limbs) | ||
446 | + xLimbs := x.limbs[:size] | ||
447 | + yLimbs := y.limbs[:size] | ||
448 | + | ||
449 | + for i := 0; i < size; i++ { | ||
450 | + res := xLimbs[i] - yLimbs[i] - c | ||
451 | + xLimbs[i] = ctSelect(on, res&_MASK, xLimbs[i]) | ||
452 | + c = res >> _W | ||
453 | + } | ||
454 | + return | ||
455 | +} | ||
456 | + | ||
457 | +// modulus is used for modular arithmetic, precomputing relevant constants. | ||
458 | +// | ||
459 | +// Moduli are assumed to be odd numbers. Moduli can also leak the exact | ||
460 | +// number of bits needed to store their value, and are stored without padding. | ||
461 | +// | ||
462 | +// Their actual value is still kept secret. | ||
463 | +type modulus struct { | ||
464 | + // The underlying natural number for this modulus. | ||
465 | + // | ||
466 | + // This will be stored without any padding, and shouldn't alias with any | ||
467 | + // other natural number being used. | ||
468 | + nat *nat | ||
469 | + leading int // number of leading zeros in the modulus | ||
470 | + m0inv uint // -nat.limbs[0]⁻¹ mod _W | ||
471 | +} | ||
472 | + | ||
473 | +// minusInverseModW computes -x⁻¹ mod _W with x odd. | ||
474 | +// | ||
475 | +// This operation is used to precompute a constant involved in Montgomery | ||
476 | +// multiplication. | ||
477 | +func minusInverseModW(x uint) uint { | ||
478 | + // Every iteration of this loop doubles the least-significant bits of | ||
479 | + // correct inverse in y. The first three bits are already correct (1⁻¹ = 1, | ||
480 | + // 3⁻¹ = 3, 5⁻¹ = 5, and 7⁻¹ = 7 mod 8), so doubling five times is enough | ||
481 | + // for 61 bits (and wastes only one iteration for 31 bits). | ||
482 | + // | ||
483 | + // See https://crypto.stackexchange.com/a/47496. | ||
484 | + y := x | ||
485 | + for i := 0; i < 5; i++ { | ||
486 | + y = y * (2 - x*y) | ||
487 | + } | ||
488 | + return (1 << _W) - (y & _MASK) | ||
489 | +} | ||
490 | + | ||
491 | +// modulusFromNat creates a new modulus from a nat. | ||
492 | +// | ||
493 | +// The nat should be odd, nonzero, and the number of significant bits in the | ||
494 | +// number should be leakable. The nat shouldn't be reused. | ||
495 | +func modulusFromNat(nat *nat) *modulus { | ||
496 | + m := &modulus{} | ||
497 | + m.nat = nat | ||
498 | + size := len(m.nat.limbs) | ||
499 | + for m.nat.limbs[size-1] == 0 { | ||
500 | + size-- | ||
501 | + } | ||
502 | + m.nat.limbs = m.nat.limbs[:size] | ||
503 | + m.leading = _W - bitLen(m.nat.limbs[size-1]) | ||
504 | + m.m0inv = minusInverseModW(m.nat.limbs[0]) | ||
505 | + return m | ||
506 | +} | ||
507 | + | ||
508 | +// bitLen is a version of bits.Len that only leaks the bit length of n, but not | ||
509 | +// its value. bits.Len and bits.LeadingZeros use a lookup table for the | ||
510 | +// low-order bits on some architectures. | ||
511 | +func bitLen(n uint) int { | ||
512 | + var len int | ||
513 | + // We assume, here and elsewhere, that comparison to zero is constant time | ||
514 | + // with respect to different non-zero values. | ||
515 | + for n != 0 { | ||
516 | + len++ | ||
517 | + n >>= 1 | ||
518 | + } | ||
519 | + return len | ||
520 | +} | ||
521 | + | ||
522 | +// bigBitLen is a version of big.Int.BitLen that only leaks the bit length of x, | ||
523 | +// but not its value. big.Int.BitLen uses bits.Len. | ||
524 | +func bigBitLen(x *big.Int) int { | ||
525 | + xLimbs := x.Bits() | ||
526 | + fullLimbs := len(xLimbs) - 1 | ||
527 | + topLimb := uint(xLimbs[len(xLimbs)-1]) | ||
528 | + return fullLimbs*bits.UintSize + bitLen(topLimb) | ||
529 | +} | ||
530 | + | ||
531 | +// modulusSize returns the size of m in bytes. | ||
532 | +func modulusSize(m *modulus) int { | ||
533 | + bits := len(m.nat.limbs)*_W - int(m.leading) | ||
534 | + return (bits + 7) / 8 | ||
535 | +} | ||
536 | + | ||
537 | +// shiftIn calculates x = x << _W + y mod m. | ||
538 | +// | ||
539 | +// This assumes that x is already reduced mod m, and that y < 2^_W. | ||
540 | +func (x *nat) shiftIn(y uint, m *modulus) *nat { | ||
541 | + d := new(nat).resetFor(m) | ||
542 | + | ||
543 | + // Eliminate bounds checks in the loop. | ||
544 | + size := len(m.nat.limbs) | ||
545 | + xLimbs := x.limbs[:size] | ||
546 | + dLimbs := d.limbs[:size] | ||
547 | + mLimbs := m.nat.limbs[:size] | ||
548 | + | ||
549 | + // Each iteration of this loop computes x = 2x + b mod m, where b is a bit | ||
550 | + // from y. Effectively, it left-shifts x and adds y one bit at a time, | ||
551 | + // reducing it every time. | ||
552 | + // | ||
553 | + // To do the reduction, each iteration computes both 2x + b and 2x + b - m. | ||
554 | + // The next iteration (and finally the return line) will use either result | ||
555 | + // based on whether the subtraction underflowed. | ||
556 | + needSubtraction := no | ||
557 | + for i := _W - 1; i >= 0; i-- { | ||
558 | + carry := (y >> i) & 1 | ||
559 | + var borrow uint | ||
560 | + for i := 0; i < size; i++ { | ||
561 | + l := ctSelect(needSubtraction, dLimbs[i], xLimbs[i]) | ||
562 | + | ||
563 | + res := l<<1 + carry | ||
564 | + xLimbs[i] = res & _MASK | ||
565 | + carry = res >> _W | ||
566 | + | ||
567 | + res = xLimbs[i] - mLimbs[i] - borrow | ||
568 | + dLimbs[i] = res & _MASK | ||
569 | + borrow = res >> _W | ||
570 | + } | ||
571 | + // See modAdd for how carry (aka overflow), borrow (aka underflow), and | ||
572 | + // needSubtraction relate. | ||
573 | + needSubtraction = ctEq(carry, borrow) | ||
574 | + } | ||
575 | + return x.assign(needSubtraction, d) | ||
576 | +} | ||
577 | + | ||
578 | +// mod calculates out = x mod m. | ||
579 | +// | ||
580 | +// This works regardless how large the value of x is. | ||
581 | +// | ||
582 | +// The output will be resized to the size of m and overwritten. | ||
583 | +func (out *nat) mod(x *nat, m *modulus) *nat { | ||
584 | + out.resetFor(m) | ||
585 | + // Working our way from the most significant to the least significant limb, | ||
586 | + // we can insert each limb at the least significant position, shifting all | ||
587 | + // previous limbs left by _W. This way each limb will get shifted by the | ||
588 | + // correct number of bits. We can insert at least N - 1 limbs without | ||
589 | + // overflowing m. After that, we need to reduce every time we shift. | ||
590 | + i := len(x.limbs) - 1 | ||
591 | + // For the first N - 1 limbs we can skip the actual shifting and position | ||
592 | + // them at the shifted position, which starts at min(N - 2, i). | ||
593 | + start := len(m.nat.limbs) - 2 | ||
594 | + if i < start { | ||
595 | + start = i | ||
596 | + } | ||
597 | + for j := start; j >= 0; j-- { | ||
598 | + out.limbs[j] = x.limbs[i] | ||
599 | + i-- | ||
600 | + } | ||
601 | + // We shift in the remaining limbs, reducing modulo m each time. | ||
602 | + for i >= 0 { | ||
603 | + out.shiftIn(x.limbs[i], m) | ||
604 | + i-- | ||
605 | + } | ||
606 | + return out | ||
607 | +} | ||
608 | + | ||
609 | +// expandFor ensures out has the right size to work with operations modulo m. | ||
610 | +// | ||
611 | +// This assumes that out has as many or fewer limbs than m, or that the extra | ||
612 | +// limbs are all zero (which may happen when decoding a value that has leading | ||
613 | +// zeroes in its bytes representation that spill over the limb threshold). | ||
614 | +func (out *nat) expandFor(m *modulus) *nat { | ||
615 | + return out.expand(len(m.nat.limbs)) | ||
616 | +} | ||
617 | + | ||
618 | +// resetFor ensures out has the right size to work with operations modulo m. | ||
619 | +// | ||
620 | +// out is zeroed and may start at any size. | ||
621 | +func (out *nat) resetFor(m *modulus) *nat { | ||
622 | + return out.reset(len(m.nat.limbs)) | ||
623 | +} | ||
624 | + | ||
625 | +// modSub computes x = x - y mod m. | ||
626 | +// | ||
627 | +// The length of both operands must be the same as the modulus. Both operands | ||
628 | +// must already be reduced modulo m. | ||
629 | +func (x *nat) modSub(y *nat, m *modulus) *nat { | ||
630 | + underflow := x.sub(yes, y) | ||
631 | + // If the subtraction underflowed, add m. | ||
632 | + x.add(choice(underflow), m.nat) | ||
633 | + return x | ||
634 | +} | ||
635 | + | ||
636 | +// modAdd computes x = x + y mod m. | ||
637 | +// | ||
638 | +// The length of both operands must be the same as the modulus. Both operands | ||
639 | +// must already be reduced modulo m. | ||
640 | +func (x *nat) modAdd(y *nat, m *modulus) *nat { | ||
641 | + overflow := x.add(yes, y) | ||
642 | + underflow := not(x.cmpGeq(m.nat)) // x < m | ||
643 | + | ||
644 | + // Three cases are possible: | ||
645 | + // | ||
646 | + // - overflow = 0, underflow = 0 | ||
647 | + // | ||
648 | + // In this case, addition fits in our limbs, but we can still subtract away | ||
649 | + // m without an underflow, so we need to perform the subtraction to reduce | ||
650 | + // our result. | ||
651 | + // | ||
652 | + // - overflow = 0, underflow = 1 | ||
653 | + // | ||
654 | + // The addition fits in our limbs, but we can't subtract m without | ||
655 | + // underflowing. The result is already reduced. | ||
656 | + // | ||
657 | + // - overflow = 1, underflow = 1 | ||
658 | + // | ||
659 | + // The addition does not fit in our limbs, and the subtraction's borrow | ||
660 | + // would cancel out with the addition's carry. We need to subtract m to | ||
661 | + // reduce our result. | ||
662 | + // | ||
663 | + // The overflow = 1, underflow = 0 case is not possible, because y is at | ||
664 | + // most m - 1, and if adding m - 1 overflows, then subtracting m must | ||
665 | + // necessarily underflow. | ||
666 | + needSubtraction := ctEq(overflow, uint(underflow)) | ||
667 | + | ||
668 | + x.sub(needSubtraction, m.nat) | ||
669 | + return x | ||
670 | +} | ||
671 | + | ||
672 | +// montgomeryRepresentation calculates x = x * R mod m, with R = 2^(_W * n) and | ||
673 | +// n = len(m.nat.limbs). | ||
674 | +// | ||
675 | +// Faster Montgomery multiplication replaces standard modular multiplication for | ||
676 | +// numbers in this representation. | ||
677 | +// | ||
678 | +// This assumes that x is already reduced mod m. | ||
679 | +func (x *nat) montgomeryRepresentation(m *modulus) *nat { | ||
680 | + for i := 0; i < len(m.nat.limbs); i++ { | ||
681 | + x.shiftIn(0, m) // x = x * 2^_W mod m | ||
682 | + } | ||
683 | + return x | ||
684 | +} | ||
685 | + | ||
686 | +// montgomeryMul calculates d = a * b / R mod m, with R = 2^(_W * n) and | ||
687 | +// n = len(m.nat.limbs), using the Montgomery Multiplication technique. | ||
688 | +// | ||
689 | +// All inputs should be the same length, not aliasing d, and already | ||
690 | +// reduced modulo m. d will be resized to the size of m and overwritten. | ||
691 | +func (d *nat) montgomeryMul(a *nat, b *nat, m *modulus) *nat { | ||
692 | + // See https://bearssl.org/bigint.html#montgomery-reduction-and-multiplication | ||
693 | + // for a description of the algorithm. | ||
694 | + | ||
695 | + // Eliminate bounds checks in the loop. | ||
696 | + size := len(m.nat.limbs) | ||
697 | + aLimbs := a.limbs[:size] | ||
698 | + bLimbs := b.limbs[:size] | ||
699 | + dLimbs := d.resetFor(m).limbs[:size] | ||
700 | + mLimbs := m.nat.limbs[:size] | ||
701 | + | ||
702 | + var overflow uint | ||
703 | + for i := 0; i < size; i++ { | ||
704 | + f := ((dLimbs[0] + aLimbs[i]*bLimbs[0]) * m.m0inv) & _MASK | ||
705 | + carry := uint(0) | ||
706 | + for j := 0; j < size; j++ { | ||
707 | + // z = d[j] + a[i] * b[j] + f * m[j] + carry <= 2^(2W+1) - 2^(W+1) + 2^W | ||
708 | + hi, lo := bits.Mul(aLimbs[i], bLimbs[j]) | ||
709 | + z_lo, c := bits.Add(dLimbs[j], lo, 0) | ||
710 | + z_hi, _ := bits.Add(0, hi, c) | ||
711 | + hi, lo = bits.Mul(f, mLimbs[j]) | ||
712 | + z_lo, c = bits.Add(z_lo, lo, 0) | ||
713 | + z_hi, _ = bits.Add(z_hi, hi, c) | ||
714 | + z_lo, c = bits.Add(z_lo, carry, 0) | ||
715 | + z_hi, _ = bits.Add(z_hi, 0, c) | ||
716 | + if j > 0 { | ||
717 | + dLimbs[j-1] = z_lo & _MASK | ||
718 | + } | ||
719 | + carry = z_hi<<1 | z_lo>>_W // carry <= 2^(W+1) - 2 | ||
720 | + } | ||
721 | + z := overflow + carry // z <= 2^(W+1) - 1 | ||
722 | + dLimbs[size-1] = z & _MASK | ||
723 | + overflow = z >> _W // overflow <= 1 | ||
724 | + } | ||
725 | + // See modAdd for how overflow, underflow, and needSubtraction relate. | ||
726 | + underflow := not(d.cmpGeq(m.nat)) // d < m | ||
727 | + needSubtraction := ctEq(overflow, uint(underflow)) | ||
728 | + d.sub(needSubtraction, m.nat) | ||
729 | + | ||
730 | + return d | ||
731 | +} | ||
732 | + | ||
733 | +// modMul calculates x *= y mod m. | ||
734 | +// | ||
735 | +// x and y must already be reduced modulo m, they must share its announced | ||
736 | +// length, and they may not alias. | ||
737 | +func (x *nat) modMul(y *nat, m *modulus) *nat { | ||
738 | + // A Montgomery multiplication by a value out of the Montgomery domain | ||
739 | + // takes the result out of Montgomery representation. | ||
740 | + xR := x.clone().montgomeryRepresentation(m) // xR = x * R mod m | ||
741 | + return x.montgomeryMul(xR, y, m) // x = xR * y / R mod m | ||
742 | +} | ||
743 | + | ||
744 | +// exp calculates out = x^e mod m. | ||
745 | +// | ||
746 | +// The exponent e is represented in big-endian order. The output will be resized | ||
747 | +// to the size of m and overwritten. x must already be reduced modulo m. | ||
748 | +func (out *nat) exp(x *nat, e []byte, m *modulus) *nat { | ||
749 | + // We use a 4 bit window. For our RSA workload, 4 bit windows are faster | ||
750 | + // than 2 bit windows, but use an extra 12 nats worth of scratch space. | ||
751 | + // Using bit sizes that don't divide 8 are more complex to implement. | ||
752 | + table := make([]*nat, (1<<4)-1) // table[i] = x ^ (i+1) | ||
753 | + table[0] = x.clone().montgomeryRepresentation(m) | ||
754 | + for i := 1; i < len(table); i++ { | ||
755 | + table[i] = new(nat).expandFor(m) | ||
756 | + table[i].montgomeryMul(table[i-1], table[0], m) | ||
757 | + } | ||
758 | + | ||
759 | + out.resetFor(m) | ||
760 | + out.limbs[0] = 1 | ||
761 | + out.montgomeryRepresentation(m) | ||
762 | + t0 := new(nat).expandFor(m) | ||
763 | + t1 := new(nat).expandFor(m) | ||
764 | + for _, b := range e { | ||
765 | + for _, j := range []int{4, 0} { | ||
766 | + // Square four times. | ||
767 | + t1.montgomeryMul(out, out, m) | ||
768 | + out.montgomeryMul(t1, t1, m) | ||
769 | + t1.montgomeryMul(out, out, m) | ||
770 | + out.montgomeryMul(t1, t1, m) | ||
771 | + | ||
772 | + // Select x^k in constant time from the table. | ||
773 | + k := uint((b >> j) & 0b1111) | ||
774 | + for i := range table { | ||
775 | + t0.assign(ctEq(k, uint(i+1)), table[i]) | ||
776 | + } | ||
777 | + | ||
778 | + // Multiply by x^k, discarding the result if k = 0. | ||
779 | + t1.montgomeryMul(out, t0, m) | ||
780 | + out.assign(not(ctEq(k, 0)), t1) | ||
781 | + } | ||
782 | + } | ||
783 | + | ||
784 | + // By Montgomery multiplying with 1 not in Montgomery representation, we | ||
785 | + // convert out back from Montgomery representation, because it works out to | ||
786 | + // dividing by R. | ||
787 | + t0.assign(yes, out) | ||
788 | + t1.resetFor(m) | ||
789 | + t1.limbs[0] = 1 | ||
790 | + out.montgomeryMul(t0, t1, m) | ||
791 | + | ||
792 | + return out | ||
793 | +} | ||
794 | diff --git a/src/crypto/rsa/nat_test.go b/src/crypto/rsa/nat_test.go | ||
795 | new file mode 100644 | ||
796 | index 0000000..3e6eb10 | ||
797 | --- /dev/null | ||
798 | +++ b/src/crypto/rsa/nat_test.go | ||
799 | @@ -0,0 +1,384 @@ | ||
800 | +// Copyright 2021 The Go Authors. All rights reserved. | ||
801 | +// Use of this source code is governed by a BSD-style | ||
802 | +// license that can be found in the LICENSE file. | ||
803 | + | ||
804 | +package rsa | ||
805 | + | ||
806 | +import ( | ||
807 | + "bytes" | ||
808 | + "math/big" | ||
809 | + "math/bits" | ||
810 | + "math/rand" | ||
811 | + "reflect" | ||
812 | + "testing" | ||
813 | + "testing/quick" | ||
814 | +) | ||
815 | + | ||
816 | +// Generate generates an even nat. It's used by testing/quick to produce random | ||
817 | +// *nat values for quick.Check invocations. | ||
818 | +func (*nat) Generate(r *rand.Rand, size int) reflect.Value { | ||
819 | + limbs := make([]uint, size) | ||
820 | + for i := 0; i < size; i++ { | ||
821 | + limbs[i] = uint(r.Uint64()) & ((1 << _W) - 2) | ||
822 | + } | ||
823 | + return reflect.ValueOf(&nat{limbs}) | ||
824 | +} | ||
825 | + | ||
826 | +func testModAddCommutative(a *nat, b *nat) bool { | ||
827 | + mLimbs := make([]uint, len(a.limbs)) | ||
828 | + for i := 0; i < len(mLimbs); i++ { | ||
829 | + mLimbs[i] = _MASK | ||
830 | + } | ||
831 | + m := modulusFromNat(&nat{mLimbs}) | ||
832 | + aPlusB := a.clone() | ||
833 | + aPlusB.modAdd(b, m) | ||
834 | + bPlusA := b.clone() | ||
835 | + bPlusA.modAdd(a, m) | ||
836 | + return aPlusB.cmpEq(bPlusA) == 1 | ||
837 | +} | ||
838 | + | ||
839 | +func TestModAddCommutative(t *testing.T) { | ||
840 | + err := quick.Check(testModAddCommutative, &quick.Config{}) | ||
841 | + if err != nil { | ||
842 | + t.Error(err) | ||
843 | + } | ||
844 | +} | ||
845 | + | ||
846 | +func testModSubThenAddIdentity(a *nat, b *nat) bool { | ||
847 | + mLimbs := make([]uint, len(a.limbs)) | ||
848 | + for i := 0; i < len(mLimbs); i++ { | ||
849 | + mLimbs[i] = _MASK | ||
850 | + } | ||
851 | + m := modulusFromNat(&nat{mLimbs}) | ||
852 | + original := a.clone() | ||
853 | + a.modSub(b, m) | ||
854 | + a.modAdd(b, m) | ||
855 | + return a.cmpEq(original) == 1 | ||
856 | +} | ||
857 | + | ||
858 | +func TestModSubThenAddIdentity(t *testing.T) { | ||
859 | + err := quick.Check(testModSubThenAddIdentity, &quick.Config{}) | ||
860 | + if err != nil { | ||
861 | + t.Error(err) | ||
862 | + } | ||
863 | +} | ||
864 | + | ||
865 | +func testMontgomeryRoundtrip(a *nat) bool { | ||
866 | + one := &nat{make([]uint, len(a.limbs))} | ||
867 | + one.limbs[0] = 1 | ||
868 | + aPlusOne := a.clone() | ||
869 | + aPlusOne.add(1, one) | ||
870 | + m := modulusFromNat(aPlusOne) | ||
871 | + monty := a.clone() | ||
872 | + monty.montgomeryRepresentation(m) | ||
873 | + aAgain := monty.clone() | ||
874 | + aAgain.montgomeryMul(monty, one, m) | ||
875 | + return a.cmpEq(aAgain) == 1 | ||
876 | +} | ||
877 | + | ||
878 | +func TestMontgomeryRoundtrip(t *testing.T) { | ||
879 | + err := quick.Check(testMontgomeryRoundtrip, &quick.Config{}) | ||
880 | + if err != nil { | ||
881 | + t.Error(err) | ||
882 | + } | ||
883 | +} | ||
884 | + | ||
885 | +func TestFromBig(t *testing.T) { | ||
886 | + expected := []byte{0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} | ||
887 | + theBig := new(big.Int).SetBytes(expected) | ||
888 | + actual := natFromBig(theBig).fillBytes(make([]byte, len(expected))) | ||
889 | + if !bytes.Equal(actual, expected) { | ||
890 | + t.Errorf("%+x != %+x", actual, expected) | ||
891 | + } | ||
892 | +} | ||
893 | + | ||
894 | +func TestFillBytes(t *testing.T) { | ||
895 | + xBytes := []byte{0xAA, 0xFF, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88} | ||
896 | + x := natFromBytes(xBytes) | ||
897 | + for l := 20; l >= len(xBytes); l-- { | ||
898 | + buf := make([]byte, l) | ||
899 | + rand.Read(buf) | ||
900 | + actual := x.fillBytes(buf) | ||
901 | + expected := make([]byte, l) | ||
902 | + copy(expected[l-len(xBytes):], xBytes) | ||
903 | + if !bytes.Equal(actual, expected) { | ||
904 | + t.Errorf("%d: %+v != %+v", l, actual, expected) | ||
905 | + } | ||
906 | + } | ||
907 | + for l := len(xBytes) - 1; l >= 0; l-- { | ||
908 | + (func() { | ||
909 | + defer func() { | ||
910 | + if recover() == nil { | ||
911 | + t.Errorf("%d: expected panic", l) | ||
912 | + } | ||
913 | + }() | ||
914 | + x.fillBytes(make([]byte, l)) | ||
915 | + })() | ||
916 | + } | ||
917 | +} | ||
918 | + | ||
919 | +func TestFromBytes(t *testing.T) { | ||
920 | + f := func(xBytes []byte) bool { | ||
921 | + if len(xBytes) == 0 { | ||
922 | + return true | ||
923 | + } | ||
924 | + actual := natFromBytes(xBytes).fillBytes(make([]byte, len(xBytes))) | ||
925 | + if !bytes.Equal(actual, xBytes) { | ||
926 | + t.Errorf("%+x != %+x", actual, xBytes) | ||
927 | + return false | ||
928 | + } | ||
929 | + return true | ||
930 | + } | ||
931 | + | ||
932 | + err := quick.Check(f, &quick.Config{}) | ||
933 | + if err != nil { | ||
934 | + t.Error(err) | ||
935 | + } | ||
936 | + | ||
937 | + f([]byte{0xFF, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) | ||
938 | + f(bytes.Repeat([]byte{0xFF}, _W)) | ||
939 | +} | ||
940 | + | ||
941 | +func TestShiftIn(t *testing.T) { | ||
942 | + if bits.UintSize != 64 { | ||
943 | + t.Skip("examples are only valid in 64 bit") | ||
944 | + } | ||
945 | + examples := []struct { | ||
946 | + m, x, expected []byte | ||
947 | + y uint64 | ||
948 | + }{{ | ||
949 | + m: []byte{13}, | ||
950 | + x: []byte{0}, | ||
951 | + y: 0x7FFF_FFFF_FFFF_FFFF, | ||
952 | + expected: []byte{7}, | ||
953 | + }, { | ||
954 | + m: []byte{13}, | ||
955 | + x: []byte{7}, | ||
956 | + y: 0x7FFF_FFFF_FFFF_FFFF, | ||
957 | + expected: []byte{11}, | ||
958 | + }, { | ||
959 | + m: []byte{0x06, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d}, | ||
960 | + x: make([]byte, 9), | ||
961 | + y: 0x7FFF_FFFF_FFFF_FFFF, | ||
962 | + expected: []byte{0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, | ||
963 | + }, { | ||
964 | + m: []byte{0x06, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d}, | ||
965 | + x: []byte{0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, | ||
966 | + y: 0, | ||
967 | + expected: []byte{0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, | ||
968 | + }} | ||
969 | + | ||
970 | + for i, tt := range examples { | ||
971 | + m := modulusFromNat(natFromBytes(tt.m)) | ||
972 | + got := natFromBytes(tt.x).expandFor(m).shiftIn(uint(tt.y), m) | ||
973 | + if got.cmpEq(natFromBytes(tt.expected).expandFor(m)) != 1 { | ||
974 | + t.Errorf("%d: got %x, expected %x", i, got, tt.expected) | ||
975 | + } | ||
976 | + } | ||
977 | +} | ||
978 | + | ||
979 | +func TestModulusAndNatSizes(t *testing.T) { | ||
980 | + // These are 126 bit (2 * _W on 64-bit architectures) values, serialized as | ||
981 | + // 128 bits worth of bytes. If leading zeroes are stripped, they fit in two | ||
982 | + // limbs, if they are not, they fit in three. This can be a problem because | ||
983 | + // modulus strips leading zeroes and nat does not. | ||
984 | + m := modulusFromNat(natFromBytes([]byte{ | ||
985 | + 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
986 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})) | ||
987 | + x := natFromBytes([]byte{ | ||
988 | + 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
989 | + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}) | ||
990 | + x.expandFor(m) // must not panic for shrinking | ||
991 | +} | ||
992 | + | ||
993 | +func TestExpand(t *testing.T) { | ||
994 | + sliced := []uint{1, 2, 3, 4} | ||
995 | + examples := []struct { | ||
996 | + in []uint | ||
997 | + n int | ||
998 | + out []uint | ||
999 | + }{{ | ||
1000 | + []uint{1, 2}, | ||
1001 | + 4, | ||
1002 | + []uint{1, 2, 0, 0}, | ||
1003 | + }, { | ||
1004 | + sliced[:2], | ||
1005 | + 4, | ||
1006 | + []uint{1, 2, 0, 0}, | ||
1007 | + }, { | ||
1008 | + []uint{1, 2}, | ||
1009 | + 2, | ||
1010 | + []uint{1, 2}, | ||
1011 | + }, { | ||
1012 | + []uint{1, 2, 0}, | ||
1013 | + 2, | ||
1014 | + []uint{1, 2}, | ||
1015 | + }} | ||
1016 | + | ||
1017 | + for i, tt := range examples { | ||
1018 | + got := (&nat{tt.in}).expand(tt.n) | ||
1019 | + if len(got.limbs) != len(tt.out) || got.cmpEq(&nat{tt.out}) != 1 { | ||
1020 | + t.Errorf("%d: got %x, expected %x", i, got, tt.out) | ||
1021 | + } | ||
1022 | + } | ||
1023 | +} | ||
1024 | + | ||
1025 | +func TestMod(t *testing.T) { | ||
1026 | + m := modulusFromNat(natFromBytes([]byte{0x06, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d})) | ||
1027 | + x := natFromBytes([]byte{0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}) | ||
1028 | + out := new(nat) | ||
1029 | + out.mod(x, m) | ||
1030 | + expected := natFromBytes([]byte{0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09}) | ||
1031 | + if out.cmpEq(expected) != 1 { | ||
1032 | + t.Errorf("%+v != %+v", out, expected) | ||
1033 | + } | ||
1034 | +} | ||
1035 | + | ||
1036 | +func TestModSub(t *testing.T) { | ||
1037 | + m := modulusFromNat(&nat{[]uint{13}}) | ||
1038 | + x := &nat{[]uint{6}} | ||
1039 | + y := &nat{[]uint{7}} | ||
1040 | + x.modSub(y, m) | ||
1041 | + expected := &nat{[]uint{12}} | ||
1042 | + if x.cmpEq(expected) != 1 { | ||
1043 | + t.Errorf("%+v != %+v", x, expected) | ||
1044 | + } | ||
1045 | + x.modSub(y, m) | ||
1046 | + expected = &nat{[]uint{5}} | ||
1047 | + if x.cmpEq(expected) != 1 { | ||
1048 | + t.Errorf("%+v != %+v", x, expected) | ||
1049 | + } | ||
1050 | +} | ||
1051 | + | ||
1052 | +func TestModAdd(t *testing.T) { | ||
1053 | + m := modulusFromNat(&nat{[]uint{13}}) | ||
1054 | + x := &nat{[]uint{6}} | ||
1055 | + y := &nat{[]uint{7}} | ||
1056 | + x.modAdd(y, m) | ||
1057 | + expected := &nat{[]uint{0}} | ||
1058 | + if x.cmpEq(expected) != 1 { | ||
1059 | + t.Errorf("%+v != %+v", x, expected) | ||
1060 | + } | ||
1061 | + x.modAdd(y, m) | ||
1062 | + expected = &nat{[]uint{7}} | ||
1063 | + if x.cmpEq(expected) != 1 { | ||
1064 | + t.Errorf("%+v != %+v", x, expected) | ||
1065 | + } | ||
1066 | +} | ||
1067 | + | ||
1068 | +func TestExp(t *testing.T) { | ||
1069 | + m := modulusFromNat(&nat{[]uint{13}}) | ||
1070 | + x := &nat{[]uint{3}} | ||
1071 | + out := &nat{[]uint{0}} | ||
1072 | + out.exp(x, []byte{12}, m) | ||
1073 | + expected := &nat{[]uint{1}} | ||
1074 | + if out.cmpEq(expected) != 1 { | ||
1075 | + t.Errorf("%+v != %+v", out, expected) | ||
1076 | + } | ||
1077 | +} | ||
1078 | + | ||
1079 | +func makeBenchmarkModulus() *modulus { | ||
1080 | + m := make([]uint, 32) | ||
1081 | + for i := 0; i < 32; i++ { | ||
1082 | + m[i] = _MASK | ||
1083 | + } | ||
1084 | + return modulusFromNat(&nat{limbs: m}) | ||
1085 | +} | ||
1086 | + | ||
1087 | +func makeBenchmarkValue() *nat { | ||
1088 | + x := make([]uint, 32) | ||
1089 | + for i := 0; i < 32; i++ { | ||
1090 | + x[i] = _MASK - 1 | ||
1091 | + } | ||
1092 | + return &nat{limbs: x} | ||
1093 | +} | ||
1094 | + | ||
1095 | +func makeBenchmarkExponent() []byte { | ||
1096 | + e := make([]byte, 256) | ||
1097 | + for i := 0; i < 32; i++ { | ||
1098 | + e[i] = 0xFF | ||
1099 | + } | ||
1100 | + return e | ||
1101 | +} | ||
1102 | + | ||
1103 | +func BenchmarkModAdd(b *testing.B) { | ||
1104 | + x := makeBenchmarkValue() | ||
1105 | + y := makeBenchmarkValue() | ||
1106 | + m := makeBenchmarkModulus() | ||
1107 | + | ||
1108 | + b.ResetTimer() | ||
1109 | + for i := 0; i < b.N; i++ { | ||
1110 | + x.modAdd(y, m) | ||
1111 | + } | ||
1112 | +} | ||
1113 | + | ||
1114 | +func BenchmarkModSub(b *testing.B) { | ||
1115 | + x := makeBenchmarkValue() | ||
1116 | + y := makeBenchmarkValue() | ||
1117 | + m := makeBenchmarkModulus() | ||
1118 | + | ||
1119 | + b.ResetTimer() | ||
1120 | + for i := 0; i < b.N; i++ { | ||
1121 | + x.modSub(y, m) | ||
1122 | + } | ||
1123 | +} | ||
1124 | + | ||
1125 | +func BenchmarkMontgomeryRepr(b *testing.B) { | ||
1126 | + x := makeBenchmarkValue() | ||
1127 | + m := makeBenchmarkModulus() | ||
1128 | + | ||
1129 | + b.ResetTimer() | ||
1130 | + for i := 0; i < b.N; i++ { | ||
1131 | + x.montgomeryRepresentation(m) | ||
1132 | + } | ||
1133 | +} | ||
1134 | + | ||
1135 | +func BenchmarkMontgomeryMul(b *testing.B) { | ||
1136 | + x := makeBenchmarkValue() | ||
1137 | + y := makeBenchmarkValue() | ||
1138 | + out := makeBenchmarkValue() | ||
1139 | + m := makeBenchmarkModulus() | ||
1140 | + | ||
1141 | + b.ResetTimer() | ||
1142 | + for i := 0; i < b.N; i++ { | ||
1143 | + out.montgomeryMul(x, y, m) | ||
1144 | + } | ||
1145 | +} | ||
1146 | + | ||
1147 | +func BenchmarkModMul(b *testing.B) { | ||
1148 | + x := makeBenchmarkValue() | ||
1149 | + y := makeBenchmarkValue() | ||
1150 | + m := makeBenchmarkModulus() | ||
1151 | + | ||
1152 | + b.ResetTimer() | ||
1153 | + for i := 0; i < b.N; i++ { | ||
1154 | + x.modMul(y, m) | ||
1155 | + } | ||
1156 | +} | ||
1157 | + | ||
1158 | +func BenchmarkExpBig(b *testing.B) { | ||
1159 | + out := new(big.Int) | ||
1160 | + exponentBytes := makeBenchmarkExponent() | ||
1161 | + x := new(big.Int).SetBytes(exponentBytes) | ||
1162 | + e := new(big.Int).SetBytes(exponentBytes) | ||
1163 | + n := new(big.Int).SetBytes(exponentBytes) | ||
1164 | + one := new(big.Int).SetUint64(1) | ||
1165 | + n.Add(n, one) | ||
1166 | + | ||
1167 | + b.ResetTimer() | ||
1168 | + for i := 0; i < b.N; i++ { | ||
1169 | + out.Exp(x, e, n) | ||
1170 | + } | ||
1171 | +} | ||
1172 | + | ||
1173 | +func BenchmarkExp(b *testing.B) { | ||
1174 | + x := makeBenchmarkValue() | ||
1175 | + e := makeBenchmarkExponent() | ||
1176 | + out := makeBenchmarkValue() | ||
1177 | + m := makeBenchmarkModulus() | ||
1178 | + | ||
1179 | + b.ResetTimer() | ||
1180 | + for i := 0; i < b.N; i++ { | ||
1181 | + out.exp(x, e, m) | ||
1182 | + } | ||
1183 | +} | ||
1184 | diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go | ||
1185 | index a216be3..ce89f92 100644 | ||
1186 | --- a/src/crypto/rsa/pkcs1v15.go | ||
1187 | +++ b/src/crypto/rsa/pkcs1v15.go | ||
1188 | @@ -9,7 +9,6 @@ import ( | ||
1189 | "crypto/subtle" | ||
1190 | "errors" | ||
1191 | "io" | ||
1192 | - "math/big" | ||
1193 | |||
1194 | "crypto/internal/randutil" | ||
1195 | ) | ||
1196 | @@ -58,14 +57,11 @@ func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error) | ||
1197 | em[len(em)-len(msg)-1] = 0 | ||
1198 | copy(mm, msg) | ||
1199 | |||
1200 | - m := new(big.Int).SetBytes(em) | ||
1201 | - c := encrypt(new(big.Int), pub, m) | ||
1202 | - | ||
1203 | - return c.FillBytes(em), nil | ||
1204 | + return encrypt(pub, em), nil | ||
1205 | } | ||
1206 | |||
1207 | // DecryptPKCS1v15 decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5. | ||
1208 | -// If rand != nil, it uses RSA blinding to avoid timing side-channel attacks. | ||
1209 | +// The rand parameter is legacy and ignored, and it can be as nil. | ||
1210 | // | ||
1211 | // Note that whether this function returns an error or not discloses secret | ||
1212 | // information. If an attacker can cause this function to run repeatedly and | ||
1213 | @@ -76,7 +72,7 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) ([]byt | ||
1214 | if err := checkPub(&priv.PublicKey); err != nil { | ||
1215 | return nil, err | ||
1216 | } | ||
1217 | - valid, out, index, err := decryptPKCS1v15(rand, priv, ciphertext) | ||
1218 | + valid, out, index, err := decryptPKCS1v15(priv, ciphertext) | ||
1219 | if err != nil { | ||
1220 | return nil, err | ||
1221 | } | ||
1222 | @@ -87,7 +83,7 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) ([]byt | ||
1223 | } | ||
1224 | |||
1225 | // DecryptPKCS1v15SessionKey decrypts a session key using RSA and the padding scheme from PKCS#1 v1.5. | ||
1226 | -// If rand != nil, it uses RSA blinding to avoid timing side-channel attacks. | ||
1227 | +// The rand parameter is legacy and ignored, and it can be as nil. | ||
1228 | // It returns an error if the ciphertext is the wrong length or if the | ||
1229 | // ciphertext is greater than the public modulus. Otherwise, no error is | ||
1230 | // returned. If the padding is valid, the resulting plaintext message is copied | ||
1231 | @@ -114,7 +110,7 @@ func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []by | ||
1232 | return ErrDecryption | ||
1233 | } | ||
1234 | |||
1235 | - valid, em, index, err := decryptPKCS1v15(rand, priv, ciphertext) | ||
1236 | + valid, em, index, err := decryptPKCS1v15(priv, ciphertext) | ||
1237 | if err != nil { | ||
1238 | return err | ||
1239 | } | ||
1240 | @@ -130,26 +126,24 @@ func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []by | ||
1241 | return nil | ||
1242 | } | ||
1243 | |||
1244 | -// decryptPKCS1v15 decrypts ciphertext using priv and blinds the operation if | ||
1245 | -// rand is not nil. It returns one or zero in valid that indicates whether the | ||
1246 | -// plaintext was correctly structured. In either case, the plaintext is | ||
1247 | -// returned in em so that it may be read independently of whether it was valid | ||
1248 | -// in order to maintain constant memory access patterns. If the plaintext was | ||
1249 | -// valid then index contains the index of the original message in em. | ||
1250 | -func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, em []byte, index int, err error) { | ||
1251 | +// decryptPKCS1v15 decrypts ciphertext using priv. It returns one or zero in | ||
1252 | +// valid that indicates whether the plaintext was correctly structured. | ||
1253 | +// In either case, the plaintext is returned in em so that it may be read | ||
1254 | +// independently of whether it was valid in order to maintain constant memory | ||
1255 | +// access patterns. If the plaintext was valid then index contains the index of | ||
1256 | +// the original message in em, to allow constant time padding removal. | ||
1257 | +func decryptPKCS1v15(priv *PrivateKey, ciphertext []byte) (valid int, em []byte, index int, err error) { | ||
1258 | k := priv.Size() | ||
1259 | if k < 11 { | ||
1260 | err = ErrDecryption | ||
1261 | return | ||
1262 | } | ||
1263 | |||
1264 | - c := new(big.Int).SetBytes(ciphertext) | ||
1265 | - m, err := decrypt(rand, priv, c) | ||
1266 | + em, err = decrypt(priv, ciphertext) | ||
1267 | if err != nil { | ||
1268 | return | ||
1269 | } | ||
1270 | |||
1271 | - em = m.FillBytes(make([]byte, k)) | ||
1272 | firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0) | ||
1273 | secondByteIsTwo := subtle.ConstantTimeByteEq(em[1], 2) | ||
1274 | |||
1275 | @@ -221,8 +215,7 @@ var hashPrefixes = map[crypto.Hash][]byte{ | ||
1276 | // function. If hash is zero, hashed is signed directly. This isn't | ||
1277 | // advisable except for interoperability. | ||
1278 | // | ||
1279 | -// If rand is not nil then RSA blinding will be used to avoid timing | ||
1280 | -// side-channel attacks. | ||
1281 | +// The rand parameter is legacy and ignored, and it can be as nil. | ||
1282 | // | ||
1283 | // This function is deterministic. Thus, if the set of possible | ||
1284 | // messages is small, an attacker may be able to build a map from | ||
1285 | @@ -249,13 +242,7 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b | ||
1286 | copy(em[k-tLen:k-hashLen], prefix) | ||
1287 | copy(em[k-hashLen:k], hashed) | ||
1288 | |||
1289 | - m := new(big.Int).SetBytes(em) | ||
1290 | - c, err := decryptAndCheck(rand, priv, m) | ||
1291 | - if err != nil { | ||
1292 | - return nil, err | ||
1293 | - } | ||
1294 | - | ||
1295 | - return c.FillBytes(em), nil | ||
1296 | + return decryptAndCheck(priv, em) | ||
1297 | } | ||
1298 | |||
1299 | // VerifyPKCS1v15 verifies an RSA PKCS#1 v1.5 signature. | ||
1300 | @@ -275,9 +262,7 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) | ||
1301 | return ErrVerification | ||
1302 | } | ||
1303 | |||
1304 | - c := new(big.Int).SetBytes(sig) | ||
1305 | - m := encrypt(new(big.Int), pub, c) | ||
1306 | - em := m.FillBytes(make([]byte, k)) | ||
1307 | + em := encrypt(pub, sig) | ||
1308 | // EM = 0x00 || 0x01 || PS || 0x00 || T | ||
1309 | |||
1310 | ok := subtle.ConstantTimeByteEq(em[0], 0) | ||
1311 | diff --git a/src/crypto/rsa/pss.go b/src/crypto/rsa/pss.go | ||
1312 | index 814522d..eaba4be 100644 | ||
1313 | --- a/src/crypto/rsa/pss.go | ||
1314 | +++ b/src/crypto/rsa/pss.go | ||
1315 | @@ -12,7 +12,6 @@ import ( | ||
1316 | "errors" | ||
1317 | "hash" | ||
1318 | "io" | ||
1319 | - "math/big" | ||
1320 | ) | ||
1321 | |||
1322 | // Per RFC 8017, Section 9.1 | ||
1323 | @@ -207,19 +206,27 @@ func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error { | ||
1324 | // Note that hashed must be the result of hashing the input message using the | ||
1325 | // given hash function. salt is a random sequence of bytes whose length will be | ||
1326 | // later used to verify the signature. | ||
1327 | -func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) ([]byte, error) { | ||
1328 | - emBits := priv.N.BitLen() - 1 | ||
1329 | +func signPSSWithSalt(priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) ([]byte, error) { | ||
1330 | + emBits := bigBitLen(priv.N) - 1 | ||
1331 | em, err := emsaPSSEncode(hashed, emBits, salt, hash.New()) | ||
1332 | if err != nil { | ||
1333 | return nil, err | ||
1334 | } | ||
1335 | - m := new(big.Int).SetBytes(em) | ||
1336 | - c, err := decryptAndCheck(rand, priv, m) | ||
1337 | - if err != nil { | ||
1338 | - return nil, err | ||
1339 | + | ||
1340 | + // RFC 8017: "Note that the octet length of EM will be one less than k if | ||
1341 | + // modBits - 1 is divisible by 8 and equal to k otherwise, where k is the | ||
1342 | + // length in octets of the RSA modulus n." | ||
1343 | + // | ||
1344 | + // This is extremely annoying, as all other encrypt and decrypt inputs are | ||
1345 | + // always the exact same size as the modulus. Since it only happens for | ||
1346 | + // weird modulus sizes, fix it by padding inefficiently. | ||
1347 | + if emLen, k := len(em), priv.Size(); emLen < k { | ||
1348 | + emNew := make([]byte, k) | ||
1349 | + copy(emNew[k-emLen:], em) | ||
1350 | + em = emNew | ||
1351 | } | ||
1352 | - s := make([]byte, priv.Size()) | ||
1353 | - return c.FillBytes(s), nil | ||
1354 | + | ||
1355 | + return decryptAndCheck(priv, em) | ||
1356 | } | ||
1357 | |||
1358 | const ( | ||
1359 | @@ -269,7 +276,7 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, | ||
1360 | saltLength := opts.saltLength() | ||
1361 | switch saltLength { | ||
1362 | case PSSSaltLengthAuto: | ||
1363 | - saltLength = (priv.N.BitLen()-1+7)/8 - 2 - hash.Size() | ||
1364 | + saltLength = (bigBitLen(priv.N)-1+7)/8 - 2 - hash.Size() | ||
1365 | case PSSSaltLengthEqualsHash: | ||
1366 | saltLength = hash.Size() | ||
1367 | } | ||
1368 | @@ -278,7 +285,7 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, | ||
1369 | if _, err := io.ReadFull(rand, salt); err != nil { | ||
1370 | return nil, err | ||
1371 | } | ||
1372 | - return signPSSWithSalt(rand, priv, hash, digest, salt) | ||
1373 | + return signPSSWithSalt(priv, hash, digest, salt) | ||
1374 | } | ||
1375 | |||
1376 | // VerifyPSS verifies a PSS signature. | ||
1377 | @@ -291,13 +298,22 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts | ||
1378 | if len(sig) != pub.Size() { | ||
1379 | return ErrVerification | ||
1380 | } | ||
1381 | - s := new(big.Int).SetBytes(sig) | ||
1382 | - m := encrypt(new(big.Int), pub, s) | ||
1383 | - emBits := pub.N.BitLen() - 1 | ||
1384 | + | ||
1385 | + emBits := bigBitLen(pub.N) - 1 | ||
1386 | emLen := (emBits + 7) / 8 | ||
1387 | - if m.BitLen() > emLen*8 { | ||
1388 | - return ErrVerification | ||
1389 | + em := encrypt(pub, sig) | ||
1390 | + | ||
1391 | + // Like in signPSSWithSalt, deal with mismatches between emLen and the size | ||
1392 | + // of the modulus. The spec would have us wire emLen into the encoding | ||
1393 | + // function, but we'd rather always encode to the size of the modulus and | ||
1394 | + // then strip leading zeroes if necessary. This only happens for weird | ||
1395 | + // modulus sizes anyway. | ||
1396 | + for len(em) > emLen && len(em) > 0 { | ||
1397 | + if em[0] != 0 { | ||
1398 | + return ErrVerification | ||
1399 | + } | ||
1400 | + em = em[1:] | ||
1401 | } | ||
1402 | - em := m.FillBytes(make([]byte, emLen)) | ||
1403 | + | ||
1404 | return emsaPSSVerify(digest, em, emBits, opts.saltLength(), hash.New()) | ||
1405 | } | ||
1406 | diff --git a/src/crypto/rsa/pss_test.go b/src/crypto/rsa/pss_test.go | ||
1407 | index c3a6d46..d018b43 100644 | ||
1408 | --- a/src/crypto/rsa/pss_test.go | ||
1409 | +++ b/src/crypto/rsa/pss_test.go | ||
1410 | @@ -233,7 +233,10 @@ func TestPSSSigning(t *testing.T) { | ||
1411 | } | ||
1412 | } | ||
1413 | |||
1414 | -func TestSignWithPSSSaltLengthAuto(t *testing.T) { | ||
1415 | +func TestPSS513(t *testing.T) { | ||
1416 | + // See Issue 42741, and separately, RFC 8017: "Note that the octet length of | ||
1417 | + // EM will be one less than k if modBits - 1 is divisible by 8 and equal to | ||
1418 | + // k otherwise, where k is the length in octets of the RSA modulus n." | ||
1419 | key, err := GenerateKey(rand.Reader, 513) | ||
1420 | if err != nil { | ||
1421 | t.Fatal(err) | ||
1422 | @@ -246,8 +249,9 @@ func TestSignWithPSSSaltLengthAuto(t *testing.T) { | ||
1423 | if err != nil { | ||
1424 | t.Fatal(err) | ||
1425 | } | ||
1426 | - if len(signature) == 0 { | ||
1427 | - t.Fatal("empty signature returned") | ||
1428 | + err = VerifyPSS(&key.PublicKey, crypto.SHA256, digest[:], signature, nil) | ||
1429 | + if err != nil { | ||
1430 | + t.Error(err) | ||
1431 | } | ||
1432 | } | ||
1433 | |||
1434 | diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go | ||
1435 | index 5a00ed2..29d9d31 100644 | ||
1436 | --- a/src/crypto/rsa/rsa.go | ||
1437 | +++ b/src/crypto/rsa/rsa.go | ||
1438 | @@ -19,13 +19,17 @@ | ||
1439 | // over the public key primitive, the PrivateKey type implements the | ||
1440 | // Decrypter and Signer interfaces from the crypto package. | ||
1441 | // | ||
1442 | -// The RSA operations in this package are not implemented using constant-time algorithms. | ||
1443 | +// Operations in this package are implemented using constant-time algorithms, | ||
1444 | +// except for [GenerateKey], [PrivateKey.Precompute], and [PrivateKey.Validate]. | ||
1445 | +// Every other operation only leaks the bit size of the involved values, which | ||
1446 | +// all depend on the selected key size. | ||
1447 | package rsa | ||
1448 | |||
1449 | import ( | ||
1450 | "crypto" | ||
1451 | "crypto/rand" | ||
1452 | "crypto/subtle" | ||
1453 | + "encoding/binary" | ||
1454 | "errors" | ||
1455 | "hash" | ||
1456 | "io" | ||
1457 | @@ -35,7 +39,6 @@ import ( | ||
1458 | "crypto/internal/randutil" | ||
1459 | ) | ||
1460 | |||
1461 | -var bigZero = big.NewInt(0) | ||
1462 | var bigOne = big.NewInt(1) | ||
1463 | |||
1464 | // A PublicKey represents the public part of an RSA key. | ||
1465 | @@ -47,7 +50,7 @@ type PublicKey struct { | ||
1466 | // Size returns the modulus size in bytes. Raw signatures and ciphertexts | ||
1467 | // for or by this public key will have the same size. | ||
1468 | func (pub *PublicKey) Size() int { | ||
1469 | - return (pub.N.BitLen() + 7) / 8 | ||
1470 | + return (bigBitLen(pub.N) + 7) / 8 | ||
1471 | } | ||
1472 | |||
1473 | // OAEPOptions is an interface for passing options to OAEP decryption using the | ||
1474 | @@ -351,10 +354,19 @@ func mgf1XOR(out []byte, hash hash.Hash, seed []byte) { | ||
1475 | // too large for the size of the public key. | ||
1476 | var ErrMessageTooLong = errors.New("crypto/rsa: message too long for RSA public key size") | ||
1477 | |||
1478 | -func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int { | ||
1479 | - e := big.NewInt(int64(pub.E)) | ||
1480 | - c.Exp(m, e, pub.N) | ||
1481 | - return c | ||
1482 | +func encrypt(pub *PublicKey, plaintext []byte) []byte { | ||
1483 | + | ||
1484 | + N := modulusFromNat(natFromBig(pub.N)) | ||
1485 | + m := natFromBytes(plaintext).expandFor(N) | ||
1486 | + | ||
1487 | + e := make([]byte, 8) | ||
1488 | + binary.BigEndian.PutUint64(e, uint64(pub.E)) | ||
1489 | + for len(e) > 1 && e[0] == 0 { | ||
1490 | + e = e[1:] | ||
1491 | + } | ||
1492 | + | ||
1493 | + out := make([]byte, modulusSize(N)) | ||
1494 | + return new(nat).exp(m, e, N).fillBytes(out) | ||
1495 | } | ||
1496 | |||
1497 | // EncryptOAEP encrypts the given message with RSA-OAEP. | ||
1498 | @@ -404,12 +416,7 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l | ||
1499 | mgf1XOR(db, hash, seed) | ||
1500 | mgf1XOR(seed, hash, db) | ||
1501 | |||
1502 | - m := new(big.Int) | ||
1503 | - m.SetBytes(em) | ||
1504 | - c := encrypt(new(big.Int), pub, m) | ||
1505 | - | ||
1506 | - out := make([]byte, k) | ||
1507 | - return c.FillBytes(out), nil | ||
1508 | + return encrypt(pub, em), nil | ||
1509 | } | ||
1510 | |||
1511 | // ErrDecryption represents a failure to decrypt a message. | ||
1512 | @@ -451,98 +458,71 @@ func (priv *PrivateKey) Precompute() { | ||
1513 | } | ||
1514 | } | ||
1515 | |||
1516 | -// decrypt performs an RSA decryption, resulting in a plaintext integer. If a | ||
1517 | -// random source is given, RSA blinding is used. | ||
1518 | -func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err error) { | ||
1519 | - // TODO(agl): can we get away with reusing blinds? | ||
1520 | - if c.Cmp(priv.N) > 0 { | ||
1521 | - err = ErrDecryption | ||
1522 | - return | ||
1523 | +// decrypt performs an RSA decryption of ciphertext into out. | ||
1524 | +func decrypt(priv *PrivateKey, ciphertext []byte) ([]byte, error) { | ||
1525 | + | ||
1526 | + N := modulusFromNat(natFromBig(priv.N)) | ||
1527 | + c := natFromBytes(ciphertext).expandFor(N) | ||
1528 | + if c.cmpGeq(N.nat) == 1 { | ||
1529 | + return nil, ErrDecryption | ||
1530 | } | ||
1531 | if priv.N.Sign() == 0 { | ||
1532 | return nil, ErrDecryption | ||
1533 | } | ||
1534 | |||
1535 | - var ir *big.Int | ||
1536 | - if random != nil { | ||
1537 | - randutil.MaybeReadByte(random) | ||
1538 | - | ||
1539 | - // Blinding enabled. Blinding involves multiplying c by r^e. | ||
1540 | - // Then the decryption operation performs (m^e * r^e)^d mod n | ||
1541 | - // which equals mr mod n. The factor of r can then be removed | ||
1542 | - // by multiplying by the multiplicative inverse of r. | ||
1543 | - | ||
1544 | - var r *big.Int | ||
1545 | - ir = new(big.Int) | ||
1546 | - for { | ||
1547 | - r, err = rand.Int(random, priv.N) | ||
1548 | - if err != nil { | ||
1549 | - return | ||
1550 | - } | ||
1551 | - if r.Cmp(bigZero) == 0 { | ||
1552 | - r = bigOne | ||
1553 | - } | ||
1554 | - ok := ir.ModInverse(r, priv.N) | ||
1555 | - if ok != nil { | ||
1556 | - break | ||
1557 | - } | ||
1558 | - } | ||
1559 | - bigE := big.NewInt(int64(priv.E)) | ||
1560 | - rpowe := new(big.Int).Exp(r, bigE, priv.N) // N != 0 | ||
1561 | - cCopy := new(big.Int).Set(c) | ||
1562 | - cCopy.Mul(cCopy, rpowe) | ||
1563 | - cCopy.Mod(cCopy, priv.N) | ||
1564 | - c = cCopy | ||
1565 | - } | ||
1566 | - | ||
1567 | + // Note that because our private decryption exponents are stored as big.Int, | ||
1568 | + // we potentially leak the exact number of bits of these exponents. This | ||
1569 | + // isn't great, but should be fine. | ||
1570 | if priv.Precomputed.Dp == nil { | ||
1571 | - m = new(big.Int).Exp(c, priv.D, priv.N) | ||
1572 | - } else { | ||
1573 | - // We have the precalculated values needed for the CRT. | ||
1574 | - m = new(big.Int).Exp(c, priv.Precomputed.Dp, priv.Primes[0]) | ||
1575 | - m2 := new(big.Int).Exp(c, priv.Precomputed.Dq, priv.Primes[1]) | ||
1576 | - m.Sub(m, m2) | ||
1577 | - if m.Sign() < 0 { | ||
1578 | - m.Add(m, priv.Primes[0]) | ||
1579 | - } | ||
1580 | - m.Mul(m, priv.Precomputed.Qinv) | ||
1581 | - m.Mod(m, priv.Primes[0]) | ||
1582 | - m.Mul(m, priv.Primes[1]) | ||
1583 | - m.Add(m, m2) | ||
1584 | - | ||
1585 | - for i, values := range priv.Precomputed.CRTValues { | ||
1586 | - prime := priv.Primes[2+i] | ||
1587 | - m2.Exp(c, values.Exp, prime) | ||
1588 | - m2.Sub(m2, m) | ||
1589 | - m2.Mul(m2, values.Coeff) | ||
1590 | - m2.Mod(m2, prime) | ||
1591 | - if m2.Sign() < 0 { | ||
1592 | - m2.Add(m2, prime) | ||
1593 | - } | ||
1594 | - m2.Mul(m2, values.R) | ||
1595 | - m.Add(m, m2) | ||
1596 | - } | ||
1597 | - } | ||
1598 | - | ||
1599 | - if ir != nil { | ||
1600 | - // Unblind. | ||
1601 | - m.Mul(m, ir) | ||
1602 | - m.Mod(m, priv.N) | ||
1603 | - } | ||
1604 | - | ||
1605 | - return | ||
1606 | + out := make([]byte, modulusSize(N)) | ||
1607 | + return new(nat).exp(c, priv.D.Bytes(), N).fillBytes(out), nil | ||
1608 | + } | ||
1609 | + | ||
1610 | + t0 := new(nat) | ||
1611 | + P := modulusFromNat(natFromBig(priv.Primes[0])) | ||
1612 | + Q := modulusFromNat(natFromBig(priv.Primes[1])) | ||
1613 | + // m = c ^ Dp mod p | ||
1614 | + m := new(nat).exp(t0.mod(c, P), priv.Precomputed.Dp.Bytes(), P) | ||
1615 | + // m2 = c ^ Dq mod q | ||
1616 | + m2 := new(nat).exp(t0.mod(c, Q), priv.Precomputed.Dq.Bytes(), Q) | ||
1617 | + // m = m - m2 mod p | ||
1618 | + m.modSub(t0.mod(m2, P), P) | ||
1619 | + // m = m * Qinv mod p | ||
1620 | + m.modMul(natFromBig(priv.Precomputed.Qinv).expandFor(P), P) | ||
1621 | + // m = m * q mod N | ||
1622 | + m.expandFor(N).modMul(t0.mod(Q.nat, N), N) | ||
1623 | + // m = m + m2 mod N | ||
1624 | + m.modAdd(m2.expandFor(N), N) | ||
1625 | + | ||
1626 | + for i, values := range priv.Precomputed.CRTValues { | ||
1627 | + p := modulusFromNat(natFromBig(priv.Primes[2+i])) | ||
1628 | + // m2 = c ^ Exp mod p | ||
1629 | + m2.exp(t0.mod(c, p), values.Exp.Bytes(), p) | ||
1630 | + // m2 = m2 - m mod p | ||
1631 | + m2.modSub(t0.mod(m, p), p) | ||
1632 | + // m2 = m2 * Coeff mod p | ||
1633 | + m2.modMul(natFromBig(values.Coeff).expandFor(p), p) | ||
1634 | + // m2 = m2 * R mod N | ||
1635 | + R := natFromBig(values.R).expandFor(N) | ||
1636 | + m2.expandFor(N).modMul(R, N) | ||
1637 | + // m = m + m2 mod N | ||
1638 | + m.modAdd(m2, N) | ||
1639 | + } | ||
1640 | + | ||
1641 | + out := make([]byte, modulusSize(N)) | ||
1642 | + return m.fillBytes(out), nil | ||
1643 | } | ||
1644 | |||
1645 | -func decryptAndCheck(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err error) { | ||
1646 | - m, err = decrypt(random, priv, c) | ||
1647 | +func decryptAndCheck(priv *PrivateKey, ciphertext []byte) (m []byte, err error) { | ||
1648 | + m, err = decrypt(priv, ciphertext) | ||
1649 | if err != nil { | ||
1650 | return nil, err | ||
1651 | } | ||
1652 | |||
1653 | // In order to defend against errors in the CRT computation, m^e is | ||
1654 | // calculated, which should match the original ciphertext. | ||
1655 | - check := encrypt(new(big.Int), &priv.PublicKey, m) | ||
1656 | - if c.Cmp(check) != 0 { | ||
1657 | + check := encrypt(&priv.PublicKey, m) | ||
1658 | + if subtle.ConstantTimeCompare(ciphertext, check) != 1 { | ||
1659 | return nil, errors.New("rsa: internal error") | ||
1660 | } | ||
1661 | return m, nil | ||
1662 | @@ -554,9 +534,7 @@ func decryptAndCheck(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int | ||
1663 | // Encryption and decryption of a given message must use the same hash function | ||
1664 | // and sha256.New() is a reasonable choice. | ||
1665 | // | ||
1666 | -// The random parameter, if not nil, is used to blind the private-key operation | ||
1667 | -// and avoid timing side-channel attacks. Blinding is purely internal to this | ||
1668 | -// function – the random data need not match that used when encrypting. | ||
1669 | +// The random parameter is legacy and ignored, and it can be as nil. | ||
1670 | // | ||
1671 | // The label parameter must match the value given when encrypting. See | ||
1672 | // EncryptOAEP for details. | ||
1673 | @@ -570,9 +548,7 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext | ||
1674 | return nil, ErrDecryption | ||
1675 | } | ||
1676 | |||
1677 | - c := new(big.Int).SetBytes(ciphertext) | ||
1678 | - | ||
1679 | - m, err := decrypt(random, priv, c) | ||
1680 | + em, err := decrypt(priv, ciphertext) | ||
1681 | if err != nil { | ||
1682 | return nil, err | ||
1683 | } | ||
1684 | @@ -581,10 +557,6 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext | ||
1685 | lHash := hash.Sum(nil) | ||
1686 | hash.Reset() | ||
1687 | |||
1688 | - // We probably leak the number of leading zeros. | ||
1689 | - // It's not clear that we can do anything about this. | ||
1690 | - em := m.FillBytes(make([]byte, k)) | ||
1691 | - | ||
1692 | firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0) | ||
1693 | |||
1694 | seed := em[1 : hash.Size()+1] | ||
1695 | -- | ||
1696 | 2.25.1 | ||
1697 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-45289.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-45289.patch new file mode 100644 index 0000000000..13d3510504 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-45289.patch | |||
@@ -0,0 +1,121 @@ | |||
1 | From 20586c0dbe03d144f914155f879fa5ee287591a1 Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Thu, 11 Jan 2024 11:31:57 -0800 | ||
4 | Subject: [PATCH] [release-branch.go1.21] net/http, net/http/cookiejar: avoid | ||
5 | subdomain matches on IPv6 zones | ||
6 | |||
7 | When deciding whether to forward cookies or sensitive headers | ||
8 | across a redirect, do not attempt to interpret an IPv6 address | ||
9 | as a domain name. | ||
10 | |||
11 | Avoids a case where a maliciously-crafted redirect to an | ||
12 | IPv6 address with a scoped addressing zone could be | ||
13 | misinterpreted as a within-domain redirect. For example, | ||
14 | we could interpret "::1%.www.example.com" as a subdomain | ||
15 | of "www.example.com". | ||
16 | |||
17 | Thanks to Juho Nurminen of Mattermost for reporting this issue. | ||
18 | |||
19 | Fixes CVE-2023-45289 | ||
20 | Fixes #65385 | ||
21 | For #65065 | ||
22 | |||
23 | Change-Id: I8f463f59f0e700c8a18733d2b264a8bcb3a19599 | ||
24 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2131938 | ||
25 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
26 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
27 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2173775 | ||
28 | Reviewed-by: Carlos Amedee <amedee@google.com> | ||
29 | Reviewed-on: https://go-review.googlesource.com/c/go/+/569239 | ||
30 | Reviewed-by: Carlos Amedee <carlos@golang.org> | ||
31 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
32 | TryBot-Bypass: Michael Knyszek <mknyszek@google.com> | ||
33 | |||
34 | Upstream-Status: Backport [https://github.com/golang/go/commit/20586c0dbe03d144f914155f879fa5ee287591a1] | ||
35 | CVE: CVE-2023-45289 | ||
36 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
37 | --- | ||
38 | src/net/http/client.go | 6 ++++++ | ||
39 | src/net/http/client_test.go | 1 + | ||
40 | src/net/http/cookiejar/jar.go | 7 +++++++ | ||
41 | src/net/http/cookiejar/jar_test.go | 10 ++++++++++ | ||
42 | 4 files changed, 24 insertions(+) | ||
43 | |||
44 | diff --git a/src/net/http/client.go b/src/net/http/client.go | ||
45 | index a496f1c..2031834 100644 | ||
46 | --- a/src/net/http/client.go | ||
47 | +++ b/src/net/http/client.go | ||
48 | @@ -973,6 +973,12 @@ func isDomainOrSubdomain(sub, parent string) bool { | ||
49 | if sub == parent { | ||
50 | return true | ||
51 | } | ||
52 | + // If sub contains a :, it's probably an IPv6 address (and is definitely not a hostname). | ||
53 | + // Don't check the suffix in this case, to avoid matching the contents of a IPv6 zone. | ||
54 | + // For example, "::1%.www.example.com" is not a subdomain of "www.example.com". | ||
55 | + if strings.ContainsAny(sub, ":%") { | ||
56 | + return false | ||
57 | + } | ||
58 | // If sub is "foo.example.com" and parent is "example.com", | ||
59 | // that means sub must end in "."+parent. | ||
60 | // Do it without allocating. | ||
61 | diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go | ||
62 | index 2b4f53f..442fe35 100644 | ||
63 | --- a/src/net/http/client_test.go | ||
64 | +++ b/src/net/http/client_test.go | ||
65 | @@ -1703,6 +1703,7 @@ func TestShouldCopyHeaderOnRedirect(t *testing.T) { | ||
66 | {"cookie2", "http://foo.com/", "http://bar.com/", false}, | ||
67 | {"authorization", "http://foo.com/", "http://bar.com/", false}, | ||
68 | {"www-authenticate", "http://foo.com/", "http://bar.com/", false}, | ||
69 | + {"authorization", "http://foo.com/", "http://[::1%25.foo.com]/", false}, | ||
70 | |||
71 | // But subdomains should work: | ||
72 | {"www-authenticate", "http://foo.com/", "http://foo.com/", true}, | ||
73 | diff --git a/src/net/http/cookiejar/jar.go b/src/net/http/cookiejar/jar.go | ||
74 | index 9f19917..18cbfc2 100644 | ||
75 | --- a/src/net/http/cookiejar/jar.go | ||
76 | +++ b/src/net/http/cookiejar/jar.go | ||
77 | @@ -356,6 +356,13 @@ func jarKey(host string, psl PublicSuffixList) string { | ||
78 | |||
79 | // isIP reports whether host is an IP address. | ||
80 | func isIP(host string) bool { | ||
81 | + if strings.ContainsAny(host, ":%") { | ||
82 | + // Probable IPv6 address. | ||
83 | + // Hostnames can't contain : or %, so this is definitely not a valid host. | ||
84 | + // Treating it as an IP is the more conservative option, and avoids the risk | ||
85 | + // of interpeting ::1%.www.example.com as a subtomain of www.example.com. | ||
86 | + return true | ||
87 | + } | ||
88 | return net.ParseIP(host) != nil | ||
89 | } | ||
90 | |||
91 | diff --git a/src/net/http/cookiejar/jar_test.go b/src/net/http/cookiejar/jar_test.go | ||
92 | index 47fb1ab..fd8d40e 100644 | ||
93 | --- a/src/net/http/cookiejar/jar_test.go | ||
94 | +++ b/src/net/http/cookiejar/jar_test.go | ||
95 | @@ -251,6 +251,7 @@ var isIPTests = map[string]bool{ | ||
96 | "127.0.0.1": true, | ||
97 | "1.2.3.4": true, | ||
98 | "2001:4860:0:2001::68": true, | ||
99 | + "::1%zone": true, | ||
100 | "example.com": false, | ||
101 | "1.1.1.300": false, | ||
102 | "www.foo.bar.net": false, | ||
103 | @@ -613,6 +614,15 @@ var basicsTests = [...]jarTest{ | ||
104 | {"http://www.host.test:1234/", "a=1"}, | ||
105 | }, | ||
106 | }, | ||
107 | + { | ||
108 | + "IPv6 zone is not treated as a host.", | ||
109 | + "https://example.com/", | ||
110 | + []string{"a=1"}, | ||
111 | + "a=1", | ||
112 | + []query{ | ||
113 | + {"https://[::1%25.example.com]:80/", ""}, | ||
114 | + }, | ||
115 | + }, | ||
116 | } | ||
117 | |||
118 | func TestBasics(t *testing.T) { | ||
119 | -- | ||
120 | 2.25.1 | ||
121 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-45290.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-45290.patch new file mode 100644 index 0000000000..ddc2f67c96 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-45290.patch | |||
@@ -0,0 +1,271 @@ | |||
1 | From bf80213b121074f4ad9b449410a4d13bae5e9be0 Mon Sep 17 00:00:00 2001 | ||
2 | From: Damien Neil <dneil@google.com> | ||
3 | Date: Tue, 16 Jan 2024 15:37:52 -0800 | ||
4 | Subject: [PATCH] [release-branch.go1.21] net/textproto, mime/multipart: avoid | ||
5 | unbounded read in MIME header | ||
6 | |||
7 | mime/multipart.Reader.ReadForm allows specifying the maximum amount | ||
8 | of memory that will be consumed by the form. While this limit is | ||
9 | correctly applied to the parsed form data structure, it was not | ||
10 | being applied to individual header lines in a form. | ||
11 | |||
12 | For example, when presented with a form containing a header line | ||
13 | that never ends, ReadForm will continue to read the line until it | ||
14 | runs out of memory. | ||
15 | |||
16 | Limit the amount of data consumed when reading a header. | ||
17 | |||
18 | Fixes CVE-2023-45290 | ||
19 | Fixes #65389 | ||
20 | For #65383 | ||
21 | |||
22 | Change-Id: I7f9264d25752009e95f6b2c80e3d76aaf321d658 | ||
23 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2134435 | ||
24 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
25 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
26 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2173776 | ||
27 | Reviewed-by: Carlos Amedee <amedee@google.com> | ||
28 | Reviewed-on: https://go-review.googlesource.com/c/go/+/569240 | ||
29 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
30 | LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> | ||
31 | Reviewed-by: Carlos Amedee <carlos@golang.org> | ||
32 | |||
33 | Upstream-Status: Backport [https://github.com/golang/go/commit/bf80213b121074f4ad9b449410a4d13bae5e9be0] | ||
34 | CVE: CVE-2023-45290 | ||
35 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
36 | --- | ||
37 | src/mime/multipart/formdata_test.go | 42 +++++++++++++++++++++++++ | ||
38 | src/net/textproto/reader.go | 48 ++++++++++++++++++++--------- | ||
39 | src/net/textproto/reader_test.go | 12 ++++++++ | ||
40 | 3 files changed, 87 insertions(+), 15 deletions(-) | ||
41 | |||
42 | diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go | ||
43 | index c78eeb7..f729da6 100644 | ||
44 | --- a/src/mime/multipart/formdata_test.go | ||
45 | +++ b/src/mime/multipart/formdata_test.go | ||
46 | @@ -421,6 +421,48 @@ func TestReadFormLimits(t *testing.T) { | ||
47 | } | ||
48 | } | ||
49 | |||
50 | +func TestReadFormEndlessHeaderLine(t *testing.T) { | ||
51 | + for _, test := range []struct { | ||
52 | + name string | ||
53 | + prefix string | ||
54 | + }{{ | ||
55 | + name: "name", | ||
56 | + prefix: "X-", | ||
57 | + }, { | ||
58 | + name: "value", | ||
59 | + prefix: "X-Header: ", | ||
60 | + }, { | ||
61 | + name: "continuation", | ||
62 | + prefix: "X-Header: foo\r\n ", | ||
63 | + }} { | ||
64 | + t.Run(test.name, func(t *testing.T) { | ||
65 | + const eol = "\r\n" | ||
66 | + s := `--boundary` + eol | ||
67 | + s += `Content-Disposition: form-data; name="a"` + eol | ||
68 | + s += `Content-Type: text/plain` + eol | ||
69 | + s += test.prefix | ||
70 | + fr := io.MultiReader( | ||
71 | + strings.NewReader(s), | ||
72 | + neverendingReader('X'), | ||
73 | + ) | ||
74 | + r := NewReader(fr, "boundary") | ||
75 | + _, err := r.ReadForm(1 << 20) | ||
76 | + if err != ErrMessageTooLarge { | ||
77 | + t.Fatalf("ReadForm(1 << 20): %v, want ErrMessageTooLarge", err) | ||
78 | + } | ||
79 | + }) | ||
80 | + } | ||
81 | +} | ||
82 | + | ||
83 | +type neverendingReader byte | ||
84 | + | ||
85 | +func (r neverendingReader) Read(p []byte) (n int, err error) { | ||
86 | + for i := range p { | ||
87 | + p[i] = byte(r) | ||
88 | + } | ||
89 | + return len(p), nil | ||
90 | +} | ||
91 | + | ||
92 | func BenchmarkReadForm(b *testing.B) { | ||
93 | for _, test := range []struct { | ||
94 | name string | ||
95 | diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go | ||
96 | index ad2d777..cea6613 100644 | ||
97 | --- a/src/net/textproto/reader.go | ||
98 | +++ b/src/net/textproto/reader.go | ||
99 | @@ -17,6 +17,10 @@ import ( | ||
100 | "sync" | ||
101 | ) | ||
102 | |||
103 | +// TODO: This should be a distinguishable error (ErrMessageTooLarge) | ||
104 | +// to allow mime/multipart to detect it. | ||
105 | +var errMessageTooLarge = errors.New("message too large") | ||
106 | + | ||
107 | // A Reader implements convenience methods for reading requests | ||
108 | // or responses from a text protocol network connection. | ||
109 | type Reader struct { | ||
110 | @@ -38,13 +42,13 @@ func NewReader(r *bufio.Reader) *Reader { | ||
111 | // ReadLine reads a single line from r, | ||
112 | // eliding the final \n or \r\n from the returned string. | ||
113 | func (r *Reader) ReadLine() (string, error) { | ||
114 | - line, err := r.readLineSlice() | ||
115 | + line, err := r.readLineSlice(-1) | ||
116 | return string(line), err | ||
117 | } | ||
118 | |||
119 | // ReadLineBytes is like ReadLine but returns a []byte instead of a string. | ||
120 | func (r *Reader) ReadLineBytes() ([]byte, error) { | ||
121 | - line, err := r.readLineSlice() | ||
122 | + line, err := r.readLineSlice(-1) | ||
123 | if line != nil { | ||
124 | buf := make([]byte, len(line)) | ||
125 | copy(buf, line) | ||
126 | @@ -53,7 +57,10 @@ func (r *Reader) ReadLineBytes() ([]byte, error) { | ||
127 | return line, err | ||
128 | } | ||
129 | |||
130 | -func (r *Reader) readLineSlice() ([]byte, error) { | ||
131 | +// readLineSlice reads a single line from r, | ||
132 | +// up to lim bytes long (or unlimited if lim is less than 0), | ||
133 | +// eliding the final \r or \r\n from the returned string. | ||
134 | +func (r *Reader) readLineSlice(lim int64) ([]byte, error) { | ||
135 | r.closeDot() | ||
136 | var line []byte | ||
137 | for { | ||
138 | @@ -61,6 +68,9 @@ func (r *Reader) readLineSlice() ([]byte, error) { | ||
139 | if err != nil { | ||
140 | return nil, err | ||
141 | } | ||
142 | + if lim >= 0 && int64(len(line))+int64(len(l)) > lim { | ||
143 | + return nil, errMessageTooLarge | ||
144 | + } | ||
145 | // Avoid the copy if the first call produced a full line. | ||
146 | if line == nil && !more { | ||
147 | return l, nil | ||
148 | @@ -93,7 +103,7 @@ func (r *Reader) readLineSlice() ([]byte, error) { | ||
149 | // A line consisting of only white space is never continued. | ||
150 | // | ||
151 | func (r *Reader) ReadContinuedLine() (string, error) { | ||
152 | - line, err := r.readContinuedLineSlice(noValidation) | ||
153 | + line, err := r.readContinuedLineSlice(-1, noValidation) | ||
154 | return string(line), err | ||
155 | } | ||
156 | |||
157 | @@ -114,7 +124,7 @@ func trim(s []byte) []byte { | ||
158 | // ReadContinuedLineBytes is like ReadContinuedLine but | ||
159 | // returns a []byte instead of a string. | ||
160 | func (r *Reader) ReadContinuedLineBytes() ([]byte, error) { | ||
161 | - line, err := r.readContinuedLineSlice(noValidation) | ||
162 | + line, err := r.readContinuedLineSlice(-1, noValidation) | ||
163 | if line != nil { | ||
164 | buf := make([]byte, len(line)) | ||
165 | copy(buf, line) | ||
166 | @@ -127,13 +137,14 @@ func (r *Reader) ReadContinuedLineBytes() ([]byte, error) { | ||
167 | // returning a byte slice with all lines. The validateFirstLine function | ||
168 | // is run on the first read line, and if it returns an error then this | ||
169 | // error is returned from readContinuedLineSlice. | ||
170 | -func (r *Reader) readContinuedLineSlice(validateFirstLine func([]byte) error) ([]byte, error) { | ||
171 | +// It reads up to lim bytes of data (or unlimited if lim is less than 0). | ||
172 | +func (r *Reader) readContinuedLineSlice(lim int64, validateFirstLine func([]byte) error) ([]byte, error) { | ||
173 | if validateFirstLine == nil { | ||
174 | return nil, fmt.Errorf("missing validateFirstLine func") | ||
175 | } | ||
176 | |||
177 | // Read the first line. | ||
178 | - line, err := r.readLineSlice() | ||
179 | + line, err := r.readLineSlice(lim) | ||
180 | if err != nil { | ||
181 | return nil, err | ||
182 | } | ||
183 | @@ -161,13 +172,21 @@ func (r *Reader) readContinuedLineSlice(validateFirstLine func([]byte) error) ([ | ||
184 | // copy the slice into buf. | ||
185 | r.buf = append(r.buf[:0], trim(line)...) | ||
186 | |||
187 | + if lim < 0 { | ||
188 | + lim = math.MaxInt64 | ||
189 | + } | ||
190 | + lim -= int64(len(r.buf)) | ||
191 | + | ||
192 | // Read continuation lines. | ||
193 | for r.skipSpace() > 0 { | ||
194 | - line, err := r.readLineSlice() | ||
195 | + r.buf = append(r.buf, ' ') | ||
196 | + if int64(len(r.buf)) >= lim { | ||
197 | + return nil, errMessageTooLarge | ||
198 | + } | ||
199 | + line, err := r.readLineSlice(lim - int64(len(r.buf))) | ||
200 | if err != nil { | ||
201 | break | ||
202 | } | ||
203 | - r.buf = append(r.buf, ' ') | ||
204 | r.buf = append(r.buf, trim(line)...) | ||
205 | } | ||
206 | return r.buf, nil | ||
207 | @@ -512,7 +531,8 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) | ||
208 | |||
209 | // The first line cannot start with a leading space. | ||
210 | if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') { | ||
211 | - line, err := r.readLineSlice() | ||
212 | + const errorLimit = 80 // arbitrary limit on how much of the line we'll quote | ||
213 | + line, err := r.readLineSlice(errorLimit) | ||
214 | if err != nil { | ||
215 | return m, err | ||
216 | } | ||
217 | @@ -520,7 +540,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) | ||
218 | } | ||
219 | |||
220 | for { | ||
221 | - kv, err := r.readContinuedLineSlice(mustHaveFieldNameColon) | ||
222 | + kv, err := r.readContinuedLineSlice(maxMemory, mustHaveFieldNameColon) | ||
223 | if len(kv) == 0 { | ||
224 | return m, err | ||
225 | } | ||
226 | @@ -541,7 +561,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) | ||
227 | |||
228 | maxHeaders-- | ||
229 | if maxHeaders < 0 { | ||
230 | - return nil, errors.New("message too large") | ||
231 | + return nil, errMessageTooLarge | ||
232 | } | ||
233 | |||
234 | // backport 5c55ac9bf1e5f779220294c843526536605f42ab | ||
235 | @@ -567,9 +587,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) | ||
236 | } | ||
237 | maxMemory -= int64(len(value)) | ||
238 | if maxMemory < 0 { | ||
239 | - // TODO: This should be a distinguishable error (ErrMessageTooLarge) | ||
240 | - // to allow mime/multipart to detect it. | ||
241 | - return m, errors.New("message too large") | ||
242 | + return m, errMessageTooLarge | ||
243 | } | ||
244 | if vv == nil && len(strs) > 0 { | ||
245 | // More than likely this will be a single-element key. | ||
246 | diff --git a/src/net/textproto/reader_test.go b/src/net/textproto/reader_test.go | ||
247 | index 3ae0de1..db1ed91 100644 | ||
248 | --- a/src/net/textproto/reader_test.go | ||
249 | +++ b/src/net/textproto/reader_test.go | ||
250 | @@ -34,6 +34,18 @@ func TestReadLine(t *testing.T) { | ||
251 | } | ||
252 | } | ||
253 | |||
254 | +func TestReadLineLongLine(t *testing.T) { | ||
255 | + line := strings.Repeat("12345", 10000) | ||
256 | + r := reader(line + "\r\n") | ||
257 | + s, err := r.ReadLine() | ||
258 | + if err != nil { | ||
259 | + t.Fatalf("Line 1: %v", err) | ||
260 | + } | ||
261 | + if s != line { | ||
262 | + t.Fatalf("%v-byte line does not match expected %v-byte line", len(s), len(line)) | ||
263 | + } | ||
264 | +} | ||
265 | + | ||
266 | func TestReadContinuedLine(t *testing.T) { | ||
267 | r := reader("line1\nline\n 2\nline3\n") | ||
268 | s, err := r.ReadContinuedLine() | ||
269 | -- | ||
270 | 2.25.1 | ||
271 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2024-24784.patch b/meta/recipes-devtools/go/go-1.14/CVE-2024-24784.patch new file mode 100644 index 0000000000..e9d9d972b9 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2024-24784.patch | |||
@@ -0,0 +1,205 @@ | |||
1 | From 5330cd225ba54c7dc78c1b46dcdf61a4671a632c Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Wed, 10 Jan 2024 11:02:14 -0800 | ||
4 | Subject: [PATCH] [release-branch.go1.22] net/mail: properly handle special | ||
5 | characters in phrase and obs-phrase | ||
6 | |||
7 | Fixes a couple of misalignments with RFC 5322 which introduce | ||
8 | significant diffs between (mostly) conformant parsers. | ||
9 | |||
10 | This change reverts the changes made in CL50911, which allowed certain | ||
11 | special RFC 5322 characters to appear unquoted in the "phrase" syntax. | ||
12 | It is unclear why this change was made in the first place, and created | ||
13 | a divergence from comformant parsers. In particular this resulted in | ||
14 | treating comments in display names incorrectly. | ||
15 | |||
16 | Additionally properly handle trailing malformed comments in the group | ||
17 | syntax. | ||
18 | |||
19 | For #65083 | ||
20 | Fixed #65849 | ||
21 | |||
22 | Change-Id: I00dddc044c6ae3381154e43236632604c390f672 | ||
23 | Reviewed-on: https://go-review.googlesource.com/c/go/+/555596 | ||
24 | Reviewed-by: Damien Neil <dneil@google.com> | ||
25 | LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> | ||
26 | Reviewed-on: https://go-review.googlesource.com/c/go/+/566215 | ||
27 | Reviewed-by: Carlos Amedee <carlos@golang.org> | ||
28 | |||
29 | Upstream-Status: Backport [https://github.com/golang/go/commit/5330cd225ba54c7dc78c1b46dcdf61a4671a632c] | ||
30 | CVE: CVE-2024-24784 | ||
31 | Signed-off-by: Ashish Sharma <asharma@mvista.com> | ||
32 | |||
33 | src/net/mail/message.go | 30 +++++++++++++++------------ | ||
34 | src/net/mail/message_test.go | 40 ++++++++++++++++++++++++++---------- | ||
35 | 2 files changed, 46 insertions(+), 24 deletions(-) | ||
36 | |||
37 | diff --git a/src/net/mail/message.go b/src/net/mail/message.go | ||
38 | index af516fc30f470..fc2a9e46f811b 100644 | ||
39 | --- a/src/net/mail/message.go | ||
40 | +++ b/src/net/mail/message.go | ||
41 | @@ -280,7 +280,7 @@ func (a *Address) String() string { | ||
42 | // Add quotes if needed | ||
43 | quoteLocal := false | ||
44 | for i, r := range local { | ||
45 | - if isAtext(r, false, false) { | ||
46 | + if isAtext(r, false) { | ||
47 | continue | ||
48 | } | ||
49 | if r == '.' { | ||
50 | @@ -444,7 +444,7 @@ func (p *addrParser) parseAddress(handleGroup bool) ([]*Address, error) { | ||
51 | if !p.consume('<') { | ||
52 | atext := true | ||
53 | for _, r := range displayName { | ||
54 | - if !isAtext(r, true, false) { | ||
55 | + if !isAtext(r, true) { | ||
56 | atext = false | ||
57 | break | ||
58 | } | ||
59 | @@ -479,7 +479,9 @@ func (p *addrParser) consumeGroupList() ([]*Address, error) { | ||
60 | // handle empty group. | ||
61 | p.skipSpace() | ||
62 | if p.consume(';') { | ||
63 | - p.skipCFWS() | ||
64 | + if !p.skipCFWS() { | ||
65 | + return nil, errors.New("mail: misformatted parenthetical comment") | ||
66 | + } | ||
67 | return group, nil | ||
68 | } | ||
69 | |||
70 | @@ -496,7 +498,9 @@ func (p *addrParser) consumeGroupList() ([]*Address, error) { | ||
71 | return nil, errors.New("mail: misformatted parenthetical comment") | ||
72 | } | ||
73 | if p.consume(';') { | ||
74 | - p.skipCFWS() | ||
75 | + if !p.skipCFWS() { | ||
76 | + return nil, errors.New("mail: misformatted parenthetical comment") | ||
77 | + } | ||
78 | break | ||
79 | } | ||
80 | if !p.consume(',') { | ||
81 | @@ -566,6 +570,12 @@ func (p *addrParser) consumePhrase() (phrase string, err error) { | ||
82 | var words []string | ||
83 | var isPrevEncoded bool | ||
84 | for { | ||
85 | + // obs-phrase allows CFWS after one word | ||
86 | + if len(words) > 0 { | ||
87 | + if !p.skipCFWS() { | ||
88 | + return "", errors.New("mail: misformatted parenthetical comment") | ||
89 | + } | ||
90 | + } | ||
91 | // word = atom / quoted-string | ||
92 | var word string | ||
93 | p.skipSpace() | ||
94 | @@ -661,7 +671,6 @@ Loop: | ||
95 | // If dot is true, consumeAtom parses an RFC 5322 dot-atom instead. | ||
96 | // If permissive is true, consumeAtom will not fail on: | ||
97 | // - leading/trailing/double dots in the atom (see golang.org/issue/4938) | ||
98 | -// - special characters (RFC 5322 3.2.3) except '<', '>', ':' and '"' (see golang.org/issue/21018) | ||
99 | func (p *addrParser) consumeAtom(dot bool, permissive bool) (atom string, err error) { | ||
100 | i := 0 | ||
101 | |||
102 | @@ -672,7 +681,7 @@ Loop: | ||
103 | case size == 1 && r == utf8.RuneError: | ||
104 | return "", fmt.Errorf("mail: invalid utf-8 in address: %q", p.s) | ||
105 | |||
106 | - case size == 0 || !isAtext(r, dot, permissive): | ||
107 | + case size == 0 || !isAtext(r, dot): | ||
108 | break Loop | ||
109 | |||
110 | default: | ||
111 | @@ -850,18 +859,13 @@ func (e charsetError) Error() string { | ||
112 | |||
113 | // isAtext reports whether r is an RFC 5322 atext character. | ||
114 | // If dot is true, period is included. | ||
115 | -// If permissive is true, RFC 5322 3.2.3 specials is included, | ||
116 | -// except '<', '>', ':' and '"'. | ||
117 | -func isAtext(r rune, dot, permissive bool) bool { | ||
118 | +func isAtext(r rune, dot bool) bool { | ||
119 | switch r { | ||
120 | case '.': | ||
121 | return dot | ||
122 | |||
123 | // RFC 5322 3.2.3. specials | ||
124 | - case '(', ')', '[', ']', ';', '@', '\\', ',': | ||
125 | - return permissive | ||
126 | - | ||
127 | - case '<', '>', '"', ':': | ||
128 | + case '(', ')', '<', '>', '[', ']', ':', ';', '@', '\\', ',', '"': // RFC 5322 3.2.3. specials | ||
129 | return false | ||
130 | } | ||
131 | return isVchar(r) | ||
132 | diff --git a/src/net/mail/message_test.go b/src/net/mail/message_test.go | ||
133 | index 1e1bb4092f659..1f2f62afbf406 100644 | ||
134 | --- a/src/net/mail/message_test.go | ||
135 | +++ b/src/net/mail/message_test.go | ||
136 | @@ -385,8 +385,11 @@ func TestAddressParsingError(t *testing.T) { | ||
137 | 13: {"group not closed: null@example.com", "expected comma"}, | ||
138 | 14: {"group: first@example.com, second@example.com;", "group with multiple addresses"}, | ||
139 | 15: {"john.doe", "missing '@' or angle-addr"}, | ||
140 | - 16: {"john.doe@", "no angle-addr"}, | ||
141 | + 16: {"john.doe@", "missing '@' or angle-addr"}, | ||
142 | 17: {"John Doe@foo.bar", "no angle-addr"}, | ||
143 | + 18: {" group: null@example.com; (asd", "misformatted parenthetical comment"}, | ||
144 | + 19: {" group: ; (asd", "misformatted parenthetical comment"}, | ||
145 | + 20: {`(John) Doe <jdoe@machine.example>`, "missing word in phrase:"}, | ||
146 | } | ||
147 | |||
148 | for i, tc := range mustErrTestCases { | ||
149 | @@ -436,24 +439,19 @@ func TestAddressParsing(t *testing.T) { | ||
150 | Address: "john.q.public@example.com", | ||
151 | }}, | ||
152 | }, | ||
153 | - { | ||
154 | - `"John (middle) Doe" <jdoe@machine.example>`, | ||
155 | - []*Address{{ | ||
156 | - Name: "John (middle) Doe", | ||
157 | - Address: "jdoe@machine.example", | ||
158 | - }}, | ||
159 | - }, | ||
160 | + // Comment in display name | ||
161 | { | ||
162 | `John (middle) Doe <jdoe@machine.example>`, | ||
163 | []*Address{{ | ||
164 | - Name: "John (middle) Doe", | ||
165 | + Name: "John Doe", | ||
166 | Address: "jdoe@machine.example", | ||
167 | }}, | ||
168 | }, | ||
169 | + // Display name is quoted string, so comment is not a comment | ||
170 | { | ||
171 | - `John !@M@! Doe <jdoe@machine.example>`, | ||
172 | + `"John (middle) Doe" <jdoe@machine.example>`, | ||
173 | []*Address{{ | ||
174 | - Name: "John !@M@! Doe", | ||
175 | + Name: "John (middle) Doe", | ||
176 | Address: "jdoe@machine.example", | ||
177 | }}, | ||
178 | }, | ||
179 | @@ -788,6 +786,26 @@ func TestAddressParsing(t *testing.T) { | ||
180 | }, | ||
181 | }, | ||
182 | }, | ||
183 | + // Comment in group display name | ||
184 | + { | ||
185 | + `group (comment:): a@example.com, b@example.com;`, | ||
186 | + []*Address{ | ||
187 | + { | ||
188 | + Address: "a@example.com", | ||
189 | + }, | ||
190 | + { | ||
191 | + Address: "b@example.com", | ||
192 | + }, | ||
193 | + }, | ||
194 | + }, | ||
195 | + { | ||
196 | + `x(:"):"@a.example;("@b.example;`, | ||
197 | + []*Address{ | ||
198 | + { | ||
199 | + Address: `@a.example;(@b.example`, | ||
200 | + }, | ||
201 | + }, | ||
202 | + }, | ||
203 | } | ||
204 | for _, test := range tests { | ||
205 | if len(test.exp) == 1 { | ||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2024-24785.patch b/meta/recipes-devtools/go/go-1.14/CVE-2024-24785.patch new file mode 100644 index 0000000000..1398a2ca48 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2024-24785.patch | |||
@@ -0,0 +1,197 @@ | |||
1 | From 3643147a29352ca2894fd5d0d2069bc4b4335a7e Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <roland@golang.org> | ||
3 | Date: Wed, 14 Feb 2024 17:18:36 -0800 | ||
4 | Subject: [PATCH] [release-branch.go1.21] html/template: escape additional | ||
5 | tokens in MarshalJSON errors | ||
6 | |||
7 | Escape "</script" and "<!--" in errors returned from MarshalJSON errors | ||
8 | when attempting to marshal types in script blocks. This prevents any | ||
9 | user controlled content from prematurely terminating the script block. | ||
10 | |||
11 | Updates #65697 | ||
12 | Fixes #65968 | ||
13 | |||
14 | Change-Id: Icf0e26c54ea7d9c1deed0bff11b6506c99ddef1b | ||
15 | Reviewed-on: https://go-review.googlesource.com/c/go/+/564196 | ||
16 | LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> | ||
17 | Reviewed-by: Damien Neil <dneil@google.com> | ||
18 | (cherry picked from commit ccbc725f2d678255df1bd326fa511a492aa3a0aa) | ||
19 | Reviewed-on: https://go-review.googlesource.com/c/go/+/567515 | ||
20 | Reviewed-by: Carlos Amedee <carlos@golang.org> | ||
21 | |||
22 | Upstream-Status: Backport [https://github.com/golang/go/commit/3643147a29352ca2894fd5d0d2069bc4b4335a7e] | ||
23 | CVE: CVE-2024-24785 | ||
24 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
25 | --- | ||
26 | src/html/template/js.go | 22 ++++++++- | ||
27 | src/html/template/js_test.go | 96 ++++++++++++++++++++---------------- | ||
28 | 2 files changed, 74 insertions(+), 44 deletions(-) | ||
29 | |||
30 | diff --git a/src/html/template/js.go b/src/html/template/js.go | ||
31 | index 35994f0..4d3b25d 100644 | ||
32 | --- a/src/html/template/js.go | ||
33 | +++ b/src/html/template/js.go | ||
34 | @@ -171,13 +171,31 @@ func jsValEscaper(args ...interface{}) string { | ||
35 | // cyclic data. This may be an unacceptable DoS risk. | ||
36 | b, err := json.Marshal(a) | ||
37 | if err != nil { | ||
38 | - // Put a space before comment so that if it is flush against | ||
39 | + // While the standard JSON marshaller does not include user controlled | ||
40 | + // information in the error message, if a type has a MarshalJSON method, | ||
41 | + // the content of the error message is not guaranteed. Since we insert | ||
42 | + // the error into the template, as part of a comment, we attempt to | ||
43 | + // prevent the error from either terminating the comment, or the script | ||
44 | + // block itself. | ||
45 | + // | ||
46 | + // In particular we: | ||
47 | + // * replace "*/" comment end tokens with "* /", which does not | ||
48 | + // terminate the comment | ||
49 | + // * replace "</script" with "\x3C/script", and "<!--" with | ||
50 | + // "\x3C!--", which prevents confusing script block termination | ||
51 | + // semantics | ||
52 | + // | ||
53 | + // We also put a space before the comment so that if it is flush against | ||
54 | // a division operator it is not turned into a line comment: | ||
55 | // x/{{y}} | ||
56 | // turning into | ||
57 | // x//* error marshaling y: | ||
58 | // second line of error message */null | ||
59 | - return fmt.Sprintf(" /* %s */null ", strings.ReplaceAll(err.Error(), "*/", "* /")) | ||
60 | + errStr := err.Error() | ||
61 | + errStr = strings.ReplaceAll(errStr, "*/", "* /") | ||
62 | + errStr = strings.ReplaceAll(errStr, "</script", `\x3C/script`) | ||
63 | + errStr = strings.ReplaceAll(errStr, "<!--", `\x3C!--`) | ||
64 | + return fmt.Sprintf(" /* %s */null ", errStr) | ||
65 | } | ||
66 | |||
67 | // TODO: maybe post-process output to prevent it from containing | ||
68 | diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go | ||
69 | index de9ef28..3fc3baf 100644 | ||
70 | --- a/src/html/template/js_test.go | ||
71 | +++ b/src/html/template/js_test.go | ||
72 | @@ -5,6 +5,7 @@ | ||
73 | package template | ||
74 | |||
75 | import ( | ||
76 | + "errors" | ||
77 | "bytes" | ||
78 | "math" | ||
79 | "strings" | ||
80 | @@ -104,61 +105,72 @@ func TestNextJsCtx(t *testing.T) { | ||
81 | } | ||
82 | } | ||
83 | |||
84 | +type jsonErrType struct{} | ||
85 | + | ||
86 | +func (e *jsonErrType) MarshalJSON() ([]byte, error) { | ||
87 | + return nil, errors.New("beep */ boop </script blip <!--") | ||
88 | +} | ||
89 | + | ||
90 | func TestJSValEscaper(t *testing.T) { | ||
91 | tests := []struct { | ||
92 | - x interface{} | ||
93 | - js string | ||
94 | + x interface{} | ||
95 | + js string | ||
96 | + skipNest bool | ||
97 | }{ | ||
98 | - {int(42), " 42 "}, | ||
99 | - {uint(42), " 42 "}, | ||
100 | - {int16(42), " 42 "}, | ||
101 | - {uint16(42), " 42 "}, | ||
102 | - {int32(-42), " -42 "}, | ||
103 | - {uint32(42), " 42 "}, | ||
104 | - {int16(-42), " -42 "}, | ||
105 | - {uint16(42), " 42 "}, | ||
106 | - {int64(-42), " -42 "}, | ||
107 | - {uint64(42), " 42 "}, | ||
108 | - {uint64(1) << 53, " 9007199254740992 "}, | ||
109 | + {int(42), " 42 ", false}, | ||
110 | + {uint(42), " 42 ", false}, | ||
111 | + {int16(42), " 42 ", false}, | ||
112 | + {uint16(42), " 42 ", false}, | ||
113 | + {int32(-42), " -42 ", false}, | ||
114 | + {uint32(42), " 42 ", false}, | ||
115 | + {int16(-42), " -42 ", false}, | ||
116 | + {uint16(42), " 42 ", false}, | ||
117 | + {int64(-42), " -42 ", false}, | ||
118 | + {uint64(42), " 42 ", false}, | ||
119 | + {uint64(1) << 53, " 9007199254740992 ", false}, | ||
120 | // ulp(1 << 53) > 1 so this loses precision in JS | ||
121 | // but it is still a representable integer literal. | ||
122 | - {uint64(1)<<53 + 1, " 9007199254740993 "}, | ||
123 | - {float32(1.0), " 1 "}, | ||
124 | - {float32(-1.0), " -1 "}, | ||
125 | - {float32(0.5), " 0.5 "}, | ||
126 | - {float32(-0.5), " -0.5 "}, | ||
127 | - {float32(1.0) / float32(256), " 0.00390625 "}, | ||
128 | - {float32(0), " 0 "}, | ||
129 | - {math.Copysign(0, -1), " -0 "}, | ||
130 | - {float64(1.0), " 1 "}, | ||
131 | - {float64(-1.0), " -1 "}, | ||
132 | - {float64(0.5), " 0.5 "}, | ||
133 | - {float64(-0.5), " -0.5 "}, | ||
134 | - {float64(0), " 0 "}, | ||
135 | - {math.Copysign(0, -1), " -0 "}, | ||
136 | - {"", `""`}, | ||
137 | - {"foo", `"foo"`}, | ||
138 | + {uint64(1)<<53 + 1, " 9007199254740993 ", false}, | ||
139 | + {float32(1.0), " 1 ", false}, | ||
140 | + {float32(-1.0), " -1 ", false}, | ||
141 | + {float32(0.5), " 0.5 ", false}, | ||
142 | + {float32(-0.5), " -0.5 ", false}, | ||
143 | + {float32(1.0) / float32(256), " 0.00390625 ", false}, | ||
144 | + {float32(0), " 0 ", false}, | ||
145 | + {math.Copysign(0, -1), " -0 ", false}, | ||
146 | + {float64(1.0), " 1 ", false}, | ||
147 | + {float64(-1.0), " -1 ", false}, | ||
148 | + {float64(0.5), " 0.5 ", false}, | ||
149 | + {float64(-0.5), " -0.5 ", false}, | ||
150 | + {float64(0), " 0 ", false}, | ||
151 | + {math.Copysign(0, -1), " -0 ", false}, | ||
152 | + {"", `""`, false}, | ||
153 | + {"foo", `"foo"`, false}, | ||
154 | // Newlines. | ||
155 | - {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`}, | ||
156 | + {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`, false}, | ||
157 | // "\v" == "v" on IE 6 so use "\u000b" instead. | ||
158 | - {"\t\x0b", `"\t\u000b"`}, | ||
159 | - {struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`}, | ||
160 | - {[]interface{}{}, "[]"}, | ||
161 | - {[]interface{}{42, "foo", nil}, `[42,"foo",null]`}, | ||
162 | - {[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`}, | ||
163 | - {"<!--", `"\u003c!--"`}, | ||
164 | - {"-->", `"--\u003e"`}, | ||
165 | - {"<![CDATA[", `"\u003c![CDATA["`}, | ||
166 | - {"]]>", `"]]\u003e"`}, | ||
167 | - {"</script", `"\u003c/script"`}, | ||
168 | - {"\U0001D11E", "\"\U0001D11E\""}, // or "\uD834\uDD1E" | ||
169 | - {nil, " null "}, | ||
170 | + {"\t\x0b", `"\t\u000b"`, false}, | ||
171 | + {struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`, false}, | ||
172 | + {[]interface{}{}, "[]", false}, | ||
173 | + {[]interface{}{42, "foo", nil}, `[42,"foo",null]`, false}, | ||
174 | + {[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`, false}, | ||
175 | + {"<!--", `"\u003c!--"`, false}, | ||
176 | + {"-->", `"--\u003e"`, false}, | ||
177 | + {"<![CDATA[", `"\u003c![CDATA["`, false}, | ||
178 | + {"]]>", `"]]\u003e"`, false}, | ||
179 | + {"</script", `"\u003c/script"`, false}, | ||
180 | + {"\U0001D11E", "\"\U0001D11E\"", false}, // or "\uD834\uDD1E" | ||
181 | + {nil, " null ", false}, | ||
182 | + {&jsonErrType{}, " /* json: error calling MarshalJSON for type *template.jsonErrType: beep * / boop \\x3C/script blip \\x3C!-- */null ", true}, | ||
183 | } | ||
184 | |||
185 | for _, test := range tests { | ||
186 | if js := jsValEscaper(test.x); js != test.js { | ||
187 | t.Errorf("%+v: want\n\t%q\ngot\n\t%q", test.x, test.js, js) | ||
188 | } | ||
189 | + if test.skipNest { | ||
190 | + continue | ||
191 | + } | ||
192 | // Make sure that escaping corner cases are not broken | ||
193 | // by nesting. | ||
194 | a := []interface{}{test.x} | ||
195 | -- | ||
196 | 2.25.1 | ||
197 | |||