diff --git a/0009-release-branch.go1.13-net-http-don-t-cache-http2.err.patch b/0009-release-branch.go1.13-net-http-don-t-cache-http2.err.patch deleted file mode 100644 index fc12ac4bf10f2706120885af09ac1103eda56929..0000000000000000000000000000000000000000 --- a/0009-release-branch.go1.13-net-http-don-t-cache-http2.err.patch +++ /dev/null @@ -1,130 +0,0 @@ -From b2df6bf225859f82b3f69d471e1c99b72c208ec3 Mon Sep 17 00:00:00 2001 -From: Brad Fitzpatrick -Date: Fri, 18 Oct 2019 20:45:33 +0000 -Subject: [PATCH] [release-branch.go1.13] net/http: don't cache - http2.erringRoundTripper connections - -Fixes #35087 -Updates #34978 - -Change-Id: I3baf1392ba7366ae6628889c47c343ef702ec438 -Reviewed-on: https://go-review.googlesource.com/c/go/+/202078 -Reviewed-by: Bryan C. Mills -Run-TryBot: Brad Fitzpatrick -TryBot-Result: Gobot Gobot -(cherry picked from commit 88186e5e232625f9c91d639e0cb90a88c6cf1172) -Reviewed-on: https://go-review.googlesource.com/c/go/+/202642 -Reviewed-by: Emmanuel Odeke ---- - src/net/http/transport.go | 9 +++- - src/net/http/transport_test.go | 75 ++++++++++++++++++++++++++++++++++ - 2 files changed, 82 insertions(+), 2 deletions(-) - -diff --git a/src/net/http/transport.go b/src/net/http/transport.go -index caa4bdc36f..b46c4e7066 100644 ---- a/src/net/http/transport.go -+++ b/src/net/http/transport.go -@@ -537,10 +537,15 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) { - if err == nil { - return resp, nil - } -- if http2isNoCachedConnError(err) { -+ -+ // Failed. Clean up and determine whether to retry. -+ -+ _, isH2DialError := pconn.alt.(http2erringRoundTripper) -+ if http2isNoCachedConnError(err) || isH2DialError { - t.removeIdleConn(pconn) - t.decConnsPerHost(pconn.cacheKey) -- } else if !pconn.shouldRetryRequest(req, err) { -+ } -+ if !pconn.shouldRetryRequest(req, err) { - // Issue 16465: return underlying net.Conn.Read error from peek, - // as we've historically done. - if e, ok := err.(transportReadFromServerError); ok { -diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go -index e43c8956ee..f8c13a7091 100644 ---- a/src/net/http/transport_test.go -+++ b/src/net/http/transport_test.go -@@ -5719,3 +5719,78 @@ func TestInvalidHeaderResponse(t *testing.T) { - t.Errorf(`bad "Foo " header value: %q, want %q`, v, "bar") - } - } -+ -+// breakableConn is a net.Conn wrapper with a Write method -+// that will fail when its brokenState is true. -+type breakableConn struct { -+ net.Conn -+ *brokenState -+} -+ -+type brokenState struct { -+ sync.Mutex -+ broken bool -+} -+ -+func (w *breakableConn) Write(b []byte) (n int, err error) { -+ w.Lock() -+ defer w.Unlock() -+ if w.broken { -+ return 0, errors.New("some write error") -+ } -+ return w.Conn.Write(b) -+} -+ -+// Issue 34978: don't cache a broken HTTP/2 connection -+func TestDontCacheBrokenHTTP2Conn(t *testing.T) { -+ cst := newClientServerTest(t, h2Mode, HandlerFunc(func(w ResponseWriter, r *Request) {}), optQuietLog) -+ defer cst.close() -+ -+ var brokenState brokenState -+ -+ cst.tr.Dial = func(netw, addr string) (net.Conn, error) { -+ c, err := net.Dial(netw, addr) -+ if err != nil { -+ t.Errorf("unexpected Dial error: %v", err) -+ return nil, err -+ } -+ return &breakableConn{c, &brokenState}, err -+ } -+ -+ const numReqs = 5 -+ var gotConns uint32 // atomic -+ for i := 1; i <= numReqs; i++ { -+ brokenState.Lock() -+ brokenState.broken = false -+ brokenState.Unlock() -+ -+ // doBreak controls whether we break the TCP connection after the TLS -+ // handshake (before the HTTP/2 handshake). We test a few failures -+ // in a row followed by a final success. -+ doBreak := i != numReqs -+ -+ ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{ -+ GotConn: func(info httptrace.GotConnInfo) { -+ atomic.AddUint32(&gotConns, 1) -+ }, -+ TLSHandshakeDone: func(cfg tls.ConnectionState, err error) { -+ brokenState.Lock() -+ defer brokenState.Unlock() -+ if doBreak { -+ brokenState.broken = true -+ } -+ }, -+ }) -+ req, err := NewRequestWithContext(ctx, "GET", cst.ts.URL, nil) -+ if err != nil { -+ t.Fatal(err) -+ } -+ _, err = cst.c.Do(req) -+ if doBreak != (err != nil) { -+ t.Errorf("for iteration %d, doBreak=%v; unexpected error %v", i, doBreak, err) -+ } -+ } -+ if got, want := atomic.LoadUint32(&gotConns), 1; int(got) != want { -+ t.Errorf("GotConn calls = %v; want %v", got, want) -+ } -+} --- -2.17.1 - diff --git a/0010-release-branch.go1.13-net-http-fix-Server.ConnContex.patch b/0010-release-branch.go1.13-net-http-fix-Server.ConnContex.patch deleted file mode 100644 index c23adb8a9ade1bc752743efa2cdf3d40b5996a27..0000000000000000000000000000000000000000 --- a/0010-release-branch.go1.13-net-http-fix-Server.ConnContex.patch +++ /dev/null @@ -1,96 +0,0 @@ -From 94b6eec5dc639e87f0e2f739cfd257a432160881 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Roman=20Koll=C3=A1r?= -Date: Thu, 21 Nov 2019 22:25:52 +0000 -Subject: [PATCH] [release-branch.go1.13] net/http: fix Server.ConnContext - modifying context for all new connections - -Updates #35750 -Fixes #35765 - -Change-Id: I65d38cfc5ddd66131777e104c269cc3559b2471d -GitHub-Last-Rev: 953fdfd49b2be665be43f8148d2a6180dae3b91c -GitHub-Pull-Request: golang/go#35751 -Reviewed-on: https://go-review.googlesource.com/c/go/+/208318 -Reviewed-by: Brad Fitzpatrick -Run-TryBot: Brad Fitzpatrick -TryBot-Result: Gobot Gobot -(cherry picked from commit bbbc6589dfbc05be2bfa59f51c20f9eaa8d0c531) -Reviewed-on: https://go-review.googlesource.com/c/go/+/208235 -Reviewed-by: Bryan C. Mills ---- - src/net/http/serve_test.go | 33 +++++++++++++++++++++++++++++++++ - src/net/http/server.go | 7 ++++--- - 2 files changed, 37 insertions(+), 3 deletions(-) - -diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go -index 1d1449aa65..90f112b2ee 100644 ---- a/src/net/http/serve_test.go -+++ b/src/net/http/serve_test.go -@@ -6121,6 +6121,39 @@ func TestServerContextsHTTP2(t *testing.T) { - } - } - -+// Issue 35750: check ConnContext not modifying context for other connections -+func TestConnContextNotModifyingAllContexts(t *testing.T) { -+ setParallel(t) -+ defer afterTest(t) -+ type connKey struct{} -+ ts := httptest.NewUnstartedServer(HandlerFunc(func(rw ResponseWriter, r *Request) { -+ rw.Header().Set("Connection", "close") -+ })) -+ ts.Config.ConnContext = func(ctx context.Context, c net.Conn) context.Context { -+ if got := ctx.Value(connKey{}); got != nil { -+ t.Errorf("in ConnContext, unexpected context key = %#v", got) -+ } -+ return context.WithValue(ctx, connKey{}, "conn") -+ } -+ ts.Start() -+ defer ts.Close() -+ -+ var res *Response -+ var err error -+ -+ res, err = ts.Client().Get(ts.URL) -+ if err != nil { -+ t.Fatal(err) -+ } -+ res.Body.Close() -+ -+ res, err = ts.Client().Get(ts.URL) -+ if err != nil { -+ t.Fatal(err) -+ } -+ res.Body.Close() -+} -+ - // Issue 30710: ensure that as per the spec, a server responds - // with 501 Not Implemented for unsupported transfer-encodings. - func TestUnsupportedTransferEncodingsReturn501(t *testing.T) { -diff --git a/src/net/http/server.go b/src/net/http/server.go -index 95a5eabaa2..8252e45aca 100644 ---- a/src/net/http/server.go -+++ b/src/net/http/server.go -@@ -2915,16 +2915,17 @@ func (srv *Server) Serve(l net.Listener) error { - } - return e - } -+ connCtx := ctx - if cc := srv.ConnContext; cc != nil { -- ctx = cc(ctx, rw) -- if ctx == nil { -+ connCtx = cc(connCtx, rw) -+ if connCtx == nil { - panic("ConnContext returned nil") - } - } - tempDelay = 0 - c := srv.newConn(rw) - c.setState(c.rwc, StateNew) // before Serve can return -- go c.serve(ctx) -+ go c.serve(connCtx) - } - } - --- -2.17.1 - diff --git a/0011-release-branch.go1.13-runtime-fix-textOff-for-multip.patch b/0011-release-branch.go1.13-runtime-fix-textOff-for-multip.patch deleted file mode 100644 index 7de5e384adff3449d133ea5c1a4d376be4094d14..0000000000000000000000000000000000000000 --- a/0011-release-branch.go1.13-runtime-fix-textOff-for-multip.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 6eee9903c7f970defbc0c9770397790b2bed5709 Mon Sep 17 00:00:00 2001 -From: Lynn Boger -Date: Mon, 28 Oct 2019 09:29:40 -0400 -Subject: [PATCH] [release-branch.go1.13] runtime: fix textOff for multiple - text sections - -If a compilation has multiple text sections, code in -textOff must compare the offset argument against the range -for each text section to determine which one it is in. -The comparison looks like this: - -if uintptr(off) >= sectaddr && uintptr(off) <= sectaddr+sectlen - -If the off value being compared is equal to sectaddr+sectlen then it -is not within the range of the text section but after it. The -comparison should be just '<'. - -Fixes #35211 - -Change-Id: I114633fd734563d38f4e842dd884c6c239f73c95 -Reviewed-on: https://go-review.googlesource.com/c/go/+/203817 -Run-TryBot: Lynn Boger -TryBot-Result: Gobot Gobot -Reviewed-by: Ian Lance Taylor -Reviewed-by: Cherry Zhang -(cherry picked from commit 0ae9389609f23dc905c58fc2ad7bcc16b770f337) -Reviewed-on: https://go-review.googlesource.com/c/go/+/203819 -Run-TryBot: Carlos Amedee -Reviewed-by: Keith Randall ---- - src/runtime/type.go | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/runtime/type.go b/src/runtime/type.go -index 660b45ef39..5ae3c4b09e 100644 ---- a/src/runtime/type.go -+++ b/src/runtime/type.go -@@ -287,7 +287,7 @@ func (t *_type) textOff(off textOff) unsafe.Pointer { - for i := range md.textsectmap { - sectaddr := md.textsectmap[i].vaddr - sectlen := md.textsectmap[i].length -- if uintptr(off) >= sectaddr && uintptr(off) <= sectaddr+sectlen { -+ if uintptr(off) >= sectaddr && uintptr(off) < sectaddr+sectlen { - res = md.textsectmap[i].baseaddr + uintptr(off) - uintptr(md.textsectmap[i].vaddr) - break - } --- -2.17.1 - diff --git a/0012-release-branch.go1.13-runtime-ensure-memmove-write-p.patch b/0012-release-branch.go1.13-runtime-ensure-memmove-write-p.patch deleted file mode 100644 index 20c49cd48b435409998d6e9840bdb1dc4d1d809b..0000000000000000000000000000000000000000 --- a/0012-release-branch.go1.13-runtime-ensure-memmove-write-p.patch +++ /dev/null @@ -1,126 +0,0 @@ -From acc723af2646200d13f76ffde80b000c4095074a Mon Sep 17 00:00:00 2001 -From: Cherry Zhang -Date: Fri, 27 Dec 2019 12:02:00 -0500 -Subject: [PATCH] [release-branch.go1.13] runtime: ensure memmove write pointer - atomically on ARM64 - -If a pointer write is not atomic, if the GC is running -concurrently, it may observe a partially updated pointer, which -may point to unallocated or already dead memory. Most pointer -writes, like the store instructions generated by the compiler, -are already atomic. But we still need to be careful in places -like memmove. In memmove, we don't know which bits are pointers -(or too expensive to query), so we ensure that all aligned -pointer-sized units are written atomically. - -Fixes #36361. -Updates #36101. - -Change-Id: I1b3ca24c6b1ac8a8aaf9ee470115e9a89ec1b00b -Reviewed-on: https://go-review.googlesource.com/c/go/+/212626 -Reviewed-by: Austin Clements -(cherry picked from commit ffbc02761abb47106ce88e09290a31513b5f6c8a) -Reviewed-on: https://go-review.googlesource.com/c/go/+/213683 -Run-TryBot: Cherry Zhang -TryBot-Result: Gobot Gobot ---- - src/runtime/memmove_arm64.s | 42 +++++++++++++++++++++++++++++++++++++----- - 1 file changed, 37 insertions(+), 5 deletions(-) - -diff --git a/src/runtime/memmove_arm64.s b/src/runtime/memmove_arm64.s -index ac29f94..cedb018 100644 ---- a/src/runtime/memmove_arm64.s -+++ b/src/runtime/memmove_arm64.s -@@ -22,7 +22,7 @@ check: - CMP R3, R4 - BLT backward - -- // Copying forward proceeds by copying R7/8 words then copying R6 bytes. -+ // Copying forward proceeds by copying R7/32 quadwords then R6 <= 31 tail bytes. - // R3 and R4 are advanced as we copy. - - // (There may be implementations of armv8 where copying by bytes until -@@ -30,11 +30,12 @@ check: - // optimization, but the on the one tested so far (xgene) it did not - // make a significance difference.) - -- CBZ R7, noforwardlarge // Do we need to do any doubleword-by-doubleword copying? -+ CBZ R7, noforwardlarge // Do we need to do any quadword copying? - - ADD R3, R7, R9 // R9 points just past where we copy by word - - forwardlargeloop: -+ // Copy 32 bytes at a time. - LDP.P 32(R4), (R8, R10) - STP.P (R8, R10), 32(R3) - LDP -16(R4), (R11, R12) -@@ -43,10 +44,26 @@ forwardlargeloop: - CBNZ R7, forwardlargeloop - - noforwardlarge: -- CBNZ R6, forwardtail // Do we need to do any byte-by-byte copying? -+ CBNZ R6, forwardtail // Do we need to copy any tail bytes? - RET - - forwardtail: -+ // There are R6 <= 31 bytes remaining to copy. -+ // This is large enough to still contain pointers, -+ // which must be copied atomically. -+ // Copy the next 16 bytes, then 8 bytes, then any remaining bytes. -+ TBZ $4, R6, 3(PC) // write 16 bytes if R6&16 != 0 -+ LDP.P 16(R4), (R8, R10) -+ STP.P (R8, R10), 16(R3) -+ -+ TBZ $3, R6, 3(PC) // write 8 bytes if R6&8 != 0 -+ MOVD.P 8(R4), R8 -+ MOVD.P R8, 8(R3) -+ -+ AND $7, R6 -+ CBNZ R6, 2(PC) -+ RET -+ - ADD R3, R6, R9 // R9 points just past the destination memory - - forwardtailloop: -@@ -90,7 +107,7 @@ copy1: - RET - - backward: -- // Copying backwards proceeds by copying R6 bytes then copying R7/8 words. -+ // Copying backwards first copies R6 <= 31 tail bytes, then R7/32 quadwords. - // R3 and R4 are advanced to the end of the destination/source buffers - // respectively and moved back as we copy. - -@@ -99,13 +116,28 @@ backward: - - CBZ R6, nobackwardtail // Do we need to do any byte-by-byte copying? - -- SUB R6, R3, R9 // R9 points at the lowest destination byte that should be copied by byte. -+ AND $7, R6, R12 -+ CBZ R12, backwardtaillarge -+ -+ SUB R12, R3, R9 // R9 points at the lowest destination byte that should be copied by byte. - backwardtailloop: -+ // Copy sub-pointer-size tail. - MOVBU.W -1(R4), R8 - MOVBU.W R8, -1(R3) - CMP R9, R3 - BNE backwardtailloop - -+backwardtaillarge: -+ // Do 8/16-byte write if possible. -+ // See comment at forwardtail. -+ TBZ $3, R6, 3(PC) -+ MOVD.W -8(R4), R8 -+ MOVD.W R8, -8(R3) -+ -+ TBZ $4, R6, 3(PC) -+ LDP.W -16(R4), (R8, R10) -+ STP.W (R8, R10), -16(R3) -+ - nobackwardtail: - CBNZ R7, backwardlarge // Do we need to do any doubleword-by-doubleword copying? - RET --- -1.8.3.1 - diff --git a/0019-net-http-cgi-net-http-fcgi-add-Content-Type-detectio.patch b/0019-net-http-cgi-net-http-fcgi-add-Content-Type-detectio.patch new file mode 100644 index 0000000000000000000000000000000000000000..b265af09603fdd8591b98ff6839502171d0551bf --- /dev/null +++ b/0019-net-http-cgi-net-http-fcgi-add-Content-Type-detectio.patch @@ -0,0 +1,363 @@ +From 2ad2c042555908cfd791b7e6a3318c1d3df10aeb Mon Sep 17 00:00:00 2001 +From: Roberto Clapis +Date: Wed, 26 Aug 2020 08:53:03 +0200 +Subject: [PATCH] net/http/cgi,net/http/fcgi: add Content-Type detection + +This CL ensures that responses served via CGI and FastCGI +have a Content-Type header based on the content of the +response if not explicitly set by handlers. + +If the implementers of the handler did not explicitly +specify a Content-Type both CGI implementations would default +to "text/html", potentially causing cross-site scripting. + +Thanks to RedTeam Pentesting GmbH for reporting this. + +Fixes #40928 +Fixes CVE-2020-24553 + +Change-Id: I82cfc396309b5ab2e8d6e9a87eda8ea7e3799473 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/823217 +Reviewed-by: Russ Cox +Reviewed-on: https://go-review.googlesource.com/c/go/+/252179 +Run-TryBot: Filippo Valsorda +TryBot-Result: Go Bot +Reviewed-by: Katie Hockman +--- + src/net/http/cgi/child.go | 36 ++++++++++++------ + src/net/http/cgi/child_test.go | 58 +++++++++++++++++++++++++++++ + src/net/http/cgi/matryoshka_test.go | 8 +++- + src/net/http/fcgi/child.go | 39 ++++++++++++++----- + src/net/http/fcgi/fcgi_test.go | 52 ++++++++++++++++++++++++++ + 5 files changed, 171 insertions(+), 22 deletions(-) + +diff --git a/src/net/http/cgi/child.go b/src/net/http/cgi/child.go +index cb140f8f2f..2b210ea4a1 100644 +--- a/src/net/http/cgi/child.go ++++ b/src/net/http/cgi/child.go +@@ -165,10 +165,12 @@ func Serve(handler http.Handler) error { + } + + type response struct { +- req *http.Request +- header http.Header +- bufw *bufio.Writer +- headerSent bool ++ req *http.Request ++ header http.Header ++ code int ++ wroteHeader bool ++ wroteCGIHeader bool ++ bufw *bufio.Writer + } + + func (r *response) Flush() { +@@ -180,26 +182,38 @@ func (r *response) Header() http.Header { + } + + func (r *response) Write(p []byte) (n int, err error) { +- if !r.headerSent { ++ if !r.wroteHeader { + r.WriteHeader(http.StatusOK) + } ++ if !r.wroteCGIHeader { ++ r.writeCGIHeader(p) ++ } + return r.bufw.Write(p) + } + + func (r *response) WriteHeader(code int) { +- if r.headerSent { ++ if r.wroteHeader { + // Note: explicitly using Stderr, as Stdout is our HTTP output. + fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on request for %s", r.req.URL) + return + } +- r.headerSent = true +- fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code)) ++ r.wroteHeader = true ++ r.code = code ++} + +- // Set a default Content-Type ++// writeCGIHeader finalizes the header sent to the client and writes it to the output. ++// p is not written by writeHeader, but is the first chunk of the body ++// that will be written. It is sniffed for a Content-Type if none is ++// set explicitly. ++func (r *response) writeCGIHeader(p []byte) { ++ if r.wroteCGIHeader { ++ return ++ } ++ r.wroteCGIHeader = true ++ fmt.Fprintf(r.bufw, "Status: %d %s\r\n", r.code, http.StatusText(r.code)) + if _, hasType := r.header["Content-Type"]; !hasType { +- r.header.Add("Content-Type", "text/html; charset=utf-8") ++ r.header.Set("Content-Type", http.DetectContentType(p)) + } +- + r.header.Write(r.bufw) + r.bufw.WriteString("\r\n") + r.bufw.Flush() +diff --git a/src/net/http/cgi/child_test.go b/src/net/http/cgi/child_test.go +index 14e0af475f..18cf789bd5 100644 +--- a/src/net/http/cgi/child_test.go ++++ b/src/net/http/cgi/child_test.go +@@ -7,6 +7,11 @@ + package cgi + + import ( ++ "bufio" ++ "bytes" ++ "net/http" ++ "net/http/httptest" ++ "strings" + "testing" + ) + +@@ -148,3 +153,56 @@ func TestRequestWithoutRemotePort(t *testing.T) { + t.Errorf("RemoteAddr: got %q; want %q", g, e) + } + } ++ ++func TestResponse(t *testing.T) { ++ var tests = []struct { ++ name string ++ body string ++ wantCT string ++ }{ ++ { ++ name: "no body", ++ wantCT: "text/plain; charset=utf-8", ++ }, ++ { ++ name: "html", ++ body: "test pageThis is a body", ++ wantCT: "text/html; charset=utf-8", ++ }, ++ { ++ name: "text", ++ body: strings.Repeat("gopher", 86), ++ wantCT: "text/plain; charset=utf-8", ++ }, ++ { ++ name: "jpg", ++ body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024), ++ wantCT: "image/jpeg", ++ }, ++ } ++ for _, tt := range tests { ++ t.Run(tt.name, func(t *testing.T) { ++ var buf bytes.Buffer ++ resp := response{ ++ req: httptest.NewRequest("GET", "/", nil), ++ header: http.Header{}, ++ bufw: bufio.NewWriter(&buf), ++ } ++ n, err := resp.Write([]byte(tt.body)) ++ if err != nil { ++ t.Errorf("Write: unexpected %v", err) ++ } ++ if want := len(tt.body); n != want { ++ t.Errorf("reported short Write: got %v want %v", n, want) ++ } ++ resp.writeCGIHeader(nil) ++ resp.Flush() ++ if got := resp.Header().Get("Content-Type"); got != tt.wantCT { ++ t.Errorf("wrong content-type: got %q, want %q", got, tt.wantCT) ++ } ++ if !bytes.HasSuffix(buf.Bytes(), []byte(tt.body)) { ++ t.Errorf("body was not correctly written") ++ } ++ }) ++ } ++} +diff --git a/src/net/http/cgi/matryoshka_test.go b/src/net/http/cgi/matryoshka_test.go +index 32d59c09a3..41a27889f2 100644 +--- a/src/net/http/cgi/matryoshka_test.go ++++ b/src/net/http/cgi/matryoshka_test.go +@@ -16,7 +16,9 @@ import ( + "io" + "net/http" + "net/http/httptest" ++ "net/url" + "os" ++ "strings" + "testing" + "time" + ) +@@ -52,7 +54,7 @@ func TestHostingOurselves(t *testing.T) { + } + replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap) + +- if expected, got := "text/html; charset=utf-8", replay.Header().Get("Content-Type"); got != expected { ++ if expected, got := "text/plain; charset=utf-8", replay.Header().Get("Content-Type"); got != expected { + t.Errorf("got a Content-Type of %q; expected %q", got, expected) + } + if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected { +@@ -203,6 +205,10 @@ func TestBeChildCGIProcess(t *testing.T) { + if req.FormValue("no-body") == "1" { + return + } ++ if eb, ok := req.Form["exact-body"]; ok { ++ io.WriteString(rw, eb[0]) ++ return ++ } + if req.FormValue("write-forever") == "1" { + io.Copy(rw, neverEnding('a')) + for { +diff --git a/src/net/http/fcgi/child.go b/src/net/http/fcgi/child.go +index 30a6b2ce2d..a31273b3ec 100644 +--- a/src/net/http/fcgi/child.go ++++ b/src/net/http/fcgi/child.go +@@ -74,10 +74,12 @@ func (r *request) parseParams() { + + // response implements http.ResponseWriter. + type response struct { +- req *request +- header http.Header +- w *bufWriter +- wroteHeader bool ++ req *request ++ header http.Header ++ code int ++ wroteHeader bool ++ wroteCGIHeader bool ++ w *bufWriter + } + + func newResponse(c *child, req *request) *response { +@@ -92,11 +94,14 @@ func (r *response) Header() http.Header { + return r.header + } + +-func (r *response) Write(data []byte) (int, error) { ++func (r *response) Write(p []byte) (n int, err error) { + if !r.wroteHeader { + r.WriteHeader(http.StatusOK) + } +- return r.w.Write(data) ++ if !r.wroteCGIHeader { ++ r.writeCGIHeader(p) ++ } ++ return r.w.Write(p) + } + + func (r *response) WriteHeader(code int) { +@@ -104,22 +109,34 @@ func (r *response) WriteHeader(code int) { + return + } + r.wroteHeader = true ++ r.code = code + if code == http.StatusNotModified { + // Must not have body. + r.header.Del("Content-Type") + r.header.Del("Content-Length") + r.header.Del("Transfer-Encoding") +- } else if r.header.Get("Content-Type") == "" { +- r.header.Set("Content-Type", "text/html; charset=utf-8") + } +- + if r.header.Get("Date") == "" { + r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat)) + } ++} + +- fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code)) ++// writeCGIHeader finalizes the header sent to the client and writes it to the output. ++// p is not written by writeHeader, but is the first chunk of the body ++// that will be written. It is sniffed for a Content-Type if none is ++// set explicitly. ++func (r *response) writeCGIHeader(p []byte) { ++ if r.wroteCGIHeader { ++ return ++ } ++ r.wroteCGIHeader = true ++ fmt.Fprintf(r.w, "Status: %d %s\r\n", r.code, http.StatusText(r.code)) ++ if _, hasType := r.header["Content-Type"]; r.code != http.StatusNotModified && !hasType { ++ r.header.Set("Content-Type", http.DetectContentType(p)) ++ } + r.header.Write(r.w) + r.w.WriteString("\r\n") ++ r.w.Flush() + } + + func (r *response) Flush() { +@@ -290,6 +307,8 @@ func (c *child) serveRequest(req *request, body io.ReadCloser) { + httpReq = httpReq.WithContext(envVarCtx) + c.handler.ServeHTTP(r, httpReq) + } ++ // Make sure we serve something even if nothing was written to r ++ r.Write(nil) + r.Close() + c.mu.Lock() + delete(c.requests, req.reqId) +diff --git a/src/net/http/fcgi/fcgi_test.go b/src/net/http/fcgi/fcgi_test.go +index e9d2b34023..4a27a12c35 100644 +--- a/src/net/http/fcgi/fcgi_test.go ++++ b/src/net/http/fcgi/fcgi_test.go +@@ -10,6 +10,7 @@ import ( + "io" + "io/ioutil" + "net/http" ++ "strings" + "testing" + ) + +@@ -344,3 +345,54 @@ func TestChildServeReadsEnvVars(t *testing.T) { + <-done + } + } ++ ++func TestResponseWriterSniffsContentType(t *testing.T) { ++ var tests = []struct { ++ name string ++ body string ++ wantCT string ++ }{ ++ { ++ name: "no body", ++ wantCT: "text/plain; charset=utf-8", ++ }, ++ { ++ name: "html", ++ body: "test pageThis is a body", ++ wantCT: "text/html; charset=utf-8", ++ }, ++ { ++ name: "text", ++ body: strings.Repeat("gopher", 86), ++ wantCT: "text/plain; charset=utf-8", ++ }, ++ { ++ name: "jpg", ++ body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024), ++ wantCT: "image/jpeg", ++ }, ++ } ++ for _, tt := range tests { ++ t.Run(tt.name, func(t *testing.T) { ++ input := make([]byte, len(streamFullRequestStdin)) ++ copy(input, streamFullRequestStdin) ++ rc := nopWriteCloser{bytes.NewBuffer(input)} ++ done := make(chan bool) ++ var resp *response ++ c := newChild(rc, http.HandlerFunc(func( ++ w http.ResponseWriter, ++ r *http.Request, ++ ) { ++ io.WriteString(w, tt.body) ++ resp = w.(*response) ++ done <- true ++ })) ++ defer c.cleanUp() ++ go c.serve() ++ <-done ++ if got := resp.Header().Get("Content-Type"); got != tt.wantCT { ++ t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT) ++ } ++ }) ++ } ++} +-- +2.17.1 + diff --git a/0020-golang-fix-CVE-2020-28366.patch b/0020-golang-fix-CVE-2020-28366.patch new file mode 100644 index 0000000000000000000000000000000000000000..dfc8941d19641fbb69751f73fb13ad445dac7f08 --- /dev/null +++ b/0020-golang-fix-CVE-2020-28366.patch @@ -0,0 +1,413 @@ +From 24fb490f5c5ba855ad9feba179f820a835f68d66 Mon Sep 17 00:00:00 2001 +From: liuzekun +Date: Tue, 1 Dec 2020 22:50:08 -0500 +Subject: [PATCH] golang: fix CVE-2020-28366 + +Upstream: https://github.com/golang/go/commit/062e0e5ce6df339dc26732438ad771f73dbf2292 +cmd/go, cmd/cgo: don't let bogus symbol set cgo_ldflag + +A hand-edited object file can have a symbol name that uses newline and +other normally invalid characters. The cgo tool will generate Go files +containing symbol names, unquoted. That can permit those symbol names +to inject Go code into a cgo-generated file. If that Go code uses the +//go:cgo_ldflag pragma, it can cause the C linker to run arbitrary +code when building a package. If you build an imported package we +permit arbitrary code at run time, but we don't want to permit it at +package build time. This CL prevents this in two ways. + +In cgo, reject invalid symbols that contain non-printable or space +characters, or that contain anything that looks like a Go comment. + +In the go tool, double check all //go:cgo_ldflag directives in +generated code, to make sure they follow the existing LDFLAG restrictions. + +Thanks to Imre Rad / https://www.linkedin.com/in/imre-rad-2358749b for +reporting this. + +Fixes CVE-2020-28367 + +Change-Id: Ia1ad8f3791ea79612690fa7d26ac451d0f6df7c1 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/895832 +Reviewed-by: Than McIntosh +Reviewed-by: Cherry Zhang +Reviewed-on: https://go-review.googlesource.com/c/go/+/269658 +Trust: Katie Hockman +Trust: Roland Shoemaker +Run-TryBot: Katie Hockman +TryBot-Result: Go Bot +Reviewed-by: Roland Shoemaker + +Signed-off-by: liuzekun +--- + misc/cgo/errors/badsym_test.go | 216 +++++++++++++++++++++++++++++++ + src/cmd/cgo/out.go | 24 ++++ + src/cmd/go/internal/work/exec.go | 60 +++++++++ + 3 files changed, 300 insertions(+) + create mode 100644 misc/cgo/errors/badsym_test.go + +diff --git a/misc/cgo/errors/badsym_test.go b/misc/cgo/errors/badsym_test.go +new file mode 100644 +index 0000000..b2701bf +--- /dev/null ++++ b/misc/cgo/errors/badsym_test.go +@@ -0,0 +1,216 @@ ++// Copyright 2020 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++package errorstest ++ ++import ( ++ "bytes" ++ "io/ioutil" ++ "os" ++ "os/exec" ++ "path/filepath" ++ "strings" ++ "testing" ++ "unicode" ++) ++ ++// A manually modified object file could pass unexpected characters ++// into the files generated by cgo. ++ ++const magicInput = "abcdefghijklmnopqrstuvwxyz0123" ++const magicReplace = "\n//go:cgo_ldflag \"-badflag\"\n//" ++ ++const cSymbol = "BadSymbol" + magicInput + "Name" ++const cDefSource = "int " + cSymbol + " = 1;" ++const cRefSource = "extern int " + cSymbol + "; int F() { return " + cSymbol + "; }" ++ ++// goSource is the source code for the trivial Go file we use. ++// We will replace TMPDIR with the temporary directory name. ++const goSource = ` ++package main ++ ++// #cgo LDFLAGS: TMPDIR/cbad.o TMPDIR/cbad.so ++// extern int F(); ++import "C" ++ ++func main() { ++ println(C.F()) ++} ++` ++ ++func TestBadSymbol(t *testing.T) { ++ dir := t.TempDir() ++ ++ mkdir := func(base string) string { ++ ret := filepath.Join(dir, base) ++ if err := os.Mkdir(ret, 0755); err != nil { ++ t.Fatal(err) ++ } ++ return ret ++ } ++ ++ cdir := mkdir("c") ++ godir := mkdir("go") ++ ++ makeFile := func(mdir, base, source string) string { ++ ret := filepath.Join(mdir, base) ++ if err := ioutil.WriteFile(ret, []byte(source), 0644); err != nil { ++ t.Fatal(err) ++ } ++ return ret ++ } ++ ++ cDefFile := makeFile(cdir, "cdef.c", cDefSource) ++ cRefFile := makeFile(cdir, "cref.c", cRefSource) ++ ++ ccCmd := cCompilerCmd(t) ++ ++ cCompile := func(arg, base, src string) string { ++ out := filepath.Join(cdir, base) ++ run := append(ccCmd, arg, "-o", out, src) ++ output, err := exec.Command(run[0], run[1:]...).CombinedOutput() ++ if err != nil { ++ t.Log(run) ++ t.Logf("%s", output) ++ t.Fatal(err) ++ } ++ if err := os.Remove(src); err != nil { ++ t.Fatal(err) ++ } ++ return out ++ } ++ ++ // Build a shared library that defines a symbol whose name ++ // contains magicInput. ++ ++ cShared := cCompile("-shared", "c.so", cDefFile) ++ ++ // Build an object file that refers to the symbol whose name ++ // contains magicInput. ++ ++ cObj := cCompile("-c", "c.o", cRefFile) ++ ++ // Rewrite the shared library and the object file, replacing ++ // magicInput with magicReplace. This will have the effect of ++ // introducing a symbol whose name looks like a cgo command. ++ // The cgo tool will use that name when it generates the ++ // _cgo_import.go file, thus smuggling a magic //go:cgo_ldflag ++ // pragma into a Go file. We used to not check the pragmas in ++ // _cgo_import.go. ++ ++ rewrite := func(from, to string) { ++ obj, err := ioutil.ReadFile(from) ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ if bytes.Count(obj, []byte(magicInput)) == 0 { ++ t.Fatalf("%s: did not find magic string", from) ++ } ++ ++ if len(magicInput) != len(magicReplace) { ++ t.Fatalf("internal test error: different magic lengths: %d != %d", len(magicInput), len(magicReplace)) ++ } ++ ++ obj = bytes.ReplaceAll(obj, []byte(magicInput), []byte(magicReplace)) ++ ++ if err := ioutil.WriteFile(to, obj, 0644); err != nil { ++ t.Fatal(err) ++ } ++ } ++ ++ cBadShared := filepath.Join(godir, "cbad.so") ++ rewrite(cShared, cBadShared) ++ ++ cBadObj := filepath.Join(godir, "cbad.o") ++ rewrite(cObj, cBadObj) ++ ++ goSourceBadObject := strings.ReplaceAll(goSource, "TMPDIR", godir) ++ makeFile(godir, "go.go", goSourceBadObject) ++ ++ makeFile(godir, "go.mod", "module badsym") ++ ++ // Try to build our little package. ++ cmd := exec.Command("go", "build", "-ldflags=-v") ++ cmd.Dir = godir ++ output, err := cmd.CombinedOutput() ++ ++ // The build should fail, but we want it to fail because we ++ // detected the error, not because we passed a bad flag to the ++ // C linker. ++ ++ if err == nil { ++ t.Errorf("go build succeeded unexpectedly") ++ } ++ ++ t.Logf("%s", output) ++ ++ for _, line := range bytes.Split(output, []byte("\n")) { ++ if bytes.Contains(line, []byte("dynamic symbol")) && bytes.Contains(line, []byte("contains unsupported character")) { ++ // This is the error from cgo. ++ continue ++ } ++ ++ // We passed -ldflags=-v to see the external linker invocation, ++ // which should not include -badflag. ++ if bytes.Contains(line, []byte("-badflag")) { ++ t.Error("output should not mention -badflag") ++ } ++ ++ // Also check for compiler errors, just in case. ++ // GCC says "unrecognized command line option". ++ // clang says "unknown argument". ++ if bytes.Contains(line, []byte("unrecognized")) || bytes.Contains(output, []byte("unknown")) { ++ t.Error("problem should have been caught before invoking C linker") ++ } ++ } ++} ++ ++func cCompilerCmd(t *testing.T) []string { ++ cc := []string{goEnv(t, "CC")} ++ ++ out := goEnv(t, "GOGCCFLAGS") ++ quote := '\000' ++ start := 0 ++ lastSpace := true ++ backslash := false ++ s := string(out) ++ for i, c := range s { ++ if quote == '\000' && unicode.IsSpace(c) { ++ if !lastSpace { ++ cc = append(cc, s[start:i]) ++ lastSpace = true ++ } ++ } else { ++ if lastSpace { ++ start = i ++ lastSpace = false ++ } ++ if quote == '\000' && !backslash && (c == '"' || c == '\'') { ++ quote = c ++ backslash = false ++ } else if !backslash && quote == c { ++ quote = '\000' ++ } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' { ++ backslash = true ++ } else { ++ backslash = false ++ } ++ } ++ } ++ if !lastSpace { ++ cc = append(cc, s[start:]) ++ } ++ return cc ++} ++ ++func goEnv(t *testing.T, key string) string { ++ out, err := exec.Command("go", "env", key).CombinedOutput() ++ if err != nil { ++ t.Logf("go env %s\n", key) ++ t.Logf("%s", out) ++ t.Fatal(err) ++ } ++ return strings.TrimSpace(string(out)) ++} +diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go +index 1fddbb6..65a8d66 100644 +--- a/src/cmd/cgo/out.go ++++ b/src/cmd/cgo/out.go +@@ -22,6 +22,7 @@ import ( + "regexp" + "sort" + "strings" ++ "unicode" + ) + + var ( +@@ -325,6 +326,8 @@ func dynimport(obj string) { + if s.Version != "" { + targ += "#" + s.Version + } ++ checkImportSymName(s.Name) ++ checkImportSymName(targ) + fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library) + } + lib, _ := f.ImportedLibraries() +@@ -340,6 +343,7 @@ func dynimport(obj string) { + if len(s) > 0 && s[0] == '_' { + s = s[1:] + } ++ checkImportSymName(s) + fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s, s, "") + } + lib, _ := f.ImportedLibraries() +@@ -354,6 +358,8 @@ func dynimport(obj string) { + for _, s := range sym { + ss := strings.Split(s, ":") + name := strings.Split(ss[0], "@")[0] ++ checkImportSymName(name) ++ checkImportSymName(ss[0]) + fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", name, ss[0], strings.ToLower(ss[1])) + } + return +@@ -371,6 +377,7 @@ func dynimport(obj string) { + // Go symbols. + continue + } ++ checkImportSymName(s.Name) + fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, s.Name, s.Library) + } + lib, err := f.ImportedLibraries() +@@ -386,6 +393,23 @@ func dynimport(obj string) { + fatalf("cannot parse %s as ELF, Mach-O, PE or XCOFF", obj) + } + ++// checkImportSymName checks a symbol name we are going to emit as part ++// of a //go:cgo_import_dynamic pragma. These names come from object ++// files, so they may be corrupt. We are going to emit them unquoted, ++// so while they don't need to be valid symbol names (and in some cases, ++// involving symbol versions, they won't be) they must contain only ++// graphic characters and must not contain Go comments. ++func checkImportSymName(s string) { ++ for _, c := range s { ++ if !unicode.IsGraphic(c) || unicode.IsSpace(c) { ++ fatalf("dynamic symbol %q contains unsupported character", s) ++ } ++ } ++ if strings.Index(s, "//") >= 0 || strings.Index(s, "/*") >= 0 { ++ fatalf("dynamic symbol %q contains Go comment") ++ } ++} ++ + // Construct a gcc struct matching the gc argument frame. + // Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes. + // These assumptions are checked by the gccProlog. +diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go +index 7dd9a90..0e5c34f 100644 +--- a/src/cmd/go/internal/work/exec.go ++++ b/src/cmd/go/internal/work/exec.go +@@ -2630,6 +2630,66 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo + noCompiler() + } + ++ // Double check the //go:cgo_ldflag comments in the generated files. ++ // The compiler only permits such comments in files whose base name ++ // starts with "_cgo_". Make sure that the comments in those files ++ // are safe. This is a backstop against people somehow smuggling ++ // such a comment into a file generated by cgo. ++ if cfg.BuildToolchainName == "gc" && !cfg.BuildN { ++ var flags []string ++ for _, f := range outGo { ++ if !strings.HasPrefix(filepath.Base(f), "_cgo_") { ++ continue ++ } ++ ++ src, err := ioutil.ReadFile(f) ++ if err != nil { ++ return nil, nil, err ++ } ++ ++ const cgoLdflag = "//go:cgo_ldflag" ++ idx := bytes.Index(src, []byte(cgoLdflag)) ++ for idx >= 0 { ++ // We are looking at //go:cgo_ldflag. ++ // Find start of line. ++ start := bytes.LastIndex(src[:idx], []byte("\n")) ++ if start == -1 { ++ start = 0 ++ } ++ ++ // Find end of line. ++ end := bytes.Index(src[idx:], []byte("\n")) ++ if end == -1 { ++ end = len(src) ++ } else { ++ end += idx ++ } ++ ++ // Check for first line comment in line. ++ // We don't worry about /* */ comments, ++ // which normally won't appear in files ++ // generated by cgo. ++ commentStart := bytes.Index(src[start:], []byte("//")) ++ commentStart += start ++ // If that line comment is //go:cgo_ldflag, ++ // it's a match. ++ if bytes.HasPrefix(src[commentStart:], []byte(cgoLdflag)) { ++ // Pull out the flag, and unquote it. ++ // This is what the compiler does. ++ flag := string(src[idx+len(cgoLdflag) : end]) ++ flag = strings.TrimSpace(flag) ++ flag = strings.Trim(flag, `"`) ++ flags = append(flags, flag) ++ } ++ src = src[end:] ++ idx = bytes.Index(src, []byte(cgoLdflag)) ++ } ++ } ++ if err := checkLinkerFlags("LDFLAGS", "go:cgo_ldflag", flags); err != nil { ++ return nil, nil, err ++ } ++ } ++ + return outGo, outObj, nil + } + +-- +2.19.1 + diff --git a/0021-golang-fix-CVE-2020-28367.patch b/0021-golang-fix-CVE-2020-28367.patch new file mode 100644 index 0000000000000000000000000000000000000000..a5ca6edc2c76be026951c98269da14a39e2cd74d --- /dev/null +++ b/0021-golang-fix-CVE-2020-28367.patch @@ -0,0 +1,67 @@ +From ac9a264a575bbbc9de2374a65a6c8fd50c32000d Mon Sep 17 00:00:00 2001 +From: liuzekun +Date: Mon, 30 Nov 2020 22:18:43 -0500 +Subject: [PATCH] golang: fix CVE-2020-28367 + +Upstream: https://github.com/golang/go/commit/da7aa86917811a571e6634b45a457f918b8e6561 +cmd/go: in cgoflags, permit -DX1, prohibit -Wp,-D,opt + +Restrict -D and -U to ASCII C identifiers, but do permit trailing digits. +When using -Wp, prohibit commas in -D values. + +Change-Id: Ibfc4dfdd6e6c258e131448e7682610c44eee9492 +Reviewed-on: https://go-review.googlesource.com/c/go/+/267277 +Trust: Ian Lance Taylor +Run-TryBot: Ian Lance Taylor +TryBot-Result: Go Bot +Reviewed-by: Bryan C. Mills +--- + src/cmd/go/internal/work/security.go | 4 ++-- + src/cmd/go/internal/work/security_test.go | 2 ++ + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/cmd/go/internal/work/security.go b/src/cmd/go/internal/work/security.go +index 0d8da21..b00c21e 100644 +--- a/src/cmd/go/internal/work/security.go ++++ b/src/cmd/go/internal/work/security.go +@@ -42,7 +42,7 @@ import ( + var re = lazyregexp.New + + var validCompilerFlags = []*lazyregexp.Regexp{ +- re(`-D([A-Za-z_].*)`), ++ re(`-D([A-Za-z_][A-Za-z0-9_]*)(=[^@\-]*)?`), + re(`-F([^@\-].*)`), + re(`-I([^@\-].*)`), + re(`-O`), +@@ -50,7 +50,7 @@ var validCompilerFlags = []*lazyregexp.Regexp{ + re(`-W`), + re(`-W([^@,]+)`), // -Wall but not -Wa,-foo. + re(`-Wa,-mbig-obj`), +- re(`-Wp,-D([A-Za-z_].*)`), ++ re(`-Wp,-D([A-Za-z_][A-Za-z0-9_]*)(=[^@,\-]*)?`), + re(`-ansi`), + re(`-f(no-)?asynchronous-unwind-tables`), + re(`-f(no-)?blocks`), +diff --git a/src/cmd/go/internal/work/security_test.go b/src/cmd/go/internal/work/security_test.go +index fd8caea..eac029d 100644 +--- a/src/cmd/go/internal/work/security_test.go ++++ b/src/cmd/go/internal/work/security_test.go +@@ -21,6 +21,7 @@ var goodCompilerFlags = [][]string{ + {"-Osmall"}, + {"-W"}, + {"-Wall"}, ++ {"-Wp,-Dfoo1"}, + {"-fobjc-arc"}, + {"-fno-objc-arc"}, + {"-fomit-frame-pointer"}, +@@ -70,6 +71,7 @@ var badCompilerFlags = [][]string{ + {"-I-dir"}, + {"-O@1"}, + {"-Wa,-foo"}, ++ {"-Wp,-DX,-D@X"}, + {"-W@foo"}, + {"-g@gdb"}, + {"-g-gdb"}, +-- +2.19.1 + diff --git a/0022-fix-CVE-2020-29509-CVE-2020-29511.patch b/0022-fix-CVE-2020-29509-CVE-2020-29511.patch new file mode 100644 index 0000000000000000000000000000000000000000..a64000bf5875cc04b9fc686ad42c2230ce149d92 --- /dev/null +++ b/0022-fix-CVE-2020-29509-CVE-2020-29511.patch @@ -0,0 +1,118 @@ +From 96ffe2941c07e4a6b92357b6eb8739304f839bef Mon Sep 17 00:00:00 2001 +From: jingrui +Date: Wed, 23 Dec 2020 15:58:14 +0800 +Subject: [PATCH 1/2] encoding/xml: handle leading, trailing, or double colons + in names + +Before this change, <:name> would parse as , which could cause +issues in applications that rely on the parse-encode cycle to +round-trip. Similarly, would parse as expected but then +have the attribute dropped when serializing because its name was empty. +Finally, would parse and get serialized incorrectly. All these +values are invalid XML, but to minimize the impact of this change, we +parse them whole into Name.Local. + +This issue was reported by Juho Nurminen of Mattermost as it leads to +round-trip mismatches. See #43168. It's not being fixed in a security +release because round-trip stability is not a currently supported +security property of encoding/xml, and we don't believe these fixes +would be sufficient to reliably guarantee it in the future. + +Fixes CVE-2020-29509 +Fixes CVE-2020-29511 +Updates #43168 +Conflict: NA +Reference: https://go-review.googlesource.com/c/go/+/277892 +Change-Id: I68321c4d867305046f664347192948a889af3c7f + +Signed-off-by: jingrui +--- + src/encoding/xml/xml.go | 5 ++-- + src/encoding/xml/xml_test.go | 58 ++++++++++++++++++++++++++++++++++++ + 2 files changed, 61 insertions(+), 2 deletions(-) + +diff --git a/src/encoding/xml/xml.go b/src/encoding/xml/xml.go +index ca059440a1..073ceee1b2 100644 +--- a/src/encoding/xml/xml.go ++++ b/src/encoding/xml/xml.go +@@ -1152,8 +1152,9 @@ func (d *Decoder) nsname() (name Name, ok bool) { + if !ok { + return + } +- i := strings.Index(s, ":") +- if i < 0 { ++ if strings.Count(s, ":") > 1 { ++ name.Local = s ++ } else if i := strings.Index(s, ":"); i < 1 || i > len(s)-2 { + name.Local = s + } else { + name.Space = s[0:i] +diff --git a/src/encoding/xml/xml_test.go b/src/encoding/xml/xml_test.go +index ee4ffa2420..eef4dc3001 100644 +--- a/src/encoding/xml/xml_test.go ++++ b/src/encoding/xml/xml_test.go +@@ -898,3 +898,61 @@ func TestTokenUnmarshaler(t *testing.T) { + d := NewTokenDecoder(tokReader{}) + d.Decode(&Failure{}) + } ++ ++func testRoundTrip(t *testing.T, input string) { ++ t.Logf("input: %q", input) ++ d := NewDecoder(strings.NewReader(input)) ++ var tokens []Token ++ var buf bytes.Buffer ++ e := NewEncoder(&buf) ++ for { ++ tok, err := d.Token() ++ if err == io.EOF { ++ break ++ } ++ if err != nil { ++ t.Fatalf("invalid input: %v", err) ++ } ++ if err := e.EncodeToken(tok); err != nil { ++ t.Fatalf("failed to re-encode input: %v", err) ++ } ++ tokens = append(tokens, CopyToken(tok)) ++ } ++ if err := e.Flush(); err != nil { ++ t.Fatal(err) ++ } ++ t.Logf("output: %q", buf.String()) ++ ++ d = NewDecoder(&buf) ++ for { ++ tok, err := d.Token() ++ if err == io.EOF { ++ break ++ } ++ if err != nil { ++ t.Fatalf("failed to decode output: %v", err) ++ } ++ if len(tokens) == 0 { ++ t.Fatalf("unexpected token: %#v", tok) ++ } ++ a, b := tokens[0], tok ++ if !reflect.DeepEqual(a, b) { ++ t.Fatalf("token mismatch: %#v vs %#v", a, b) ++ } ++ tokens = tokens[1:] ++ } ++ if len(tokens) > 0 { ++ t.Fatalf("lost tokens: %#v", tokens) ++ } ++} ++ ++func TestRoundTrip(t *testing.T) { ++ tests := map[string]string{ ++ "leading colon": `<::Test ::foo="bar"><:::Hello>`, ++ "trailing colon": ``, ++ "double colon": ``, ++ } ++ for name, input := range tests { ++ t.Run(name, func(t *testing.T) { testRoundTrip(t, input) }) ++ } ++} +-- +2.17.1 + diff --git a/0023-fix-CVE-2020-29510.patch b/0023-fix-CVE-2020-29510.patch new file mode 100644 index 0000000000000000000000000000000000000000..0f8298a3b00946233a7b85b13b1d8dc6a1ce4e28 --- /dev/null +++ b/0023-fix-CVE-2020-29510.patch @@ -0,0 +1,62 @@ +From e2710c3983b3249ba30f2d21802c984aef5fb163 Mon Sep 17 00:00:00 2001 +From: jingrui +Date: Wed, 23 Dec 2020 16:03:15 +0800 +Subject: [PATCH 2/2] encoding/xml: replace comments inside directives with a + space + +A Directive (like ) can't have other nodes nested inside +it (in our data structure representation), so there is no way to +preserve comments. The previous behavior was to just elide them, which +however might change the semantic meaning of the surrounding markup. +Instead, replace them with a space which hopefully has the same semantic +effect of the comment. + +Directives are not actually a node type in the XML spec, which instead +specifies each of them separately ( +--- + src/encoding/xml/xml.go | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/encoding/xml/xml.go b/src/encoding/xml/xml.go +index 073ceee1b2..3746018613 100644 +--- a/src/encoding/xml/xml.go ++++ b/src/encoding/xml/xml.go +@@ -764,6 +764,12 @@ func (d *Decoder) rawToken() (Token, error) { + } + b0, b1 = b1, b + } ++ ++ // Replace the comment with a space in the returned Directive ++ // body, so that markup parts that were separated by the comment ++ // (like a "<" and a "!") don't get joined when re-encoding the ++ // Directive, taking new semantic meaning. ++ d.buf.WriteByte(' ') + } + } + return Directive(d.buf.Bytes()), nil +-- +2.17.1 + diff --git a/0024-crypto-elliptic-fix-P-224-field-reduction.patch b/0024-crypto-elliptic-fix-P-224-field-reduction.patch new file mode 100644 index 0000000000000000000000000000000000000000..ac295d663dcbc922d53f2b0e8831ec7b96957313 --- /dev/null +++ b/0024-crypto-elliptic-fix-P-224-field-reduction.patch @@ -0,0 +1,457 @@ +From dcfa9752c3b187ac5b6c232b36f208dbd23918e9 Mon Sep 17 00:00:00 2001 +From: Filippo Valsorda +Date: Fri, 8 Jan 2021 03:56:58 +0100 +Subject: [PATCH] crypto/elliptic: fix P-224 field reduction + +This patch fixes two independent bugs in p224Contract, the function that +performs the final complete reduction in the P-224 field. Incorrect +outputs due to these bugs were observable from a high-level +P224().ScalarMult() call. + +The first bug was in the calculation of out3GT. That mask was supposed +to be all ones if the third limb of the value is greater than the third +limb of P (out[3] > 0xffff000). Instead, it was also set if they are +equal. That meant that if the third limb was equal, the value was always +considered greater than or equal to P, even when the three bottom limbs +were all zero. There is exactly one affected value, P - 1, which would +trigger the subtraction by P even if it's lower than P already. + +The second bug was more easily hit, and is the one that caused the known +high-level incorrect output: after the conditional subtraction by P, a +potential underflow of the lowest limb was not handled. Any values that +trigger the subtraction by P (values between P and 2^224-1, and P - 1 +due to the bug above) but have a zero lowest limb would produce invalid +outputs. Those conditions apply to the intermediate representation +before the subtraction, so they are hard to trace to precise inputs. + +This patch also adds a test suite for the P-224 field arithmetic, +including a custom fuzzer that automatically explores potential edge +cases by combining limb values that have various meanings in the code. +contractMatchesBigInt in TestP224Contract finds the second bug in less +than a second without being tailored to it, and could eventually find +the first one too by combining 0, (1 << 28) - 1, and the difference of +(1 << 28) and (1 << 12). + +The incorrect P224().ScalarMult() output was found by the +elliptic-curve-differential-fuzzer project running on OSS-Fuzz and +reported by Philippe Antoine (Catena cyber). + +Fixes CVE-2021-3114 + +Change-Id: I50176602d544de3da854270d66a293bcaca57ad7 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/947792 +Reviewed-by: Katie Hockman +(cherry picked from commit 5fa534e9c7eaeaf875e53b98eac9342b0855b283) +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/955175 +--- + src/crypto/elliptic/p224.go | 41 +++-- + src/crypto/elliptic/p224_test.go | 277 ++++++++++++++++++++++++++++++- + 2 files changed, 298 insertions(+), 20 deletions(-) + +diff --git a/src/crypto/elliptic/p224.go b/src/crypto/elliptic/p224.go +index 2ea63f3..8c76021 100644 +--- a/src/crypto/elliptic/p224.go ++++ b/src/crypto/elliptic/p224.go +@@ -386,10 +386,11 @@ func p224Invert(out, in *p224FieldElement) { + // p224Contract converts a FieldElement to its unique, minimal form. + // + // On entry, in[i] < 2**29 +-// On exit, in[i] < 2**28 ++// On exit, out[i] < 2**28 and out < p + func p224Contract(out, in *p224FieldElement) { + copy(out[:], in[:]) + ++ // First, carry the bits above 28 to the higher limb. + for i := 0; i < 7; i++ { + out[i+1] += out[i] >> 28 + out[i] &= bottom28Bits +@@ -397,10 +398,13 @@ func p224Contract(out, in *p224FieldElement) { + top := out[7] >> 28 + out[7] &= bottom28Bits + ++ // Use the reduction identity to carry the overflow. ++ // ++ // a + top * 2²²⁴ = a + top * 2⁹⁶ - top + out[0] -= top + out[3] += top << 12 + +- // We may just have made out[i] negative. So we carry down. If we made ++ // We may just have made out[0] negative. So we carry down. If we made + // out[0] negative then we know that out[3] is sufficiently positive + // because we just added to it. + for i := 0; i < 3; i++ { +@@ -425,13 +429,12 @@ func p224Contract(out, in *p224FieldElement) { + // There are two cases to consider for out[3]: + // 1) The first time that we eliminated top, we didn't push out[3] over + // 2**28. In this case, the partial carry chain didn't change any values +- // and top is zero. ++ // and top is now zero. + // 2) We did push out[3] over 2**28 the first time that we eliminated top. +- // The first value of top was in [0..16), therefore, prior to eliminating +- // the first top, 0xfff1000 <= out[3] <= 0xfffffff. Therefore, after +- // overflowing and being reduced by the second carry chain, out[3] <= +- // 0xf000. Thus it cannot have overflowed when we eliminated top for the +- // second time. ++ // The first value of top was in [0..2], therefore, after overflowing ++ // and being reduced by the second carry chain, out[3] <= 2<<12 - 1. ++ // In both cases, out[3] cannot have overflowed when we eliminated top for ++ // the second time. + + // Again, we may just have made out[0] negative, so do the same carry down. + // As before, if we made out[0] negative then we know that out[3] is +@@ -470,12 +473,11 @@ func p224Contract(out, in *p224FieldElement) { + bottom3NonZero |= bottom3NonZero >> 1 + bottom3NonZero = uint32(int32(bottom3NonZero<<31) >> 31) + +- // Everything depends on the value of out[3]. +- // If it's > 0xffff000 and top4AllOnes != 0 then the whole value is >= p +- // If it's = 0xffff000 and top4AllOnes != 0 and bottom3NonZero != 0, +- // then the whole value is >= p ++ // Assuming top4AllOnes != 0, everything depends on the value of out[3]. ++ // If it's > 0xffff000 then the whole value is > p ++ // If it's = 0xffff000 and bottom3NonZero != 0, then the whole value is >= p + // If it's < 0xffff000, then the whole value is < p +- n := out[3] - 0xffff000 ++ n := 0xffff000 - out[3] + out3Equal := n + out3Equal |= out3Equal >> 16 + out3Equal |= out3Equal >> 8 +@@ -484,8 +486,8 @@ func p224Contract(out, in *p224FieldElement) { + out3Equal |= out3Equal >> 1 + out3Equal = ^uint32(int32(out3Equal<<31) >> 31) + +- // If out[3] > 0xffff000 then n's MSB will be zero. +- out3GT := ^uint32(int32(n) >> 31) ++ // If out[3] > 0xffff000 then n's MSB will be one. ++ out3GT := uint32(int32(n) >> 31) + + mask := top4AllOnes & ((out3Equal & bottom3NonZero) | out3GT) + out[0] -= 1 & mask +@@ -494,6 +496,15 @@ func p224Contract(out, in *p224FieldElement) { + out[5] -= 0xfffffff & mask + out[6] -= 0xfffffff & mask + out[7] -= 0xfffffff & mask ++ ++ // Do one final carry down, in case we made out[0] negative. One of ++ // out[0..3] needs to be positive and able to absorb the -1 or the value ++ // would have been < p, and the subtraction wouldn't have happened. ++ for i := 0; i < 3; i++ { ++ mask := uint32(int32(out[i]) >> 31) ++ out[i] += (1 << 28) & mask ++ out[i+1] -= 1 & mask ++ } + } + + // Group element functions. +diff --git a/src/crypto/elliptic/p224_test.go b/src/crypto/elliptic/p224_test.go +index 8b4fa04..eeb24d9 100644 +--- a/src/crypto/elliptic/p224_test.go ++++ b/src/crypto/elliptic/p224_test.go +@@ -6,7 +6,11 @@ package elliptic + + import ( + "math/big" ++ "math/bits" ++ "math/rand" ++ "reflect" + "testing" ++ "testing/quick" + ) + + var toFromBigTests = []string{ +@@ -21,16 +25,16 @@ func p224AlternativeToBig(in *p224FieldElement) *big.Int { + ret := new(big.Int) + tmp := new(big.Int) + +- for i := uint(0); i < 8; i++ { ++ for i := len(in) - 1; i >= 0; i-- { ++ ret.Lsh(ret, 28) + tmp.SetInt64(int64(in[i])) +- tmp.Lsh(tmp, 28*i) + ret.Add(ret, tmp) + } +- ret.Mod(ret, p224.P) ++ ret.Mod(ret, P224().Params().P) + return ret + } + +-func TestToFromBig(t *testing.T) { ++func TestP224ToFromBig(t *testing.T) { + for i, test := range toFromBigTests { + n, _ := new(big.Int).SetString(test, 16) + var x p224FieldElement +@@ -41,7 +45,270 @@ func TestToFromBig(t *testing.T) { + } + q := p224AlternativeToBig(&x) + if n.Cmp(q) != 0 { +- t.Errorf("#%d: %x != %x (alternative)", i, n, m) ++ t.Errorf("#%d: %x != %x (alternative)", i, n, q) + } + } + } ++ ++// quickCheckConfig32 will make each quickcheck test run (32 * -quickchecks) ++// times. The default value of -quickchecks is 100. ++var quickCheckConfig32 = &quick.Config{MaxCountScale: 32} ++ ++// weirdLimbs can be combined to generate a range of edge-case field elements. ++var weirdLimbs = [...]uint32{ ++ 0, 1, (1 << 29) - 1, ++ (1 << 12), (1 << 12) - 1, ++ (1 << 28), (1 << 28) - 1, ++} ++ ++func generateLimb(rand *rand.Rand) uint32 { ++ const bottom29Bits = 0x1fffffff ++ n := rand.Intn(len(weirdLimbs) + 3) ++ switch n { ++ case len(weirdLimbs): ++ // Random value. ++ return uint32(rand.Int31n(1 << 29)) ++ case len(weirdLimbs) + 1: ++ // Sum of two values. ++ k := generateLimb(rand) + generateLimb(rand) ++ return k & bottom29Bits ++ case len(weirdLimbs) + 2: ++ // Difference of two values. ++ k := generateLimb(rand) - generateLimb(rand) ++ return k & bottom29Bits ++ default: ++ return weirdLimbs[n] ++ } ++} ++ ++func (p224FieldElement) Generate(rand *rand.Rand, size int) reflect.Value { ++ return reflect.ValueOf(p224FieldElement{ ++ weirdLimbs[rand.Intn(len(weirdLimbs))], ++ weirdLimbs[rand.Intn(len(weirdLimbs))], ++ weirdLimbs[rand.Intn(len(weirdLimbs))], ++ weirdLimbs[rand.Intn(len(weirdLimbs))], ++ weirdLimbs[rand.Intn(len(weirdLimbs))], ++ weirdLimbs[rand.Intn(len(weirdLimbs))], ++ weirdLimbs[rand.Intn(len(weirdLimbs))], ++ weirdLimbs[rand.Intn(len(weirdLimbs))], ++ }) ++} ++ ++func isInBounds(x *p224FieldElement) bool { ++ return bits.Len32(x[0]) <= 29 && ++ bits.Len32(x[1]) <= 29 && ++ bits.Len32(x[2]) <= 29 && ++ bits.Len32(x[3]) <= 29 && ++ bits.Len32(x[4]) <= 29 && ++ bits.Len32(x[5]) <= 29 && ++ bits.Len32(x[6]) <= 29 && ++ bits.Len32(x[7]) <= 29 ++} ++ ++func TestP224Mul(t *testing.T) { ++ mulMatchesBigInt := func(a, b, out p224FieldElement) bool { ++ var tmp p224LargeFieldElement ++ p224Mul(&out, &a, &b, &tmp) ++ ++ exp := new(big.Int).Mul(p224AlternativeToBig(&a), p224AlternativeToBig(&b)) ++ exp.Mod(exp, P224().Params().P) ++ got := p224AlternativeToBig(&out) ++ if exp.Cmp(got) != 0 || !isInBounds(&out) { ++ t.Logf("a = %x", a) ++ t.Logf("b = %x", b) ++ t.Logf("p224Mul(a, b) = %x = %v", out, got) ++ t.Logf("a * b = %v", exp) ++ return false ++ } ++ ++ return true ++ } ++ ++ a := p224FieldElement{0xfffffff, 0xfffffff, 0xf00ffff, 0x20f, 0x0, 0x0, 0x0, 0x0} ++ b := p224FieldElement{1, 0, 0, 0, 0, 0, 0, 0} ++ if !mulMatchesBigInt(a, b, p224FieldElement{}) { ++ t.Fail() ++ } ++ ++ if err := quick.Check(mulMatchesBigInt, quickCheckConfig32); err != nil { ++ t.Error(err) ++ } ++} ++ ++func TestP224Square(t *testing.T) { ++ squareMatchesBigInt := func(a, out p224FieldElement) bool { ++ var tmp p224LargeFieldElement ++ p224Square(&out, &a, &tmp) ++ ++ exp := p224AlternativeToBig(&a) ++ exp.Mul(exp, exp) ++ exp.Mod(exp, P224().Params().P) ++ got := p224AlternativeToBig(&out) ++ if exp.Cmp(got) != 0 || !isInBounds(&out) { ++ t.Logf("a = %x", a) ++ t.Logf("p224Square(a, b) = %x = %v", out, got) ++ t.Logf("a * a = %v", exp) ++ return false ++ } ++ ++ return true ++ } ++ ++ if err := quick.Check(squareMatchesBigInt, quickCheckConfig32); err != nil { ++ t.Error(err) ++ } ++} ++ ++func TestP224Add(t *testing.T) { ++ addMatchesBigInt := func(a, b, out p224FieldElement) bool { ++ p224Add(&out, &a, &b) ++ ++ exp := new(big.Int).Add(p224AlternativeToBig(&a), p224AlternativeToBig(&b)) ++ exp.Mod(exp, P224().Params().P) ++ got := p224AlternativeToBig(&out) ++ if exp.Cmp(got) != 0 { ++ t.Logf("a = %x", a) ++ t.Logf("b = %x", b) ++ t.Logf("p224Add(a, b) = %x = %v", out, got) ++ t.Logf("a + b = %v", exp) ++ return false ++ } ++ ++ return true ++ } ++ ++ if err := quick.Check(addMatchesBigInt, quickCheckConfig32); err != nil { ++ t.Error(err) ++ } ++} ++ ++func TestP224Reduce(t *testing.T) { ++ reduceMatchesBigInt := func(a p224FieldElement) bool { ++ out := a ++ // TODO: generate higher values for functions like p224Reduce that are ++ // expected to work with higher input bounds. ++ p224Reduce(&out) ++ ++ exp := p224AlternativeToBig(&a) ++ got := p224AlternativeToBig(&out) ++ if exp.Cmp(got) != 0 || !isInBounds(&out) { ++ t.Logf("a = %x = %v", a, exp) ++ t.Logf("p224Reduce(a) = %x = %v", out, got) ++ return false ++ } ++ ++ return true ++ } ++ ++ if err := quick.Check(reduceMatchesBigInt, quickCheckConfig32); err != nil { ++ t.Error(err) ++ } ++} ++ ++func TestP224Contract(t *testing.T) { ++ contractMatchesBigInt := func(a, out p224FieldElement) bool { ++ p224Contract(&out, &a) ++ ++ exp := p224AlternativeToBig(&a) ++ got := p224AlternativeToBig(&out) ++ if exp.Cmp(got) != 0 { ++ t.Logf("a = %x = %v", a, exp) ++ t.Logf("p224Contract(a) = %x = %v", out, got) ++ return false ++ } ++ ++ // Check that out < P. ++ for i := range p224P { ++ k := 8 - i - 1 ++ if out[k] > p224P[k] { ++ t.Logf("p224Contract(a) = %x", out) ++ return false ++ } ++ if out[k] < p224P[k] { ++ return true ++ } ++ } ++ t.Logf("p224Contract(a) = %x", out) ++ return false ++ } ++ ++ if !contractMatchesBigInt(p224P, p224FieldElement{}) { ++ t.Error("p224Contract(p) is broken") ++ } ++ pMinus1 := p224FieldElement{0, 0, 0, 0xffff000, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff} ++ if !contractMatchesBigInt(pMinus1, p224FieldElement{}) { ++ t.Error("p224Contract(p - 1) is broken") ++ } ++ // Check that we can handle input above p, but lowest limb zero. ++ a := p224FieldElement{0, 1, 0, 0xffff000, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff} ++ if !contractMatchesBigInt(a, p224FieldElement{}) { ++ t.Error("p224Contract(p + 2²⁸) is broken") ++ } ++ // Check that we can handle input above p, but lowest three limbs zero. ++ b := p224FieldElement{0, 0, 0, 0xffff001, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff} ++ if !contractMatchesBigInt(b, p224FieldElement{}) { ++ t.Error("p224Contract(p + 2⁸⁴) is broken") ++ } ++ ++ if err := quick.Check(contractMatchesBigInt, quickCheckConfig32); err != nil { ++ t.Error(err) ++ } ++} ++ ++func TestP224IsZero(t *testing.T) { ++ if got := p224IsZero(&p224FieldElement{}); got != 1 { ++ t.Errorf("p224IsZero(0) = %d, expected 1", got) ++ } ++ if got := p224IsZero((*p224FieldElement)(&p224P)); got != 1 { ++ t.Errorf("p224IsZero(p) = %d, expected 1", got) ++ } ++ if got := p224IsZero(&p224FieldElement{1}); got != 0 { ++ t.Errorf("p224IsZero(1) = %d, expected 0", got) ++ } ++ ++ isZeroMatchesBigInt := func(a p224FieldElement) bool { ++ isZero := p224IsZero(&a) ++ ++ big := p224AlternativeToBig(&a) ++ if big.Sign() == 0 && isZero != 1 { ++ return false ++ } ++ if big.Sign() != 0 && isZero != 0 { ++ return false ++ } ++ return true ++ } ++ ++ if err := quick.Check(isZeroMatchesBigInt, quickCheckConfig32); err != nil { ++ t.Error(err) ++ } ++} ++ ++func TestP224Invert(t *testing.T) { ++ var out p224FieldElement ++ ++ p224Invert(&out, &p224FieldElement{}) ++ if got := p224IsZero(&out); got != 1 { ++ t.Errorf("p224Invert(0) = %x, expected 0", out) ++ } ++ ++ p224Invert(&out, (*p224FieldElement)(&p224P)) ++ if got := p224IsZero(&out); got != 1 { ++ t.Errorf("p224Invert(p) = %x, expected 0", out) ++ } ++ ++ p224Invert(&out, &p224FieldElement{1}) ++ p224Contract(&out, &out) ++ if out != (p224FieldElement{1}) { ++ t.Errorf("p224Invert(1) = %x, expected 1", out) ++ } ++ ++ var tmp p224LargeFieldElement ++ a := p224FieldElement{1, 2, 3, 4, 5, 6, 7, 8} ++ p224Invert(&out, &a) ++ p224Mul(&out, &out, &a, &tmp) ++ p224Contract(&out, &out) ++ if out != (p224FieldElement{1}) { ++ t.Errorf("p224Invert(a) * a = %x, expected 1", out) ++ } ++} +-- +2.17.1 + diff --git a/0025-fix-CVE-2021-27918.patch b/0025-fix-CVE-2021-27918.patch new file mode 100644 index 0000000000000000000000000000000000000000..ca4e361d6811be7023b1a208a5d7d2aa06e188d0 --- /dev/null +++ b/0025-fix-CVE-2021-27918.patch @@ -0,0 +1,215 @@ +From 01444c00b012d8500c574aba65ad0525422fde8c Mon Sep 17 00:00:00 2001 +From: chenjiankun +Date: Fri, 9 Apr 2021 15:24:20 +0800 +Subject: [PATCH] fix CVE-2021-27918 + +encoding/xml in Go before 1.15.9 and 1.16.x before 1.16.1 +has an infinite loop if a custom TokenReader (for xml.NewTokenDecoder) +returns EOF in the middle of an element. This can occur in the Decode, +DecodeElement, or Skip method. + +Conflict:NA +Reference:https://github.com/golang/go/commit/91062c2e4cbbf78a108919f6ed3ded1173937cf3 +--- + src/encoding/xml/xml.go | 13 ++-- + src/encoding/xml/xml_test.go | 122 ++++++++++++++++++++++++++++++++--- + 2 files changed, 122 insertions(+), 13 deletions(-) + +diff --git a/src/encoding/xml/xml.go b/src/encoding/xml/xml.go +index 3746018..42c103e 100644 +--- a/src/encoding/xml/xml.go ++++ b/src/encoding/xml/xml.go +@@ -285,11 +285,16 @@ func (d *Decoder) Token() (Token, error) { + if d.nextToken != nil { + t = d.nextToken + d.nextToken = nil +- } else if t, err = d.rawToken(); err != nil { +- if err == io.EOF && d.stk != nil && d.stk.kind != stkEOF { +- err = d.syntaxError("unexpected EOF") ++ } else { ++ if t, err = d.rawToken(); t == nil && err != nil { ++ if err == io.EOF && d.stk != nil && d.stk.kind != stkEOF { ++ err = d.syntaxError("unexpected EOF") ++ } ++ return nil, err + } +- return t, err ++ // We still have a token to process, so clear any ++ // errors (e.g. EOF) and proceed. ++ err = nil + } + + if !d.Strict { +diff --git a/src/encoding/xml/xml_test.go b/src/encoding/xml/xml_test.go +index eef4dc3..19152db 100644 +--- a/src/encoding/xml/xml_test.go ++++ b/src/encoding/xml/xml_test.go +@@ -14,6 +14,111 @@ import ( + "unicode/utf8" + ) + ++type toks struct { ++ earlyEOF bool ++ t []Token ++} ++ ++func (t *toks) Token() (Token, error) { ++ if len(t.t) == 0 { ++ return nil, io.EOF ++ } ++ var tok Token ++ tok, t.t = t.t[0], t.t[1:] ++ if t.earlyEOF && len(t.t) == 0 { ++ return tok, io.EOF ++ } ++ return tok, nil ++} ++ ++func TestDecodeEOF(t *testing.T) { ++ start := StartElement{Name: Name{Local: "test"}} ++ tests := []struct { ++ name string ++ tokens []Token ++ ok bool ++ }{ ++ { ++ name: "OK", ++ tokens: []Token{ ++ start, ++ start.End(), ++ }, ++ ok: true, ++ }, ++ { ++ name: "Malformed", ++ tokens: []Token{ ++ start, ++ StartElement{Name: Name{Local: "bad"}}, ++ start.End(), ++ }, ++ ok: false, ++ }, ++ } ++ for _, tc := range tests { ++ for _, eof := range []bool{true, false} { ++ name := fmt.Sprintf("%s/earlyEOF=%v", tc.name, eof) ++ t.Run(name, func(t *testing.T) { ++ d := NewTokenDecoder(&toks{ ++ earlyEOF: eof, ++ t: tc.tokens, ++ }) ++ err := d.Decode(&struct { ++ XMLName Name `xml:"test"` ++ }{}) ++ if tc.ok && err != nil { ++ t.Fatalf("d.Decode: expected nil error, got %v", err) ++ } ++ if _, ok := err.(*SyntaxError); !tc.ok && !ok { ++ t.Errorf("d.Decode: expected syntax error, got %v", err) ++ } ++ }) ++ } ++ } ++} ++ ++type toksNil struct { ++ returnEOF bool ++ t []Token ++} ++ ++func (t *toksNil) Token() (Token, error) { ++ if len(t.t) == 0 { ++ if !t.returnEOF { ++ // Return nil, nil before returning an EOF. It's legal, but ++ // discouraged. ++ t.returnEOF = true ++ return nil, nil ++ } ++ return nil, io.EOF ++ } ++ var tok Token ++ tok, t.t = t.t[0], t.t[1:] ++ return tok, nil ++} ++ ++func TestDecodeNilToken(t *testing.T) { ++ for _, strict := range []bool{true, false} { ++ name := fmt.Sprintf("Strict=%v", strict) ++ t.Run(name, func(t *testing.T) { ++ start := StartElement{Name: Name{Local: "test"}} ++ bad := StartElement{Name: Name{Local: "bad"}} ++ d := NewTokenDecoder(&toksNil{ ++ // Malformed ++ t: []Token{start, bad, start.End()}, ++ }) ++ d.Strict = strict ++ err := d.Decode(&struct { ++ XMLName Name `xml:"test"` ++ }{}) ++ if _, ok := err.(*SyntaxError); !ok { ++ t.Errorf("d.Decode: expected syntax error, got %v", err) ++ } ++ }) ++ } ++} ++ + const testInput = ` + + ]`), ++ Directive(`DOCTYPE [ ]`), + CharData("\n"), +- Directive(`DOCTYPE []`), ++ Directive(`DOCTYPE [ ]`), + CharData("\n"), +- Directive(`DOCTYPE []`), ++ Directive(`DOCTYPE [ ]`), + CharData("\n"), + } + +@@ -835,7 +940,7 @@ func (m mapper) Token() (Token, error) { + } + + func TestNewTokenDecoderIdempotent(t *testing.T) { +- d := NewDecoder(strings.NewReader(`
`)) ++ d := NewDecoder(strings.NewReader(`
`)) + d2 := NewTokenDecoder(d) + if d != d2 { + t.Error("NewTokenDecoder did not detect underlying Decoder") +@@ -900,7 +1005,6 @@ func TestTokenUnmarshaler(t *testing.T) { + } + + func testRoundTrip(t *testing.T, input string) { +- t.Logf("input: %q", input) + d := NewDecoder(strings.NewReader(input)) + var tokens []Token + var buf bytes.Buffer +@@ -921,7 +1025,6 @@ func testRoundTrip(t *testing.T, input string) { + if err := e.Flush(); err != nil { + t.Fatal(err) + } +- t.Logf("output: %q", buf.String()) + + d = NewDecoder(&buf) + for { +@@ -948,9 +1051,10 @@ func testRoundTrip(t *testing.T, input string) { + + func TestRoundTrip(t *testing.T) { + tests := map[string]string{ +- "leading colon": `<::Test ::foo="bar"><:::Hello>`, +- "trailing colon": ``, +- "double colon": ``, ++ "leading colon": `<::Test ::foo="bar"><:::Hello>`, ++ "trailing colon": ``, ++ "double colon": ``, ++ "comments in directives": `--x --> > --x ]>`, + } + for name, input := range tests { + t.Run(name, func(t *testing.T) { testRoundTrip(t, input) }) +-- +2.27.0 + diff --git a/backport-0013-release-branch.go1.13-security-src-go.mod-import-x-c.patch b/backport-0013-release-branch.go1.13-security-src-go.mod-import-x-c.patch deleted file mode 100644 index 5a6166b548173a555a66912ceedfca041d44bedc..0000000000000000000000000000000000000000 --- a/backport-0013-release-branch.go1.13-security-src-go.mod-import-x-c.patch +++ /dev/null @@ -1,124 +0,0 @@ -From f938e06d0623d0e1de202575d16f1e126741f6e0 Mon Sep 17 00:00:00 2001 -From: Filippo Valsorda -Date: Fri, 24 Jan 2020 18:04:20 -0500 -Subject: [PATCH] [release-branch.go1.13-security] src/go.mod: import - x/crypto/cryptobyte security fix for 32-bit archs - - cryptobyte: fix panic due to malformed ASN.1 inputs on 32-bit archs - - When int is 32 bits wide (on 32-bit architectures like 386 and arm), an - overflow could occur, causing a panic, due to malformed ASN.1 being - passed to any of the ASN1 methods of String. - - Tested on linux/386 and darwin/amd64. - - This fixes CVE-2020-7919 and was found thanks to the Project Wycheproof - test vectors. - - Change-Id: I8c9696a8bfad1b40ec877cd740dba3467d66ab54 - Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/645211 - Reviewed-by: Katie Hockman - Reviewed-by: Adam Langley - -x/crypto/cryptobyte is used in crypto/x509 for parsing certificates. -Malformed certificates might cause a panic during parsing on 32-bit -architectures (like arm and 386). - -Change-Id: I840feb54eba880dbb96780ef7adcade073c4c4e3 -Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/647741 -Reviewed-by: Katie Hockman ---- - src/go.mod | 2 +- - src/go.sum | 4 ++-- - src/vendor/golang.org/x/crypto/cryptobyte/asn1.go | 5 +++-- - src/vendor/golang.org/x/crypto/cryptobyte/string.go | 7 +------ - src/vendor/modules.txt | 2 +- - 5 files changed, 8 insertions(+), 12 deletions(-) - -diff --git a/src/go.mod b/src/go.mod -index 90af2a7ea0..9c9026f0d8 100644 ---- a/src/go.mod -+++ b/src/go.mod -@@ -3,7 +3,7 @@ module std - go 1.12 - - require ( -- golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 -+ golang.org/x/crypto v0.0.0-20200124225646-8b5121be2f68 - golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 - golang.org/x/sys v0.0.0-20190529130038-5219a1e1c5f8 // indirect - golang.org/x/text v0.3.2 // indirect -diff --git a/src/go.sum b/src/go.sum -index e358118e4c..e408f66328 100644 ---- a/src/go.sum -+++ b/src/go.sum -@@ -1,6 +1,6 @@ - golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= --golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= --golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -+golang.org/x/crypto v0.0.0-20200124225646-8b5121be2f68 h1:WPLCzSEbawp58wezcvLvLnvhiDJAai54ESbc41NdXS0= -+golang.org/x/crypto v0.0.0-20200124225646-8b5121be2f68/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= - golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= - golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= - golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -diff --git a/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go b/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go -index 528b9bff67..f930f7e526 100644 ---- a/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go -+++ b/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go -@@ -470,7 +470,8 @@ func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool { - // It reports whether the read was successful. - func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool { - var bytes String -- if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 { -+ if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 || -+ len(bytes)*8/8 != len(bytes) { - return false - } - -@@ -740,7 +741,7 @@ func (s *String) readASN1(out *String, outTag *asn1.Tag, skipHeader bool) bool { - length = headerLen + len32 - } - -- if uint32(int(length)) != length || !s.ReadBytes((*[]byte)(out), int(length)) { -+ if int(length) < 0 || !s.ReadBytes((*[]byte)(out), int(length)) { - return false - } - if skipHeader && !out.Skip(int(headerLen)) { -diff --git a/src/vendor/golang.org/x/crypto/cryptobyte/string.go b/src/vendor/golang.org/x/crypto/cryptobyte/string.go -index 39bf98aeea..589d297e6b 100644 ---- a/src/vendor/golang.org/x/crypto/cryptobyte/string.go -+++ b/src/vendor/golang.org/x/crypto/cryptobyte/string.go -@@ -24,7 +24,7 @@ type String []byte - // read advances a String by n bytes and returns them. If less than n bytes - // remain, it returns nil. - func (s *String) read(n int) []byte { -- if len(*s) < n { -+ if len(*s) < n || n < 0 { - return nil - } - v := (*s)[:n] -@@ -105,11 +105,6 @@ func (s *String) readLengthPrefixed(lenLen int, outChild *String) bool { - length = length << 8 - length = length | uint32(b) - } -- if int(length) < 0 { -- // This currently cannot overflow because we read uint24 at most, but check -- // anyway in case that changes in the future. -- return false -- } - v := s.read(int(length)) - if v == nil { - return false -diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt -index 453a312661..cff8acd02e 100644 ---- a/src/vendor/modules.txt -+++ b/src/vendor/modules.txt -@@ -1,4 +1,4 @@ --# golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 -+# golang.org/x/crypto v0.0.0-20200124225646-8b5121be2f68 - golang.org/x/crypto/chacha20poly1305 - golang.org/x/crypto/cryptobyte - golang.org/x/crypto/cryptobyte/asn1 --- -2.17.1 - diff --git a/go1.13.3.src.tar.gz b/go1.13.15.src.tar.gz similarity index 69% rename from go1.13.3.src.tar.gz rename to go1.13.15.src.tar.gz index c3aadce8927e5ac1168fbb816e9d5bfec3be4e2a..2b551e218945996fbd6a6ca5354030983e2d87f2 100644 Binary files a/go1.13.3.src.tar.gz and b/go1.13.15.src.tar.gz differ diff --git a/golang.spec b/golang.spec index 2cddb145e3ece4e722ad1837df5810491733e24b..b285328f792777a0444a0675ddc9e82e96ad33ba 100644 --- a/golang.spec +++ b/golang.spec @@ -61,11 +61,11 @@ %endif Name: golang -Version: 1.13.3 -Release: 6 +Version: 1.13.15 +Release: 5 Summary: The Go Programming Language License: BSD and Public Domain -URL: http://golang.org/ +URL: https://golang.org/ Source0: https://dl.google.com/go/go%{version}.src.tar.gz %if !%{golang_bootstrap} @@ -156,12 +156,14 @@ Patch6005: 0005-runtime-fix-crash-during-VDSO-calls-on-arm.patch Patch6006: 0006-runtime-save-fetch-g-register-during-VDSO-on-ARM-and.patch Patch6007: 0007-runtime-don-t-fetch-G-from-signal-stack-when-using-c.patch Patch6008: 0008-runtime-don-t-save-G-during-VDSO-if-we-re-handling-s.patch -Patch6009: 0009-release-branch.go1.13-net-http-don-t-cache-http2.err.patch -Patch6010: 0010-release-branch.go1.13-net-http-fix-Server.ConnContex.patch -Patch6011: 0011-release-branch.go1.13-runtime-fix-textOff-for-multip.patch -Patch6012: 0012-release-branch.go1.13-runtime-ensure-memmove-write-p.patch -Patch6013: backport-0013-release-branch.go1.13-security-src-go.mod-import-x-c.patch -Patch6014: 0013-drop-hard-code-cert.patch +Patch6013: 0013-drop-hard-code-cert.patch +Patch6019: 0019-net-http-cgi-net-http-fcgi-add-Content-Type-detectio.patch +Patch6020: 0020-golang-fix-CVE-2020-28366.patch +Patch6021: 0021-golang-fix-CVE-2020-28367.patch +Patch6022: 0022-fix-CVE-2020-29509-CVE-2020-29511.patch +Patch6023: 0023-fix-CVE-2020-29510.patch +Patch6024: 0024-crypto-elliptic-fix-P-224-field-reduction.patch +Patch6025: 0025-fix-CVE-2021-27918.patch ExclusiveArch: %{golang_arches} @@ -395,6 +397,18 @@ fi %files devel -f go-tests.list -f go-misc.list -f go-src.list %changelog +* Thu Apr 17 2021 chenjiankun - 1.13.15-5 +- fix CVE-2021-27918 + +* Thu Feb 18 2021 jingrui - 1.13.15-3 +- fix CVE-2021-3114 + +* Mon Jan 18 2021 jingrui - 1.13.15-2 +- sync cve fix + +* Tue Aug 18 2020 xiadanni - 1.13.15-1 +- upgrade to 1.13.15 + * Tue May 12 2020 lixiang - 1.13.6 - rename tar name and make it same with upstream diff --git a/golang.yaml b/golang.yaml new file mode 100644 index 0000000000000000000000000000000000000000..73d095a10f43977bbf4052fd1e397d8309ab3c1d --- /dev/null +++ b/golang.yaml @@ -0,0 +1,4 @@ +version_control: github +src_repo: golang/go +tag_prefix: ^go +seperator: .