diff options
Diffstat (limited to 'misc/cgo/errors')
-rw-r--r-- | misc/cgo/errors/argposition_test.go | 133 | ||||
-rw-r--r-- | misc/cgo/errors/badsym_test.go | 219 | ||||
-rw-r--r-- | misc/cgo/errors/errors_test.go | 166 | ||||
-rw-r--r-- | misc/cgo/errors/ptr_test.go | 632 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/err1.go | 22 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/err2.go | 110 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/err4.go | 15 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/err5.go | 11 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/issue11097a.go | 15 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/issue11097b.go | 15 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/issue14669.go | 23 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/issue18452.go | 18 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/issue18889.go | 7 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/issue28069.go | 26 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/issue28721.go | 29 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/issue33061.go | 17 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/issue42580.go | 44 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/issue50710.go | 14 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/long_double_size.go | 16 | ||||
-rw-r--r-- | misc/cgo/errors/testdata/malloc.go | 34 |
20 files changed, 1566 insertions, 0 deletions
diff --git a/misc/cgo/errors/argposition_test.go b/misc/cgo/errors/argposition_test.go new file mode 100644 index 0000000..dd26663 --- /dev/null +++ b/misc/cgo/errors/argposition_test.go @@ -0,0 +1,133 @@ +// Copyright 2021 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. + +// Issue 42580: cmd/cgo: shifting identifier position in ast + +package errorstest + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +type ShortPosition struct { + Line int + Column int + Visited bool +} + +type IdentPositionInfo map[string][]ShortPosition + +type Visitor struct { + identPosInfo IdentPositionInfo + fset *token.FileSet + t *testing.T +} + +func (v *Visitor) Visit(node ast.Node) ast.Visitor { + if ident, ok := node.(*ast.Ident); ok { + if expectedPositions, ok := v.identPosInfo[ident.Name]; ok { + gotMatch := false + var errorMessage strings.Builder + for caseIndex, expectedPos := range expectedPositions { + actualPosition := v.fset.PositionFor(ident.Pos(), true) + errorOccured := false + if expectedPos.Line != actualPosition.Line { + fmt.Fprintf(&errorMessage, "wrong line number for ident %s: expected: %d got: %d\n", ident.Name, expectedPos.Line, actualPosition.Line) + errorOccured = true + } + if expectedPos.Column != actualPosition.Column { + fmt.Fprintf(&errorMessage, "wrong column number for ident %s: expected: %d got: %d\n", ident.Name, expectedPos.Column, actualPosition.Column) + errorOccured = true + } + if errorOccured { + continue + } + gotMatch = true + expectedPositions[caseIndex].Visited = true + } + + if !gotMatch { + v.t.Errorf(errorMessage.String()) + } + } + } + return v +} + +func TestArgumentsPositions(t *testing.T) { + testdata, err := filepath.Abs("testdata") + if err != nil { + t.Fatal(err) + } + + tmpPath := t.TempDir() + + dir := filepath.Join(tmpPath, "src", "testpositions") + if err := os.MkdirAll(dir, 0755); err != nil { + t.Fatal(err) + } + + cmd := exec.Command("go", "tool", "cgo", + "-srcdir", testdata, + "-objdir", dir, + "issue42580.go") + cmd.Stderr = new(bytes.Buffer) + + err = cmd.Run() + if err != nil { + t.Fatalf("%s: %v\n%s", cmd, err, cmd.Stderr) + } + mainProcessed, err := os.ReadFile(filepath.Join(dir, "issue42580.cgo1.go")) + if err != nil { + t.Fatal(err) + } + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "", mainProcessed, parser.AllErrors) + if err != nil { + fmt.Println(err) + return + } + + expectation := IdentPositionInfo{ + "checkedPointer": []ShortPosition{ + ShortPosition{ + Line: 32, + Column: 56, + }, + }, + "singleInnerPointerChecked": []ShortPosition{ + ShortPosition{ + Line: 37, + Column: 91, + }, + }, + "doublePointerChecked": []ShortPosition{ + ShortPosition{ + Line: 42, + Column: 91, + }, + }, + } + for _, decl := range f.Decls { + if fdecl, ok := decl.(*ast.FuncDecl); ok { + ast.Walk(&Visitor{expectation, fset, t}, fdecl.Body) + } + } + for ident, positions := range expectation { + for _, position := range positions { + if !position.Visited { + t.Errorf("Position %d:%d missed for %s ident", position.Line, position.Column, ident) + } + } + } +} diff --git a/misc/cgo/errors/badsym_test.go b/misc/cgo/errors/badsym_test.go new file mode 100644 index 0000000..bc3ba2b --- /dev/null +++ b/misc/cgo/errors/badsym_test.go @@ -0,0 +1,219 @@ +// 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" + "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 := os.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 := os.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 := os.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:]) + } + + // Force reallocation (and avoid aliasing bugs) for tests that append to cc. + cc = cc[:len(cc):len(cc)] + + 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/misc/cgo/errors/errors_test.go b/misc/cgo/errors/errors_test.go new file mode 100644 index 0000000..8168032 --- /dev/null +++ b/misc/cgo/errors/errors_test.go @@ -0,0 +1,166 @@ +// Copyright 2017 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" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "strconv" + "strings" + "testing" +) + +func path(file string) string { + return filepath.Join("testdata", file) +} + +func check(t *testing.T, file string) { + t.Run(file, func(t *testing.T) { + t.Parallel() + + contents, err := os.ReadFile(path(file)) + if err != nil { + t.Fatal(err) + } + var errors []*regexp.Regexp + for i, line := range bytes.Split(contents, []byte("\n")) { + if bytes.HasSuffix(line, []byte("ERROR HERE")) { + re := regexp.MustCompile(regexp.QuoteMeta(fmt.Sprintf("%s:%d:", file, i+1))) + errors = append(errors, re) + continue + } + + if _, frag, ok := bytes.Cut(line, []byte("ERROR HERE: ")); ok { + re, err := regexp.Compile(fmt.Sprintf(":%d:.*%s", i+1, frag)) + if err != nil { + t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frag) + continue + } + errors = append(errors, re) + } + + if _, frag, ok := bytes.Cut(line, []byte("ERROR MESSAGE: ")); ok { + re, err := regexp.Compile(string(frag)) + if err != nil { + t.Errorf("Invalid regexp after `ERROR MESSAGE: `: %#q", frag) + continue + } + errors = append(errors, re) + } + } + if len(errors) == 0 { + t.Fatalf("cannot find ERROR HERE") + } + expect(t, file, errors) + }) +} + +func expect(t *testing.T, file string, errors []*regexp.Regexp) { + dir, err := os.MkdirTemp("", filepath.Base(t.Name())) + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + dst := filepath.Join(dir, strings.TrimSuffix(file, ".go")) + cmd := exec.Command("go", "build", "-gcflags=-L -e", "-o="+dst, path(file)) // TODO(gri) no need for -gcflags=-L if go tool is adjusted + out, err := cmd.CombinedOutput() + if err == nil { + t.Errorf("expected cgo to fail but it succeeded") + } + + lines := bytes.Split(out, []byte("\n")) + for _, re := range errors { + found := false + for _, line := range lines { + if re.Match(line) { + t.Logf("found match for %#q: %q", re, line) + found = true + break + } + } + if !found { + t.Errorf("expected error output to contain %#q", re) + } + } + + if t.Failed() { + t.Logf("actual output:\n%s", out) + } +} + +func sizeofLongDouble(t *testing.T) int { + cmd := exec.Command("go", "run", path("long_double_size.go")) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out) + } + + i, err := strconv.Atoi(strings.TrimSpace(string(out))) + if err != nil { + t.Fatalf("long_double_size.go printed invalid size: %s", out) + } + return i +} + +func TestReportsTypeErrors(t *testing.T) { + for _, file := range []string{ + "err1.go", + "err2.go", + "err5.go", + "issue11097a.go", + "issue11097b.go", + "issue18452.go", + "issue18889.go", + "issue28721.go", + "issue33061.go", + "issue50710.go", + } { + check(t, file) + } + + if sizeofLongDouble(t) > 8 { + for _, file := range []string{ + "err4.go", + "issue28069.go", + } { + check(t, file) + } + } +} + +func TestToleratesOptimizationFlag(t *testing.T) { + for _, cflags := range []string{ + "", + "-O", + } { + cflags := cflags + t.Run(cflags, func(t *testing.T) { + t.Parallel() + + cmd := exec.Command("go", "build", path("issue14669.go")) + cmd.Env = append(os.Environ(), "CGO_CFLAGS="+cflags) + out, err := cmd.CombinedOutput() + if err != nil { + t.Errorf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out) + } + }) + } +} + +func TestMallocCrashesOnNil(t *testing.T) { + t.Parallel() + + cmd := exec.Command("go", "run", path("malloc.go")) + out, err := cmd.CombinedOutput() + if err == nil { + t.Logf("%#q:\n%s", strings.Join(cmd.Args, " "), out) + t.Fatalf("succeeded unexpectedly") + } +} diff --git a/misc/cgo/errors/ptr_test.go b/misc/cgo/errors/ptr_test.go new file mode 100644 index 0000000..0f39dc8 --- /dev/null +++ b/misc/cgo/errors/ptr_test.go @@ -0,0 +1,632 @@ +// Copyright 2015 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. + +// Tests that cgo detects invalid pointer passing at runtime. + +package errorstest + +import ( + "bytes" + "flag" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "sync/atomic" + "testing" +) + +var tmp = flag.String("tmp", "", "use `dir` for temporary files and do not clean up") + +// ptrTest is the tests without the boilerplate. +type ptrTest struct { + name string // for reporting + c string // the cgo comment + c1 string // cgo comment forced into non-export cgo file + imports []string // a list of imports + support string // supporting functions + body string // the body of the main function + extra []extra // extra files + fail bool // whether the test should fail + expensive bool // whether the test requires the expensive check +} + +type extra struct { + name string + contents string +} + +var ptrTests = []ptrTest{ + { + // Passing a pointer to a struct that contains a Go pointer. + name: "ptr1", + c: `typedef struct s1 { int *p; } s1; void f1(s1 *ps) {}`, + body: `C.f1(&C.s1{new(C.int)})`, + fail: true, + }, + { + // Passing a pointer to a struct that contains a Go pointer. + name: "ptr2", + c: `typedef struct s2 { int *p; } s2; void f2(s2 *ps) {}`, + body: `p := &C.s2{new(C.int)}; C.f2(p)`, + fail: true, + }, + { + // Passing a pointer to an int field of a Go struct + // that (irrelevantly) contains a Go pointer. + name: "ok1", + c: `struct s3 { int i; int *p; }; void f3(int *p) {}`, + body: `p := &C.struct_s3{i: 0, p: new(C.int)}; C.f3(&p.i)`, + fail: false, + }, + { + // Passing a pointer to a pointer field of a Go struct. + name: "ptrfield", + c: `struct s4 { int i; int *p; }; void f4(int **p) {}`, + body: `p := &C.struct_s4{i: 0, p: new(C.int)}; C.f4(&p.p)`, + fail: true, + }, + { + // Passing a pointer to a pointer field of a Go + // struct, where the field does not contain a Go + // pointer, but another field (irrelevantly) does. + name: "ptrfieldok", + c: `struct s5 { int *p1; int *p2; }; void f5(int **p) {}`, + body: `p := &C.struct_s5{p1: nil, p2: new(C.int)}; C.f5(&p.p1)`, + fail: false, + }, + { + // Passing the address of a slice with no Go pointers. + name: "sliceok1", + c: `void f6(void **p) {}`, + imports: []string{"unsafe"}, + body: `s := []unsafe.Pointer{nil}; C.f6(&s[0])`, + fail: false, + }, + { + // Passing the address of a slice with a Go pointer. + name: "sliceptr1", + c: `void f7(void **p) {}`, + imports: []string{"unsafe"}, + body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f7(&s[0])`, + fail: true, + }, + { + // Passing the address of a slice with a Go pointer, + // where we are passing the address of an element that + // is not a Go pointer. + name: "sliceptr2", + c: `void f8(void **p) {}`, + imports: []string{"unsafe"}, + body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f8(&s[0])`, + fail: true, + }, + { + // Passing the address of a slice that is an element + // in a struct only looks at the slice. + name: "sliceok2", + c: `void f9(void **p) {}`, + imports: []string{"unsafe"}, + support: `type S9 struct { p *int; s []unsafe.Pointer }`, + body: `i := 0; p := &S9{p:&i, s:[]unsafe.Pointer{nil}}; C.f9(&p.s[0])`, + fail: false, + }, + { + // Passing the address of a slice of an array that is + // an element in a struct, with a type conversion. + name: "sliceok3", + c: `void f10(void* p) {}`, + imports: []string{"unsafe"}, + support: `type S10 struct { p *int; a [4]byte }`, + body: `i := 0; p := &S10{p:&i}; s := p.a[:]; C.f10(unsafe.Pointer(&s[0]))`, + fail: false, + }, + { + // Passing the address of a slice of an array that is + // an element in a struct, with a type conversion. + name: "sliceok4", + c: `typedef void* PV11; void f11(PV11 p) {}`, + imports: []string{"unsafe"}, + support: `type S11 struct { p *int; a [4]byte }`, + body: `i := 0; p := &S11{p:&i}; C.f11(C.PV11(unsafe.Pointer(&p.a[0])))`, + fail: false, + }, + { + // Passing the address of a static variable with no + // pointers doesn't matter. + name: "varok", + c: `void f12(char** parg) {}`, + support: `var hello12 = [...]C.char{'h', 'e', 'l', 'l', 'o'}`, + body: `parg := [1]*C.char{&hello12[0]}; C.f12(&parg[0])`, + fail: false, + }, + { + // Passing the address of a static variable with + // pointers does matter. + name: "var1", + c: `void f13(char*** parg) {}`, + support: `var hello13 = [...]*C.char{new(C.char)}`, + body: `parg := [1]**C.char{&hello13[0]}; C.f13(&parg[0])`, + fail: true, + }, + { + // Storing a Go pointer into C memory should fail. + name: "barrier", + c: `#include <stdlib.h> + char **f14a() { return malloc(sizeof(char*)); } + void f14b(char **p) {}`, + body: `p := C.f14a(); *p = new(C.char); C.f14b(p)`, + fail: true, + expensive: true, + }, + { + // Storing a Go pointer into C memory by assigning a + // large value should fail. + name: "barrierstruct", + c: `#include <stdlib.h> + struct s15 { char *a[10]; }; + struct s15 *f15() { return malloc(sizeof(struct s15)); } + void f15b(struct s15 *p) {}`, + body: `p := C.f15(); p.a = [10]*C.char{new(C.char)}; C.f15b(p)`, + fail: true, + expensive: true, + }, + { + // Storing a Go pointer into C memory using a slice + // copy should fail. + name: "barrierslice", + c: `#include <stdlib.h> + struct s16 { char *a[10]; }; + struct s16 *f16() { return malloc(sizeof(struct s16)); } + void f16b(struct s16 *p) {}`, + body: `p := C.f16(); copy(p.a[:], []*C.char{new(C.char)}); C.f16b(p)`, + fail: true, + expensive: true, + }, + { + // A very large value uses a GC program, which is a + // different code path. + name: "barriergcprogarray", + c: `#include <stdlib.h> + struct s17 { char *a[32769]; }; + struct s17 *f17() { return malloc(sizeof(struct s17)); } + void f17b(struct s17 *p) {}`, + body: `p := C.f17(); p.a = [32769]*C.char{new(C.char)}; C.f17b(p)`, + fail: true, + expensive: true, + }, + { + // Similar case, with a source on the heap. + name: "barriergcprogarrayheap", + c: `#include <stdlib.h> + struct s18 { char *a[32769]; }; + struct s18 *f18() { return malloc(sizeof(struct s18)); } + void f18b(struct s18 *p) {} + void f18c(void *p) {}`, + imports: []string{"unsafe"}, + body: `p := C.f18(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f18b(p); n[0] = nil; C.f18c(unsafe.Pointer(n))`, + fail: true, + expensive: true, + }, + { + // A GC program with a struct. + name: "barriergcprogstruct", + c: `#include <stdlib.h> + struct s19a { char *a[32769]; }; + struct s19b { struct s19a f; }; + struct s19b *f19() { return malloc(sizeof(struct s19b)); } + void f19b(struct s19b *p) {}`, + body: `p := C.f19(); p.f = C.struct_s19a{[32769]*C.char{new(C.char)}}; C.f19b(p)`, + fail: true, + expensive: true, + }, + { + // Similar case, with a source on the heap. + name: "barriergcprogstructheap", + c: `#include <stdlib.h> + struct s20a { char *a[32769]; }; + struct s20b { struct s20a f; }; + struct s20b *f20() { return malloc(sizeof(struct s20b)); } + void f20b(struct s20b *p) {} + void f20c(void *p) {}`, + imports: []string{"unsafe"}, + body: `p := C.f20(); n := &C.struct_s20a{[32769]*C.char{new(C.char)}}; p.f = *n; C.f20b(p); n.a[0] = nil; C.f20c(unsafe.Pointer(n))`, + fail: true, + expensive: true, + }, + { + // Exported functions may not return Go pointers. + name: "export1", + c: `extern unsigned char *GoFn21();`, + support: `//export GoFn21 + func GoFn21() *byte { return new(byte) }`, + body: `C.GoFn21()`, + fail: true, + }, + { + // Returning a C pointer is fine. + name: "exportok", + c: `#include <stdlib.h> + extern unsigned char *GoFn22();`, + support: `//export GoFn22 + func GoFn22() *byte { return (*byte)(C.malloc(1)) }`, + body: `C.GoFn22()`, + }, + { + // Passing a Go string is fine. + name: "passstring", + c: `#include <stddef.h> + typedef struct { const char *p; ptrdiff_t n; } gostring23; + gostring23 f23(gostring23 s) { return s; }`, + imports: []string{"unsafe"}, + body: `s := "a"; r := C.f23(*(*C.gostring23)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`, + }, + { + // Passing a slice of Go strings fails. + name: "passstringslice", + c: `void f24(void *p) {}`, + imports: []string{"strings", "unsafe"}, + support: `type S24 struct { a [1]string }`, + body: `s := S24{a:[1]string{strings.Repeat("a", 2)}}; C.f24(unsafe.Pointer(&s.a[0]))`, + fail: true, + }, + { + // Exported functions may not return strings. + name: "retstring", + c: `extern void f25();`, + imports: []string{"strings"}, + support: `//export GoStr25 + func GoStr25() string { return strings.Repeat("a", 2) }`, + body: `C.f25()`, + c1: `#include <stddef.h> + typedef struct { const char *p; ptrdiff_t n; } gostring25; + extern gostring25 GoStr25(); + void f25() { GoStr25(); }`, + fail: true, + }, + { + // Don't check non-pointer data. + // Uses unsafe code to get a pointer we shouldn't check. + // Although we use unsafe, the uintptr represents an integer + // that happens to have the same representation as a pointer; + // that is, we are testing something that is not unsafe. + name: "ptrdata1", + c: `#include <stdlib.h> + void f26(void* p) {}`, + imports: []string{"unsafe"}, + support: `type S26 struct { p *int; a [8*8]byte; u uintptr }`, + body: `i := 0; p := &S26{u:uintptr(unsafe.Pointer(&i))}; q := (*S26)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f26(unsafe.Pointer(q))`, + fail: false, + }, + { + // Like ptrdata1, but with a type that uses a GC program. + name: "ptrdata2", + c: `#include <stdlib.h> + void f27(void* p) {}`, + imports: []string{"unsafe"}, + support: `type S27 struct { p *int; a [32769*8]byte; q *int; u uintptr }`, + body: `i := 0; p := S27{u:uintptr(unsafe.Pointer(&i))}; q := (*S27)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f27(unsafe.Pointer(q))`, + fail: false, + }, + { + // Check deferred pointers when they are used, not + // when the defer statement is run. + name: "defer1", + c: `typedef struct s28 { int *p; } s28; void f28(s28 *ps) {}`, + body: `p := &C.s28{}; defer C.f28(p); p.p = new(C.int)`, + fail: true, + }, + { + // Check a pointer to a union if the union has any + // pointer fields. + name: "union1", + c: `typedef union { char **p; unsigned long i; } u29; void f29(u29 *pu) {}`, + imports: []string{"unsafe"}, + body: `var b C.char; p := &b; C.f29((*C.u29)(unsafe.Pointer(&p)))`, + fail: true, + }, + { + // Don't check a pointer to a union if the union does + // not have any pointer fields. + // Like ptrdata1 above, the uintptr represents an + // integer that happens to have the same + // representation as a pointer. + name: "union2", + c: `typedef union { unsigned long i; } u39; void f39(u39 *pu) {}`, + imports: []string{"unsafe"}, + body: `var b C.char; p := &b; C.f39((*C.u39)(unsafe.Pointer(&p)))`, + fail: false, + }, + { + // Test preemption while entering a cgo call. Issue #21306. + name: "preemptduringcall", + c: `void f30() {}`, + imports: []string{"runtime", "sync"}, + body: `var wg sync.WaitGroup; wg.Add(100); for i := 0; i < 100; i++ { go func(i int) { for j := 0; j < 100; j++ { C.f30(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`, + fail: false, + }, + { + // Test poller deadline with cgocheck=2. Issue #23435. + name: "deadline", + c: `#define US31 10`, + imports: []string{"os", "time"}, + body: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US31 * time.Microsecond))`, + fail: false, + }, + { + // Test for double evaluation of channel receive. + name: "chanrecv", + c: `void f32(char** p) {}`, + imports: []string{"time"}, + body: `c := make(chan []*C.char, 2); c <- make([]*C.char, 1); go func() { time.Sleep(10 * time.Second); panic("received twice from chan") }(); C.f32(&(<-c)[0]);`, + fail: false, + }, + { + // Test that converting the address of a struct field + // to unsafe.Pointer still just checks that field. + // Issue #25941. + name: "structfield", + c: `void f33(void* p) {}`, + imports: []string{"unsafe"}, + support: `type S33 struct { p *int; a [8]byte; u uintptr }`, + body: `s := &S33{p: new(int)}; C.f33(unsafe.Pointer(&s.a))`, + fail: false, + }, + { + // Test that converting multiple struct field + // addresses to unsafe.Pointer still just checks those + // fields. Issue #25941. + name: "structfield2", + c: `void f34(void* p, int r, void* s) {}`, + imports: []string{"unsafe"}, + support: `type S34 struct { a [8]byte; p *int; b int64; }`, + body: `s := &S34{p: new(int)}; C.f34(unsafe.Pointer(&s.a), 32, unsafe.Pointer(&s.b))`, + fail: false, + }, + { + // Test that second argument to cgoCheckPointer is + // evaluated when a deferred function is deferred, not + // when it is run. + name: "defer2", + c: `void f35(char **pc) {}`, + support: `type S35a struct { s []*C.char }; type S35b struct { ps *S35a }`, + body: `p := &S35b{&S35a{[]*C.char{nil}}}; defer C.f35(&p.ps.s[0]); p.ps = nil`, + fail: false, + }, + { + // Test that indexing into a function call still + // examines only the slice being indexed. + name: "buffer", + c: `void f36(void *p) {}`, + imports: []string{"bytes", "unsafe"}, + body: `var b bytes.Buffer; b.WriteString("a"); C.f36(unsafe.Pointer(&b.Bytes()[0]))`, + fail: false, + }, + { + // Test that bgsweep releasing a finalizer is OK. + name: "finalizer", + c: `// Nothing to declare.`, + imports: []string{"os"}, + support: `func open37() { os.Open(os.Args[0]) }; var G37 [][]byte`, + body: `for i := 0; i < 10000; i++ { G37 = append(G37, make([]byte, 4096)); if i % 100 == 0 { G37 = nil; open37() } }`, + fail: false, + }, + { + // Test that converting generated struct to interface is OK. + name: "structof", + c: `// Nothing to declare.`, + imports: []string{"reflect"}, + support: `type MyInt38 int; func (i MyInt38) Get() int { return int(i) }; type Getter38 interface { Get() int }`, + body: `t := reflect.StructOf([]reflect.StructField{{Name: "MyInt38", Type: reflect.TypeOf(MyInt38(0)), Anonymous: true}}); v := reflect.New(t).Elem(); v.Interface().(Getter38).Get()`, + fail: false, + }, + { + // Test that a converted address of a struct field results + // in a check for just that field and not the whole struct. + name: "structfieldcast", + c: `struct S40i { int i; int* p; }; void f40(struct S40i* p) {}`, + support: `type S40 struct { p *int; a C.struct_S40i }`, + body: `s := &S40{p: new(int)}; C.f40((*C.struct_S40i)(&s.a))`, + fail: false, + }, +} + +func TestPointerChecks(t *testing.T) { + dir, exe := buildPtrTests(t) + + // We (TestPointerChecks) return before the parallel subtest functions do, + // so we can't just defer os.RemoveAll(dir). Instead we have to wait for + // the parallel subtests to finish. This code looks racy but is not: + // the add +1 run in serial before testOne blocks. The -1 run in parallel + // after testOne finishes. + var pending int32 + for _, pt := range ptrTests { + pt := pt + t.Run(pt.name, func(t *testing.T) { + atomic.AddInt32(&pending, +1) + defer func() { + if atomic.AddInt32(&pending, -1) == 0 { + os.RemoveAll(dir) + } + }() + testOne(t, pt, exe) + }) + } +} + +func buildPtrTests(t *testing.T) (dir, exe string) { + var gopath string + if *tmp != "" { + gopath = *tmp + dir = "" + } else { + d, err := os.MkdirTemp("", filepath.Base(t.Name())) + if err != nil { + t.Fatal(err) + } + dir = d + gopath = d + } + + src := filepath.Join(gopath, "src", "ptrtest") + if err := os.MkdirAll(src, 0777); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(src, "go.mod"), []byte("module ptrtest"), 0666); err != nil { + t.Fatal(err) + } + + // Prepare two cgo inputs: one for standard cgo and one for //export cgo. + // (The latter cannot have C definitions, only declarations.) + var cgo1, cgo2 bytes.Buffer + fmt.Fprintf(&cgo1, "package main\n\n/*\n") + fmt.Fprintf(&cgo2, "package main\n\n/*\n") + + // C code + for _, pt := range ptrTests { + cgo := &cgo1 + if strings.Contains(pt.support, "//export") { + cgo = &cgo2 + } + fmt.Fprintf(cgo, "%s\n", pt.c) + fmt.Fprintf(&cgo1, "%s\n", pt.c1) + } + fmt.Fprintf(&cgo1, "*/\nimport \"C\"\n\n") + fmt.Fprintf(&cgo2, "*/\nimport \"C\"\n\n") + + // Imports + did1 := make(map[string]bool) + did2 := make(map[string]bool) + did1["os"] = true // for ptrTestMain + fmt.Fprintf(&cgo1, "import \"os\"\n") + + for _, pt := range ptrTests { + did := did1 + cgo := &cgo1 + if strings.Contains(pt.support, "//export") { + did = did2 + cgo = &cgo2 + } + for _, imp := range pt.imports { + if !did[imp] { + did[imp] = true + fmt.Fprintf(cgo, "import %q\n", imp) + } + } + } + + // Func support and bodies. + for _, pt := range ptrTests { + cgo := &cgo1 + if strings.Contains(pt.support, "//export") { + cgo = &cgo2 + } + fmt.Fprintf(cgo, "%s\nfunc %s() {\n%s\n}\n", pt.support, pt.name, pt.body) + } + + // Func list and main dispatch. + fmt.Fprintf(&cgo1, "var funcs = map[string]func() {\n") + for _, pt := range ptrTests { + fmt.Fprintf(&cgo1, "\t%q: %s,\n", pt.name, pt.name) + } + fmt.Fprintf(&cgo1, "}\n\n") + fmt.Fprintf(&cgo1, "%s\n", ptrTestMain) + + if err := os.WriteFile(filepath.Join(src, "cgo1.go"), cgo1.Bytes(), 0666); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(src, "cgo2.go"), cgo2.Bytes(), 0666); err != nil { + t.Fatal(err) + } + + cmd := exec.Command("go", "build", "-o", "ptrtest.exe") + cmd.Dir = src + cmd.Env = append(os.Environ(), "GOPATH="+gopath) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("go build: %v\n%s", err, out) + } + + return dir, filepath.Join(src, "ptrtest.exe") +} + +const ptrTestMain = ` +func main() { + for _, arg := range os.Args[1:] { + f := funcs[arg] + if f == nil { + panic("missing func "+arg) + } + f() + } +} +` + +var csem = make(chan bool, 16) + +func testOne(t *testing.T, pt ptrTest, exe string) { + t.Parallel() + + // Run the tests in parallel, but don't run too many + // executions in parallel, to avoid overloading the system. + runcmd := func(cgocheck string) ([]byte, error) { + csem <- true + defer func() { <-csem }() + cmd := exec.Command(exe, pt.name) + cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck) + return cmd.CombinedOutput() + } + + if pt.expensive { + buf, err := runcmd("1") + if err != nil { + t.Logf("%s", buf) + if pt.fail { + t.Fatalf("test marked expensive, but failed when not expensive: %v", err) + } else { + t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err) + } + } + + } + + cgocheck := "" + if pt.expensive { + cgocheck = "2" + } + + buf, err := runcmd(cgocheck) + if pt.fail { + if err == nil { + t.Logf("%s", buf) + t.Fatalf("did not fail as expected") + } else if !bytes.Contains(buf, []byte("Go pointer")) { + t.Logf("%s", buf) + t.Fatalf("did not print expected error (failed with %v)", err) + } + } else { + if err != nil { + t.Logf("%s", buf) + t.Fatalf("failed unexpectedly: %v", err) + } + + if !pt.expensive { + // Make sure it passes with the expensive checks. + buf, err := runcmd("2") + if err != nil { + t.Logf("%s", buf) + t.Fatalf("failed unexpectedly with expensive checks: %v", err) + } + } + } + + if pt.fail { + buf, err := runcmd("0") + if err != nil { + t.Logf("%s", buf) + t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err) + } + } +} diff --git a/misc/cgo/errors/testdata/err1.go b/misc/cgo/errors/testdata/err1.go new file mode 100644 index 0000000..ced7443 --- /dev/null +++ b/misc/cgo/errors/testdata/err1.go @@ -0,0 +1,22 @@ +// Copyright 2013 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 main + +/* +#cgo LDFLAGS: -L/nonexist + +void test() { + xxx; // ERROR HERE +} + +// Issue 8442. Cgo output unhelpful error messages for +// invalid C preambles. +void issue8442foo(UNDEF*); // ERROR HERE +*/ +import "C" + +func main() { + C.test() +} diff --git a/misc/cgo/errors/testdata/err2.go b/misc/cgo/errors/testdata/err2.go new file mode 100644 index 0000000..aa94158 --- /dev/null +++ b/misc/cgo/errors/testdata/err2.go @@ -0,0 +1,110 @@ +// Copyright 2013 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 main + +/* +#include <stdio.h> + +typedef struct foo foo_t; +typedef struct bar bar_t; + +foo_t *foop; + +long double x = 0; + +static int transform(int x) { return x; } + +typedef void v; +void F(v** p) {} + +void fvi(void *p, int x) {} + +void fppi(int** p) {} + +int i; +void fi(int i) {} +*/ +import "C" +import ( + "unsafe" +) + +func main() { + s := "" + _ = s + C.malloc(s) // ERROR HERE + + x := (*C.bar_t)(nil) + C.foop = x // ERROR HERE + + // issue 13129: used to output error about C.unsignedshort with CC=clang + var x1 C.ushort + x1 = int(0) // ERROR HERE: C\.ushort + + // issue 13423 + _ = C.fopen() // ERROR HERE + + // issue 13467 + var x2 rune = '✈' + var _ rune = C.transform(x2) // ERROR HERE: C\.int + + // issue 13635: used to output error about C.unsignedchar. + // This test tests all such types. + var ( + _ C.uchar = "uc" // ERROR HERE: C\.uchar + _ C.schar = "sc" // ERROR HERE: C\.schar + _ C.ushort = "us" // ERROR HERE: C\.ushort + _ C.uint = "ui" // ERROR HERE: C\.uint + _ C.ulong = "ul" // ERROR HERE: C\.ulong + _ C.longlong = "ll" // ERROR HERE: C\.longlong + _ C.ulonglong = "ull" // ERROR HERE: C\.ulonglong + _ C.complexfloat = "cf" // ERROR HERE: C\.complexfloat + _ C.complexdouble = "cd" // ERROR HERE: C\.complexdouble + ) + + // issue 13830 + // cgo converts C void* to Go unsafe.Pointer, so despite appearances C + // void** is Go *unsafe.Pointer. This test verifies that we detect the + // problem at build time. + { + type v [0]byte + + f := func(p **v) { + C.F((**C.v)(unsafe.Pointer(p))) // ERROR HERE + } + var p *v + f(&p) + } + + // issue 16116 + _ = C.fvi(1) // ERROR HERE + + // Issue 16591: Test that we detect an invalid call that was being + // hidden by a type conversion inserted by cgo checking. + { + type x *C.int + var p *x + C.fppi(p) // ERROR HERE + } + + // issue 26745 + _ = func(i int) int { + // typecheck reports at column 14 ('+'), but types2 reports at + // column 10 ('C'). + // TODO(mdempsky): Investigate why, and see if types2 can be + // updated to match typecheck behavior. + return C.i + 1 // ERROR HERE: \b(10|14)\b + } + _ = func(i int) { + // typecheck reports at column 7 ('('), but types2 reports at + // column 8 ('i'). The types2 position is more correct, but + // updating typecheck here is fundamentally challenging because of + // IR limitations. + C.fi(i) // ERROR HERE: \b(7|8)\b + } + + C.fi = C.fi // ERROR HERE + +} diff --git a/misc/cgo/errors/testdata/err4.go b/misc/cgo/errors/testdata/err4.go new file mode 100644 index 0000000..8e5f78e --- /dev/null +++ b/misc/cgo/errors/testdata/err4.go @@ -0,0 +1,15 @@ +// Copyright 2017 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 main + +/* +long double x = 0; +*/ +import "C" + +func main() { + _ = C.x // ERROR HERE + _ = C.x +} diff --git a/misc/cgo/errors/testdata/err5.go b/misc/cgo/errors/testdata/err5.go new file mode 100644 index 0000000..c12a290 --- /dev/null +++ b/misc/cgo/errors/testdata/err5.go @@ -0,0 +1,11 @@ +// Copyright 2023 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 main + +//line /tmp/_cgo_.go:1 +//go:cgo_dynamic_linker "/elf/interp" +// ERROR MESSAGE: only allowed in cgo-generated code + +func main() {} diff --git a/misc/cgo/errors/testdata/issue11097a.go b/misc/cgo/errors/testdata/issue11097a.go new file mode 100644 index 0000000..028d10c --- /dev/null +++ b/misc/cgo/errors/testdata/issue11097a.go @@ -0,0 +1,15 @@ +// Copyright 2015 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 main + +/* +//enum test { foo, bar }; +*/ +import "C" + +func main() { + var a = C.enum_test(1) // ERROR HERE + _ = a +} diff --git a/misc/cgo/errors/testdata/issue11097b.go b/misc/cgo/errors/testdata/issue11097b.go new file mode 100644 index 0000000..b00f24f --- /dev/null +++ b/misc/cgo/errors/testdata/issue11097b.go @@ -0,0 +1,15 @@ +// Copyright 2015 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 main + +/* +//enum test { foo, bar }; +*/ +import "C" + +func main() { + p := new(C.enum_test) // ERROR HERE + _ = p +} diff --git a/misc/cgo/errors/testdata/issue14669.go b/misc/cgo/errors/testdata/issue14669.go new file mode 100644 index 0000000..04d2bcb --- /dev/null +++ b/misc/cgo/errors/testdata/issue14669.go @@ -0,0 +1,23 @@ +// Copyright 2016 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. + +// Issue 14669: test that fails when build with CGO_CFLAGS selecting +// optimization. + +package p + +/* +const int E = 1; + +typedef struct s { + int c; +} s; +*/ +import "C" + +func F() { + _ = C.s{ + c: C.E, + } +} diff --git a/misc/cgo/errors/testdata/issue18452.go b/misc/cgo/errors/testdata/issue18452.go new file mode 100644 index 0000000..0386d76 --- /dev/null +++ b/misc/cgo/errors/testdata/issue18452.go @@ -0,0 +1,18 @@ +// Copyright 2017 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. + +// Issue 18452: show pos info in undefined name errors + +package p + +import ( + "C" + "fmt" +) + +func a() { + fmt.Println("Hello, world!") + C.function_that_does_not_exist() // ERROR HERE + C.pi // ERROR HERE +} diff --git a/misc/cgo/errors/testdata/issue18889.go b/misc/cgo/errors/testdata/issue18889.go new file mode 100644 index 0000000..bba6b8f --- /dev/null +++ b/misc/cgo/errors/testdata/issue18889.go @@ -0,0 +1,7 @@ +package main + +import "C" + +func main() { + _ = C.malloc // ERROR HERE +} diff --git a/misc/cgo/errors/testdata/issue28069.go b/misc/cgo/errors/testdata/issue28069.go new file mode 100644 index 0000000..e19a3b4 --- /dev/null +++ b/misc/cgo/errors/testdata/issue28069.go @@ -0,0 +1,26 @@ +// Copyright 2018 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. + +// Test that the error message for an unrepresentable typedef in a +// union appears on the right line. This test is only run if the size +// of long double is larger than 64. + +package main + +/* +typedef long double Float128; + +typedef struct SV { + union { + Float128 float128; + } value; +} SV; +*/ +import "C" + +type ts struct { + tv *C.SV // ERROR HERE +} + +func main() {} diff --git a/misc/cgo/errors/testdata/issue28721.go b/misc/cgo/errors/testdata/issue28721.go new file mode 100644 index 0000000..0eb2a92 --- /dev/null +++ b/misc/cgo/errors/testdata/issue28721.go @@ -0,0 +1,29 @@ +// Copyright 2018 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. + +// cgo should reject the use of mangled C names. + +package main + +/* +typedef struct a { + int i; +} a; +void fn(void) {} +*/ +import "C" + +type B _Ctype_struct_a // ERROR HERE + +var a _Ctype_struct_a // ERROR HERE + +type A struct { + a *_Ctype_struct_a // ERROR HERE +} + +var notExist _Ctype_NotExist // ERROR HERE + +func main() { + _Cfunc_fn() // ERROR HERE +} diff --git a/misc/cgo/errors/testdata/issue33061.go b/misc/cgo/errors/testdata/issue33061.go new file mode 100644 index 0000000..77d5f7a --- /dev/null +++ b/misc/cgo/errors/testdata/issue33061.go @@ -0,0 +1,17 @@ +// Copyright 2019 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. + +// cgo shouldn't crash if there is an extra argument with a C reference. + +package main + +// void F(void* p) {}; +import "C" + +import "unsafe" + +func F() { + var i int + C.F(unsafe.Pointer(&i), C.int(0)) // ERROR HERE +} diff --git a/misc/cgo/errors/testdata/issue42580.go b/misc/cgo/errors/testdata/issue42580.go new file mode 100644 index 0000000..aba80df --- /dev/null +++ b/misc/cgo/errors/testdata/issue42580.go @@ -0,0 +1,44 @@ +// Copyright 2021 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. + +// Issue 42580: cmd/cgo: shifting identifier position in ast + +package cgotest + +// typedef int (*intFunc) (); +// +// char* strarg = ""; +// +// int func_with_char(char* arg, void* dummy) +// {return 5;} +// +// int* get_arr(char* arg, void* dummy) +// {return NULL;} +import "C" +import "unsafe" + +// Test variables +var ( + checkedPointer = []byte{1} + doublePointerChecked = []byte{1} + singleInnerPointerChecked = []byte{1} +) + +// This test checks the positions of variable identifiers. +// Changing the positions of the test variables idents after this point will break the test. + +func TestSingleArgumentCast() C.int { + retcode := C.func_with_char((*C.char)(unsafe.Pointer(&checkedPointer[0])), unsafe.Pointer(C.strarg)) + return retcode +} + +func TestSingleArgumentCastRecFuncAsSimpleArg() C.int { + retcode := C.func_with_char((*C.char)(unsafe.Pointer(C.get_arr((*C.char)(unsafe.Pointer(&singleInnerPointerChecked[0])), unsafe.Pointer(C.strarg)))), nil) + return retcode +} + +func TestSingleArgumentCastRecFunc() C.int { + retcode := C.func_with_char((*C.char)(unsafe.Pointer(C.get_arr((*C.char)(unsafe.Pointer(&doublePointerChecked[0])), unsafe.Pointer(C.strarg)))), unsafe.Pointer(C.strarg)) + return retcode +} diff --git a/misc/cgo/errors/testdata/issue50710.go b/misc/cgo/errors/testdata/issue50710.go new file mode 100644 index 0000000..dffea22 --- /dev/null +++ b/misc/cgo/errors/testdata/issue50710.go @@ -0,0 +1,14 @@ +// Copyright 2022 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 main + +// size_t StrLen(_GoString_ s) { +// return _GoStringLen(s); +// } +import "C" + +func main() { + C.StrLen1() // ERROR HERE +} diff --git a/misc/cgo/errors/testdata/long_double_size.go b/misc/cgo/errors/testdata/long_double_size.go new file mode 100644 index 0000000..8b797f8 --- /dev/null +++ b/misc/cgo/errors/testdata/long_double_size.go @@ -0,0 +1,16 @@ +// Copyright 2017 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 main + +/* +const int sizeofLongDouble = sizeof(long double); +*/ +import "C" + +import "fmt" + +func main() { + fmt.Println(C.sizeofLongDouble) +} diff --git a/misc/cgo/errors/testdata/malloc.go b/misc/cgo/errors/testdata/malloc.go new file mode 100644 index 0000000..65da020 --- /dev/null +++ b/misc/cgo/errors/testdata/malloc.go @@ -0,0 +1,34 @@ +// Copyright 2016 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. + +// Test that C.malloc does not return nil. + +package main + +// #include <stdlib.h> +import "C" + +import ( + "fmt" + "runtime" +) + +func main() { + var size C.size_t + size-- + + // The Dragonfly libc succeeds when asked to allocate + // 0xffffffffffffffff bytes, so pass a different value that + // causes it to fail. + if runtime.GOOS == "dragonfly" { + size = C.size_t(0x7fffffff << (32 * (^uintptr(0) >> 63))) + } + + p := C.malloc(size) + if p == nil { + fmt.Println("malloc: C.malloc returned nil") + // Just exit normally--the test script expects this + // program to crash, so exiting normally indicates failure. + } +} |