gcc:
[official-gcc.git] / libgo / misc / cgo / errors / ptr_test.go
blobfe8dfff1d891b76eea60b67d06f4c0b9441e8759
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.
7 package errorstest
9 import (
10 "bufio"
11 "bytes"
12 "fmt"
13 "io/ioutil"
14 "os"
15 "os/exec"
16 "path/filepath"
17 "strings"
18 "testing"
21 // ptrTest is the tests without the boilerplate.
22 type ptrTest struct {
23 name string // for reporting
24 c string // the cgo comment
25 imports []string // a list of imports
26 support string // supporting functions
27 body string // the body of the main function
28 extra []extra // extra files
29 fail bool // whether the test should fail
30 expensive bool // whether the test requires the expensive check
33 type extra struct {
34 name string
35 contents string
38 var ptrTests = []ptrTest{
40 // Passing a pointer to a struct that contains a Go pointer.
41 name: "ptr1",
42 c: `typedef struct s { int *p; } s; void f(s *ps) {}`,
43 body: `C.f(&C.s{new(C.int)})`,
44 fail: true,
47 // Passing a pointer to a struct that contains a Go pointer.
48 name: "ptr2",
49 c: `typedef struct s { int *p; } s; void f(s *ps) {}`,
50 body: `p := &C.s{new(C.int)}; C.f(p)`,
51 fail: true,
54 // Passing a pointer to an int field of a Go struct
55 // that (irrelevantly) contains a Go pointer.
56 name: "ok1",
57 c: `struct s { int i; int *p; }; void f(int *p) {}`,
58 body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.i)`,
59 fail: false,
62 // Passing a pointer to a pointer field of a Go struct.
63 name: "ptr-field",
64 c: `struct s { int i; int *p; }; void f(int **p) {}`,
65 body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.p)`,
66 fail: true,
69 // Passing a pointer to a pointer field of a Go
70 // struct, where the field does not contain a Go
71 // pointer, but another field (irrelevantly) does.
72 name: "ptr-field-ok",
73 c: `struct s { int *p1; int *p2; }; void f(int **p) {}`,
74 body: `p := &C.struct_s{p1: nil, p2: new(C.int)}; C.f(&p.p1)`,
75 fail: false,
78 // Passing the address of a slice with no Go pointers.
79 name: "slice-ok-1",
80 c: `void f(void **p) {}`,
81 imports: []string{"unsafe"},
82 body: `s := []unsafe.Pointer{nil}; C.f(&s[0])`,
83 fail: false,
86 // Passing the address of a slice with a Go pointer.
87 name: "slice-ptr-1",
88 c: `void f(void **p) {}`,
89 imports: []string{"unsafe"},
90 body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f(&s[0])`,
91 fail: true,
94 // Passing the address of a slice with a Go pointer,
95 // where we are passing the address of an element that
96 // is not a Go pointer.
97 name: "slice-ptr-2",
98 c: `void f(void **p) {}`,
99 imports: []string{"unsafe"},
100 body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f(&s[0])`,
101 fail: true,
104 // Passing the address of a slice that is an element
105 // in a struct only looks at the slice.
106 name: "slice-ok-2",
107 c: `void f(void **p) {}`,
108 imports: []string{"unsafe"},
109 support: `type S struct { p *int; s []unsafe.Pointer }`,
110 body: `i := 0; p := &S{p:&i, s:[]unsafe.Pointer{nil}}; C.f(&p.s[0])`,
111 fail: false,
114 // Passing the address of a slice of an array that is
115 // an element in a struct, with a type conversion.
116 name: "slice-ok-3",
117 c: `void f(void* p) {}`,
118 imports: []string{"unsafe"},
119 support: `type S struct { p *int; a [4]byte }`,
120 body: `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`,
121 fail: false,
124 // Passing the address of a slice of an array that is
125 // an element in a struct, with a type conversion.
126 name: "slice-ok-4",
127 c: `typedef void* PV; void f(PV p) {}`,
128 imports: []string{"unsafe"},
129 support: `type S struct { p *int; a [4]byte }`,
130 body: `i := 0; p := &S{p:&i}; C.f(C.PV(unsafe.Pointer(&p.a[0])))`,
131 fail: false,
134 // Passing the address of a static variable with no
135 // pointers doesn't matter.
136 name: "varok",
137 c: `void f(char** parg) {}`,
138 support: `var hello = [...]C.char{'h', 'e', 'l', 'l', 'o'}`,
139 body: `parg := [1]*C.char{&hello[0]}; C.f(&parg[0])`,
140 fail: false,
143 // Passing the address of a static variable with
144 // pointers does matter.
145 name: "var",
146 c: `void f(char*** parg) {}`,
147 support: `var hello = [...]*C.char{new(C.char)}`,
148 body: `parg := [1]**C.char{&hello[0]}; C.f(&parg[0])`,
149 fail: true,
152 // Storing a Go pointer into C memory should fail.
153 name: "barrier",
154 c: `#include <stdlib.h>
155 char **f1() { return malloc(sizeof(char*)); }
156 void f2(char **p) {}`,
157 body: `p := C.f1(); *p = new(C.char); C.f2(p)`,
158 fail: true,
159 expensive: true,
162 // Storing a Go pointer into C memory by assigning a
163 // large value should fail.
164 name: "barrier-struct",
165 c: `#include <stdlib.h>
166 struct s { char *a[10]; };
167 struct s *f1() { return malloc(sizeof(struct s)); }
168 void f2(struct s *p) {}`,
169 body: `p := C.f1(); p.a = [10]*C.char{new(C.char)}; C.f2(p)`,
170 fail: true,
171 expensive: true,
174 // Storing a Go pointer into C memory using a slice
175 // copy should fail.
176 name: "barrier-slice",
177 c: `#include <stdlib.h>
178 struct s { char *a[10]; };
179 struct s *f1() { return malloc(sizeof(struct s)); }
180 void f2(struct s *p) {}`,
181 body: `p := C.f1(); copy(p.a[:], []*C.char{new(C.char)}); C.f2(p)`,
182 fail: true,
183 expensive: true,
186 // A very large value uses a GC program, which is a
187 // different code path.
188 name: "barrier-gcprog-array",
189 c: `#include <stdlib.h>
190 struct s { char *a[32769]; };
191 struct s *f1() { return malloc(sizeof(struct s)); }
192 void f2(struct s *p) {}`,
193 body: `p := C.f1(); p.a = [32769]*C.char{new(C.char)}; C.f2(p)`,
194 fail: true,
195 expensive: true,
198 // Similar case, with a source on the heap.
199 name: "barrier-gcprog-array-heap",
200 c: `#include <stdlib.h>
201 struct s { char *a[32769]; };
202 struct s *f1() { return malloc(sizeof(struct s)); }
203 void f2(struct s *p) {}
204 void f3(void *p) {}`,
205 imports: []string{"unsafe"},
206 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))`,
207 fail: true,
208 expensive: true,
211 // A GC program with a struct.
212 name: "barrier-gcprog-struct",
213 c: `#include <stdlib.h>
214 struct s { char *a[32769]; };
215 struct s2 { struct s f; };
216 struct s2 *f1() { return malloc(sizeof(struct s2)); }
217 void f2(struct s2 *p) {}`,
218 body: `p := C.f1(); p.f = C.struct_s{[32769]*C.char{new(C.char)}}; C.f2(p)`,
219 fail: true,
220 expensive: true,
223 // Similar case, with a source on the heap.
224 name: "barrier-gcprog-struct-heap",
225 c: `#include <stdlib.h>
226 struct s { char *a[32769]; };
227 struct s2 { struct s f; };
228 struct s2 *f1() { return malloc(sizeof(struct s2)); }
229 void f2(struct s2 *p) {}
230 void f3(void *p) {}`,
231 imports: []string{"unsafe"},
232 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))`,
233 fail: true,
234 expensive: true,
237 // Exported functions may not return Go pointers.
238 name: "export1",
239 c: `extern unsigned char *GoFn();`,
240 support: `//export GoFn
241 func GoFn() *byte { return new(byte) }`,
242 body: `C.GoFn()`,
243 fail: true,
246 // Returning a C pointer is fine.
247 name: "exportok",
248 c: `#include <stdlib.h>
249 extern unsigned char *GoFn();`,
250 support: `//export GoFn
251 func GoFn() *byte { return (*byte)(C.malloc(1)) }`,
252 body: `C.GoFn()`,
255 // Passing a Go string is fine.
256 name: "pass-string",
257 c: `#include <stddef.h>
258 typedef struct { const char *p; ptrdiff_t n; } gostring;
259 gostring f(gostring s) { return s; }`,
260 imports: []string{"unsafe"},
261 body: `s := "a"; r := C.f(*(*C.gostring)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`,
264 // Passing a slice of Go strings fails.
265 name: "pass-string-slice",
266 c: `void f(void *p) {}`,
267 imports: []string{"strings", "unsafe"},
268 support: `type S struct { a [1]string }`,
269 body: `s := S{a:[1]string{strings.Repeat("a", 2)}}; C.f(unsafe.Pointer(&s.a[0]))`,
270 fail: true,
273 // Exported functions may not return strings.
274 name: "ret-string",
275 c: `extern void f();`,
276 imports: []string{"strings"},
277 support: `//export GoStr
278 func GoStr() string { return strings.Repeat("a", 2) }`,
279 body: `C.f()`,
280 extra: []extra{
282 "call.c",
283 `#include <stddef.h>
284 typedef struct { const char *p; ptrdiff_t n; } gostring;
285 extern gostring GoStr();
286 void f() { GoStr(); }`,
289 fail: true,
292 // Don't check non-pointer data.
293 // Uses unsafe code to get a pointer we shouldn't check.
294 // Although we use unsafe, the uintptr represents an integer
295 // that happens to have the same representation as a pointer;
296 // that is, we are testing something that is not unsafe.
297 name: "ptrdata1",
298 c: `#include <stdlib.h>
299 void f(void* p) {}`,
300 imports: []string{"unsafe"},
301 support: `type S struct { p *int; a [8*8]byte; u uintptr }`,
302 body: `i := 0; p := &S{u:uintptr(unsafe.Pointer(&i))}; q := (*S)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f(unsafe.Pointer(q))`,
303 fail: false,
306 // Like ptrdata1, but with a type that uses a GC program.
307 name: "ptrdata2",
308 c: `#include <stdlib.h>
309 void f(void* p) {}`,
310 imports: []string{"unsafe"},
311 support: `type S struct { p *int; a [32769*8]byte; q *int; u uintptr }`,
312 body: `i := 0; p := S{u:uintptr(unsafe.Pointer(&i))}; q := (*S)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f(unsafe.Pointer(q))`,
313 fail: false,
316 // Check deferred pointers when they are used, not
317 // when the defer statement is run.
318 name: "defer",
319 c: `typedef struct s { int *p; } s; void f(s *ps) {}`,
320 body: `p := &C.s{}; defer C.f(p); p.p = new(C.int)`,
321 fail: true,
324 // Check a pointer to a union if the union has any
325 // pointer fields.
326 name: "union1",
327 c: `typedef union { char **p; unsigned long i; } u; void f(u *pu) {}`,
328 imports: []string{"unsafe"},
329 body: `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`,
330 fail: true,
333 // Don't check a pointer to a union if the union does
334 // not have any pointer fields.
335 // Like ptrdata1 above, the uintptr represents an
336 // integer that happens to have the same
337 // representation as a pointer.
338 name: "union2",
339 c: `typedef union { unsigned long i; } u; void f(u *pu) {}`,
340 imports: []string{"unsafe"},
341 body: `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`,
342 fail: false,
345 // Test preemption while entering a cgo call. Issue #21306.
346 name: "preempt-during-call",
347 c: `void f() {}`,
348 imports: []string{"runtime", "sync"},
349 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.f(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`,
350 fail: false,
353 // Test poller deadline with cgocheck=2. Issue #23435.
354 name: "deadline",
355 c: `#define US 10`,
356 imports: []string{"os", "time"},
357 body: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US * time.Microsecond))`,
358 fail: false,
362 func TestPointerChecks(t *testing.T) {
363 for _, pt := range ptrTests {
364 pt := pt
365 t.Run(pt.name, func(t *testing.T) {
366 testOne(t, pt)
371 func testOne(t *testing.T, pt ptrTest) {
372 t.Parallel()
374 gopath, err := ioutil.TempDir("", filepath.Base(t.Name()))
375 if err != nil {
376 t.Fatal(err)
378 defer os.RemoveAll(gopath)
380 src := filepath.Join(gopath, "src")
381 if err := os.Mkdir(src, 0777); err != nil {
382 t.Fatal(err)
385 name := filepath.Join(src, fmt.Sprintf("%s.go", filepath.Base(t.Name())))
386 f, err := os.Create(name)
387 if err != nil {
388 t.Fatal(err)
391 b := bufio.NewWriter(f)
392 fmt.Fprintln(b, `package main`)
393 fmt.Fprintln(b)
394 fmt.Fprintln(b, `/*`)
395 fmt.Fprintln(b, pt.c)
396 fmt.Fprintln(b, `*/`)
397 fmt.Fprintln(b, `import "C"`)
398 fmt.Fprintln(b)
399 for _, imp := range pt.imports {
400 fmt.Fprintln(b, `import "`+imp+`"`)
402 if len(pt.imports) > 0 {
403 fmt.Fprintln(b)
405 if len(pt.support) > 0 {
406 fmt.Fprintln(b, pt.support)
407 fmt.Fprintln(b)
409 fmt.Fprintln(b, `func main() {`)
410 fmt.Fprintln(b, pt.body)
411 fmt.Fprintln(b, `}`)
413 if err := b.Flush(); err != nil {
414 t.Fatalf("flushing %s: %v", name, err)
416 if err := f.Close(); err != nil {
417 t.Fatalf("closing %s: %v", name, err)
420 for _, e := range pt.extra {
421 if err := ioutil.WriteFile(filepath.Join(src, e.name), []byte(e.contents), 0644); err != nil {
422 t.Fatalf("writing %s: %v", e.name, err)
426 args := func(cmd *exec.Cmd) string {
427 return strings.Join(cmd.Args, " ")
430 cmd := exec.Command("go", "build")
431 cmd.Dir = src
432 cmd.Env = addEnv("GOPATH", gopath)
433 buf, err := cmd.CombinedOutput()
434 if err != nil {
435 t.Logf("%#q:\n%s", args(cmd), buf)
436 t.Fatalf("failed to build: %v", err)
439 exe := filepath.Join(src, filepath.Base(src))
440 cmd = exec.Command(exe)
441 cmd.Dir = src
443 if pt.expensive {
444 cmd.Env = cgocheckEnv("1")
445 buf, err := cmd.CombinedOutput()
446 if err != nil {
447 t.Logf("%#q:\n%s", args(cmd), buf)
448 if pt.fail {
449 t.Fatalf("test marked expensive, but failed when not expensive: %v", err)
450 } else {
451 t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err)
455 cmd = exec.Command(exe)
456 cmd.Dir = src
459 if pt.expensive {
460 cmd.Env = cgocheckEnv("2")
463 buf, err = cmd.CombinedOutput()
464 if pt.fail {
465 if err == nil {
466 t.Logf("%#q:\n%s", args(cmd), buf)
467 t.Fatalf("did not fail as expected")
468 } else if !bytes.Contains(buf, []byte("Go pointer")) {
469 t.Logf("%#q:\n%s", args(cmd), buf)
470 t.Fatalf("did not print expected error (failed with %v)", err)
472 } else {
473 if err != nil {
474 t.Logf("%#q:\n%s", args(cmd), buf)
475 t.Fatalf("failed unexpectedly: %v", err)
478 if !pt.expensive {
479 // Make sure it passes with the expensive checks.
480 cmd := exec.Command(exe)
481 cmd.Dir = src
482 cmd.Env = cgocheckEnv("2")
483 buf, err := cmd.CombinedOutput()
484 if err != nil {
485 t.Logf("%#q:\n%s", args(cmd), buf)
486 t.Fatalf("failed unexpectedly with expensive checks: %v", err)
491 if pt.fail {
492 cmd = exec.Command(exe)
493 cmd.Dir = src
494 cmd.Env = cgocheckEnv("0")
495 buf, err := cmd.CombinedOutput()
496 if err != nil {
497 t.Logf("%#q:\n%s", args(cmd), buf)
498 t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err)
503 func cgocheckEnv(val string) []string {
504 return addEnv("GODEBUG", "cgocheck="+val)
507 func addEnv(key, val string) []string {
508 env := []string{key + "=" + val}
509 look := key + "="
510 for _, e := range os.Environ() {
511 if !strings.HasPrefix(e, look) {
512 env = append(env, e)
515 return env