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 extra []extra // extra files
31 fail bool // whether the test should fail
32 expensive bool // whether the test requires the expensive check
40 var ptrTests = []ptrTest{
42 // Passing a pointer to a struct that contains a Go pointer.
44 c: `typedef struct s { int *p; } s; void f(s *ps) {}`,
45 body: `C.f(&C.s{new(C.int)})`,
49 // Passing a pointer to a struct that contains a Go pointer.
51 c: `typedef struct s { int *p; } s; void f(s *ps) {}`,
52 body: `p := &C.s{new(C.int)}; C.f(p)`,
56 // Passing a pointer to an int field of a Go struct
57 // that (irrelevantly) contains a Go pointer.
59 c: `struct s { int i; int *p; }; void f(int *p) {}`,
60 body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.i)`,
64 // Passing a pointer to a pointer field of a Go struct.
66 c: `struct s { int i; int *p; }; void f(int **p) {}`,
67 body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.p)`,
71 // Passing a pointer to a pointer field of a Go
72 // struct, where the field does not contain a Go
73 // pointer, but another field (irrelevantly) does.
75 c: `struct s { int *p1; int *p2; }; void f(int **p) {}`,
76 body: `p := &C.struct_s{p1: nil, p2: new(C.int)}; C.f(&p.p1)`,
80 // Passing the address of a slice with no Go pointers.
82 c: `void f(void **p) {}`,
83 imports: []string{"unsafe"},
84 body: `s := []unsafe.Pointer{nil}; C.f(&s[0])`,
88 // Passing the address of a slice with a Go pointer.
90 c: `void f(void **p) {}`,
91 imports: []string{"unsafe"},
92 body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f(&s[0])`,
96 // Passing the address of a slice with a Go pointer,
97 // where we are passing the address of an element that
98 // is not a Go pointer.
100 c: `void f(void **p) {}`,
101 imports: []string{"unsafe"},
102 body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f(&s[0])`,
106 // Passing the address of a slice that is an element
107 // in a struct only looks at the slice.
109 c: `void f(void **p) {}`,
110 imports: []string{"unsafe"},
111 support: `type S struct { p *int; s []unsafe.Pointer }`,
112 body: `i := 0; p := &S{p:&i, s:[]unsafe.Pointer{nil}}; C.f(&p.s[0])`,
116 // Passing the address of a slice of an array that is
117 // an element in a struct, with a type conversion.
119 c: `void f(void* p) {}`,
120 imports: []string{"unsafe"},
121 support: `type S struct { p *int; a [4]byte }`,
122 body: `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`,
126 // Passing the address of a slice of an array that is
127 // an element in a struct, with a type conversion.
129 c: `typedef void* PV; void f(PV p) {}`,
130 imports: []string{"unsafe"},
131 support: `type S struct { p *int; a [4]byte }`,
132 body: `i := 0; p := &S{p:&i}; C.f(C.PV(unsafe.Pointer(&p.a[0])))`,
136 // Passing the address of a static variable with no
137 // pointers doesn't matter.
139 c: `void f(char** parg) {}`,
140 support: `var hello = [...]C.char{'h', 'e', 'l', 'l', 'o'}`,
141 body: `parg := [1]*C.char{&hello[0]}; C.f(&parg[0])`,
145 // Passing the address of a static variable with
146 // pointers does matter.
148 c: `void f(char*** parg) {}`,
149 support: `var hello = [...]*C.char{new(C.char)}`,
150 body: `parg := [1]**C.char{&hello[0]}; C.f(&parg[0])`,
154 // Storing a Go pointer into C memory should fail.
156 c: `#include <stdlib.h>
157 char **f1() { return malloc(sizeof(char*)); }
158 void f2(char **p) {}`,
159 body: `p := C.f1(); *p = new(C.char); C.f2(p)`,
164 // Storing a Go pointer into C memory by assigning a
165 // large value should fail.
166 name: "barrier-struct",
167 c: `#include <stdlib.h>
168 struct s { char *a[10]; };
169 struct s *f1() { return malloc(sizeof(struct s)); }
170 void f2(struct s *p) {}`,
171 body: `p := C.f1(); p.a = [10]*C.char{new(C.char)}; C.f2(p)`,
176 // Storing a Go pointer into C memory using a slice
178 name: "barrier-slice",
179 c: `#include <stdlib.h>
180 struct s { char *a[10]; };
181 struct s *f1() { return malloc(sizeof(struct s)); }
182 void f2(struct s *p) {}`,
183 body: `p := C.f1(); copy(p.a[:], []*C.char{new(C.char)}); C.f2(p)`,
188 // A very large value uses a GC program, which is a
189 // different code path.
190 name: "barrier-gcprog-array",
191 c: `#include <stdlib.h>
192 struct s { char *a[32769]; };
193 struct s *f1() { return malloc(sizeof(struct s)); }
194 void f2(struct s *p) {}`,
195 body: `p := C.f1(); p.a = [32769]*C.char{new(C.char)}; C.f2(p)`,
200 // Similar case, with a source on the heap.
201 name: "barrier-gcprog-array-heap",
202 c: `#include <stdlib.h>
203 struct s { char *a[32769]; };
204 struct s *f1() { return malloc(sizeof(struct s)); }
205 void f2(struct s *p) {}
206 void f3(void *p) {}`,
207 imports: []string{"unsafe"},
208 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))`,
213 // A GC program with a struct.
214 name: "barrier-gcprog-struct",
215 c: `#include <stdlib.h>
216 struct s { char *a[32769]; };
217 struct s2 { struct s f; };
218 struct s2 *f1() { return malloc(sizeof(struct s2)); }
219 void f2(struct s2 *p) {}`,
220 body: `p := C.f1(); p.f = C.struct_s{[32769]*C.char{new(C.char)}}; C.f2(p)`,
225 // Similar case, with a source on the heap.
226 name: "barrier-gcprog-struct-heap",
227 c: `#include <stdlib.h>
228 struct s { char *a[32769]; };
229 struct s2 { struct s f; };
230 struct s2 *f1() { return malloc(sizeof(struct s2)); }
231 void f2(struct s2 *p) {}
232 void f3(void *p) {}`,
233 imports: []string{"unsafe"},
234 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))`,
239 // Exported functions may not return Go pointers.
241 c: `extern unsigned char *GoFn();`,
242 support: `//export GoFn
243 func GoFn() *byte { return new(byte) }`,
248 // Returning a C pointer is fine.
250 c: `#include <stdlib.h>
251 extern unsigned char *GoFn();`,
252 support: `//export GoFn
253 func GoFn() *byte { return (*byte)(C.malloc(1)) }`,
257 // Passing a Go string is fine.
259 c: `#include <stddef.h>
260 typedef struct { const char *p; ptrdiff_t n; } gostring;
261 gostring f(gostring s) { return s; }`,
262 imports: []string{"unsafe"},
263 body: `s := "a"; r := C.f(*(*C.gostring)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`,
266 // Passing a slice of Go strings fails.
267 name: "pass-string-slice",
268 c: `void f(void *p) {}`,
269 imports: []string{"strings", "unsafe"},
270 support: `type S struct { a [1]string }`,
271 body: `s := S{a:[1]string{strings.Repeat("a", 2)}}; C.f(unsafe.Pointer(&s.a[0]))`,
275 // Exported functions may not return strings.
277 c: `extern void f();`,
278 imports: []string{"strings"},
279 support: `//export GoStr
280 func GoStr() string { return strings.Repeat("a", 2) }`,
286 typedef struct { const char *p; ptrdiff_t n; } gostring;
287 extern gostring GoStr();
288 void f() { GoStr(); }`,
300 gopath, err := ioutil.TempDir("", "cgoerrors")
302 fmt.Fprintln(os.Stderr, err)
305 defer os.RemoveAll(gopath)
307 if err := os.MkdirAll(filepath.Join(gopath, "src"), 0777); err != nil {
308 fmt.Fprintln(os.Stderr, err)
312 workers := runtime.NumCPU() + 1
314 var wg sync.WaitGroup
316 errs := make(chan int)
317 for i := 0; i < workers; i++ {
320 worker(gopath, c, errs)
325 for i := range ptrTests {
336 for e := range errs {
342 func worker(gopath string, c, errs chan int) {
345 if !doOne(gopath, i) {
354 func doOne(gopath string, i int) bool {
357 dir := filepath.Join(gopath, "src", fmt.Sprintf("dir%d", i))
358 if err := os.Mkdir(dir, 0777); err != nil {
359 fmt.Fprintln(os.Stderr, err)
363 name := filepath.Join(dir, fmt.Sprintf("t%d.go", i))
364 f, err := os.Create(name)
366 fmt.Fprintln(os.Stderr, err)
370 b := bufio.NewWriter(f)
371 fmt.Fprintln(b, `package main`)
373 fmt.Fprintln(b, `/*`)
375 fmt.Fprintln(b, `*/`)
376 fmt.Fprintln(b, `import "C"`)
378 for _, imp := range t.imports {
379 fmt.Fprintln(b, `import "`+imp+`"`)
381 if len(t.imports) > 0 {
384 if len(t.support) > 0 {
385 fmt.Fprintln(b, t.support)
388 fmt.Fprintln(b, `func main() {`)
389 fmt.Fprintln(b, t.body)
392 if err := b.Flush(); err != nil {
393 fmt.Fprintf(os.Stderr, "flushing %s: %v\n", name, err)
396 if err := f.Close(); err != nil {
397 fmt.Fprintf(os.Stderr, "closing %s: %v\n", name, err)
401 for _, e := range t.extra {
402 if err := ioutil.WriteFile(filepath.Join(dir, e.name), []byte(e.contents), 0644); err != nil {
403 fmt.Fprintf(os.Stderr, "writing %s: %v\n", e.name, err)
410 cmd := exec.Command("go", "build")
412 cmd.Env = addEnv("GOPATH", gopath)
413 buf, err := cmd.CombinedOutput()
415 fmt.Fprintf(os.Stderr, "test %s failed to build: %v\n%s", t.name, err, buf)
419 exe := filepath.Join(dir, filepath.Base(dir))
420 cmd = exec.Command(exe)
424 cmd.Env = cgocheckEnv("1")
425 buf, err := cmd.CombinedOutput()
427 var errbuf bytes.Buffer
429 fmt.Fprintf(&errbuf, "test %s marked expensive but failed when not expensive: %v\n", t.name, err)
431 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=1: %v\n", t.name, err)
433 reportTestOutput(&errbuf, t.name, buf)
434 os.Stderr.Write(errbuf.Bytes())
438 cmd = exec.Command(exe)
443 cmd.Env = cgocheckEnv("2")
446 buf, err = cmd.CombinedOutput()
450 var errbuf bytes.Buffer
451 fmt.Fprintf(&errbuf, "test %s did not fail as expected\n", t.name)
452 reportTestOutput(&errbuf, t.name, buf)
453 os.Stderr.Write(errbuf.Bytes())
455 } else if !bytes.Contains(buf, []byte("Go pointer")) {
456 var errbuf bytes.Buffer
457 fmt.Fprintf(&errbuf, "test %s output does not contain expected error (failed with %v)\n", t.name, err)
458 reportTestOutput(&errbuf, t.name, buf)
459 os.Stderr.Write(errbuf.Bytes())
464 var errbuf bytes.Buffer
465 fmt.Fprintf(&errbuf, "test %s failed unexpectedly: %v\n", t.name, err)
466 reportTestOutput(&errbuf, t.name, buf)
467 os.Stderr.Write(errbuf.Bytes())
471 if !t.expensive && ok {
472 // Make sure it passes with the expensive checks.
473 cmd := exec.Command(exe)
475 cmd.Env = cgocheckEnv("2")
476 buf, err := cmd.CombinedOutput()
478 var errbuf bytes.Buffer
479 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with expensive checks: %v\n", t.name, err)
480 reportTestOutput(&errbuf, t.name, buf)
481 os.Stderr.Write(errbuf.Bytes())
488 cmd = exec.Command(exe)
490 cmd.Env = cgocheckEnv("0")
491 buf, err := cmd.CombinedOutput()
493 var errbuf bytes.Buffer
494 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=0: %v\n", t.name, err)
495 reportTestOutput(&errbuf, t.name, buf)
496 os.Stderr.Write(errbuf.Bytes())
504 func reportTestOutput(w io.Writer, name string, buf []byte) {
505 fmt.Fprintf(w, "=== test %s output ===\n", name)
506 fmt.Fprintf(w, "%s", buf)
507 fmt.Fprintf(w, "=== end of test %s output ===\n", name)
510 func cgocheckEnv(val string) []string {
511 return addEnv("GODEBUG", "cgocheck="+val)
514 func addEnv(key, val string) []string {
515 env := []string{key + "=" + val}
517 for _, e := range os.Environ() {
518 if !strings.HasPrefix(e, look) {