From d2049ce1ac4a3e7897983c7f3427c1821d064769 Mon Sep 17 00:00:00 2001 From: jingrui Date: Mon, 18 Jan 2021 17:37:07 +0800 Subject: [PATCH] golang: sync cve fix Signed-off-by: jingrui --- ...-http-fcgi-add-Content-Type-detectio.patch | 363 +++++++++++++++ 0020-golang-fix-CVE-2020-28366.patch | 413 ++++++++++++++++++ 0021-golang-fix-CVE-2020-28367.patch | 67 +++ 0022-fix-CVE-2020-29509-CVE-2020-29511.patch | 118 +++++ 0023-fix-CVE-2020-29510.patch | 62 +++ golang.spec | 14 +- 6 files changed, 1034 insertions(+), 3 deletions(-) create mode 100644 0019-net-http-cgi-net-http-fcgi-add-Content-Type-detectio.patch create mode 100644 0020-golang-fix-CVE-2020-28366.patch create mode 100644 0021-golang-fix-CVE-2020-28367.patch create mode 100644 0022-fix-CVE-2020-29509-CVE-2020-29511.patch create mode 100644 0023-fix-CVE-2020-29510.patch 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 0000000..b265af0 --- /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 0000000..dfc8941 --- /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 0000000..a5ca6ed --- /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 0000000..a64000b --- /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 0000000..0f8298a --- /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/golang.spec b/golang.spec index 4014e9d..cb0fefb 100644 --- a/golang.spec +++ b/golang.spec @@ -62,7 +62,7 @@ Name: golang Version: 1.13.15 -Release: 1 +Release: 2 Summary: The Go Programming Language License: BSD and Public Domain URL: https://golang.org/ @@ -156,7 +156,12 @@ 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 -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 ExclusiveArch: %{golang_arches} @@ -390,7 +395,10 @@ fi %files devel -f go-tests.list -f go-misc.list -f go-src.list %changelog -* Tue Aug 18 xiadanni - 1.13.15-1 +* 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 -- Gitee