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.
21 // ptrTest is the tests without the boilerplate.
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
38 var ptrTests
= []ptrTest
{
40 // Passing a pointer to a struct that contains a Go pointer.
42 c
: `typedef struct s { int *p; } s; void f(s *ps) {}`,
43 body
: `C.f(&C.s{new(C.int)})`,
47 // Passing a pointer to a struct that contains a Go pointer.
49 c
: `typedef struct s { int *p; } s; void f(s *ps) {}`,
50 body
: `p := &C.s{new(C.int)}; C.f(p)`,
54 // Passing a pointer to an int field of a Go struct
55 // that (irrelevantly) contains a Go pointer.
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)`,
62 // Passing a pointer to a pointer field of a Go struct.
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)`,
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.
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)`,
78 // Passing the address of a slice with no Go pointers.
80 c
: `void f(void **p) {}`,
81 imports
: []string{"unsafe"},
82 body
: `s := []unsafe.Pointer{nil}; C.f(&s[0])`,
86 // Passing the address of a slice with a Go pointer.
88 c
: `void f(void **p) {}`,
89 imports
: []string{"unsafe"},
90 body
: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f(&s[0])`,
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.
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])`,
104 // Passing the address of a slice that is an element
105 // in a struct only looks at the slice.
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])`,
114 // Passing the address of a slice of an array that is
115 // an element in a struct, with a type conversion.
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]))`,
124 // Passing the address of a slice of an array that is
125 // an element in a struct, with a type conversion.
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])))`,
134 // Passing the address of a static variable with no
135 // pointers doesn't matter.
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])`,
143 // Passing the address of a static variable with
144 // pointers does matter.
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])`,
152 // Storing a Go pointer into C memory should fail.
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)`,
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)`,
174 // Storing a Go pointer into C memory using a slice
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)`,
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)`,
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))`,
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)`,
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))`,
237 // Exported functions may not return Go pointers.
239 c
: `extern unsigned char *GoFn();`,
240 support
: `//export GoFn
241 func GoFn() *byte { return new(byte) }`,
246 // Returning a C pointer is fine.
248 c
: `#include <stdlib.h>
249 extern unsigned char *GoFn();`,
250 support
: `//export GoFn
251 func GoFn() *byte { return (*byte)(C.malloc(1)) }`,
255 // Passing a Go string is fine.
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]))`,
273 // Exported functions may not return strings.
275 c
: `extern void f();`,
276 imports
: []string{"strings"},
277 support
: `//export GoStr
278 func GoStr() string { return strings.Repeat("a", 2) }`,
284 typedef struct { const char *p; ptrdiff_t n; } gostring;
285 extern gostring GoStr();
286 void f() { GoStr(); }`,
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.
298 c
: `#include <stdlib.h>
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))`,
306 // Like ptrdata1, but with a type that uses a GC program.
308 c
: `#include <stdlib.h>
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))`,
316 // Check deferred pointers when they are used, not
317 // when the defer statement is run.
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)`,
324 // Check a pointer to a union if the union has any
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)))`,
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.
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)))`,
345 // Test preemption while entering a cgo call. Issue #21306.
346 name
: "preempt-during-call",
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()`,
353 // Test poller deadline with cgocheck=2. Issue #23435.
356 imports
: []string{"os", "time"},
357 body
: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US * time.Microsecond))`,
362 func TestPointerChecks(t
*testing
.T
) {
363 for _
, pt
:= range ptrTests
{
365 t
.Run(pt
.name
, func(t
*testing
.T
) {
371 func testOne(t
*testing
.T
, pt ptrTest
) {
374 gopath
, err
:= ioutil
.TempDir("", filepath
.Base(t
.Name()))
378 defer os
.RemoveAll(gopath
)
380 src
:= filepath
.Join(gopath
, "src")
381 if err
:= os
.Mkdir(src
, 0777); err
!= nil {
385 name
:= filepath
.Join(src
, fmt
.Sprintf("%s.go", filepath
.Base(t
.Name())))
386 f
, err
:= os
.Create(name
)
391 b
:= bufio
.NewWriter(f
)
392 fmt
.Fprintln(b
, `package main`)
394 fmt
.Fprintln(b
, `/*`)
395 fmt
.Fprintln(b
, pt
.c
)
396 fmt
.Fprintln(b
, `*/`)
397 fmt
.Fprintln(b
, `import "C"`)
399 for _
, imp
:= range pt
.imports
{
400 fmt
.Fprintln(b
, `import "`+imp
+`"`)
402 if len(pt
.imports
) > 0 {
405 if len(pt
.support
) > 0 {
406 fmt
.Fprintln(b
, pt
.support
)
409 fmt
.Fprintln(b
, `func main() {`)
410 fmt
.Fprintln(b
, pt
.body
)
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")
432 cmd
.Env
= addEnv("GOPATH", gopath
)
433 buf
, err
:= cmd
.CombinedOutput()
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
)
444 cmd
.Env
= cgocheckEnv("1")
445 buf
, err
:= cmd
.CombinedOutput()
447 t
.Logf("%#q:\n%s", args(cmd
), buf
)
449 t
.Fatalf("test marked expensive, but failed when not expensive: %v", err
)
451 t
.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err
)
455 cmd
= exec
.Command(exe
)
460 cmd
.Env
= cgocheckEnv("2")
463 buf
, err
= cmd
.CombinedOutput()
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
)
474 t
.Logf("%#q:\n%s", args(cmd
), buf
)
475 t
.Fatalf("failed unexpectedly: %v", err
)
479 // Make sure it passes with the expensive checks.
480 cmd
:= exec
.Command(exe
)
482 cmd
.Env
= cgocheckEnv("2")
483 buf
, err
:= cmd
.CombinedOutput()
485 t
.Logf("%#q:\n%s", args(cmd
), buf
)
486 t
.Fatalf("failed unexpectedly with expensive checks: %v", err
)
492 cmd
= exec
.Command(exe
)
494 cmd
.Env
= cgocheckEnv("0")
495 buf
, err
:= cmd
.CombinedOutput()
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
}
510 for _
, e
:= range os
.Environ() {
511 if !strings
.HasPrefix(e
, look
) {