1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Tests that cgo detects invalid pointer passing at runtime.
23 // ptrTest is the tests without the boilerplate.
25 name string // for reporting
26 c string // the cgo comment
27 imports []string // a list of imports
28 support string // supporting functions
29 body string // the body of the main function
30 fail bool // whether the test should fail
31 expensive bool // whether the test requires the expensive check
34 var ptrTests = []ptrTest{
36 // Passing a pointer to a struct that contains a Go pointer.
38 c: `typedef struct s { int *p; } s; void f(s *ps) {}`,
39 body: `C.f(&C.s{new(C.int)})`,
43 // Passing a pointer to a struct that contains a Go pointer.
45 c: `typedef struct s { int *p; } s; void f(s *ps) {}`,
46 body: `p := &C.s{new(C.int)}; C.f(p)`,
50 // Passing a pointer to an int field of a Go struct
51 // that (irrelevantly) contains a Go pointer.
53 c: `struct s { int i; int *p; }; void f(int *p) {}`,
54 body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.i)`,
58 // Passing a pointer to a pointer field of a Go struct.
60 c: `struct s { int i; int *p; }; void f(int **p) {}`,
61 body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.p)`,
65 // Passing a pointer to a pointer field of a Go
66 // struct, where the field does not contain a Go
67 // pointer, but another field (irrelevantly) does.
69 c: `struct s { int *p1; int *p2; }; void f(int **p) {}`,
70 body: `p := &C.struct_s{p1: nil, p2: new(C.int)}; C.f(&p.p1)`,
74 // Passing the address of a slice with no Go pointers.
76 c: `void f(void **p) {}`,
77 imports: []string{"unsafe"},
78 body: `s := []unsafe.Pointer{nil}; C.f(&s[0])`,
82 // Passing the address of a slice with a Go pointer.
84 c: `void f(void **p) {}`,
85 imports: []string{"unsafe"},
86 body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f(&s[0])`,
90 // Passing the address of a slice with a Go pointer,
91 // where we are passing the address of an element that
92 // is not a Go pointer.
94 c: `void f(void **p) {}`,
95 imports: []string{"unsafe"},
96 body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f(&s[0])`,
100 // Passing the address of a slice that is an element
101 // in a struct only looks at the slice.
103 c: `void f(void **p) {}`,
104 imports: []string{"unsafe"},
105 support: `type S struct { p *int; s []unsafe.Pointer }`,
106 body: `i := 0; p := &S{p:&i, s:[]unsafe.Pointer{nil}}; C.f(&p.s[0])`,
110 // Passing the address of a static variable with no
111 // pointers doesn't matter.
113 c: `void f(char** parg) {}`,
114 support: `var hello = [...]C.char{'h', 'e', 'l', 'l', 'o'}`,
115 body: `parg := [1]*C.char{&hello[0]}; C.f(&parg[0])`,
119 // Passing the address of a static variable with
120 // pointers does matter.
122 c: `void f(char*** parg) {}`,
123 support: `var hello = [...]*C.char{new(C.char)}`,
124 body: `parg := [1]**C.char{&hello[0]}; C.f(&parg[0])`,
128 // Storing a Go pointer into C memory should fail.
130 c: `#include <stdlib.h>
131 char **f1() { return malloc(sizeof(char*)); }
132 void f2(char **p) {}`,
133 body: `p := C.f1(); *p = new(C.char); C.f2(p)`,
138 // Storing a Go pointer into C memory by assigning a
139 // large value should fail.
140 name: "barrier-struct",
141 c: `#include <stdlib.h>
142 struct s { char *a[10]; };
143 struct s *f1() { return malloc(sizeof(struct s)); }
144 void f2(struct s *p) {}`,
145 body: `p := C.f1(); p.a = [10]*C.char{new(C.char)}; C.f2(p)`,
150 // Storing a Go pointer into C memory using a slice
152 name: "barrier-slice",
153 c: `#include <stdlib.h>
154 struct s { char *a[10]; };
155 struct s *f1() { return malloc(sizeof(struct s)); }
156 void f2(struct s *p) {}`,
157 body: `p := C.f1(); copy(p.a[:], []*C.char{new(C.char)}); C.f2(p)`,
162 // A very large value uses a GC program, which is a
163 // different code path.
164 name: "barrier-gcprog-array",
165 c: `#include <stdlib.h>
166 struct s { char *a[32769]; };
167 struct s *f1() { return malloc(sizeof(struct s)); }
168 void f2(struct s *p) {}`,
169 body: `p := C.f1(); p.a = [32769]*C.char{new(C.char)}; C.f2(p)`,
174 // Similar case, with a source on the heap.
175 name: "barrier-gcprog-array-heap",
176 c: `#include <stdlib.h>
177 struct s { char *a[32769]; };
178 struct s *f1() { return malloc(sizeof(struct s)); }
179 void f2(struct s *p) {}
180 void f3(void *p) {}`,
181 imports: []string{"unsafe"},
182 body: `p := C.f1(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f2(p); n[0] = nil; C.f3(unsafe.Pointer(n))`,
187 // A GC program with a struct.
188 name: "barrier-gcprog-struct",
189 c: `#include <stdlib.h>
190 struct s { char *a[32769]; };
191 struct s2 { struct s f; };
192 struct s2 *f1() { return malloc(sizeof(struct s2)); }
193 void f2(struct s2 *p) {}`,
194 body: `p := C.f1(); p.f = C.struct_s{[32769]*C.char{new(C.char)}}; C.f2(p)`,
199 // Similar case, with a source on the heap.
200 name: "barrier-gcprog-struct-heap",
201 c: `#include <stdlib.h>
202 struct s { char *a[32769]; };
203 struct s2 { struct s f; };
204 struct s2 *f1() { return malloc(sizeof(struct s2)); }
205 void f2(struct s2 *p) {}
206 void f3(void *p) {}`,
207 imports: []string{"unsafe"},
208 body: `p := C.f1(); n := &C.struct_s{[32769]*C.char{new(C.char)}}; p.f = *n; C.f2(p); n.a[0] = nil; C.f3(unsafe.Pointer(n))`,
219 dir, err := ioutil.TempDir("", "cgoerrors")
221 fmt.Fprintln(os.Stderr, err)
224 defer os.RemoveAll(dir)
226 workers := runtime.NumCPU() + 1
228 var wg sync.WaitGroup
230 errs := make(chan int)
231 for i := 0; i < workers; i++ {
239 for i := range ptrTests {
250 for e := range errs {
256 func worker(dir string, c, errs chan int) {
268 func doOne(dir string, i int) bool {
271 name := filepath.Join(dir, fmt.Sprintf("t%d.go", i))
272 f, err := os.Create(name)
274 fmt.Fprintln(os.Stderr, err)
278 b := bufio.NewWriter(f)
279 fmt.Fprintln(b, `package main`)
281 fmt.Fprintln(b, `/*`)
283 fmt.Fprintln(b, `*/`)
284 fmt.Fprintln(b, `import "C"`)
286 for _, imp := range t.imports {
287 fmt.Fprintln(b, `import "`+imp+`"`)
289 if len(t.imports) > 0 {
292 if len(t.support) > 0 {
293 fmt.Fprintln(b, t.support)
296 fmt.Fprintln(b, `func main() {`)
297 fmt.Fprintln(b, t.body)
300 if err := b.Flush(); err != nil {
301 fmt.Fprintf(os.Stderr, "flushing %s: %v\n", name, err)
304 if err := f.Close(); err != nil {
305 fmt.Fprintln(os.Stderr, "closing %s: %v\n", name, err)
311 cmd := exec.Command("go", "run", name)
315 cmd.Env = cgocheckEnv("1")
316 buf, err := cmd.CombinedOutput()
318 var errbuf bytes.Buffer
320 fmt.Fprintf(&errbuf, "test %s marked expensive but failed when not expensive: %v\n", t.name, err)
322 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=1: %v\n", t.name, err)
324 reportTestOutput(&errbuf, t.name, buf)
325 os.Stderr.Write(errbuf.Bytes())
329 cmd = exec.Command("go", "run", name)
334 cmd.Env = cgocheckEnv("2")
337 buf, err := cmd.CombinedOutput()
341 var errbuf bytes.Buffer
342 fmt.Fprintf(&errbuf, "test %s did not fail as expected\n", t.name)
343 reportTestOutput(&errbuf, t.name, buf)
344 os.Stderr.Write(errbuf.Bytes())
346 } else if !bytes.Contains(buf, []byte("Go pointer")) {
347 var errbuf bytes.Buffer
348 fmt.Fprintf(&errbuf, "test %s output does not contain expected error (failed with %v)\n", t.name, err)
349 reportTestOutput(&errbuf, t.name, buf)
350 os.Stderr.Write(errbuf.Bytes())
355 var errbuf bytes.Buffer
356 fmt.Fprintf(&errbuf, "test %s failed unexpectedly: %v\n", t.name, err)
357 reportTestOutput(&errbuf, t.name, buf)
358 os.Stderr.Write(errbuf.Bytes())
362 if !t.expensive && ok {
363 // Make sure it passes with the expensive checks.
364 cmd := exec.Command("go", "run", name)
366 cmd.Env = cgocheckEnv("2")
367 buf, err := cmd.CombinedOutput()
369 var errbuf bytes.Buffer
370 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with expensive checks: %v\n", t.name, err)
371 reportTestOutput(&errbuf, t.name, buf)
372 os.Stderr.Write(errbuf.Bytes())
379 cmd = exec.Command("go", "run", name)
381 cmd.Env = cgocheckEnv("0")
382 buf, err := cmd.CombinedOutput()
384 var errbuf bytes.Buffer
385 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=0: %v\n", t.name, err)
386 reportTestOutput(&errbuf, t.name, buf)
387 os.Stderr.Write(errbuf.Bytes())
395 func reportTestOutput(w io.Writer, name string, buf []byte) {
396 fmt.Fprintf(w, "=== test %s output ===\n", name)
397 fmt.Fprintf(w, "%s", buf)
398 fmt.Fprintf(w, "=== end of test %s output ===\n", name)
401 func cgocheckEnv(val string) []string {
402 env := []string{"GODEBUG=cgocheck=" + val}
403 for _, e := range os.Environ() {
404 if !strings.HasPrefix(e, "GODEBUG=") {