!165 golang: upgrade master branch to go1.19.4

From: @hcnbxx 
Reviewed-by: @jing-rui 
Signed-off-by: @jing-rui
This commit is contained in:
openeuler-ci-bot 2023-01-11 01:29:53 +00:00 committed by Gitee
commit c6bde1dba7
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
26 changed files with 9 additions and 3941 deletions

View File

@ -1,60 +0,0 @@
From ad33fdc8f4bce612842d922ca701c3062fe4d4c6 Mon Sep 17 00:00:00 2001
From: Filippo Valsorda <filippo@golang.org>
Date: Thu, 31 Mar 2022 12:31:58 -0400
Subject: [Backport 1/2] [release-branch.go1.17] crypto/elliptic: tolerate
zero-padded scalars in generic P-256
Updates #52075
Fixes #52076
Fixes CVE-2022-28327
Change-Id: I595a7514c9a0aa1b9c76aedfc2307e1124271f27
Reviewed-on: https://go-review.googlesource.com/c/go/+/397136
Trust: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Julie Qiu <julie@golang.org>
Conflict:NA
Reference:https://go-review.googlesource.com/c/go/+/399816,https://go-review.googlesource.com/c/go/+/397136
---
src/crypto/elliptic/p256.go | 2 +-
src/crypto/elliptic/p256_test.go | 14 ++++++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/crypto/elliptic/p256.go b/src/crypto/elliptic/p256.go
index b2b12c8f13..da5283735c 100644
--- a/src/crypto/elliptic/p256.go
+++ b/src/crypto/elliptic/p256.go
@@ -52,7 +52,7 @@ func p256GetScalar(out *[32]byte, in []byte) {
n := new(big.Int).SetBytes(in)
var scalarBytes []byte
- if n.Cmp(p256Params.N) >= 0 {
+ if n.Cmp(p256Params.N) >= 0 || len(in) > len(out) {
n.Mod(n, p256Params.N)
scalarBytes = n.Bytes()
} else {
diff --git a/src/crypto/elliptic/p256_test.go b/src/crypto/elliptic/p256_test.go
index 1435f5e1a5..694186df81 100644
--- a/src/crypto/elliptic/p256_test.go
+++ b/src/crypto/elliptic/p256_test.go
@@ -153,3 +153,17 @@ func TestP256CombinedMult(t *testing.T) {
t.Errorf("1×G + (-1)×G = (%d, %d), should be ∞", x, y)
}
}
+
+func TestIssue52075(t *testing.T) {
+ Gx, Gy := P256().Params().Gx, P256().Params().Gy
+ scalar := make([]byte, 33)
+ scalar[32] = 1
+ x, y := P256().ScalarBaseMult(scalar)
+ if x.Cmp(Gx) != 0 || y.Cmp(Gy) != 0 {
+ t.Errorf("unexpected output (%v,%v)", x, y)
+ }
+ x, y = P256().ScalarMult(Gx, Gy, scalar)
+ if x.Cmp(Gx) != 0 || y.Cmp(Gy) != 0 {
+ t.Errorf("unexpected output (%v,%v)", x, y)
+ }
+}
--
2.30.0

View File

@ -1,291 +0,0 @@
From baaaf3ce29bf98efc00c2f06c531f2b0186b027b Mon Sep 17 00:00:00 2001
From: Julie Qiu <julie@golang.org>
Date: Tue, 1 Mar 2022 10:19:38 -0600
Subject: [Backport 2/2] [release-branch.go1.17] encoding/pem: fix stack
overflow in Decode
Previously, Decode called decodeError, a recursive function that was
prone to stack overflows when given a large PEM file containing errors.
Credit to Juho Nurminen of Mattermost who reported the error.
Fixes CVE-2022-24675
Updates #51853
Fixes #52036
Change-Id: Iffe768be53c8ddc0036fea0671d290f8f797692c
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1391157
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Filippo Valsorda <valsorda@google.com>
(cherry picked from commit 794ea5e828010e8b68493b2fc6d2963263195a02)
Reviewed-on: https://go-review.googlesource.com/c/go/+/399816
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/399816
---
src/encoding/pem/pem.go | 174 +++++++++++++++--------------------
src/encoding/pem/pem_test.go | 28 +++++-
2 files changed, 101 insertions(+), 101 deletions(-)
diff --git a/src/encoding/pem/pem.go b/src/encoding/pem/pem.go
index a7272da5ad..1bee1c12d2 100644
--- a/src/encoding/pem/pem.go
+++ b/src/encoding/pem/pem.go
@@ -87,123 +87,97 @@ func Decode(data []byte) (p *Block, rest []byte) {
// pemStart begins with a newline. However, at the very beginning of
// the byte array, we'll accept the start string without it.
rest = data
- if bytes.HasPrefix(data, pemStart[1:]) {
- rest = rest[len(pemStart)-1 : len(data)]
- } else if i := bytes.Index(data, pemStart); i >= 0 {
- rest = rest[i+len(pemStart) : len(data)]
- } else {
- return nil, data
- }
-
- typeLine, rest := getLine(rest)
- if !bytes.HasSuffix(typeLine, pemEndOfLine) {
- return decodeError(data, rest)
- }
- typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
-
- p = &Block{
- Headers: make(map[string]string),
- Type: string(typeLine),
- }
-
for {
- // This loop terminates because getLine's second result is
- // always smaller than its argument.
- if len(rest) == 0 {
+ if bytes.HasPrefix(rest, pemStart[1:]) {
+ rest = rest[len(pemStart)-1:]
+ } else if i := bytes.Index(rest, pemStart); i >= 0 {
+ rest = rest[i+len(pemStart) : len(rest)]
+ } else {
return nil, data
}
- line, next := getLine(rest)
- i := bytes.IndexByte(line, ':')
- if i == -1 {
- break
+ var typeLine []byte
+ typeLine, rest = getLine(rest)
+ if !bytes.HasSuffix(typeLine, pemEndOfLine) {
+ continue
}
+ typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
- // TODO(agl): need to cope with values that spread across lines.
- key, val := line[:i], line[i+1:]
- key = bytes.TrimSpace(key)
- val = bytes.TrimSpace(val)
- p.Headers[string(key)] = string(val)
- rest = next
- }
+ p = &Block{
+ Headers: make(map[string]string),
+ Type: string(typeLine),
+ }
- var endIndex, endTrailerIndex int
+ for {
+ // This loop terminates because getLine's second result is
+ // always smaller than its argument.
+ if len(rest) == 0 {
+ return nil, data
+ }
+ line, next := getLine(rest)
- // If there were no headers, the END line might occur
- // immediately, without a leading newline.
- if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) {
- endIndex = 0
- endTrailerIndex = len(pemEnd) - 1
- } else {
- endIndex = bytes.Index(rest, pemEnd)
- endTrailerIndex = endIndex + len(pemEnd)
- }
+ i := bytes.IndexByte(line, ':')
+ if i == -1 {
+ break
+ }
- if endIndex < 0 {
- return decodeError(data, rest)
- }
+ // TODO(agl): need to cope with values that spread across lines.
+ key, val := line[:i], line[i+1:]
+ key = bytes.TrimSpace(key)
+ val = bytes.TrimSpace(val)
+ p.Headers[string(key)] = string(val)
+ rest = next
+ }
- // After the "-----" of the ending line, there should be the same type
- // and then a final five dashes.
- endTrailer := rest[endTrailerIndex:]
- endTrailerLen := len(typeLine) + len(pemEndOfLine)
- if len(endTrailer) < endTrailerLen {
- return decodeError(data, rest)
- }
+ var endIndex, endTrailerIndex int
- restOfEndLine := endTrailer[endTrailerLen:]
- endTrailer = endTrailer[:endTrailerLen]
- if !bytes.HasPrefix(endTrailer, typeLine) ||
- !bytes.HasSuffix(endTrailer, pemEndOfLine) {
- return decodeError(data, rest)
- }
+ // If there were no headers, the END line might occur
+ // immediately, without a leading newline.
+ if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) {
+ endIndex = 0
+ endTrailerIndex = len(pemEnd) - 1
+ } else {
+ endIndex = bytes.Index(rest, pemEnd)
+ endTrailerIndex = endIndex + len(pemEnd)
+ }
- // The line must end with only whitespace.
- if s, _ := getLine(restOfEndLine); len(s) != 0 {
- return decodeError(data, rest)
- }
+ if endIndex < 0 {
+ continue
+ }
- base64Data := removeSpacesAndTabs(rest[:endIndex])
- p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
- n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
- if err != nil {
- return decodeError(data, rest)
- }
- p.Bytes = p.Bytes[:n]
+ // After the "-----" of the ending line, there should be the same type
+ // and then a final five dashes.
+ endTrailer := rest[endTrailerIndex:]
+ endTrailerLen := len(typeLine) + len(pemEndOfLine)
+ if len(endTrailer) < endTrailerLen {
+ continue
+ }
+
+ restOfEndLine := endTrailer[endTrailerLen:]
+ endTrailer = endTrailer[:endTrailerLen]
+ if !bytes.HasPrefix(endTrailer, typeLine) ||
+ !bytes.HasSuffix(endTrailer, pemEndOfLine) {
+ continue
+ }
- // the -1 is because we might have only matched pemEnd without the
- // leading newline if the PEM block was empty.
- _, rest = getLine(rest[endIndex+len(pemEnd)-1:])
+ // The line must end with only whitespace.
+ if s, _ := getLine(restOfEndLine); len(s) != 0 {
+ continue
+ }
- return
-}
+ base64Data := removeSpacesAndTabs(rest[:endIndex])
+ p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
+ n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
+ if err != nil {
+ continue
+ }
+ p.Bytes = p.Bytes[:n]
-func decodeError(data, rest []byte) (*Block, []byte) {
- // If we get here then we have rejected a likely looking, but
- // ultimately invalid PEM block. We need to start over from a new
- // position. We have consumed the preamble line and will have consumed
- // any lines which could be header lines. However, a valid preamble
- // line is not a valid header line, therefore we cannot have consumed
- // the preamble line for the any subsequent block. Thus, we will always
- // find any valid block, no matter what bytes precede it.
- //
- // For example, if the input is
- //
- // -----BEGIN MALFORMED BLOCK-----
- // junk that may look like header lines
- // or data lines, but no END line
- //
- // -----BEGIN ACTUAL BLOCK-----
- // realdata
- // -----END ACTUAL BLOCK-----
- //
- // we've failed to parse using the first BEGIN line
- // and now will try again, using the second BEGIN line.
- p, rest := Decode(rest)
- if p == nil {
- rest = data
+ // the -1 is because we might have only matched pemEnd without the
+ // leading newline if the PEM block was empty.
+ _, rest = getLine(rest[endIndex+len(pemEnd)-1:])
+ return p, rest
}
- return p, rest
}
const pemLineLength = 64
diff --git a/src/encoding/pem/pem_test.go b/src/encoding/pem/pem_test.go
index b2b6b15e73..c94b5ca53b 100644
--- a/src/encoding/pem/pem_test.go
+++ b/src/encoding/pem/pem_test.go
@@ -107,6 +107,12 @@ const pemMissingEndingSpace = `
dGVzdA==
-----ENDBAR-----`
+const pemMissingEndLine = `
+-----BEGIN FOO-----
+Header: 1`
+
+var pemRepeatingBegin = strings.Repeat("-----BEGIN \n", 10)
+
var badPEMTests = []struct {
name string
input string
@@ -131,14 +137,34 @@ var badPEMTests = []struct {
"missing ending space",
pemMissingEndingSpace,
},
+ {
+ "repeating begin",
+ pemRepeatingBegin,
+ },
+ {
+ "missing end line",
+ pemMissingEndLine,
+ },
}
func TestBadDecode(t *testing.T) {
for _, test := range badPEMTests {
- result, _ := Decode([]byte(test.input))
+ result, rest := Decode([]byte(test.input))
if result != nil {
t.Errorf("unexpected success while parsing %q", test.name)
}
+ if string(rest) != test.input {
+ t.Errorf("unexpected rest: %q; want = %q", rest, test.input)
+ }
+ }
+}
+
+func TestCVE202224675(t *testing.T) {
+ // Prior to CVE-2022-24675, this input would cause a stack overflow.
+ input := []byte(strings.Repeat("-----BEGIN \n", 10000000))
+ result, rest := Decode(input)
+ if result != nil || !reflect.DeepEqual(rest, input) {
+ t.Errorf("Encode of %#v decoded as %#v", input, rest)
}
}
--
2.30.0

View File

@ -1,79 +0,0 @@
From e7aab832069d06d77e04a585803dfdb04453253a Mon Sep 17 00:00:00 2001
From: Russ Cox <rsc@golang.org>
Date: Wed, 8 Dec 2021 18:05:11 -0500
Subject: [PATCH] [release-branch.go1.17] syscall: fix ForkLock spurious
close(0) on pipe failure
Pipe (and therefore forkLockPipe) does not make any guarantees
about the state of p after a failed Pipe(p). Avoid that assumption
and the too-clever goto, so that we don't accidentally Close a real fd
if the failed pipe leaves p[0] or p[1] set >= 0.
Updates #50057
Fixes CVE-2021-44717
Change-Id: Iff8e19a6efbba0c73cc8b13ecfae381c87600bb4
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1291270
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/370534
Trust: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alex Rakoczy <alex@golang.org>
---
src/syscall/exec_unix.go | 20 ++++++--------------
1 file changed, 6 insertions(+), 14 deletions(-)
diff --git a/src/syscall/exec_unix.go b/src/syscall/exec_unix.go
index 54b18dccd7..c9c9d1abf3 100644
--- a/src/syscall/exec_unix.go
+++ b/src/syscall/exec_unix.go
@@ -153,9 +153,6 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
sys = &zeroSysProcAttr
}
- p[0] = -1
- p[1] = -1
-
// Convert args to C form.
argv0p, err := BytePtrFromString(argv0)
if err != nil {
@@ -205,14 +202,17 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
// Allocate child status pipe close on exec.
if err = forkExecPipe(p[:]); err != nil {
- goto error
+ ForkLock.Unlock()
+ return 0, err
}
// Kick off child.
pid, err1 = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1])
if err1 != 0 {
- err = Errno(err1)
- goto error
+ Close(p[0])
+ Close(p[1])
+ ForkLock.Unlock()
+ return 0, Errno(err1)
}
ForkLock.Unlock()
@@ -244,14 +244,6 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
// Read got EOF, so pipe closed on exec, so exec succeeded.
return pid, nil
-
-error:
- if p[0] >= 0 {
- Close(p[0])
- Close(p[1])
- }
- ForkLock.Unlock()
- return 0, err
}
// Combination of fork and exec, careful to be thread safe.
--
2.30.0

View File

@ -1,39 +0,0 @@
From c872b0594f716a2a0799b07d7226a45f02c005f1 Mon Sep 17 00:00:00 2001
From: Cherry Mui <cherryyz@google.com>
Date: Wed, 16 Mar 2022 13:07:57 -0400
Subject: [PATCH] cmd/link: mark unexported methods for plugins
When plugin is used, we already mark all exported methods
reachable. However, when the plugin and the host program share
a common package, an unexported method could also be reachable
from both the plugin and the host via interfaces. We need to mark
them as well.
Fixes #51621.
Change-Id: I1a70d3f96b66b803f2d0ab14d00ed0df276ea500
Reviewed-on: https://go-review.googlesource.com/c/go/+/393365
Trust: Cherry Mui <cherryyz@google.com>
Run-TryBot: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
---
src/cmd/link/internal/ld/deadcode.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index e4fa75f..21a9703 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -350,7 +350,7 @@ func deadcode(ctxt *Link) {
// in the last pass.
rem := d.markableMethods[:0]
for _, m := range d.markableMethods {
- if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
+ if (d.reflectSeen && (m.isExported() || d.dynlink)) || d.ifaceMethod[m.m] {
d.markMethod(m)
} else {
rem = append(rem, m)
--
1.8.3.1

View File

@ -1,70 +0,0 @@
From 67bff2eb995a098f838fa4b799c0b8261292e6e7 Mon Sep 17 00:00:00 2001
From: Damien Neil <dneil@google.com>
Date: Fri, 17 Jun 2022 10:09:45 -0700
Subject: [PATCH 01/11] [release-branch.go1.17] net/http: preserve nil values
in Header.Clone
ReverseProxy makes a distinction between nil and zero-length header values.
Avoid losing nil-ness when cloning a request.
Thanks to Christian Mehlmauer for discovering this.
For #53423
For CVE-2022-32148
Fixes #53620
Change-Id: Ice369cdb4712e2d62e25bb881b080847aa4801f5
Reviewed-on: https://go-review.googlesource.com/c/go/+/412857
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
(cherry picked from commit b2cc0fecc2ccd80e6d5d16542cc684f97b3a9c8a)
Reviewed-on: https://go-review.googlesource.com/c/go/+/415221
Reviewed-by: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/415221
---
src/net/http/header.go | 6 ++++++
src/net/http/header_test.go | 5 +++++
2 files changed, 11 insertions(+)
diff --git a/src/net/http/header.go b/src/net/http/header.go
index 4c72dcb2c88..ef4ee7ffa81 100644
--- a/src/net/http/header.go
+++ b/src/net/http/header.go
@@ -101,6 +101,12 @@ func (h Header) Clone() Header {
sv := make([]string, nv) // shared backing array for headers' values
h2 := make(Header, len(h))
for k, vv := range h {
+ if vv == nil {
+ // Preserve nil values. ReverseProxy distinguishes
+ // between nil and zero-length header values.
+ h2[k] = nil
+ continue
+ }
n := copy(sv, vv)
h2[k] = sv[:n:n]
sv = sv[n:]
diff --git a/src/net/http/header_test.go b/src/net/http/header_test.go
index 47893629194..80c003551db 100644
--- a/src/net/http/header_test.go
+++ b/src/net/http/header_test.go
@@ -235,6 +235,11 @@ func TestCloneOrMakeHeader(t *testing.T) {
in: Header{"foo": {"bar"}},
want: Header{"foo": {"bar"}},
},
+ {
+ name: "nil value",
+ in: Header{"foo": nil},
+ want: Header{"foo": nil},
+ },
}
for _, tt := range tests {
--
2.30.2

View File

@ -1,421 +0,0 @@
From b78e521644334294019da243a5ff57436f70cd72 Mon Sep 17 00:00:00 2001
From: Roland Shoemaker <bracewell@google.com>
Date: Wed, 15 Jun 2022 10:43:05 -0700
Subject: [PATCH 02/11] [release-branch.go1.17] go/parser: limit recursion
depth
Limit nested parsing to 100,000, which prevents stack exhaustion when
parsing deeply nested statements, types, and expressions. Also limit
the scope depth to 1,000 during object resolution.
Thanks to Juho Nurminen of Mattermost for reporting this issue.
Fixes #53707
Updates #53616
Fixes CVE-2022-1962
Change-Id: I4d7b86c1d75d0bf3c7af1fdea91582aa74272c64
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1491025
Reviewed-by: Russ Cox <rsc@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
(cherry picked from commit 6a856f08d58e4b6705c0c337d461c540c1235c83)
Reviewed-on: https://go-review.googlesource.com/c/go/+/417070
Reviewed-by: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/417070
---
src/go/parser/interface.go | 10 ++-
src/go/parser/parser.go | 54 ++++++++++-
src/go/parser/parser_test.go | 169 +++++++++++++++++++++++++++++++++++
src/go/parser/resolver.go | 9 ++
4 files changed, 236 insertions(+), 6 deletions(-)
diff --git a/src/go/parser/interface.go b/src/go/parser/interface.go
index 85486d2f4b4..eae429e6ef3 100644
--- a/src/go/parser/interface.go
+++ b/src/go/parser/interface.go
@@ -97,8 +97,11 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode)
defer func() {
if e := recover(); e != nil {
// resume same panic if it's not a bailout
- if _, ok := e.(bailout); !ok {
+ bail, ok := e.(bailout)
+ if !ok {
panic(e)
+ } else if bail.msg != "" {
+ p.errors.Add(p.file.Position(bail.pos), bail.msg)
}
}
@@ -203,8 +206,11 @@ func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode M
defer func() {
if e := recover(); e != nil {
// resume same panic if it's not a bailout
- if _, ok := e.(bailout); !ok {
+ bail, ok := e.(bailout)
+ if !ok {
panic(e)
+ } else if bail.msg != "" {
+ p.errors.Add(p.file.Position(bail.pos), bail.msg)
}
}
p.errors.Sort()
diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index f10c8650afd..2c42b9f8cc2 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -60,6 +60,10 @@ type parser struct {
inRhs bool // if set, the parser is parsing a rhs expression
imports []*ast.ImportSpec // list of imports
+
+ // nestLev is used to track and limit the recursion depth
+ // during parsing.
+ nestLev int
}
func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mode) {
@@ -110,6 +114,24 @@ func un(p *parser) {
p.printTrace(")")
}
+// maxNestLev is the deepest we're willing to recurse during parsing
+const maxNestLev int = 1e5
+
+func incNestLev(p *parser) *parser {
+ p.nestLev++
+ if p.nestLev > maxNestLev {
+ p.error(p.pos, "exceeded max nesting depth")
+ panic(bailout{})
+ }
+ return p
+}
+
+// decNestLev is used to track nesting depth during parsing to prevent stack exhaustion.
+// It is used along with incNestLev in a similar fashion to how un and trace are used.
+func decNestLev(p *parser) {
+ p.nestLev--
+}
+
// Advance to the next token.
func (p *parser) next0() {
// Because of one-token look-ahead, print the previous token
@@ -222,8 +244,12 @@ func (p *parser) next() {
}
}
-// A bailout panic is raised to indicate early termination.
-type bailout struct{}
+// A bailout panic is raised to indicate early termination. pos and msg are
+// only populated when bailing out of object resolution.
+type bailout struct {
+ pos token.Pos
+ msg string
+}
func (p *parser) error(pos token.Pos, msg string) {
if p.trace {
@@ -1119,6 +1145,8 @@ func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr {
}
func (p *parser) tryIdentOrType() ast.Expr {
+ defer decNestLev(incNestLev(p))
+
switch p.tok {
case token.IDENT:
typ := p.parseTypeName(nil)
@@ -1531,7 +1559,13 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) {
}
x = p.parseOperand()
- for {
+ // We track the nesting here rather than at the entry for the function,
+ // since it can iteratively produce a nested output, and we want to
+ // limit how deep a structure we generate.
+ var n int
+ defer func() { p.nestLev -= n }()
+ for n = 1; ; n++ {
+ incNestLev(p)
switch p.tok {
case token.PERIOD:
p.next()
@@ -1591,6 +1625,8 @@ func (p *parser) parsePrimaryExpr() (x ast.Expr) {
}
func (p *parser) parseUnaryExpr() ast.Expr {
+ defer decNestLev(incNestLev(p))
+
if p.trace {
defer un(trace(p, "UnaryExpr"))
}
@@ -1673,7 +1709,13 @@ func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
}
x := p.parseUnaryExpr()
- for {
+ // We track the nesting here rather than at the entry for the function,
+ // since it can iteratively produce a nested output, and we want to
+ // limit how deep a structure we generate.
+ var n int
+ defer func() { p.nestLev -= n }()
+ for n = 1; ; n++ {
+ incNestLev(p)
op, oprec := p.tokPrec()
if oprec < prec1 {
return x
@@ -1962,6 +2004,8 @@ func (p *parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) {
}
func (p *parser) parseIfStmt() *ast.IfStmt {
+ defer decNestLev(incNestLev(p))
+
if p.trace {
defer un(trace(p, "IfStmt"))
}
@@ -2265,6 +2309,8 @@ func (p *parser) parseForStmt() ast.Stmt {
}
func (p *parser) parseStmt() (s ast.Stmt) {
+ defer decNestLev(incNestLev(p))
+
if p.trace {
defer un(trace(p, "Statement"))
}
diff --git a/src/go/parser/parser_test.go b/src/go/parser/parser_test.go
index a4f882d3688..1a46c878663 100644
--- a/src/go/parser/parser_test.go
+++ b/src/go/parser/parser_test.go
@@ -10,6 +10,7 @@ import (
"go/ast"
"go/token"
"io/fs"
+ "runtime"
"strings"
"testing"
)
@@ -577,3 +578,171 @@ type x int // comment
t.Errorf("got %q, want %q", comment, "// comment")
}
}
+
+var parseDepthTests = []struct {
+ name string
+ format string
+ // multipler is used when a single statement may result in more than one
+ // change in the depth level, for instance "1+(..." produces a BinaryExpr
+ // followed by a UnaryExpr, which increments the depth twice. The test
+ // case comment explains which nodes are triggering the multiple depth
+ // changes.
+ parseMultiplier int
+ // scope is true if we should also test the statement for the resolver scope
+ // depth limit.
+ scope bool
+ // scopeMultiplier does the same as parseMultiplier, but for the scope
+ // depths.
+ scopeMultiplier int
+}{
+ // The format expands the part inside « » many times.
+ // A second set of brackets nested inside the first stops the repetition,
+ // so that for example «(«1»)» expands to (((...((((1))))...))).
+ {name: "array", format: "package main; var x «[1]»int"},
+ {name: "slice", format: "package main; var x «[]»int"},
+ {name: "struct", format: "package main; var x «struct { X «int» }»", scope: true},
+ {name: "pointer", format: "package main; var x «*»int"},
+ {name: "func", format: "package main; var x «func()»int", scope: true},
+ {name: "chan", format: "package main; var x «chan »int"},
+ {name: "chan2", format: "package main; var x «<-chan »int"},
+ {name: "interface", format: "package main; var x «interface { M() «int» }»", scope: true, scopeMultiplier: 2}, // Scopes: InterfaceType, FuncType
+ {name: "map", format: "package main; var x «map[int]»int"},
+ {name: "slicelit", format: "package main; var x = «[]any{«»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
+ {name: "arraylit", format: "package main; var x = «[1]any{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
+ {name: "structlit", format: "package main; var x = «struct{x any}{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
+ {name: "maplit", format: "package main; var x = «map[int]any{1:«nil»}»", parseMultiplier: 2}, // Parser nodes: CompositeLit, KeyValueExpr
+ {name: "dot", format: "package main; var x = «x.»x"},
+ {name: "index", format: "package main; var x = x«[1]»"},
+ {name: "slice", format: "package main; var x = x«[1:2]»"},
+ {name: "slice3", format: "package main; var x = x«[1:2:3]»"},
+ {name: "dottype", format: "package main; var x = x«.(any)»"},
+ {name: "callseq", format: "package main; var x = x«()»"},
+ {name: "methseq", format: "package main; var x = x«.m()»", parseMultiplier: 2}, // Parser nodes: SelectorExpr, CallExpr
+ {name: "binary", format: "package main; var x = «1+»1"},
+ {name: "binaryparen", format: "package main; var x = «1+(«1»)»", parseMultiplier: 2}, // Parser nodes: BinaryExpr, ParenExpr
+ {name: "unary", format: "package main; var x = «^»1"},
+ {name: "addr", format: "package main; var x = «& »x"},
+ {name: "star", format: "package main; var x = «*»x"},
+ {name: "recv", format: "package main; var x = «<-»x"},
+ {name: "call", format: "package main; var x = «f(«1»)»", parseMultiplier: 2}, // Parser nodes: Ident, CallExpr
+ {name: "conv", format: "package main; var x = «(*T)(«1»)»", parseMultiplier: 2}, // Parser nodes: ParenExpr, CallExpr
+ {name: "label", format: "package main; func main() { «Label:» }"},
+ {name: "if", format: "package main; func main() { «if true { «» }»}", parseMultiplier: 2, scope: true, scopeMultiplier: 2}, // Parser nodes: IfStmt, BlockStmt. Scopes: IfStmt, BlockStmt
+ {name: "ifelse", format: "package main; func main() { «if true {} else » {} }", scope: true},
+ {name: "switch", format: "package main; func main() { «switch { default: «» }»}", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause
+ {name: "typeswitch", format: "package main; func main() { «switch x.(type) { default: «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause
+ {name: "for0", format: "package main; func main() { «for { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt
+ {name: "for1", format: "package main; func main() { «for x { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt
+ {name: "for3", format: "package main; func main() { «for f(); g(); h() { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: ForStmt, BlockStmt
+ {name: "forrange0", format: "package main; func main() { «for range x { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt
+ {name: "forrange1", format: "package main; func main() { «for x = range z { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt
+ {name: "forrange2", format: "package main; func main() { «for x, y = range z { «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: RangeStmt, BlockStmt
+ {name: "go", format: "package main; func main() { «go func() { «» }()» }", parseMultiplier: 2, scope: true}, // Parser nodes: GoStmt, FuncLit
+ {name: "defer", format: "package main; func main() { «defer func() { «» }()» }", parseMultiplier: 2, scope: true}, // Parser nodes: DeferStmt, FuncLit
+ {name: "select", format: "package main; func main() { «select { default: «» }» }", scope: true},
+}
+
+// split splits pre«mid»post into pre, mid, post.
+// If the string does not have that form, split returns x, "", "".
+func split(x string) (pre, mid, post string) {
+ start, end := strings.Index(x, "«"), strings.LastIndex(x, "»")
+ if start < 0 || end < 0 {
+ return x, "", ""
+ }
+ return x[:start], x[start+len("«") : end], x[end+len("»"):]
+}
+
+func TestParseDepthLimit(t *testing.T) {
+ if runtime.GOARCH == "wasm" {
+ t.Skip("causes call stack exhaustion on js/wasm")
+ }
+ for _, tt := range parseDepthTests {
+ for _, size := range []string{"small", "big"} {
+ t.Run(tt.name+"/"+size, func(t *testing.T) {
+ n := maxNestLev + 1
+ if tt.parseMultiplier > 0 {
+ n /= tt.parseMultiplier
+ }
+ if size == "small" {
+ // Decrease the number of statements by 10, in order to check
+ // that we do not fail when under the limit. 10 is used to
+ // provide some wiggle room for cases where the surrounding
+ // scaffolding syntax adds some noise to the depth that changes
+ // on a per testcase basis.
+ n -= 10
+ }
+
+ pre, mid, post := split(tt.format)
+ if strings.Contains(mid, "«") {
+ left, base, right := split(mid)
+ mid = strings.Repeat(left, n) + base + strings.Repeat(right, n)
+ } else {
+ mid = strings.Repeat(mid, n)
+ }
+ input := pre + mid + post
+
+ fset := token.NewFileSet()
+ _, err := ParseFile(fset, "", input, ParseComments|SkipObjectResolution)
+ if size == "small" {
+ if err != nil {
+ t.Errorf("ParseFile(...): %v (want success)", err)
+ }
+ } else {
+ expected := "exceeded max nesting depth"
+ if err == nil || !strings.HasSuffix(err.Error(), expected) {
+ t.Errorf("ParseFile(...) = _, %v, want %q", err, expected)
+ }
+ }
+ })
+ }
+ }
+}
+
+func TestScopeDepthLimit(t *testing.T) {
+ if runtime.GOARCH == "wasm" {
+ t.Skip("causes call stack exhaustion on js/wasm")
+ }
+ for _, tt := range parseDepthTests {
+ if !tt.scope {
+ continue
+ }
+ for _, size := range []string{"small", "big"} {
+ t.Run(tt.name+"/"+size, func(t *testing.T) {
+ n := maxScopeDepth + 1
+ if tt.scopeMultiplier > 0 {
+ n /= tt.scopeMultiplier
+ }
+ if size == "small" {
+ // Decrease the number of statements by 10, in order to check
+ // that we do not fail when under the limit. 10 is used to
+ // provide some wiggle room for cases where the surrounding
+ // scaffolding syntax adds some noise to the depth that changes
+ // on a per testcase basis.
+ n -= 10
+ }
+
+ pre, mid, post := split(tt.format)
+ if strings.Contains(mid, "«") {
+ left, base, right := split(mid)
+ mid = strings.Repeat(left, n) + base + strings.Repeat(right, n)
+ } else {
+ mid = strings.Repeat(mid, n)
+ }
+ input := pre + mid + post
+
+ fset := token.NewFileSet()
+ _, err := ParseFile(fset, "", input, DeclarationErrors)
+ if size == "small" {
+ if err != nil {
+ t.Errorf("ParseFile(...): %v (want success)", err)
+ }
+ } else {
+ expected := "exceeded max scope depth during object resolution"
+ if err == nil || !strings.HasSuffix(err.Error(), expected) {
+ t.Errorf("ParseFile(...) = _, %v, want %q", err, expected)
+ }
+ }
+ })
+ }
+ }
+}
diff --git a/src/go/parser/resolver.go b/src/go/parser/resolver.go
index cf92c7e4f57..f55bdb7f177 100644
--- a/src/go/parser/resolver.go
+++ b/src/go/parser/resolver.go
@@ -25,6 +25,7 @@ func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, str
declErr: declErr,
topScope: pkgScope,
pkgScope: pkgScope,
+ depth: 1,
}
for _, decl := range file.Decls {
@@ -53,6 +54,8 @@ func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, str
file.Unresolved = r.unresolved[0:i]
}
+const maxScopeDepth int = 1e3
+
type resolver struct {
handle *token.File
declErr func(token.Pos, string)
@@ -61,6 +64,7 @@ type resolver struct {
pkgScope *ast.Scope // pkgScope.Outer == nil
topScope *ast.Scope // top-most scope; may be pkgScope
unresolved []*ast.Ident // unresolved identifiers
+ depth int // scope depth
// Label scopes
// (maintained by open/close LabelScope)
@@ -83,6 +87,10 @@ func (r *resolver) sprintf(format string, args ...interface{}) string {
}
func (r *resolver) openScope(pos token.Pos) {
+ r.depth++
+ if r.depth > maxScopeDepth {
+ panic(bailout{pos: pos, msg: "exceeded max scope depth during object resolution"})
+ }
if debugResolve {
r.dump("opening scope @%v", pos)
}
@@ -90,6 +98,7 @@ func (r *resolver) openScope(pos token.Pos) {
}
func (r *resolver) closeScope() {
+ r.depth--
if debugResolve {
r.dump("closing scope")
}
--
2.30.2

View File

@ -1,62 +0,0 @@
From 4ad62aecaf57ca3adc11a04bd2113bdbee6249c3 Mon Sep 17 00:00:00 2001
From: Damien Neil <dneil@google.com>
Date: Wed, 1 Jun 2022 11:17:07 -0700
Subject: [PATCH 03/11] [release-branch.go1.17] net/http: don't strip
whitespace from Transfer-Encoding headers
Do not accept "Transfer-Encoding: \rchunked" as a valid TE header
setting chunked encoding.
Thanks to Zeyu Zhang (https://www.zeyu2001.com/) for identifying
the issue.
For #53188
For CVE-2022-1705
Fixes #53432
Change-Id: I1a16631425159267f2eca68056b057192a7edf6c
Reviewed-on: https://go-review.googlesource.com/c/go/+/409874
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
(cherry picked from commit e5017a93fcde94f09836200bca55324af037ee5f)
Reviewed-on: https://go-review.googlesource.com/c/go/+/415217
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/415217
---
src/net/http/serve_test.go | 1 +
src/net/http/transfer.go | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go
index 6394da3bb7c..bfac783e3a9 100644
--- a/src/net/http/serve_test.go
+++ b/src/net/http/serve_test.go
@@ -6189,6 +6189,7 @@ func TestUnsupportedTransferEncodingsReturn501(t *testing.T) {
"fugazi",
"foo-bar",
"unknown",
+ "\rchunked",
}
for _, badTE := range unsupportedTEs {
diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go
index 85c2e5a360d..3894007d306 100644
--- a/src/net/http/transfer.go
+++ b/src/net/http/transfer.go
@@ -639,7 +639,7 @@ func (t *transferReader) parseTransferEncoding() error {
if len(raw) != 1 {
return &unsupportedTEError{fmt.Sprintf("too many transfer encodings: %q", raw)}
}
- if !ascii.EqualFold(textproto.TrimString(raw[0]), "chunked") {
+ if !ascii.EqualFold(raw[0], "chunked") {
return &unsupportedTEError{fmt.Sprintf("unsupported transfer encoding: %q", raw[0])}
}
--
2.30.2

View File

@ -1,168 +0,0 @@
From 106c859f68c3137cfa05c433a9b90494db386fda Mon Sep 17 00:00:00 2001
From: Roland Shoemaker <roland@golang.org>
Date: Tue, 29 Mar 2022 15:52:09 -0700
Subject: [PATCH 04/11] [release-branch.go1.17] encoding/xml: limit depth of
nesting in unmarshal
Prevent exhausting the stack limit when unmarshalling extremely deeply
nested structures into nested types.
Fixes #53715
Updates #53611
Fixes CVE-2022-30633
Change-Id: Ic6c5d41674c93cfc9a316135a408db9156d39c59
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1421319
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Julie Qiu <julieqiu@google.com>
(cherry picked from commit ebee00a55e28931b2cad0e76207a73712b000432)
Reviewed-on: https://go-review.googlesource.com/c/go/+/417069
Reviewed-by: Heschi Kreinick <heschi@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/417069
---
src/encoding/xml/read.go | 27 +++++++++++++++++++--------
src/encoding/xml/read_test.go | 32 ++++++++++++++++++++++++++++++++
2 files changed, 51 insertions(+), 8 deletions(-)
diff --git a/src/encoding/xml/read.go b/src/encoding/xml/read.go
index ef5df3f7f6a..e0ed8b527ce 100644
--- a/src/encoding/xml/read.go
+++ b/src/encoding/xml/read.go
@@ -148,7 +148,7 @@ func (d *Decoder) DecodeElement(v interface{}, start *StartElement) error {
if val.Kind() != reflect.Ptr {
return errors.New("non-pointer passed to Unmarshal")
}
- return d.unmarshal(val.Elem(), start)
+ return d.unmarshal(val.Elem(), start, 0)
}
// An UnmarshalError represents an error in the unmarshaling process.
@@ -304,8 +304,15 @@ var (
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
)
+const maxUnmarshalDepth = 10000
+
+var errExeceededMaxUnmarshalDepth = errors.New("exceeded max depth")
+
// Unmarshal a single XML element into val.
-func (d *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
+func (d *Decoder) unmarshal(val reflect.Value, start *StartElement, depth int) error {
+ if depth >= maxUnmarshalDepth {
+ return errExeceededMaxUnmarshalDepth
+ }
// Find start element if we need it.
if start == nil {
for {
@@ -398,7 +405,7 @@ func (d *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
v.Set(reflect.Append(val, reflect.Zero(v.Type().Elem())))
// Recur to read element into slice.
- if err := d.unmarshal(v.Index(n), start); err != nil {
+ if err := d.unmarshal(v.Index(n), start, depth+1); err != nil {
v.SetLen(n)
return err
}
@@ -521,13 +528,15 @@ Loop:
case StartElement:
consumed := false
if sv.IsValid() {
- consumed, err = d.unmarshalPath(tinfo, sv, nil, &t)
+ // unmarshalPath can call unmarshal, so we need to pass the depth through so that
+ // we can continue to enforce the maximum recusion limit.
+ consumed, err = d.unmarshalPath(tinfo, sv, nil, &t, depth)
if err != nil {
return err
}
if !consumed && saveAny.IsValid() {
consumed = true
- if err := d.unmarshal(saveAny, &t); err != nil {
+ if err := d.unmarshal(saveAny, &t, depth+1); err != nil {
return err
}
}
@@ -672,7 +681,7 @@ func copyValue(dst reflect.Value, src []byte) (err error) {
// The consumed result tells whether XML elements have been consumed
// from the Decoder until start's matching end element, or if it's
// still untouched because start is uninteresting for sv's fields.
-func (d *Decoder) unmarshalPath(tinfo *typeInfo, sv reflect.Value, parents []string, start *StartElement) (consumed bool, err error) {
+func (d *Decoder) unmarshalPath(tinfo *typeInfo, sv reflect.Value, parents []string, start *StartElement, depth int) (consumed bool, err error) {
recurse := false
Loop:
for i := range tinfo.fields {
@@ -687,7 +696,7 @@ Loop:
}
if len(finfo.parents) == len(parents) && finfo.name == start.Name.Local {
// It's a perfect match, unmarshal the field.
- return true, d.unmarshal(finfo.value(sv, initNilPointers), start)
+ return true, d.unmarshal(finfo.value(sv, initNilPointers), start, depth+1)
}
if len(finfo.parents) > len(parents) && finfo.parents[len(parents)] == start.Name.Local {
// It's a prefix for the field. Break and recurse
@@ -716,7 +725,9 @@ Loop:
}
switch t := tok.(type) {
case StartElement:
- consumed2, err := d.unmarshalPath(tinfo, sv, parents, &t)
+ // the recursion depth of unmarshalPath is limited to the path length specified
+ // by the struct field tag, so we don't increment the depth here.
+ consumed2, err := d.unmarshalPath(tinfo, sv, parents, &t, depth)
if err != nil {
return true, err
}
diff --git a/src/encoding/xml/read_test.go b/src/encoding/xml/read_test.go
index 8c2e70fa22e..8c940aefb81 100644
--- a/src/encoding/xml/read_test.go
+++ b/src/encoding/xml/read_test.go
@@ -5,8 +5,11 @@
package xml
import (
+ "bytes"
+ "errors"
"io"
"reflect"
+ "runtime"
"strings"
"testing"
"time"
@@ -1079,3 +1082,32 @@ func TestUnmarshalWhitespaceAttrs(t *testing.T) {
t.Fatalf("whitespace attrs: Unmarshal:\nhave: %#+v\nwant: %#+v", v, want)
}
}
+
+func TestCVE202230633(t *testing.T) {
+ if runtime.GOARCH == "wasm" {
+ t.Skip("causes memory exhaustion on js/wasm")
+ }
+ defer func() {
+ p := recover()
+ if p != nil {
+ t.Fatal("Unmarshal panicked")
+ }
+ }()
+ var example struct {
+ Things []string
+ }
+ Unmarshal(bytes.Repeat([]byte("<a>"), 17_000_000), &example)
+}
+
+func TestCVE202228131(t *testing.T) {
+ type nested struct {
+ Parent *nested `xml:",any"`
+ }
+ var n nested
+ err := Unmarshal(bytes.Repeat([]byte("<a>"), maxUnmarshalDepth+1), &n)
+ if err == nil {
+ t.Fatal("Unmarshal did not fail")
+ } else if !errors.Is(err, errExeceededMaxUnmarshalDepth) {
+ t.Fatalf("Unmarshal unexpected error: got %q, want %q", err, errExeceededMaxUnmarshalDepth)
+ }
+}
--
2.30.2

View File

@ -1,138 +0,0 @@
From 8747af9a1098e8fa497441be4c4a79a23de31a98 Mon Sep 17 00:00:00 2001
From: Roland Shoemaker <bracewell@google.com>
Date: Tue, 7 Jun 2022 13:00:43 -0700
Subject: [PATCH 05/11] [release-branch.go1.17] encoding/gob: add a depth limit
for ignored fields
Enforce a nesting limit of 10,000 for ignored fields during decoding
of messages. This prevents the possibility of triggering stack
exhaustion.
Fixes #53709
Updates #53615
Fixes CVE-2022-30635
Change-Id: I05103d06dd5ca3945fcba3c1f5d3b5a645e8fb0f
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1484771
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
(cherry picked from commit 55e8f938d22bfec29cc9dc9671044c5a41d1ea9c)
Reviewed-on: https://go-review.googlesource.com/c/go/+/417074
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/417074
---
src/encoding/gob/decode.go | 19 ++++++++++++-------
src/encoding/gob/gobencdec_test.go | 24 ++++++++++++++++++++++++
2 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/src/encoding/gob/decode.go b/src/encoding/gob/decode.go
index d2f6c749b1b..0e0ec75cccc 100644
--- a/src/encoding/gob/decode.go
+++ b/src/encoding/gob/decode.go
@@ -871,8 +871,13 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg
return &op
}
+var maxIgnoreNestingDepth = 10000
+
// decIgnoreOpFor returns the decoding op for a field that has no destination.
-func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp) *decOp {
+func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp, depth int) *decOp {
+ if depth > maxIgnoreNestingDepth {
+ error_(errors.New("invalid nesting depth"))
+ }
// If this type is already in progress, it's a recursive type (e.g. map[string]*T).
// Return the pointer to the op we're already building.
if opPtr := inProgress[wireId]; opPtr != nil {
@@ -896,7 +901,7 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp)
errorf("bad data: undefined type %s", wireId.string())
case wire.ArrayT != nil:
elemId := wire.ArrayT.Elem
- elemOp := dec.decIgnoreOpFor(elemId, inProgress)
+ elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1)
op = func(i *decInstr, state *decoderState, value reflect.Value) {
state.dec.ignoreArray(state, *elemOp, wire.ArrayT.Len)
}
@@ -904,15 +909,15 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId, inProgress map[typeId]*decOp)
case wire.MapT != nil:
keyId := dec.wireType[wireId].MapT.Key
elemId := dec.wireType[wireId].MapT.Elem
- keyOp := dec.decIgnoreOpFor(keyId, inProgress)
- elemOp := dec.decIgnoreOpFor(elemId, inProgress)
+ keyOp := dec.decIgnoreOpFor(keyId, inProgress, depth+1)
+ elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1)
op = func(i *decInstr, state *decoderState, value reflect.Value) {
state.dec.ignoreMap(state, *keyOp, *elemOp)
}
case wire.SliceT != nil:
elemId := wire.SliceT.Elem
- elemOp := dec.decIgnoreOpFor(elemId, inProgress)
+ elemOp := dec.decIgnoreOpFor(elemId, inProgress, depth+1)
op = func(i *decInstr, state *decoderState, value reflect.Value) {
state.dec.ignoreSlice(state, *elemOp)
}
@@ -1073,7 +1078,7 @@ func (dec *Decoder) compileSingle(remoteId typeId, ut *userTypeInfo) (engine *de
func (dec *Decoder) compileIgnoreSingle(remoteId typeId) *decEngine {
engine := new(decEngine)
engine.instr = make([]decInstr, 1) // one item
- op := dec.decIgnoreOpFor(remoteId, make(map[typeId]*decOp))
+ op := dec.decIgnoreOpFor(remoteId, make(map[typeId]*decOp), 0)
ovfl := overflow(dec.typeString(remoteId))
engine.instr[0] = decInstr{*op, 0, nil, ovfl}
engine.numInstr = 1
@@ -1118,7 +1123,7 @@ func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEn
localField, present := srt.FieldByName(wireField.Name)
// TODO(r): anonymous names
if !present || !isExported(wireField.Name) {
- op := dec.decIgnoreOpFor(wireField.Id, make(map[typeId]*decOp))
+ op := dec.decIgnoreOpFor(wireField.Id, make(map[typeId]*decOp), 0)
engine.instr[fieldnum] = decInstr{*op, fieldnum, nil, ovfl}
continue
}
diff --git a/src/encoding/gob/gobencdec_test.go b/src/encoding/gob/gobencdec_test.go
index 6d2c8db42d0..1b52ecc6c84 100644
--- a/src/encoding/gob/gobencdec_test.go
+++ b/src/encoding/gob/gobencdec_test.go
@@ -12,6 +12,7 @@ import (
"fmt"
"io"
"net"
+ "reflect"
"strings"
"testing"
"time"
@@ -796,3 +797,26 @@ func TestNetIP(t *testing.T) {
t.Errorf("decoded to %v, want 1.2.3.4", ip.String())
}
}
+
+func TestIngoreDepthLimit(t *testing.T) {
+ // We don't test the actual depth limit because it requires building an
+ // extremely large message, which takes quite a while.
+ oldNestingDepth := maxIgnoreNestingDepth
+ maxIgnoreNestingDepth = 100
+ defer func() { maxIgnoreNestingDepth = oldNestingDepth }()
+ b := new(bytes.Buffer)
+ enc := NewEncoder(b)
+ typ := reflect.TypeOf(int(0))
+ nested := reflect.ArrayOf(1, typ)
+ for i := 0; i < 100; i++ {
+ nested = reflect.ArrayOf(1, nested)
+ }
+ badStruct := reflect.New(reflect.StructOf([]reflect.StructField{{Name: "F", Type: nested}}))
+ enc.Encode(badStruct.Interface())
+ dec := NewDecoder(b)
+ var output struct{ Hello int }
+ expectedErr := "invalid nesting depth"
+ if err := dec.Decode(&output); err == nil || err.Error() != expectedErr {
+ t.Errorf("Decode didn't fail with depth limit of 100: want %q, got %q", expectedErr, err)
+ }
+}
--
2.30.2

View File

@ -1,96 +0,0 @@
From a8d44d0477f2182563e279da115cbc164d639f33 Mon Sep 17 00:00:00 2001
From: Julie Qiu <julieqiu@google.com>
Date: Thu, 23 Jun 2022 23:17:53 +0000
Subject: [PATCH 06/11] [release-branch.go1.17] io/fs: fix stack exhaustion in
Glob
A limit is added to the number of path separators allowed by an input to
Glob, to prevent stack exhaustion issues.
Thanks to Juho Nurminen of Mattermost who reported a similar issue in
path/filepath.
Fixes #53719
Updates #53415
Fixes CVE-2022-30630
Change-Id: I5a9d02591fed90cd3d52627f5945f1301e53465d
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1497588
Reviewed-by: Roland Shoemaker <bracewell@google.com>
(cherry picked from commit fdccc5d7bd0f276d0a8de3a818ca844f0bed5d97)
Reviewed-on: https://go-review.googlesource.com/c/go/+/417072
Reviewed-by: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/417072
---
src/io/fs/glob.go | 14 ++++++++++++--
src/io/fs/glob_test.go | 10 ++++++++++
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/src/io/fs/glob.go b/src/io/fs/glob.go
index 45d9cb61b96..0e529cd05d1 100644
--- a/src/io/fs/glob.go
+++ b/src/io/fs/glob.go
@@ -31,6 +31,16 @@ type GlobFS interface {
// Otherwise, Glob uses ReadDir to traverse the directory tree
// and look for matches for the pattern.
func Glob(fsys FS, pattern string) (matches []string, err error) {
+ return globWithLimit(fsys, pattern, 0)
+}
+
+func globWithLimit(fsys FS, pattern string, depth int) (matches []string, err error) {
+ // This limit is added to prevent stack exhaustion issues. See
+ // CVE-2022-30630.
+ const pathSeparatorsLimit = 10000
+ if depth > pathSeparatorsLimit {
+ return nil, path.ErrBadPattern
+ }
if fsys, ok := fsys.(GlobFS); ok {
return fsys.Glob(pattern)
}
@@ -59,9 +69,9 @@ func Glob(fsys FS, pattern string) (matches []string, err error) {
}
var m []string
- m, err = Glob(fsys, dir)
+ m, err = globWithLimit(fsys, dir, depth+1)
if err != nil {
- return
+ return nil, err
}
for _, d := range m {
matches, err = glob(fsys, d, file, matches)
diff --git a/src/io/fs/glob_test.go b/src/io/fs/glob_test.go
index f19bebed77f..d052eab3713 100644
--- a/src/io/fs/glob_test.go
+++ b/src/io/fs/glob_test.go
@@ -8,6 +8,7 @@ import (
. "io/fs"
"os"
"path"
+ "strings"
"testing"
)
@@ -55,6 +56,15 @@ func TestGlobError(t *testing.T) {
}
}
+func TestCVE202230630(t *testing.T) {
+ // Prior to CVE-2022-30630, a stack exhaustion would occur given a large
+ // number of separators. There is now a limit of 10,000.
+ _, err := Glob(os.DirFS("."), "/*"+strings.Repeat("/", 10001))
+ if err != path.ErrBadPattern {
+ t.Fatalf("Glob returned err=%v, want %v", err, path.ErrBadPattern)
+ }
+}
+
// contains reports whether vector contains the string s.
func contains(vector []string, s string) bool {
for _, elem := range vector {
--
2.30.2

View File

@ -1,85 +0,0 @@
From 7b98bf3a8711126c532033ca89647f3b743b58ec Mon Sep 17 00:00:00 2001
From: Julie Qiu <julieqiu@google.com>
Date: Thu, 23 Jun 2022 23:18:56 +0000
Subject: [PATCH 07/11] [release-branch.go1.17] path/filepath: fix stack
exhaustion in Glob
A limit is added to the number of path separators allowed by an input to
Glob, to prevent stack exhaustion issues.
Thanks to Juho Nurminen of Mattermost who reported the issue.
Fixes #53713
Updates #53416
Fixes CVE-2022-30632
Change-Id: I1b9fd4faa85411a05dbc91dceae1c0c8eb021f07
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1498176
Reviewed-by: Roland Shoemaker <bracewell@google.com>
(cherry picked from commit d182a6d1217fd0d04c9babfa9a7ccd3515435c39)
Reviewed-on: https://go-review.googlesource.com/c/go/+/417073
Reviewed-by: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/417073
---
src/path/filepath/match.go | 12 +++++++++++-
src/path/filepath/match_test.go | 10 ++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/src/path/filepath/match.go b/src/path/filepath/match.go
index c77a26952a6..55ed1d75ae1 100644
--- a/src/path/filepath/match.go
+++ b/src/path/filepath/match.go
@@ -241,6 +241,16 @@ func getEsc(chunk string) (r rune, nchunk string, err error) {
// The only possible returned error is ErrBadPattern, when pattern
// is malformed.
func Glob(pattern string) (matches []string, err error) {
+ return globWithLimit(pattern, 0)
+}
+
+func globWithLimit(pattern string, depth int) (matches []string, err error) {
+ // This limit is used prevent stack exhaustion issues. See CVE-2022-30632.
+ const pathSeparatorsLimit = 10000
+ if depth == pathSeparatorsLimit {
+ return nil, ErrBadPattern
+ }
+
// Check pattern is well-formed.
if _, err := Match(pattern, ""); err != nil {
return nil, err
@@ -270,7 +280,7 @@ func Glob(pattern string) (matches []string, err error) {
}
var m []string
- m, err = Glob(dir)
+ m, err = globWithLimit(dir, depth+1)
if err != nil {
return
}
diff --git a/src/path/filepath/match_test.go b/src/path/filepath/match_test.go
index 375c41a7e9d..d6282596fed 100644
--- a/src/path/filepath/match_test.go
+++ b/src/path/filepath/match_test.go
@@ -155,6 +155,16 @@ func TestGlob(t *testing.T) {
}
}
+func TestCVE202230632(t *testing.T) {
+ // Prior to CVE-2022-30632, this would cause a stack exhaustion given a
+ // large number of separators (more than 4,000,000). There is now a limit
+ // of 10,000.
+ _, err := Glob("/*" + strings.Repeat("/", 10001))
+ if err != ErrBadPattern {
+ t.Fatalf("Glob returned err=%v, want ErrBadPattern", err)
+ }
+}
+
func TestGlobError(t *testing.T) {
bad := []string{`[]`, `nonexist/[]`}
for _, pattern := range bad {
--
2.30.2

View File

@ -1,69 +0,0 @@
From cedbe8f7a1f0d174636e70de68d85499d8025000 Mon Sep 17 00:00:00 2001
From: Roland Shoemaker <roland@golang.org>
Date: Mon, 28 Mar 2022 18:41:26 -0700
Subject: [PATCH 08/11] [release-branch.go1.17] encoding/xml: use iterative
Skip, rather than recursive
Prevents exhausting the stack limit in _incredibly_ deeply nested
structures.
Fixes #53711
Updates #53614
Fixes CVE-2022-28131
Change-Id: I47db4595ce10cecc29fbd06afce7b299868599e6
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1419912
Reviewed-by: Julie Qiu <julieqiu@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
(cherry picked from commit 9278cb78443d2b4deb24cbb5b61c9ba5ac688d49)
Reviewed-on: https://go-review.googlesource.com/c/go/+/417068
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/417068
---
src/encoding/xml/read.go | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/src/encoding/xml/read.go b/src/encoding/xml/read.go
index e0ed8b527ce..c77579880cb 100644
--- a/src/encoding/xml/read.go
+++ b/src/encoding/xml/read.go
@@ -743,12 +743,12 @@ Loop:
}
// Skip reads tokens until it has consumed the end element
-// matching the most recent start element already consumed.
-// It recurs if it encounters a start element, so it can be used to
-// skip nested structures.
+// matching the most recent start element already consumed,
+// skipping nested structures.
// It returns nil if it finds an end element matching the start
// element; otherwise it returns an error describing the problem.
func (d *Decoder) Skip() error {
+ var depth int64
for {
tok, err := d.Token()
if err != nil {
@@ -756,11 +756,12 @@ func (d *Decoder) Skip() error {
}
switch tok.(type) {
case StartElement:
- if err := d.Skip(); err != nil {
- return err
- }
+ depth++
case EndElement:
- return nil
+ if depth == 0 {
+ return nil
+ }
+ depth--
}
}
}
--
2.30.2

View File

@ -1,133 +0,0 @@
From 8a445abc7f7e2ed41112f176a169b97859c8d425 Mon Sep 17 00:00:00 2001
From: Tatiana Bradley <tatiana@golang.org>
Date: Fri, 6 May 2022 11:25:06 -0400
Subject: [PATCH 09/11] [release-branch.go1.17] compress/gzip: fix stack
exhaustion bug in Reader.Read
Replace recursion with iteration in Reader.Read to avoid stack
exhaustion when there are a large number of files.
Fixes CVE-2022-30631
Fixes #53717
Updates #53168
Change-Id: I47d8afe3f2d40b0213ab61431df9b221794dbfe0
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1455673
Reviewed-by: Roland Shoemaker <bracewell@google.com>
Reviewed-by: Julie Qiu <julieqiu@google.com>
(cherry picked from commit cf498969c8a0bae9d7a24b98fc1f66c824a4775d)
Reviewed-on: https://go-review.googlesource.com/c/go/+/417071
Reviewed-by: Heschi Kreinick <heschi@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/417071
---
src/compress/gzip/gunzip.go | 60 +++++++++++++++-----------------
src/compress/gzip/gunzip_test.go | 16 +++++++++
2 files changed, 45 insertions(+), 31 deletions(-)
diff --git a/src/compress/gzip/gunzip.go b/src/compress/gzip/gunzip.go
index 924bce10b7c..237b2b928bf 100644
--- a/src/compress/gzip/gunzip.go
+++ b/src/compress/gzip/gunzip.go
@@ -248,42 +248,40 @@ func (z *Reader) Read(p []byte) (n int, err error) {
return 0, z.err
}
- n, z.err = z.decompressor.Read(p)
- z.digest = crc32.Update(z.digest, crc32.IEEETable, p[:n])
- z.size += uint32(n)
- if z.err != io.EOF {
- // In the normal case we return here.
- return n, z.err
- }
+ for n == 0 {
+ n, z.err = z.decompressor.Read(p)
+ z.digest = crc32.Update(z.digest, crc32.IEEETable, p[:n])
+ z.size += uint32(n)
+ if z.err != io.EOF {
+ // In the normal case we return here.
+ return n, z.err
+ }
- // Finished file; check checksum and size.
- if _, err := io.ReadFull(z.r, z.buf[:8]); err != nil {
- z.err = noEOF(err)
- return n, z.err
- }
- digest := le.Uint32(z.buf[:4])
- size := le.Uint32(z.buf[4:8])
- if digest != z.digest || size != z.size {
- z.err = ErrChecksum
- return n, z.err
- }
- z.digest, z.size = 0, 0
+ // Finished file; check checksum and size.
+ if _, err := io.ReadFull(z.r, z.buf[:8]); err != nil {
+ z.err = noEOF(err)
+ return n, z.err
+ }
+ digest := le.Uint32(z.buf[:4])
+ size := le.Uint32(z.buf[4:8])
+ if digest != z.digest || size != z.size {
+ z.err = ErrChecksum
+ return n, z.err
+ }
+ z.digest, z.size = 0, 0
- // File is ok; check if there is another.
- if !z.multistream {
- return n, io.EOF
- }
- z.err = nil // Remove io.EOF
+ // File is ok; check if there is another.
+ if !z.multistream {
+ return n, io.EOF
+ }
+ z.err = nil // Remove io.EOF
- if _, z.err = z.readHeader(); z.err != nil {
- return n, z.err
+ if _, z.err = z.readHeader(); z.err != nil {
+ return n, z.err
+ }
}
- // Read from next file, if necessary.
- if n > 0 {
- return n, nil
- }
- return z.Read(p)
+ return n, nil
}
// Close closes the Reader. It does not close the underlying io.Reader.
diff --git a/src/compress/gzip/gunzip_test.go b/src/compress/gzip/gunzip_test.go
index 17c23e8a9be..6fe8ddcf558 100644
--- a/src/compress/gzip/gunzip_test.go
+++ b/src/compress/gzip/gunzip_test.go
@@ -515,3 +515,19 @@ func TestTruncatedStreams(t *testing.T) {
}
}
}
+
+func TestCVE202230631(t *testing.T) {
+ var empty = []byte{0x1f, 0x8b, 0x08, 0x00, 0xa7, 0x8f, 0x43, 0x62, 0x00,
+ 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+ r := bytes.NewReader(bytes.Repeat(empty, 4e6))
+ z, err := NewReader(r)
+ if err != nil {
+ t.Fatalf("NewReader: got %v, want nil", err)
+ }
+ // Prior to CVE-2022-30631 fix, this would cause an unrecoverable panic due
+ // to stack exhaustion.
+ _, err = z.Read(make([]byte, 10))
+ if err != io.EOF {
+ t.Errorf("Reader.Read: got %v, want %v", err, io.EOF)
+ }
+}
--
2.30.2

View File

@ -1,69 +0,0 @@
From b2815a72bef3f829c5ac7735feddb034f5501cc0 Mon Sep 17 00:00:00 2001
From: Tatiana Bradley <tatiana@golang.org>
Date: Thu, 12 May 2022 14:58:29 -0400
Subject: [PATCH 10/11] [release-branch.go1.17] crypto/tls: randomly generate
ticket_age_add
As required by RFC 8446, section 4.6.1, ticket_age_add now holds a
random 32-bit value. Before this change, this value was always set
to 0.
This change also documents the reasoning for always setting
ticket_nonce to 0. The value ticket_nonce must be unique per
connection, but we only ever send one ticket per connection.
Updates #52814
Fixes #52832
Fixes CVE-2022-30629
Change-Id: I6c2fc6ca0376b7b968abd59d6d3d3854c1ab68bb
Reviewed-on: https://go-review.googlesource.com/c/go/+/405994
Reviewed-by: Tatiana Bradley <tatiana@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Run-TryBot: Tatiana Bradley <tatiana@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
(cherry picked from commit fe4de36198794c447fbd9d7cc2d7199a506c76a5)
Reviewed-on: https://go-review.googlesource.com/c/go/+/408574
Run-TryBot: Roland Shoemaker <roland@golang.org>
Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/408574
---
src/crypto/tls/handshake_server_tls13.go | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go
index 08251b84def..6aa52698a3a 100644
--- a/src/crypto/tls/handshake_server_tls13.go
+++ b/src/crypto/tls/handshake_server_tls13.go
@@ -10,6 +10,7 @@ import (
"crypto"
"crypto/hmac"
"crypto/rsa"
+ "encoding/binary"
"errors"
"hash"
"io"
@@ -741,6 +742,19 @@ func (hs *serverHandshakeStateTLS13) sendSessionTickets() error {
}
m.lifetime = uint32(maxSessionTicketLifetime / time.Second)
+ // ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1
+ // The value is not stored anywhere; we never need to check the ticket age
+ // because 0-RTT is not supported.
+ ageAdd := make([]byte, 4)
+ _, err = hs.c.config.rand().Read(ageAdd)
+ if err != nil {
+ return err
+ }
+ m.ageAdd = binary.LittleEndian.Uint32(ageAdd)
+
+ // ticket_nonce, which must be unique per connection, is always left at
+ // zero because we only ever send one ticket per connection.
+
if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
return err
}
--
2.30.2

View File

@ -1,246 +0,0 @@
From 07a30769186210c1b1e25943824743355c4e50f5 Mon Sep 17 00:00:00 2001
From: Roland Shoemaker <roland@golang.org>
Date: Mon, 25 Apr 2022 19:02:35 -0700
Subject: [PATCH 11/11] [release-branch.go1.17] crypto/rand: properly handle
large Read on windows
Use the batched reader to chunk large Read calls on windows to a max of
1 << 31 - 1 bytes. This prevents an infinite loop when trying to read
more than 1 << 32 -1 bytes, due to how RtlGenRandom works.
This change moves the batched function from rand_unix.go to rand.go,
since it is now needed for both windows and unix implementations.
Updates #52561
Fixes #52932
Fixes CVE-2022-30634
Change-Id: Id98fc4b1427e5cb2132762a445b2aed646a37473
Reviewed-on: https://go-review.googlesource.com/c/go/+/402257
Run-TryBot: Roland Shoemaker <roland@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Filippo Valsorda <valsorda@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
(cherry picked from commit bb1f4416180511231de6d17a1f2f55c82aafc863)
Reviewed-on: https://go-review.googlesource.com/c/go/+/406635
Reviewed-by: Damien Neil <dneil@google.com>
Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/406635
---
src/crypto/rand/rand.go | 18 ++++++++++++++++++
src/crypto/rand/rand_batched.go | 22 ++++++----------------
src/crypto/rand/rand_batched_test.go | 21 +++++++++++----------
src/crypto/rand/rand_getentropy.go | 6 +++---
src/crypto/rand/rand_unix.go | 4 ++--
src/crypto/rand/rand_windows.go | 18 ++++++------------
6 files changed, 46 insertions(+), 43 deletions(-)
diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go
index fddd1147e6e..f2c276008d7 100644
--- a/src/crypto/rand/rand.go
+++ b/src/crypto/rand/rand.go
@@ -23,3 +23,21 @@ var Reader io.Reader
func Read(b []byte) (n int, err error) {
return io.ReadFull(Reader, b)
}
+
+// batched returns a function that calls f to populate a []byte by chunking it
+// into subslices of, at most, readMax bytes.
+func batched(f func([]byte) error, readMax int) func([]byte) error {
+ return func(out []byte) error {
+ for len(out) > 0 {
+ read := len(out)
+ if read > readMax {
+ read = readMax
+ }
+ if err := f(out[:read]); err != nil {
+ return err
+ }
+ out = out[read:]
+ }
+ return nil
+ }
+}
diff --git a/src/crypto/rand/rand_batched.go b/src/crypto/rand/rand_batched.go
index d7c5bf3562d..8df715fdd14 100644
--- a/src/crypto/rand/rand_batched.go
+++ b/src/crypto/rand/rand_batched.go
@@ -8,6 +8,7 @@
package rand
import (
+ "errors"
"internal/syscall/unix"
)
@@ -16,20 +17,6 @@ func init() {
altGetRandom = batched(getRandomBatch, maxGetRandomRead)
}
-// batched returns a function that calls f to populate a []byte by chunking it
-// into subslices of, at most, readMax bytes.
-func batched(f func([]byte) bool, readMax int) func([]byte) bool {
- return func(buf []byte) bool {
- for len(buf) > readMax {
- if !f(buf[:readMax]) {
- return false
- }
- buf = buf[readMax:]
- }
- return len(buf) == 0 || f(buf)
- }
-}
-
// If the kernel is too old to support the getrandom syscall(),
// unix.GetRandom will immediately return ENOSYS and we will then fall back to
// reading from /dev/urandom in rand_unix.go. unix.GetRandom caches the ENOSYS
@@ -37,7 +24,10 @@ func batched(f func([]byte) bool, readMax int) func([]byte) bool {
// If the kernel supports the getrandom() syscall, unix.GetRandom will block
// until the kernel has sufficient randomness (as we don't use GRND_NONBLOCK).
// In this case, unix.GetRandom will not return an error.
-func getRandomBatch(p []byte) (ok bool) {
+func getRandomBatch(p []byte) error {
n, err := unix.GetRandom(p, 0)
- return n == len(p) && err == nil
+ if n != len(p) {
+ return errors.New("short read")
+ }
+ return err
}
diff --git a/src/crypto/rand/rand_batched_test.go b/src/crypto/rand/rand_batched_test.go
index 2d20922c825..b56345e50f1 100644
--- a/src/crypto/rand/rand_batched_test.go
+++ b/src/crypto/rand/rand_batched_test.go
@@ -9,20 +9,21 @@ package rand
import (
"bytes"
+ "errors"
"testing"
)
func TestBatched(t *testing.T) {
- fillBatched := batched(func(p []byte) bool {
+ fillBatched := batched(func(p []byte) error {
for i := range p {
p[i] = byte(i)
}
- return true
+ return nil
}, 5)
p := make([]byte, 13)
- if !fillBatched(p) {
- t.Fatal("batched function returned false")
+ if err := fillBatched(p); err != nil {
+ t.Fatalf("batched function returned error: %s", err)
}
expected := []byte{0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2}
if !bytes.Equal(expected, p) {
@@ -31,15 +32,15 @@ func TestBatched(t *testing.T) {
}
func TestBatchedError(t *testing.T) {
- b := batched(func(p []byte) bool { return false }, 5)
- if b(make([]byte, 13)) {
- t.Fatal("batched function should have returned false")
+ b := batched(func(p []byte) error { return errors.New("") }, 5)
+ if b(make([]byte, 13)) == nil {
+ t.Fatal("batched function should have returned an error")
}
}
func TestBatchedEmpty(t *testing.T) {
- b := batched(func(p []byte) bool { return false }, 5)
- if !b(make([]byte, 0)) {
- t.Fatal("empty slice should always return true")
+ b := batched(func(p []byte) error { return errors.New("") }, 5)
+ if err := b(make([]byte, 0)); err != nil {
+ t.Fatalf("empty slice should always return nil: %s", err)
}
}
diff --git a/src/crypto/rand/rand_getentropy.go b/src/crypto/rand/rand_getentropy.go
index dd725372ad9..b1c19f3d0da 100644
--- a/src/crypto/rand/rand_getentropy.go
+++ b/src/crypto/rand/rand_getentropy.go
@@ -15,7 +15,7 @@ func init() {
altGetRandom = getEntropy
}
-func getEntropy(p []byte) (ok bool) {
+func getEntropy(p []byte) error {
// getentropy(2) returns a maximum of 256 bytes per call
for i := 0; i < len(p); i += 256 {
end := i + 256
@@ -24,8 +24,8 @@ func getEntropy(p []byte) (ok bool) {
}
err := unix.GetEntropy(p[i:end])
if err != nil {
- return false
+ return err
}
}
- return true
+ return nil
}
diff --git a/src/crypto/rand/rand_unix.go b/src/crypto/rand/rand_unix.go
index 81277eb6a5d..3d11159a340 100644
--- a/src/crypto/rand/rand_unix.go
+++ b/src/crypto/rand/rand_unix.go
@@ -46,7 +46,7 @@ type devReader struct {
// altGetRandom if non-nil specifies an OS-specific function to get
// urandom-style randomness.
-var altGetRandom func([]byte) (ok bool)
+var altGetRandom func([]byte) (err error)
func warnBlocked() {
println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
@@ -59,7 +59,7 @@ func (r *devReader) Read(b []byte) (n int, err error) {
t := time.AfterFunc(60*time.Second, warnBlocked)
defer t.Stop()
}
- if altGetRandom != nil && r.name == urandomDevice && altGetRandom(b) {
+ if altGetRandom != nil && r.name == urandomDevice && altGetRandom(b) == nil {
return len(b), nil
}
r.mu.Lock()
diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go
index 7379f1489ad..6c0655c72b6 100644
--- a/src/crypto/rand/rand_windows.go
+++ b/src/crypto/rand/rand_windows.go
@@ -9,7 +9,6 @@ package rand
import (
"internal/syscall/windows"
- "os"
)
func init() { Reader = &rngReader{} }
@@ -17,16 +16,11 @@ func init() { Reader = &rngReader{} }
type rngReader struct{}
func (r *rngReader) Read(b []byte) (n int, err error) {
- // RtlGenRandom only accepts 2**32-1 bytes at a time, so truncate.
- inputLen := uint32(len(b))
-
- if inputLen == 0 {
- return 0, nil
- }
-
- err = windows.RtlGenRandom(b)
- if err != nil {
- return 0, os.NewSyscallError("RtlGenRandom", err)
+ // RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at
+ // most 1<<31-1 bytes at a time so that this works the same on 32-bit
+ // and 64-bit systems.
+ if err := batched(windows.RtlGenRandom, 1<<31-1)(b); err != nil {
+ return 0, err
}
- return int(inputLen), nil
+ return len(b), nil
}
--
2.30.2

View File

@ -1,125 +0,0 @@
From dc903a8196a831d23e9b6504239e09a9e6bcd98a Mon Sep 17 00:00:00 2001
From: Roland Shoemaker <roland@golang.org>
Date: Fri, 15 Jul 2022 10:43:44 -0700
Subject: [PATCH] [release-branch.go1.17] math/big: check buffer lengths in
GobDecode
In Float.GobDecode and Rat.GobDecode, check buffer sizes before
indexing slices.
Updates #53871
Fixes #54094
Change-Id: I1b652c32c2bc7a0e8aa7620f7be9b2740c568b0a
Reviewed-on: https://go-review.googlesource.com/c/go/+/417774
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Tatiana Bradley <tatiana@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
(cherry picked from commit 055113ef364337607e3e72ed7d48df67fde6fc66)
Reviewed-on: https://go-review.googlesource.com/c/go/+/419814
Reviewed-by: Julie Qiu <julieqiu@google.com>
---
src/math/big/floatmarsh.go | 7 +++++++
src/math/big/floatmarsh_test.go | 12 ++++++++++++
src/math/big/ratmarsh.go | 6 ++++++
src/math/big/ratmarsh_test.go | 12 ++++++++++++
4 files changed, 37 insertions(+)
diff --git a/src/math/big/floatmarsh.go b/src/math/big/floatmarsh.go
index d1c1dab069..990e085abe 100644
--- a/src/math/big/floatmarsh.go
+++ b/src/math/big/floatmarsh.go
@@ -8,6 +8,7 @@ package big
import (
"encoding/binary"
+ "errors"
"fmt"
)
@@ -67,6 +68,9 @@ func (z *Float) GobDecode(buf []byte) error {
*z = Float{}
return nil
}
+ if len(buf) < 6 {
+ return errors.New("Float.GobDecode: buffer too small")
+ }
if buf[0] != floatGobVersion {
return fmt.Errorf("Float.GobDecode: encoding version %d not supported", buf[0])
@@ -83,6 +87,9 @@ func (z *Float) GobDecode(buf []byte) error {
z.prec = binary.BigEndian.Uint32(buf[2:])
if z.form == finite {
+ if len(buf) < 10 {
+ return errors.New("Float.GobDecode: buffer too small for finite form float")
+ }
z.exp = int32(binary.BigEndian.Uint32(buf[6:]))
z.mant = z.mant.setBytes(buf[10:])
}
diff --git a/src/math/big/floatmarsh_test.go b/src/math/big/floatmarsh_test.go
index c056d78b80..401f45a51f 100644
--- a/src/math/big/floatmarsh_test.go
+++ b/src/math/big/floatmarsh_test.go
@@ -137,3 +137,15 @@ func TestFloatJSONEncoding(t *testing.T) {
}
}
}
+
+func TestFloatGobDecodeShortBuffer(t *testing.T) {
+ for _, tc := range [][]byte{
+ []byte{0x1, 0x0, 0x0, 0x0},
+ []byte{0x1, 0xfa, 0x0, 0x0, 0x0, 0x0},
+ } {
+ err := NewFloat(0).GobDecode(tc)
+ if err == nil {
+ t.Error("expected GobDecode to return error for malformed input")
+ }
+ }
+}
diff --git a/src/math/big/ratmarsh.go b/src/math/big/ratmarsh.go
index fbc7b6002d..56102e845b 100644
--- a/src/math/big/ratmarsh.go
+++ b/src/math/big/ratmarsh.go
@@ -45,12 +45,18 @@ func (z *Rat) GobDecode(buf []byte) error {
*z = Rat{}
return nil
}
+ if len(buf) < 5 {
+ return errors.New("Rat.GobDecode: buffer too small")
+ }
b := buf[0]
if b>>1 != ratGobVersion {
return fmt.Errorf("Rat.GobDecode: encoding version %d not supported", b>>1)
}
const j = 1 + 4
i := j + binary.BigEndian.Uint32(buf[j-4:j])
+ if len(buf) < int(i) {
+ return errors.New("Rat.GobDecode: buffer too small")
+ }
z.a.neg = b&1 != 0
z.a.abs = z.a.abs.setBytes(buf[j:i])
z.b.abs = z.b.abs.setBytes(buf[i:])
diff --git a/src/math/big/ratmarsh_test.go b/src/math/big/ratmarsh_test.go
index 351d109f8d..55a9878bb8 100644
--- a/src/math/big/ratmarsh_test.go
+++ b/src/math/big/ratmarsh_test.go
@@ -123,3 +123,15 @@ func TestRatXMLEncoding(t *testing.T) {
}
}
}
+
+func TestRatGobDecodeShortBuffer(t *testing.T) {
+ for _, tc := range [][]byte{
+ []byte{0x2},
+ []byte{0x2, 0x0, 0x0, 0x0, 0xff},
+ } {
+ err := NewRat(1, 2).GobDecode(tc)
+ if err == nil {
+ t.Error("expected GobDecode to return error for malformed input")
+ }
+ }
+}
--
2.30.2

View File

@ -1,103 +0,0 @@
From e903e474f9632a151fff2df3dd3e891395f1a8f1 Mon Sep 17 00:00:00 2001
From: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Date: Fri, 22 Apr 2022 10:07:51 +0900
Subject: [PATCH 1/2] path/filepath: do not remove prefix "." when following
path contains ":".
Fixes #52476
Change-Id: I9eb72ac7dbccd6322d060291f31831dc389eb9bb
Reviewed-on: https://go-review.googlesource.com/c/go/+/401595
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reference:https://go-review.googlesource.com/c/go/+/401595/
Conflict:NA
---
src/path/filepath/path.go | 14 +++++++++++++-
src/path/filepath/path_test.go | 3 +++
src/path/filepath/path_windows_test.go | 26 ++++++++++++++++++++++++++
3 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go
index b56534dead..8300a32cb1 100644
--- a/src/path/filepath/path.go
+++ b/src/path/filepath/path.go
@@ -117,9 +117,21 @@ func Clean(path string) string {
case os.IsPathSeparator(path[r]):
// empty path element
r++
- case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
+ case path[r] == '.' && r+1 == n:
// . element
r++
+ case path[r] == '.' && os.IsPathSeparator(path[r+1]):
+ // ./ element
+ r++
+
+ for r < len(path) && os.IsPathSeparator(path[r]) {
+ r++
+ }
+ if out.w == 0 && volumeNameLen(path[r:]) > 0 {
+ // When joining prefix "." and an absolute path on Windows,
+ // the prefix should not be removed.
+ out.append('.')
+ }
case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
// .. element: remove to last separator
r += 2
diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go
index bc5509b49c..ed17a8854d 100644
--- a/src/path/filepath/path_test.go
+++ b/src/path/filepath/path_test.go
@@ -93,6 +93,9 @@ var wincleantests = []PathTest{
{`//host/share/foo/../baz`, `\\host\share\baz`},
{`\\a\b\..\c`, `\\a\b\c`},
{`\\a\b`, `\\a\b`},
+ {`.\c:`, `.\c:`},
+ {`.\c:\foo`, `.\c:\foo`},
+ {`.\c:foo`, `.\c:foo`},
}
func TestClean(t *testing.T) {
diff --git a/src/path/filepath/path_windows_test.go b/src/path/filepath/path_windows_test.go
index 76a459ac96..3edafb5a85 100644
--- a/src/path/filepath/path_windows_test.go
+++ b/src/path/filepath/path_windows_test.go
@@ -530,3 +530,29 @@ func TestNTNamespaceSymlink(t *testing.T) {
t.Errorf(`EvalSymlinks(%q): got %q, want %q`, filelink, got, want)
}
}
+
+func TestIssue52476(t *testing.T) {
+ tests := []struct {
+ lhs, rhs string
+ want string
+ }{
+ {`..\.`, `C:`, `..\C:`},
+ {`..`, `C:`, `..\C:`},
+ {`.`, `:`, `:`},
+ {`.`, `C:`, `.\C:`},
+ {`.`, `C:/a/b/../c`, `.\C:\a\c`},
+ {`.`, `\C:`, `.\C:`},
+ {`C:\`, `.`, `C:\`},
+ {`C:\`, `C:\`, `C:\C:`},
+ {`C`, `:`, `C\:`},
+ {`\.`, `C:`, `\C:`},
+ {`\`, `C:`, `\C:`},
+ }
+
+ for _, test := range tests {
+ got := filepath.Join(test.lhs, test.rhs)
+ if got != test.want {
+ t.Errorf(`Join(%q, %q): got %q, want %q`, test.lhs, test.rhs, got, test.want)
+ }
+ }
+}
--
2.30.2

View File

@ -1,53 +0,0 @@
From 66cff0cda766c1533373fabf3bc26fc3397e55d5 Mon Sep 17 00:00:00 2001
From: Damien Neil <dneil@google.com>
Date: Tue, 12 Apr 2022 13:38:17 -0700
Subject: [PATCH 2/2] [release-branch.go1.17] syscall: check correct group in
Faccessat
The Faccessat call checks the user, group, or other permission bits of a
file to see if the calling process can access it. The test to see if the
group permissions should be used was made with the wrong group id, using
the process's group id rather than the file's group id. Fix this to use
the correct group id.
No test since we cannot easily change file permissions when not running
as root and the test is meaningless if running as root.
For #52313
Fixes #52439
Change-Id: I4e2c84754b0af7830b40fd15dedcbc58374d75ee
Reviewed-on: https://go-review.googlesource.com/c/go/+/399539
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
(cherry picked from commit f66925e854e71e0c54b581885380a490d7afa30c)
Reviewed-on: https://go-review.googlesource.com/c/go/+/401078
Auto-Submit: Tatiana Bradley <tatiana@golang.org>
Run-TryBot: Tatiana Bradley <tatiana@golang.org>
Run-TryBot: Damien Neil <dneil@google.com>
Auto-Submit: Damien Neil <dneil@google.com>
Reviewed-by: Tatiana Bradley <tatiana@golang.org>
Reference:https://go-review.googlesource.com/c/go/+/401078/
Conflict:NA
---
src/syscall/syscall_linux.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go
index dfce3d0a4b..3387f3bdc2 100644
--- a/src/syscall/syscall_linux.go
+++ b/src/syscall/syscall_linux.go
@@ -109,7 +109,7 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
gid = Getgid()
}
- if uint32(gid) == st.Gid || isGroupMember(gid) {
+ if uint32(gid) == st.Gid || isGroupMember(int(st.Gid)) {
fmode = (st.Mode >> 3) & 7
} else {
fmode = st.Mode & 7
--
2.30.2

View File

@ -1,99 +0,0 @@
From b2058191785138021b635f609de3d5f651ec02cd Mon Sep 17 00:00:00 2001
From: Damien Neil <dneil@google.com>
Date: Mon, 22 Aug 2022 16:21:02 -0700
Subject: [PATCH] [release-branch.go1.18] net/http: update bundled
golang.org/x/net/http2
Disable cmd/internal/moddeps test, since this update includes PRIVATE
track fixes.
Fixes CVE-2022-27664
Fixes #53977
For #54658.
Change-Id: I84b0b8f61e49e15ef55ef8d738730107a3cf849b
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1554415
Reviewed-by: Roland Shoemaker <bracewell@google.com>
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/428635
Reviewed-by: Tatiana Bradley <tatiana@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
Conflict:NA
Reference:https://go-review.googlesource.com/c/go/+/428635/
---
src/cmd/internal/moddeps/moddeps_test.go | 2 ++
src/net/http/h2_bundle.go | 21 +++++++++++++--------
2 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/src/cmd/internal/moddeps/moddeps_test.go b/src/cmd/internal/moddeps/moddeps_test.go
index 56c3b2585c..3306e29431 100644
--- a/src/cmd/internal/moddeps/moddeps_test.go
+++ b/src/cmd/internal/moddeps/moddeps_test.go
@@ -34,6 +34,8 @@ import (
// See issues 36852, 41409, and 43687.
// (Also see golang.org/issue/27348.)
func TestAllDependencies(t *testing.T) {
+ t.Skip("TODO(#53977): 1.18.5 contains unreleased changes from vendored modules")
+
goBin := testenv.GoToolPath(t)
// Ensure that all packages imported within GOROOT
diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go
index 1b73da7f21..d7e2f764c8 100644
--- a/src/net/http/h2_bundle.go
+++ b/src/net/http/h2_bundle.go
@@ -3339,10 +3339,11 @@ func (s http2SettingID) String() string {
// name (key). See httpguts.ValidHeaderName for the base rules.
//
// Further, http2 says:
-// "Just as in HTTP/1.x, header field names are strings of ASCII
-// characters that are compared in a case-insensitive
-// fashion. However, header field names MUST be converted to
-// lowercase prior to their encoding in HTTP/2. "
+//
+// "Just as in HTTP/1.x, header field names are strings of ASCII
+// characters that are compared in a case-insensitive
+// fashion. However, header field names MUST be converted to
+// lowercase prior to their encoding in HTTP/2. "
func http2validWireHeaderFieldName(v string) bool {
if len(v) == 0 {
return false
@@ -3533,8 +3534,8 @@ func (s *http2sorter) SortStrings(ss []string) {
// validPseudoPath reports whether v is a valid :path pseudo-header
// value. It must be either:
//
-// *) a non-empty string starting with '/'
-// *) the string '*', for OPTIONS requests.
+// *) a non-empty string starting with '/'
+// *) the string '*', for OPTIONS requests.
//
// For now this is only used a quick check for deciding when to clean
// up Opaque URLs before sending requests from the Transport.
@@ -4999,6 +5000,9 @@ func (sc *http2serverConn) startGracefulShutdownInternal() {
func (sc *http2serverConn) goAway(code http2ErrCode) {
sc.serveG.check()
if sc.inGoAway {
+ if sc.goAwayCode == http2ErrCodeNo {
+ sc.goAwayCode = code
+ }
return
}
sc.inGoAway = true
@@ -6211,8 +6215,9 @@ func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) {
// prior to the headers being written. If the set of trailers is fixed
// or known before the header is written, the normal Go trailers mechanism
// is preferred:
-// https://golang.org/pkg/net/http/#ResponseWriter
-// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
+//
+// https://golang.org/pkg/net/http/#ResponseWriter
+// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
const http2TrailerPrefix = "Trailer:"
// promoteUndeclaredTrailers permits http.Handlers to set trailers
--
2.30.2

View File

@ -1,386 +0,0 @@
From 8b3a5d153b7b255bafd1a82d61505088356d0458 Mon Sep 17 00:00:00 2001
From: Russ Cox <rsc@golang.org>
Date: Wed, 28 Sep 2022 11:18:51 -0400
Subject: [PATCH] regexp: limit size of parsed regexps
Set a 128 MB limit on the amount of space used by []syntax.Inst
in the compiled form corresponding to a given regexp.
Also set a 128 MB limit on the rune storage in the *syntax.Regexp
tree itself.
Thanks to Adam Korczynski (ADA Logics) and OSS-Fuzz for reporting this issue.
Fixes CVE-2022-41715.
Updates #55949.
Fixes #55950.
Change-Id: Ia656baed81564436368cf950e1c5409752f28e1b
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1592136
TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com>
Reviewed-by: Damien Neil <dneil@google.com>
Run-TryBot: Roland Shoemaker <bracewell@google.com>
Reviewed-by: Julie Qiu <julieqiu@google.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/438501
Run-TryBot: Carlos Amedee <carlos@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
---
src/regexp/syntax/parse.go | 222 +++++++++++++++++++++++++++++++-
src/regexp/syntax/parse_test.go | 11 +-
2 files changed, 224 insertions(+), 9 deletions(-)
diff --git a/src/regexp/syntax/parse.go b/src/regexp/syntax/parse.go
index 7b40309..67254d6 100644
--- a/src/regexp/syntax/parse.go
+++ b/src/regexp/syntax/parse.go
@@ -43,6 +43,7 @@ const (
ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator"
ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression"
ErrUnexpectedParen ErrorCode = "unexpected )"
+ ErrNestingDepth ErrorCode = "expression nests too deeply"
)
func (e ErrorCode) String() string {
@@ -76,13 +77,63 @@ const (
opVerticalBar
)
+// maxHeight is the maximum height of a regexp parse tree.
+// It is somewhat arbitrarily chosen, but the idea is to be large enough
+// that no one will actually hit in real use but at the same time small enough
+// that recursion on the Regexp tree will not hit the 1GB Go stack limit.
+// The maximum amount of stack for a single recursive frame is probably
+// closer to 1kB, so this could potentially be raised, but it seems unlikely
+// that people have regexps nested even this deeply.
+// We ran a test on Google's C++ code base and turned up only
+// a single use case with depth > 100; it had depth 128.
+// Using depth 1000 should be plenty of margin.
+// As an optimization, we don't even bother calculating heights
+// until we've allocated at least maxHeight Regexp structures.
+const maxHeight = 1000
+
+// maxSize is the maximum size of a compiled regexp in Insts.
+// It too is somewhat arbitrarily chosen, but the idea is to be large enough
+// to allow significant regexps while at the same time small enough that
+// the compiled form will not take up too much memory.
+// 128 MB is enough for a 3.3 million Inst structures, which roughly
+// corresponds to a 3.3 MB regexp.
+const (
+ maxSize = 128 << 20 / instSize
+ instSize = 5 * 8 // byte, 2 uint32, slice is 5 64-bit words
+)
+
+// maxRunes is the maximum number of runes allowed in a regexp tree
+// counting the runes in all the nodes.
+// Ignoring character classes p.numRunes is always less than the length of the regexp.
+// Character classes can make it much larger: each \pL adds 1292 runes.
+// 128 MB is enough for 32M runes, which is over 26k \pL instances.
+// Note that repetitions do not make copies of the rune slices,
+// so \pL{1000} is only one rune slice, not 1000.
+// We could keep a cache of character classes we've seen,
+// so that all the \pL we see use the same rune list,
+// but that doesn't remove the problem entirely:
+// consider something like [\pL01234][\pL01235][\pL01236]...[\pL^&*()].
+// And because the Rune slice is exposed directly in the Regexp,
+// there is not an opportunity to change the representation to allow
+// partial sharing between different character classes.
+// So the limit is the best we can do.
+const (
+ maxRunes = 128 << 20 / runeSize
+ runeSize = 4 // rune is int32
+)
+
type parser struct {
flags Flags // parse mode flags
stack []*Regexp // stack of parsed expressions
free *Regexp
numCap int // number of capturing groups seen
wholeRegexp string
- tmpClass []rune // temporary char class work space
+ tmpClass []rune // temporary char class work space
+ numRegexp int // number of regexps allocated
+ numRunes int // number of runes in char classes
+ repeats int64 // product of all repetitions seen
+ height map[*Regexp]int // regexp height, for height limit check
+ size map[*Regexp]int64 // regexp compiled size, for size limit check
}
func (p *parser) newRegexp(op Op) *Regexp {
@@ -92,20 +143,155 @@ func (p *parser) newRegexp(op Op) *Regexp {
*re = Regexp{}
} else {
re = new(Regexp)
+ p.numRegexp++
}
re.Op = op
return re
}
func (p *parser) reuse(re *Regexp) {
+ if p.height != nil {
+ delete(p.height, re)
+ }
re.Sub0[0] = p.free
p.free = re
}
+func (p *parser) checkLimits(re *Regexp) {
+ if p.numRunes > maxRunes {
+ panic(ErrInternalError)
+ }
+ p.checkSize(re)
+ p.checkHeight(re)
+}
+
+func (p *parser) checkSize(re *Regexp) {
+ if p.size == nil {
+ // We haven't started tracking size yet.
+ // Do a relatively cheap check to see if we need to start.
+ // Maintain the product of all the repeats we've seen
+ // and don't track if the total number of regexp nodes
+ // we've seen times the repeat product is in budget.
+ if p.repeats == 0 {
+ p.repeats = 1
+ }
+ if re.Op == OpRepeat {
+ n := re.Max
+ if n == -1 {
+ n = re.Min
+ }
+ if n <= 0 {
+ n = 1
+ }
+ if int64(n) > maxSize/p.repeats {
+ p.repeats = maxSize
+ } else {
+ p.repeats *= int64(n)
+ }
+ }
+ if int64(p.numRegexp) < maxSize/p.repeats {
+ return
+ }
+
+ // We need to start tracking size.
+ // Make the map and belatedly populate it
+ // with info about everything we've constructed so far.
+ p.size = make(map[*Regexp]int64)
+ for _, re := range p.stack {
+ p.checkSize(re)
+ }
+ }
+
+ if p.calcSize(re, true) > maxSize {
+ panic(ErrInternalError)
+ }
+}
+
+func (p *parser) calcSize(re *Regexp, force bool) int64 {
+ if !force {
+ if size, ok := p.size[re]; ok {
+ return size
+ }
+ }
+
+ var size int64
+ switch re.Op {
+ case OpLiteral:
+ size = int64(len(re.Rune))
+ case OpCapture, OpStar:
+ // star can be 1+ or 2+; assume 2 pessimistically
+ size = 2 + p.calcSize(re.Sub[0], false)
+ case OpPlus, OpQuest:
+ size = 1 + p.calcSize(re.Sub[0], false)
+ case OpConcat:
+ for _, sub := range re.Sub {
+ size += p.calcSize(sub, false)
+ }
+ case OpAlternate:
+ for _, sub := range re.Sub {
+ size += p.calcSize(sub, false)
+ }
+ if len(re.Sub) > 1 {
+ size += int64(len(re.Sub)) - 1
+ }
+ case OpRepeat:
+ sub := p.calcSize(re.Sub[0], false)
+ if re.Max == -1 {
+ if re.Min == 0 {
+ size = 2 + sub // x*
+ } else {
+ size = 1 + int64(re.Min)*sub // xxx+
+ }
+ break
+ }
+ // x{2,5} = xx(x(x(x)?)?)?
+ size = int64(re.Max)*sub + int64(re.Max-re.Min)
+ }
+
+ if size < 1 {
+ size = 1
+ }
+ p.size[re] = size
+ return size
+}
+
+func (p *parser) checkHeight(re *Regexp) {
+ if p.numRegexp < maxHeight {
+ return
+ }
+ if p.height == nil {
+ p.height = make(map[*Regexp]int)
+ for _, re := range p.stack {
+ p.checkHeight(re)
+ }
+ }
+ if p.calcHeight(re, true) > maxHeight {
+ panic(ErrNestingDepth)
+ }
+}
+
+func (p *parser) calcHeight(re *Regexp, force bool) int {
+ if !force {
+ if h, ok := p.height[re]; ok {
+ return h
+ }
+ }
+ h := 1
+ for _, sub := range re.Sub {
+ hsub := p.calcHeight(sub, false)
+ if h < 1+hsub {
+ h = 1 + hsub
+ }
+ }
+ p.height[re] = h
+ return h
+}
+
// Parse stack manipulation.
// push pushes the regexp re onto the parse stack and returns the regexp.
func (p *parser) push(re *Regexp) *Regexp {
+ p.numRunes += len(re.Rune)
if re.Op == OpCharClass && len(re.Rune) == 2 && re.Rune[0] == re.Rune[1] {
// Single rune.
if p.maybeConcat(re.Rune[0], p.flags&^FoldCase) {
@@ -137,6 +323,7 @@ func (p *parser) push(re *Regexp) *Regexp {
}
p.stack = append(p.stack, re)
+ p.checkLimits(re)
return re
}
@@ -246,6 +433,7 @@ func (p *parser) repeat(op Op, min, max int, before, after, lastRepeat string) (
re.Sub = re.Sub0[:1]
re.Sub[0] = sub
p.stack[n-1] = re
+ p.checkLimits(re)
if op == OpRepeat && (min >= 2 || max >= 2) && !repeatIsValid(re, 1000) {
return "", &Error{ErrInvalidRepeatSize, before[:len(before)-len(after)]}
@@ -390,12 +578,16 @@ func (p *parser) collapse(subs []*Regexp, op Op) *Regexp {
// frees (passes to p.reuse) any removed *Regexps.
//
// For example,
-// ABC|ABD|AEF|BCX|BCY
+//
+// ABC|ABD|AEF|BCX|BCY
+//
// simplifies by literal prefix extraction to
-// A(B(C|D)|EF)|BC(X|Y)
+//
+// A(B(C|D)|EF)|BC(X|Y)
+//
// which simplifies by character class introduction to
-// A(B[CD]|EF)|BC[XY]
//
+// A(B[CD]|EF)|BC[XY]
func (p *parser) factor(sub []*Regexp) []*Regexp {
if len(sub) < 2 {
return sub
@@ -449,6 +641,7 @@ func (p *parser) factor(sub []*Regexp) []*Regexp {
for j := start; j < i; j++ {
sub[j] = p.removeLeadingString(sub[j], len(str))
+ p.checkLimits(sub[j])
}
suffix := p.collapse(sub[start:i], OpAlternate) // recurse
@@ -506,6 +699,7 @@ func (p *parser) factor(sub []*Regexp) []*Regexp {
for j := start; j < i; j++ {
reuse := j != start // prefix came from sub[start]
sub[j] = p.removeLeadingRegexp(sub[j], reuse)
+ p.checkLimits(sub[j])
}
suffix := p.collapse(sub[start:i], OpAlternate) // recurse
@@ -693,6 +887,23 @@ func literalRegexp(s string, flags Flags) *Regexp {
// Flags, and returns a regular expression parse tree. The syntax is
// described in the top-level comment.
func Parse(s string, flags Flags) (*Regexp, error) {
+ return parse(s, flags)
+}
+
+func parse(s string, flags Flags) (_ *Regexp, err error) {
+ defer func() {
+ switch r := recover(); r {
+ default:
+ panic(r)
+ case nil:
+ // ok
+ case ErrInternalError: // too big
+ err = &Error{Code: ErrInternalError, Expr: s}
+ case ErrNestingDepth:
+ err = &Error{Code: ErrNestingDepth, Expr: s}
+ }
+ }()
+
if flags&Literal != 0 {
// Trivial parser for literal string.
if err := checkUTF8(s); err != nil {
@@ -704,7 +915,6 @@ func Parse(s string, flags Flags) (*Regexp, error) {
// Otherwise, must do real work.
var (
p parser
- err error
c rune
op Op
lastRepeat string
@@ -1733,7 +1943,7 @@ func appendClass(r []rune, x []rune) []rune {
return r
}
-// appendFolded returns the result of appending the case folding of the class x to the class r.
+// appendFoldedClass returns the result of appending the case folding of the class x to the class r.
func appendFoldedClass(r []rune, x []rune) []rune {
for i := 0; i < len(x); i += 2 {
r = appendFoldedRange(r, x[i], x[i+1])
diff --git a/src/regexp/syntax/parse_test.go b/src/regexp/syntax/parse_test.go
index 5581ba1..6044da6 100644
--- a/src/regexp/syntax/parse_test.go
+++ b/src/regexp/syntax/parse_test.go
@@ -479,10 +479,15 @@ var invalidRegexps = []string{
`(?P<>a)`,
`[a-Z]`,
`(?i)[a-Z]`,
- `a{100000}`,
- `a{100000,}`,
- "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})",
`\Q\E*`,
+ `a{100000}`, // too much repetition
+ `a{100000,}`, // too much repetition
+ "((((((((((x{2}){2}){2}){2}){2}){2}){2}){2}){2}){2})", // too much repetition
+ strings.Repeat("(", 1000) + strings.Repeat(")", 1000), // too deep
+ strings.Repeat("(?:", 1000) + strings.Repeat(")*", 1000), // too deep
+ "(" + strings.Repeat("(xx?)", 1000) + "){1000}", // too long
+ strings.Repeat("(xx?){1000}", 1000), // too long
+ strings.Repeat(`\pL`, 27000), // too many runes
}
var onlyPerl = []string{
--
2.33.0

View File

@ -1,174 +0,0 @@
From 51a477dc4f1130d53e66cd2003de0bac40e5e2be Mon Sep 17 00:00:00 2001
From: Damien Neil <dneil@google.com>
Date: Thu, 22 Sep 2022 13:32:00 -0700
Subject: [PATCH 2/3] [release-branch.go1.18] net/http/httputil: avoid query
parameter smuggling
Query parameter smuggling occurs when a proxy's interpretation
of query parameters differs from that of a downstream server.
Change ReverseProxy to avoid forwarding ignored query parameters.
Remove unparsable query parameters from the outbound request
* if req.Form != nil after calling ReverseProxy.Director; and
* before calling ReverseProxy.Rewrite.
This change preserves the existing behavior of forwarding the
raw query untouched if a Director hook does not parse the query
by calling Request.ParseForm (possibly indirectly).
Fixes #55842
For #54663
For CVE-2022-2880
Change-Id: If1621f6b0e73a49d79059dae9e6b256e0ff18ca9
Reviewed-on: https://go-review.googlesource.com/c/go/+/432976
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Damien Neil <dneil@google.com>
(cherry picked from commit 7c84234142149bd24a4096c6cab691d3593f3431)
Reviewed-on: https://go-review.googlesource.com/c/go/+/433695
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
---
src/net/http/httputil/reverseproxy.go | 36 +++++++++++
src/net/http/httputil/reverseproxy_test.go | 74 ++++++++++++++++++++++
2 files changed, 110 insertions(+)
diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go
index 8b63368386..c76eec6987 100644
--- a/src/net/http/httputil/reverseproxy.go
+++ b/src/net/http/httputil/reverseproxy.go
@@ -249,6 +249,9 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
p.Director(outreq)
+ if outreq.Form != nil {
+ outreq.URL.RawQuery = cleanQueryParams(outreq.URL.RawQuery)
+ }
outreq.Close = false
reqUpType := upgradeType(outreq.Header)
@@ -628,3 +631,36 @@ func (c switchProtocolCopier) copyToBackend(errc chan<- error) {
_, err := io.Copy(c.backend, c.user)
errc <- err
}
+
+func cleanQueryParams(s string) string {
+ reencode := func(s string) string {
+ v, _ := url.ParseQuery(s)
+ return v.Encode()
+ }
+ for i := 0; i < len(s); {
+ switch s[i] {
+ case ';':
+ return reencode(s)
+ case '%':
+ if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
+ return reencode(s)
+ }
+ i += 3
+ default:
+ i++
+ }
+ }
+ return s
+}
+
+func ishex(c byte) bool {
+ switch {
+ case '0' <= c && c <= '9':
+ return true
+ case 'a' <= c && c <= 'f':
+ return true
+ case 'A' <= c && c <= 'F':
+ return true
+ }
+ return false
+}
diff --git a/src/net/http/httputil/reverseproxy_test.go b/src/net/http/httputil/reverseproxy_test.go
index 4b6ad77a29..8c0a4f136b 100644
--- a/src/net/http/httputil/reverseproxy_test.go
+++ b/src/net/http/httputil/reverseproxy_test.go
@@ -1517,3 +1517,77 @@ func TestJoinURLPath(t *testing.T) {
}
}
}
+
+const (
+ testWantsCleanQuery = true
+ testWantsRawQuery = false
+)
+
+func TestReverseProxyQueryParameterSmugglingDirectorDoesNotParseForm(t *testing.T) {
+ testReverseProxyQueryParameterSmuggling(t, testWantsRawQuery, func(u *url.URL) *ReverseProxy {
+ proxyHandler := NewSingleHostReverseProxy(u)
+ oldDirector := proxyHandler.Director
+ proxyHandler.Director = func(r *http.Request) {
+ oldDirector(r)
+ }
+ return proxyHandler
+ })
+}
+
+func TestReverseProxyQueryParameterSmugglingDirectorParsesForm(t *testing.T) {
+ testReverseProxyQueryParameterSmuggling(t, testWantsCleanQuery, func(u *url.URL) *ReverseProxy {
+ proxyHandler := NewSingleHostReverseProxy(u)
+ oldDirector := proxyHandler.Director
+ proxyHandler.Director = func(r *http.Request) {
+ // Parsing the form causes ReverseProxy to remove unparsable
+ // query parameters before forwarding.
+ r.FormValue("a")
+ oldDirector(r)
+ }
+ return proxyHandler
+ })
+}
+
+func testReverseProxyQueryParameterSmuggling(t *testing.T, wantCleanQuery bool, newProxy func(*url.URL) *ReverseProxy) {
+ const content = "response_content"
+ backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte(r.URL.RawQuery))
+ }))
+ defer backend.Close()
+ backendURL, err := url.Parse(backend.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ proxyHandler := newProxy(backendURL)
+ frontend := httptest.NewServer(proxyHandler)
+ defer frontend.Close()
+
+ // Don't spam output with logs of queries containing semicolons.
+ backend.Config.ErrorLog = log.New(io.Discard, "", 0)
+ frontend.Config.ErrorLog = log.New(io.Discard, "", 0)
+
+ for _, test := range []struct {
+ rawQuery string
+ cleanQuery string
+ }{{
+ rawQuery: "a=1&a=2;b=3",
+ cleanQuery: "a=1",
+ }, {
+ rawQuery: "a=1&a=%zz&b=3",
+ cleanQuery: "a=1&b=3",
+ }} {
+ res, err := frontend.Client().Get(frontend.URL + "?" + test.rawQuery)
+ if err != nil {
+ t.Fatalf("Get: %v", err)
+ }
+ defer res.Body.Close()
+ body, _ := io.ReadAll(res.Body)
+ wantQuery := test.rawQuery
+ if wantCleanQuery {
+ wantQuery = test.cleanQuery
+ }
+ if got, want := string(body), wantQuery; got != want {
+ t.Errorf("proxy forwarded raw query %q as %q, want %q", test.rawQuery, got, want)
+ }
+ }
+}
--
2.33.0

View File

@ -1,186 +0,0 @@
From 7dd44b287830fbb2256aceac4a36756b955c0279 Mon Sep 17 00:00:00 2001
From: Damien Neil <dneil@google.com>
Date: Fri, 2 Sep 2022 20:45:18 -0700
Subject: [PATCH] archive/tar: limit size of headers
Set a 1MiB limit on special file blocks (PAX headers, GNU long names,
GNU link names), to avoid reading arbitrarily large amounts of data
into memory.
Thanks to Adam Korczynski (ADA Logics) and OSS-Fuzz for reporting
this issue.
Fixes CVE-2022-2879
Updates #54853
Fixes #55925
Change-Id: I85136d6ff1e0af101a112190e027987ab4335680
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1565555
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
Run-TryBot: Roland Shoemaker <bracewell@google.com>
Reviewed-by: Roland Shoemaker <bracewell@google.com>
(cherry picked from commit 6ee768cef6b82adf7a90dcf367a1699ef694f3b2)
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1590622
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Julie Qiu <julieqiu@google.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/438500
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Carlos Amedee <carlos@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
---
src/archive/tar/format.go | 4 ++++
src/archive/tar/reader.go | 14 ++++++++++++--
src/archive/tar/reader_test.go | 11 ++++++++++-
src/archive/tar/writer.go | 3 +++
src/archive/tar/writer_test.go | 27 +++++++++++++++++++++++++++
5 files changed, 56 insertions(+), 3 deletions(-)
diff --git a/src/archive/tar/format.go b/src/archive/tar/format.go
index cfe24a5..6642364 100644
--- a/src/archive/tar/format.go
+++ b/src/archive/tar/format.go
@@ -143,6 +143,10 @@ const (
blockSize = 512 // Size of each block in a tar stream
nameSize = 100 // Max length of the name field in USTAR format
prefixSize = 155 // Max length of the prefix field in USTAR format
+
+ // Max length of a special file (PAX header, GNU long name or link).
+ // This matches the limit used by libarchive.
+ maxSpecialFileSize = 1 << 20
)
// blockPadding computes the number of bytes needed to pad offset up to the
diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go
index 1b1d5b4..f645af8 100644
--- a/src/archive/tar/reader.go
+++ b/src/archive/tar/reader.go
@@ -103,7 +103,7 @@ func (tr *Reader) next() (*Header, error) {
continue // This is a meta header affecting the next header
case TypeGNULongName, TypeGNULongLink:
format.mayOnlyBe(FormatGNU)
- realname, err := io.ReadAll(tr)
+ realname, err := readSpecialFile(tr)
if err != nil {
return nil, err
}
@@ -293,7 +293,7 @@ func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) {
// parsePAX parses PAX headers.
// If an extended header (type 'x') is invalid, ErrHeader is returned
func parsePAX(r io.Reader) (map[string]string, error) {
- buf, err := io.ReadAll(r)
+ buf, err := readSpecialFile(r)
if err != nil {
return nil, err
}
@@ -826,6 +826,16 @@ func tryReadFull(r io.Reader, b []byte) (n int, err error) {
return n, err
}
+// readSpecialFile is like io.ReadAll except it returns
+// ErrFieldTooLong if more than maxSpecialFileSize is read.
+func readSpecialFile(r io.Reader) ([]byte, error) {
+ buf, err := io.ReadAll(io.LimitReader(r, maxSpecialFileSize+1))
+ if len(buf) > maxSpecialFileSize {
+ return nil, ErrFieldTooLong
+ }
+ return buf, err
+}
+
// discard skips n bytes in r, reporting an error if unable to do so.
func discard(r io.Reader, n int64) error {
// If possible, Seek to the last byte before the end of the data section.
diff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go
index 789ddc1..5a644a4 100644
--- a/src/archive/tar/reader_test.go
+++ b/src/archive/tar/reader_test.go
@@ -6,6 +6,7 @@ package tar
import (
"bytes"
+ "compress/bzip2"
"crypto/md5"
"errors"
"fmt"
@@ -243,6 +244,9 @@ func TestReader(t *testing.T) {
}, {
file: "testdata/pax-bad-hdr-file.tar",
err: ErrHeader,
+ }, {
+ file: "testdata/pax-bad-hdr-large.tar.bz2",
+ err: ErrFieldTooLong,
}, {
file: "testdata/pax-bad-mtime-file.tar",
err: ErrHeader,
@@ -625,9 +629,14 @@ func TestReader(t *testing.T) {
}
defer f.Close()
+ var fr io.Reader = f
+ if strings.HasSuffix(v.file, ".bz2") {
+ fr = bzip2.NewReader(fr)
+ }
+
// Capture all headers and checksums.
var (
- tr = NewReader(f)
+ tr = NewReader(fr)
hdrs []*Header
chksums []string
rdbuf = make([]byte, 8)
diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go
index e80498d..893eac0 100644
--- a/src/archive/tar/writer.go
+++ b/src/archive/tar/writer.go
@@ -199,6 +199,9 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
flag = TypeXHeader
}
data := buf.String()
+ if len(data) > maxSpecialFileSize {
+ return ErrFieldTooLong
+ }
if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal {
return err // Global headers return here
}
diff --git a/src/archive/tar/writer_test.go b/src/archive/tar/writer_test.go
index a00f02d..4e709e5 100644
--- a/src/archive/tar/writer_test.go
+++ b/src/archive/tar/writer_test.go
@@ -1006,6 +1006,33 @@ func TestIssue12594(t *testing.T) {
}
}
+func TestWriteLongHeader(t *testing.T) {
+ for _, test := range []struct {
+ name string
+ h *Header
+ }{{
+ name: "name too long",
+ h: &Header{Name: strings.Repeat("a", maxSpecialFileSize)},
+ }, {
+ name: "linkname too long",
+ h: &Header{Linkname: strings.Repeat("a", maxSpecialFileSize)},
+ }, {
+ name: "uname too long",
+ h: &Header{Uname: strings.Repeat("a", maxSpecialFileSize)},
+ }, {
+ name: "gname too long",
+ h: &Header{Gname: strings.Repeat("a", maxSpecialFileSize)},
+ }, {
+ name: "PAX header too long",
+ h: &Header{PAXRecords: map[string]string{"GOLANG.x": strings.Repeat("a", maxSpecialFileSize)}},
+ }} {
+ w := NewWriter(io.Discard)
+ if err := w.WriteHeader(test.h); err != ErrFieldTooLong {
+ t.Errorf("%v: w.WriteHeader() = %v, want ErrFieldTooLong", test.name, err)
+ }
+ }
+}
+
// testNonEmptyWriter wraps an io.Writer and ensures that
// Write is never called with an empty buffer.
type testNonEmptyWriter struct{ io.Writer }
--
2.33.0

View File

@ -1,248 +0,0 @@
From 0c539fa7a4a9d29252523e41e073198195ba6691 Mon Sep 17 00:00:00 2001
From: Damien Neil <dneil@google.com>
Date: Mon, 17 Oct 2022 17:38:29 -0700
Subject: [PATCH] syscall, os/exec: reject environment variables containing
NULs
Check for and reject environment variables containing NULs.
The conventions for passing environment variables to subprocesses
cause most or all systems to interpret a NUL as a separator. The
syscall package rejects environment variables containing a NUL
on most systems, but erroniously did not do so on Windows. This
causes an environment variable such as "FOO=a\x00BAR=b" to be
interpreted as "FOO=a", "BAR=b".
Check for and reject NULs in environment variables passed to
syscall.StartProcess on Windows.
Add a redundant check to os/exec as extra insurance.
Fixes #56284
Fixes CVE-2022-41716
Change-Id: I2950e2b0cb14ebd26e5629be1521858f66a7d4ae
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1609434
Run-TryBot: Damien Neil <dneil@google.com>
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
Reviewed-by: Roland Shoemaker <bracewell@google.com>
TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/446916
Reviewed-by: Tatiana Bradley <tatiana@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reference: https://go-review.googlesource.com/c/go/+/446916
Conflict: src/os/exec/exec.go;src/syscall/exec_windows.go
---
src/os/exec/env_test.go | 19 +++++++++++++------
src/os/exec/exec.go | 38 ++++++++++++++++++++++++++++++++-----
src/os/exec/exec_test.go | 9 +++++++++
src/syscall/exec_windows.go | 20 ++++++++++++++-----
4 files changed, 70 insertions(+), 16 deletions(-)
diff --git a/src/os/exec/env_test.go b/src/os/exec/env_test.go
index b5ac398..47b7c04 100644
--- a/src/os/exec/env_test.go
+++ b/src/os/exec/env_test.go
@@ -11,9 +11,10 @@ import (
func TestDedupEnv(t *testing.T) {
tests := []struct {
- noCase bool
- in []string
- want []string
+ noCase bool
+ in []string
+ want []string
+ wantErr bool
}{
{
noCase: true,
@@ -29,11 +30,17 @@ func TestDedupEnv(t *testing.T) {
in: []string{"=a", "=b", "foo", "bar"},
want: []string{"=b", "foo", "bar"},
},
+ {
+ // Filter out entries containing NULs.
+ in: []string{"A=a\x00b", "B=b", "C\x00C=c"},
+ want: []string{"B=b"},
+ wantErr: true,
+ },
}
for _, tt := range tests {
- got := dedupEnvCase(tt.noCase, tt.in)
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("Dedup(%v, %q) = %q; want %q", tt.noCase, tt.in, got, tt.want)
+ got, err := dedupEnvCase(tt.noCase, tt.in)
+ if !reflect.DeepEqual(got, tt.want) || (err != nil) != tt.wantErr {
+ t.Errorf("Dedup(%v, %q) = %q, %v; want %q, error:%v", tt.noCase, tt.in, got, err, tt.want, tt.wantErr)
}
}
}
diff --git a/src/os/exec/exec.go b/src/os/exec/exec.go
index 0c49575..6f5c61b 100644
--- a/src/os/exec/exec.go
+++ b/src/os/exec/exec.go
@@ -414,7 +414,7 @@ func (c *Cmd) Start() error {
}
c.childFiles = append(c.childFiles, c.ExtraFiles...)
- envv, err := c.envv()
+ env, err := c.environ()
if err != nil {
return err
}
@@ -422,7 +422,7 @@ func (c *Cmd) Start() error {
c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{
Dir: c.Dir,
Files: c.childFiles,
- Env: addCriticalEnv(dedupEnv(envv)),
+ Env: env,
Sys: c.SysProcAttr,
})
if err != nil {
@@ -738,16 +738,21 @@ func minInt(a, b int) int {
// dedupEnv returns a copy of env with any duplicates removed, in favor of
// later values.
// Items not of the normal environment "key=value" form are preserved unchanged.
-func dedupEnv(env []string) []string {
+func dedupEnv(env []string) ([]string, error) {
return dedupEnvCase(runtime.GOOS == "windows", env)
}
// dedupEnvCase is dedupEnv with a case option for testing.
// If caseInsensitive is true, the case of keys is ignored.
-func dedupEnvCase(caseInsensitive bool, env []string) []string {
+func dedupEnvCase(caseInsensitive bool, env []string) ([]string, error) {
+ var err error
out := make([]string, 0, len(env))
saw := make(map[string]int, len(env)) // key => index into out
for _, kv := range env {
+ if strings.IndexByte(kv, 0) != -1 {
+ err = errors.New("exec: environment variable contains NUL")
+ continue
+ }
eq := strings.Index(kv, "=")
if eq < 0 {
out = append(out, kv)
@@ -764,7 +769,7 @@ func dedupEnvCase(caseInsensitive bool, env []string) []string {
saw[k] = len(out)
out = append(out, kv)
}
- return out
+ return out, err
}
// addCriticalEnv adds any critical environment variables that are required
@@ -787,3 +792,26 @@ func addCriticalEnv(env []string) []string {
}
return append(env, "SYSTEMROOT="+os.Getenv("SYSTEMROOT"))
}
+
+// environ returns a best-effort copy of the environment in which the command
+// would be run as it is currently configured. If an error occurs in computing
+// the environment, it is returned alongside the best-effort copy.
+func (c *Cmd) environ() ([]string, error) {
+ env, err := c.envv()
+ if err != nil {
+ return env, err
+ }
+ env, dedupErr := dedupEnv(env)
+ if err == nil {
+ err = dedupErr
+ }
+ return addCriticalEnv(env), err
+}
+
+// Environ returns a copy of the environment in which the command would be run
+// as it is currently configured.
+func (c *Cmd) Environ() []string {
+ // Intentionally ignore errors: environ returns a best-effort environment no matter what.
+ env, _ := c.environ()
+ return env
+}
diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go
index d854e0d..d03eab2 100644
--- a/src/os/exec/exec_test.go
+++ b/src/os/exec/exec_test.go
@@ -1104,6 +1104,15 @@ func TestDedupEnvEcho(t *testing.T) {
}
}
+func TestEnvNULCharacter(t *testing.T) {
+ cmd := helperCommand(t, "echoenv", "FOO", "BAR")
+ cmd.Env = append(cmd.Environ(), "FOO=foo\x00BAR=bar")
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Errorf("output = %q; want error", string(out))
+ }
+}
+
func TestString(t *testing.T) {
echoPath, err := exec.LookPath("echo")
if err != nil {
diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
index 9d10d6a..50892be 100644
--- a/src/syscall/exec_windows.go
+++ b/src/syscall/exec_windows.go
@@ -7,6 +7,7 @@
package syscall
import (
+ "internal/bytealg"
"runtime"
"sync"
"unicode/utf16"
@@ -115,12 +116,16 @@ func makeCmdLine(args []string) string {
// the representation required by CreateProcess: a sequence of NUL
// terminated strings followed by a nil.
// Last bytes are two UCS-2 NULs, or four NUL bytes.
-func createEnvBlock(envv []string) *uint16 {
+// If any string contains a NUL, it returns (nil, EINVAL).
+func createEnvBlock(envv []string) (*uint16, error) {
if len(envv) == 0 {
- return &utf16.Encode([]rune("\x00\x00"))[0]
+ return &utf16.Encode([]rune("\x00\x00"))[0], nil
}
length := 0
for _, s := range envv {
+ if bytealg.IndexByteString(s, 0) != -1 {
+ return nil, EINVAL
+ }
length += len(s) + 1
}
length += 1
@@ -135,7 +140,7 @@ func createEnvBlock(envv []string) *uint16 {
}
copy(b[i:i+1], []byte{0})
- return &utf16.Encode([]rune(string(b)))[0]
+ return &utf16.Encode([]rune(string(b)))[0], nil
}
func CloseOnExec(fd Handle) {
@@ -400,12 +405,17 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
}
}
+ envBlock, err := createEnvBlock(attr.Env)
+ if err != nil {
+ return 0, 0, err
+ }
+
pi := new(ProcessInformation)
flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT | _EXTENDED_STARTUPINFO_PRESENT
if sys.Token != 0 {
- err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
+ err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, envBlock, dirp, &si.StartupInfo, pi)
} else {
- err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
+ err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, envBlock, dirp, &si.StartupInfo, pi)
}
if err != nil {
return 0, 0, err
--
2.33.0

View File

@ -1,434 +0,0 @@
From 719a248de7215aeb2ca38e71c0056a777712f1e2 Mon Sep 17 00:00:00 2001
From: Russ Cox <rsc@golang.org>
Date: Tue, 21 Sep 2021 10:59:16 -0400
Subject: [PATCH] bytes, strings: add Cut
Using Cut is a clearer way to write the vast majority (>70%)
of existing code that calls Index, IndexByte, IndexRune, and SplitN.
There is more discussion on https://golang.org/issue/46336.
Fixes #46336.
Change-Id: Ia418ed7c3706c65bf61e1b2c5baf534cb783e4d3
Reviewed-on: https://go-review.googlesource.com/c/go/+/351710
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
---
src/bytes/bytes.go | 13 ++++
src/bytes/bytes_test.go | 23 ++++++
src/bytes/example_test.go | 138 ++++++++++++++++++------------------
src/strings/example_test.go | 58 +++++++++------
src/strings/strings.go | 11 +++
src/strings/strings_test.go | 25 ++++++-
6 files changed, 178 insertions(+), 90 deletions(-)
diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go
index ce52649f13..ec9a2eb693 100644
--- a/src/bytes/bytes.go
+++ b/src/bytes/bytes.go
@@ -1174,3 +1174,16 @@ func Index(s, sep []byte) int {
}
return -1
}
+
+// Cut slices s around the first instance of sep,
+// returning the text before and after sep.
+// The found result reports whether sep appears in s.
+// If sep does not appear in s, cut returns s, "", false.
+//
+// Cut returns slices of the original slice s, not copies.
+func Cut(s, sep []byte) (before, after []byte, found bool) {
+ if i := Index(s, sep); i >= 0 {
+ return s[:i], s[i+len(sep):], true
+ }
+ return s, nil, false
+}
diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go
index 544ee46f90..538e613c8e 100644
--- a/src/bytes/bytes_test.go
+++ b/src/bytes/bytes_test.go
@@ -1565,6 +1565,29 @@ func TestEqualFold(t *testing.T) {
}
}
+var cutTests = []struct {
+ s, sep string
+ before, after string
+ found bool
+}{
+ {"abc", "b", "a", "c", true},
+ {"abc", "a", "", "bc", true},
+ {"abc", "c", "ab", "", true},
+ {"abc", "abc", "", "", true},
+ {"abc", "", "", "abc", true},
+ {"abc", "d", "abc", "", false},
+ {"", "d", "", "", false},
+ {"", "", "", "", true},
+}
+
+func TestCut(t *testing.T) {
+ for _, tt := range cutTests {
+ if before, after, found := Cut([]byte(tt.s), []byte(tt.sep)); string(before) != tt.before || string(after) != tt.after || found != tt.found {
+ t.Errorf("Cut(%q, %q) = %q, %q, %v, want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found)
+ }
+ }
+}
+
func TestBufferGrowNegative(t *testing.T) {
defer func() {
if err := recover(); err == nil {
diff --git a/src/bytes/example_test.go b/src/bytes/example_test.go
index ae93202b57..a1a6c2d292 100644
--- a/src/bytes/example_test.go
+++ b/src/bytes/example_test.go
@@ -92,36 +92,6 @@ func ExampleCompare_search() {
}
}
-func ExampleTrimSuffix() {
- var b = []byte("Hello, goodbye, etc!")
- b = bytes.TrimSuffix(b, []byte("goodbye, etc!"))
- b = bytes.TrimSuffix(b, []byte("gopher"))
- b = append(b, bytes.TrimSuffix([]byte("world!"), []byte("x!"))...)
- os.Stdout.Write(b)
- // Output: Hello, world!
-}
-
-func ExampleTrimPrefix() {
- var b = []byte("Goodbye,, world!")
- b = bytes.TrimPrefix(b, []byte("Goodbye,"))
- b = bytes.TrimPrefix(b, []byte("See ya,"))
- fmt.Printf("Hello%s", b)
- // Output: Hello, world!
-}
-
-func ExampleFields() {
- fmt.Printf("Fields are: %q", bytes.Fields([]byte(" foo bar baz ")))
- // Output: Fields are: ["foo" "bar" "baz"]
-}
-
-func ExampleFieldsFunc() {
- f := func(c rune) bool {
- return !unicode.IsLetter(c) && !unicode.IsNumber(c)
- }
- fmt.Printf("Fields are: %q", bytes.FieldsFunc([]byte(" foo1;bar2,baz3..."), f))
- // Output: Fields are: ["foo1" "bar2" "baz3"]
-}
-
func ExampleContains() {
fmt.Println(bytes.Contains([]byte("seafood"), []byte("foo")))
fmt.Println(bytes.Contains([]byte("seafood"), []byte("bar")))
@@ -168,6 +138,22 @@ func ExampleCount() {
// 5
}
+func ExampleCut() {
+ show := func(s, sep string) {
+ before, after, found := bytes.Cut([]byte(s), []byte(sep))
+ fmt.Printf("Cut(%q, %q) = %q, %q, %v\n", s, sep, before, after, found)
+ }
+ show("Gopher", "Go")
+ show("Gopher", "ph")
+ show("Gopher", "er")
+ show("Gopher", "Badger")
+ // Output:
+ // Cut("Gopher", "Go") = "", "pher", true
+ // Cut("Gopher", "ph") = "Go", "er", true
+ // Cut("Gopher", "er") = "Goph", "", true
+ // Cut("Gopher", "Badger") = "Gopher", "", false
+}
+
func ExampleEqual() {
fmt.Println(bytes.Equal([]byte("Go"), []byte("Go")))
fmt.Println(bytes.Equal([]byte("Go"), []byte("C++")))
@@ -181,6 +167,19 @@ func ExampleEqualFold() {
// Output: true
}
+func ExampleFields() {
+ fmt.Printf("Fields are: %q", bytes.Fields([]byte(" foo bar baz ")))
+ // Output: Fields are: ["foo" "bar" "baz"]
+}
+
+func ExampleFieldsFunc() {
+ f := func(c rune) bool {
+ return !unicode.IsLetter(c) && !unicode.IsNumber(c)
+ }
+ fmt.Printf("Fields are: %q", bytes.FieldsFunc([]byte(" foo1;bar2,baz3..."), f))
+ // Output: Fields are: ["foo1" "bar2" "baz3"]
+}
+
func ExampleHasPrefix() {
fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("Go")))
fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("C")))
@@ -246,6 +245,12 @@ func ExampleIndexRune() {
// -1
}
+func ExampleJoin() {
+ s := [][]byte{[]byte("foo"), []byte("bar"), []byte("baz")}
+ fmt.Printf("%s", bytes.Join(s, []byte(", ")))
+ // Output: foo, bar, baz
+}
+
func ExampleLastIndex() {
fmt.Println(bytes.Index([]byte("go gopher"), []byte("go")))
fmt.Println(bytes.LastIndex([]byte("go gopher"), []byte("go")))
@@ -286,10 +291,12 @@ func ExampleLastIndexFunc() {
// -1
}
-func ExampleJoin() {
- s := [][]byte{[]byte("foo"), []byte("bar"), []byte("baz")}
- fmt.Printf("%s", bytes.Join(s, []byte(", ")))
- // Output: foo, bar, baz
+func ExampleReader_Len() {
+ fmt.Println(bytes.NewReader([]byte("Hi!")).Len())
+ fmt.Println(bytes.NewReader([]byte("こんにちは!")).Len())
+ // Output:
+ // 3
+ // 16
}
func ExampleRepeat() {
@@ -399,20 +406,6 @@ func ExampleTrimFunc() {
// go-gopher!
}
-func ExampleMap() {
- rot13 := func(r rune) rune {
- switch {
- case r >= 'A' && r <= 'Z':
- return 'A' + (r-'A'+13)%26
- case r >= 'a' && r <= 'z':
- return 'a' + (r-'a'+13)%26
- }
- return r
- }
- fmt.Printf("%s", bytes.Map(rot13, []byte("'Twas brillig and the slithy gopher...")))
- // Output: 'Gjnf oevyyvt naq gur fyvgul tbcure...
-}
-
func ExampleTrimLeft() {
fmt.Print(string(bytes.TrimLeft([]byte("453gopher8257"), "0123456789")))
// Output:
@@ -429,11 +422,28 @@ func ExampleTrimLeftFunc() {
// go-gopher!567
}
+func ExampleTrimPrefix() {
+ var b = []byte("Goodbye,, world!")
+ b = bytes.TrimPrefix(b, []byte("Goodbye,"))
+ b = bytes.TrimPrefix(b, []byte("See ya,"))
+ fmt.Printf("Hello%s", b)
+ // Output: Hello, world!
+}
+
func ExampleTrimSpace() {
fmt.Printf("%s", bytes.TrimSpace([]byte(" \t\n a lone gopher \n\t\r\n")))
// Output: a lone gopher
}
+func ExampleTrimSuffix() {
+ var b = []byte("Hello, goodbye, etc!")
+ b = bytes.TrimSuffix(b, []byte("goodbye, etc!"))
+ b = bytes.TrimSuffix(b, []byte("gopher"))
+ b = append(b, bytes.TrimSuffix([]byte("world!"), []byte("x!"))...)
+ os.Stdout.Write(b)
+ // Output: Hello, world!
+}
+
func ExampleTrimRight() {
fmt.Print(string(bytes.TrimRight([]byte("453gopher8257"), "0123456789")))
// Output:
@@ -450,21 +460,6 @@ func ExampleTrimRightFunc() {
// 1234go-gopher!
}
-func ExampleToUpper() {
- fmt.Printf("%s", bytes.ToUpper([]byte("Gopher")))
- // Output: GOPHER
-}
-
-func ExampleToUpperSpecial() {
- str := []byte("ahoj vývojári golang")
- totitle := bytes.ToUpperSpecial(unicode.AzeriCase, str)
- fmt.Println("Original : " + string(str))
- fmt.Println("ToUpper : " + string(totitle))
- // Output:
- // Original : ahoj vývojári golang
- // ToUpper : AHOJ VÝVOJÁRİ GOLANG
-}
-
func ExampleToLower() {
fmt.Printf("%s", bytes.ToLower([]byte("Gopher")))
// Output: gopher
@@ -480,10 +475,17 @@ func ExampleToLowerSpecial() {
// ToLower : ahoj vývojári golang
}
-func ExampleReader_Len() {
- fmt.Println(bytes.NewReader([]byte("Hi!")).Len())
- fmt.Println(bytes.NewReader([]byte("こんにちは!")).Len())
+func ExampleToUpper() {
+ fmt.Printf("%s", bytes.ToUpper([]byte("Gopher")))
+ // Output: GOPHER
+}
+
+func ExampleToUpperSpecial() {
+ str := []byte("ahoj vývojári golang")
+ totitle := bytes.ToUpperSpecial(unicode.AzeriCase, str)
+ fmt.Println("Original : " + string(str))
+ fmt.Println("ToUpper : " + string(totitle))
// Output:
- // 3
- // 16
+ // Original : ahoj vývojári golang
+ // ToUpper : AHOJ VÝVOJÁRİ GOLANG
}
diff --git a/src/strings/example_test.go b/src/strings/example_test.go
index 375f9cac65..94aa167f90 100644
--- a/src/strings/example_test.go
+++ b/src/strings/example_test.go
@@ -10,17 +10,15 @@ import (
"unicode"
)
-func ExampleFields() {
- fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz "))
- // Output: Fields are: ["foo" "bar" "baz"]
-}
-
-func ExampleFieldsFunc() {
- f := func(c rune) bool {
- return !unicode.IsLetter(c) && !unicode.IsNumber(c)
+func ExampleBuilder() {
+ var b strings.Builder
+ for i := 3; i >= 1; i-- {
+ fmt.Fprintf(&b, "%d...", i)
}
- fmt.Printf("Fields are: %q", strings.FieldsFunc(" foo1;bar2,baz3...", f))
- // Output: Fields are: ["foo1" "bar2" "baz3"]
+ b.WriteString("ignition")
+ fmt.Println(b.String())
+
+ // Output: 3...2...1...ignition
}
func ExampleCompare() {
@@ -79,11 +77,40 @@ func ExampleCount() {
// 5
}
+func ExampleCut() {
+ show := func(s, sep string) {
+ before, after, found := strings.Cut(s, sep)
+ fmt.Printf("Cut(%q, %q) = %q, %q, %v\n", s, sep, before, after, found)
+ }
+ show("Gopher", "Go")
+ show("Gopher", "ph")
+ show("Gopher", "er")
+ show("Gopher", "Badger")
+ // Output:
+ // Cut("Gopher", "Go") = "", "pher", true
+ // Cut("Gopher", "ph") = "Go", "er", true
+ // Cut("Gopher", "er") = "Goph", "", true
+ // Cut("Gopher", "Badger") = "Gopher", "", false
+}
+
func ExampleEqualFold() {
fmt.Println(strings.EqualFold("Go", "go"))
// Output: true
}
+func ExampleFields() {
+ fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz "))
+ // Output: Fields are: ["foo" "bar" "baz"]
+}
+
+func ExampleFieldsFunc() {
+ f := func(c rune) bool {
+ return !unicode.IsLetter(c) && !unicode.IsNumber(c)
+ }
+ fmt.Printf("Fields are: %q", strings.FieldsFunc(" foo1;bar2,baz3...", f))
+ // Output: Fields are: ["foo1" "bar2" "baz3"]
+}
+
func ExampleHasPrefix() {
fmt.Println(strings.HasPrefix("Gopher", "Go"))
fmt.Println(strings.HasPrefix("Gopher", "C"))
@@ -370,14 +397,3 @@ func ExampleTrimRightFunc() {
}))
// Output: ¡¡¡Hello, Gophers
}
-
-func ExampleBuilder() {
- var b strings.Builder
- for i := 3; i >= 1; i-- {
- fmt.Fprintf(&b, "%d...", i)
- }
- b.WriteString("ignition")
- fmt.Println(b.String())
-
- // Output: 3...2...1...ignition
-}
diff --git a/src/strings/strings.go b/src/strings/strings.go
index b429735fea..0c94395311 100644
--- a/src/strings/strings.go
+++ b/src/strings/strings.go
@@ -1100,3 +1100,14 @@ func Index(s, substr string) int {
}
return -1
}
+
+// Cut slices s around the first instance of sep,
+// returning the text before and after sep.
+// The found result reports whether sep appears in s.
+// If sep does not appear in s, cut returns s, "", false.
+func Cut(s, sep string) (before, after string, found bool) {
+ if i := Index(s, sep); i >= 0 {
+ return s[:i], s[i+len(sep):], true
+ }
+ return s, "", false
+}
diff --git a/src/strings/strings_test.go b/src/strings/strings_test.go
index 09e5b27cc3..5a02ba496f 100644
--- a/src/strings/strings_test.go
+++ b/src/strings/strings_test.go
@@ -1577,7 +1577,30 @@ var CountTests = []struct {
func TestCount(t *testing.T) {
for _, tt := range CountTests {
if num := Count(tt.s, tt.sep); num != tt.num {
- t.Errorf("Count(\"%s\", \"%s\") = %d, want %d", tt.s, tt.sep, num, tt.num)
+ t.Errorf("Count(%q, %q) = %d, want %d", tt.s, tt.sep, num, tt.num)
+ }
+ }
+}
+
+var cutTests = []struct {
+ s, sep string
+ before, after string
+ found bool
+}{
+ {"abc", "b", "a", "c", true},
+ {"abc", "a", "", "bc", true},
+ {"abc", "c", "ab", "", true},
+ {"abc", "abc", "", "", true},
+ {"abc", "", "", "abc", true},
+ {"abc", "d", "abc", "", false},
+ {"", "d", "", "", false},
+ {"", "", "", "", true},
+}
+
+func TestCut(t *testing.T) {
+ for _, tt := range cutTests {
+ if before, after, found := Cut(tt.s, tt.sep); before != tt.before || after != tt.after || found != tt.found {
+ t.Errorf("Cut(%q, %q) = %q, %q, %v, want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found)
}
}
}
--
2.21.0

View File

@ -2,8 +2,8 @@
%global _binaries_in_noarch_packages_terminate_build 0 %global _binaries_in_noarch_packages_terminate_build 0
%global golibdir %{_libdir}/golang %global golibdir %{_libdir}/golang
%global goroot /usr/lib/%{name} %global goroot /usr/lib/%{name}
%global go_api 1.17 %global go_api 1.19
%global go_version 1.17 %global go_version 1.19
%global __spec_install_post /usr/lib/rpm/check-rpaths /usr/lib/rpm/check-buildroot /usr/lib/rpm/brp-compress %global __spec_install_post /usr/lib/rpm/check-rpaths /usr/lib/rpm/check-buildroot /usr/lib/rpm/brp-compress
%global __requires_exclude_from ^(%{_datadir}|/usr/lib)/%{name}/(doc|src)/.*$ %global __requires_exclude_from ^(%{_datadir}|/usr/lib)/%{name}/(doc|src)/.*$
%global __strip /bin/true %global __strip /bin/true
@ -60,14 +60,13 @@
%global gohostarch riscv64 %global gohostarch riscv64
%endif %endif
Name: golang Name: golang
Version: 1.17.3 Version: 1.19.4
Release: 13 Release: 1
Summary: The Go Programming Language Summary: The Go Programming Language
License: BSD and Public Domain License: BSD and Public Domain
URL: https://golang.org/ URL: https://golang.org/
Source0: https://dl.google.com/go/go1.17.3.src.tar.gz Source0: https://dl.google.com/go/go1.19.4.src.tar.gz
%if !%{golang_bootstrap} %if !%{golang_bootstrap}
BuildRequires: gcc-go >= 5 BuildRequires: gcc-go >= 5
@ -94,7 +93,6 @@ Requires(post): %{_sbindir}/update-alternatives
Requires(postun): %{_sbindir}/update-alternatives Requires(postun): %{_sbindir}/update-alternatives
Recommends: glibc gcc git subversion Recommends: glibc gcc git subversion
# generated by: # generated by:
# go list -f {{.ImportPath}} ./src/vendor/... | sed "s:_$PWD/src/vendor/::g;s:_:.:;s:.*:Provides\: bundled(golang(&)):" && go list -f {{.ImportPath}} ./src/cmd/vendor/... | sed "s:_$PWD/src/cmd/vendor/::g;s:_:.:;s:.*:Provides\: bundled(golang(&)):" # go list -f {{.ImportPath}} ./src/vendor/... | sed "s:_$PWD/src/vendor/::g;s:_:.:;s:.*:Provides\: bundled(golang(&)):" && go list -f {{.ImportPath}} ./src/cmd/vendor/... | sed "s:_$PWD/src/cmd/vendor/::g;s:_:.:;s:.*:Provides\: bundled(golang(&)):"
Provides: bundled(golang(golang.org/x/crypto/chacha20poly1305)) Provides: bundled(golang(golang.org/x/crypto/chacha20poly1305))
@ -150,30 +148,7 @@ Obsoletes: %{name}-vim < 1.4
Obsoletes: emacs-%{name} < 1.4 Obsoletes: emacs-%{name} < 1.4
Requires: %{vendor}-rpm-config Requires: %{vendor}-rpm-config
Patch6001: 0001-release-branch.go1.17-crypto-elliptic-tolerate-zero-.patch #Patch6001: 0001-release-branch.go1.17-crypto-elliptic-tolerate-zero-.patch
Patch6002: 0002-release-branch.go1.17-encoding-pem-fix-stack-overflo.patch
Patch6003: 0003-release-branch.go1.17-syscall-fix-ForkLock-spurious-.patch
Patch6004: 0004-backport-cmd-link-mark-unexported-methods-for-plugins.patch
Patch6005: 0005-release-branch.go1.17-net-http-preserve-nil-values-i.patch
Patch6006: 0006-release-branch.go1.17-go-parser-limit-recursion-dept.patch
Patch6007: 0007-release-branch.go1.17-net-http-don-t-strip-whitespac.patch
Patch6008: 0008-release-branch.go1.17-encoding-xml-limit-depth-of-ne.patch
Patch6009: 0009-release-branch.go1.17-encoding-gob-add-a-depth-limit.patch
Patch6010: 0010-release-branch.go1.17-io-fs-fix-stack-exhaustion-in-.patch
Patch6011: 0011-release-branch.go1.17-path-filepath-fix-stack-exhaus.patch
Patch6012: 0012-release-branch.go1.17-encoding-xml-use-iterative-Ski.patch
Patch6013: 0013-release-branch.go1.17-compress-gzip-fix-stack-exhaus.patch
Patch6014: 0014-release-branch.go1.17-crypto-tls-randomly-generate-t.patch
Patch6015: 0015-release-branch.go1.17-crypto-rand-properly-handle-la.patch
Patch6016: 0016-release-branch.go1.17-math-big-check-buffer-lengths-.patch
Patch6017: 0017-path-filepath-do-not-remove-prefix-.-when-following-.patch
Patch6018: 0018-release-branch.go1.17-syscall-check-correct-group-in.patch
Patch6019: 0019-release-branch.go1.18-net-http-update-bundled-golang.patch
Patch6020: 0020-release-branch.go1.18-regexp-limit-size-of-parsed-re.patch
Patch6021: 0021-release-branch.go1.18-net-http-httputil-avoid-query-.patch
Patch6022: 0022-release-branch.go1.18-archive-tar-limit-size-of-head.patch
Patch6023: 0023-syscall-os-exec-reject-environment-variables-contain.patch
Patch6024: 0024-release-branch.go1.18-add-definition-byte-string-cut.patch
ExclusiveArch: %{golang_arches} ExclusiveArch: %{golang_arches}
@ -387,7 +362,7 @@ fi
%files -f go-pkg.list %files -f go-pkg.list
%endif %endif
%doc AUTHORS CONTRIBUTORS LICENSE PATENTS %doc LICENSE PATENTS
%doc %{goroot}/VERSION %doc %{goroot}/VERSION
%dir %{goroot}/doc %dir %{goroot}/doc
%doc %{goroot}/doc/* %doc %{goroot}/doc/*
@ -412,79 +387,6 @@ fi
%files devel -f go-tests.list -f go-misc.list -f go-src.list %files devel -f go-tests.list -f go-misc.list -f go-src.list
%changelog %changelog
* Sat Dec 17 2022 wanglimin<wanglimin@xfusion.com> - 1.17.3-13
- Add string cut
* Fri Oct 11 2022 hanchao <hanchao47@huawei.com> - 1.17.3-12 * Tue Jan 10 2023 hanchao <hanchao47@huawei.com> - 1.19.4-1
- Type:CVE - upgrade to 1.19.4
- CVE:CVE-2022-41716
- SUG:NA
- DESC: remove hard code and strong dependency of git, subversion and mercurial
* Fri Oct 11 2022 hanchao <hanchao47@huawei.com> - 1.17.3-11
- Type:CVE
- CVE:CVE-2022-41716
- SUG:NA
- DESC: fix CVE-2022-41716
* Mon Oct 10 2022 hanchao <hanchao47@huawei.com> - 1.17.3-10
- Type:CVE
- CVE:CVE-2022-41715,CVE-2022-2880,CVE-2022-2879
- SUG:NA
- DESC: fix CVE-2022-41715,CVE-2022-2880,CVE-2022-2879
* Thu Sep 15 2022 hanchao <hanchao47@huawei.com> - 1.17.3-9
- Type:CVE
- CVE:CVE-2022-27664
- SUG:NA
- DESC: fix CVE-2022-27664
* Thu Sep 8 2022 hanchao<hanchao47@huawei.com> - 1.17.3-8
- Type:bugfix
- CVE:NA
- SUG:NA
- DESC: golang: modify the golang.spec to remove unnecessary files
from golang-help package
* Thu Aug 18 2022 hanchao <hanchao47@huawei.com> - 1.17.3-7
- Type:CVE
- CVE:CVE-2022-29804,CVE-2022-29526
- SUG:NA
- DESC: fix CVE-2022-29804,CVE-2022-29526
* Mon Aug 8 2022 hanchao <hanchao47@huawei.com> - 1.17.3-6
- Type:CVE
- CVE:NA
- SUG:NA
- DESC: fix CVE-2022-32189
* Tue Jul 26 2022 hanchao <hanchao47@huawei.com> - 1.17.3-5
- Type:CVE
- CVE:NA
- SUG:NA
- DESC: fix CVE-2022-32148,CVE-2022-1962,CVE-2022-1705,CVE-2022-30633,
CVE-2022-30635,CVE-2022-30630,CVE-2022-30632,CVE-2022-28131,
CVE-2022-30631,CVE-2022-30629,CVE-2022-30634
* Tue Jun 28 2022 Bin Hu <hubin73@huawei.com> - 1.17.3-4
- Type:bugfix
- CVE:NA
- SUG:NA
- DESC:backport patch to fix bug of golang plugin mode
* Fri May 6 2022 hanchao <hanchao47@huawei.com> - 1.17.3-3
- Type:CVE
- CVE:CVE-2021-44717
- SUG:NA
- DESC:fix CVE-2021-44717
- fix CVE-2021-44717
* Fri May 6 2022 hanchao <hanchao47@huawei.com> - 1.17.3-2
- Type:CVE
- CVE:CVE-2022-28327,CVE-2022-24675
- SUG:NA
- DESC:fix CVE-2022-28327,CVE-2022-24675
- fix CVE-2022-28327 CVE-2022-24675
* Mon Nov 29 2021 chenjiankun <chenjiankun1@huawei.com> - 1.17.3-1
- upgrade to 1.17.3