libgo: update to Go 1.15.5 release
authorIan Lance Taylor <iant@golang.org>
Fri, 20 Nov 2020 23:19:29 +0000 (15:19 -0800)
committerIan Lance Taylor <iant@golang.org>
Sat, 21 Nov 2020 01:09:30 +0000 (17:09 -0800)
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/272146

gcc/go/gofrontend/MERGE
libgo/MERGE
libgo/VERSION
libgo/go/cmd/cgo/out.go
libgo/go/cmd/go/internal/work/exec.go
libgo/go/cmd/go/internal/work/security.go
libgo/go/cmd/go/internal/work/security_test.go
libgo/go/math/big/nat.go
libgo/misc/cgo/errors/badsym_test.go [new file with mode: 0644]

index 9545a59873855363f54e75653ee0b1949114434e..37374d55853133c46016b087824c5ddfc5cae103 100644 (file)
@@ -1,4 +1,4 @@
-b483d0e0a289ba5fcdbd0388cbc75393367ca870
+36a7b789130b415c2fe7f8e3fc62ffbca265e3aa
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index ad239c92fa95cea460ee147130531c8095ff35a1..b753907837de58d06c0e6ebf587cda0cc2c4e2b6 100644 (file)
@@ -1,4 +1,4 @@
-0e953add9656c32a788e06438cd7b533e968b7f8
+c53315d6cf1b4bfea6ff356b4a1524778c683bb9
 
 The first line of this file holds the git revision number of the
 last merge done from the master library sources.
index baff2224d623f7da1f7d5053325e707bc064d2cc..701454707cddcddde010fc9d213ee80374d855b9 100644 (file)
@@ -1 +1 @@
-go1.15.4
+go1.15.5
index 05441398d7abd1202abf817265172b5f64e8c80f..1c143d7aa380bd590263aae466631896fc1bc97f 100644 (file)
@@ -341,6 +341,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()
@@ -356,6 +358,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()
@@ -370,6 +373,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
@@ -387,6 +392,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()
@@ -402,6 +408,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.
index 65f3011adfa4fd0e6a96a7a5259c68b3a5f9c8e0..4f689438d1d428b59156b5b50134f132d5621897 100644 (file)
@@ -2723,6 +2723,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
 }
 
index 3ee68ac1b4144e3ab32b9caa2094ddfd010cc558..0d9628241fd85e2bb0447eda1dae2b8c2a41d20e 100644 (file)
@@ -42,8 +42,8 @@ import (
 var re = lazyregexp.New
 
 var validCompilerFlags = []*lazyregexp.Regexp{
-       re(`-D([A-Za-z_].*)`),
-       re(`-U([A-Za-z_]*)`),
+       re(`-D([A-Za-z_][A-Za-z0-9_]*)(=[^@\-]*)?`),
+       re(`-U([A-Za-z_][A-Za-z0-9_]*)`),
        re(`-F([^@\-].*)`),
        re(`-I([^@\-].*)`),
        re(`-O`),
@@ -51,8 +51,8 @@ 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,-U([A-Za-z_]*)`),
+       re(`-Wp,-D([A-Za-z_][A-Za-z0-9_]*)(=[^@,\-]*)?`),
+       re(`-Wp,-U([A-Za-z_][A-Za-z0-9_]*)`),
        re(`-ansi`),
        re(`-f(no-)?asynchronous-unwind-tables`),
        re(`-f(no-)?blocks`),
index 11e74f29c6a6a7f94c6eec7bb403537a726f762e..aec9789185e8a178a295e0b3f1146b1d94c21551 100644 (file)
@@ -13,6 +13,7 @@ var goodCompilerFlags = [][]string{
        {"-DFOO"},
        {"-Dfoo=bar"},
        {"-Ufoo"},
+       {"-Ufoo1"},
        {"-F/Qt"},
        {"-I/"},
        {"-I/etc/passwd"},
@@ -24,6 +25,8 @@ var goodCompilerFlags = [][]string{
        {"-Wall"},
        {"-Wp,-Dfoo=bar"},
        {"-Wp,-Ufoo"},
+       {"-Wp,-Dfoo1"},
+       {"-Wp,-Ufoo1"},
        {"-fobjc-arc"},
        {"-fno-objc-arc"},
        {"-fomit-frame-pointer"},
@@ -78,6 +81,8 @@ var badCompilerFlags = [][]string{
        {"-O@1"},
        {"-Wa,-foo"},
        {"-W@foo"},
+       {"-Wp,-DX,-D@X"},
+       {"-Wp,-UX,-U@X"},
        {"-g@gdb"},
        {"-g-gdb"},
        {"-march=@dawn"},
index 6a3989bf9d82bf1c9ec3b57aedead466002bfb8e..8c43de69d33ff8e841c000d15808a2e7e2b160da 100644 (file)
@@ -928,7 +928,7 @@ func (z nat) divRecursiveStep(u, v nat, depth int, tmp *nat, temps []*nat) {
 
        // Now u < (v<<B), compute lower bits in the same way.
        // Choose shift = B-1 again.
-       s := B
+       s := B - 1
        qhat := *temps[depth]
        qhat.clear()
        qhat.divRecursiveStep(u[s:].norm(), v[s:], depth+1, tmp, temps)
diff --git a/libgo/misc/cgo/errors/badsym_test.go b/libgo/misc/cgo/errors/badsym_test.go
new file mode 100644 (file)
index 0000000..b2701bf
--- /dev/null
@@ -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))
+}