github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/misc/cgo/errors/ptr.go (about) 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. 4 5 // Tests that cgo detects invalid pointer passing at runtime. 6 7 package main 8 9 import ( 10 "bufio" 11 "bytes" 12 "fmt" 13 "io" 14 "io/ioutil" 15 "os" 16 "os/exec" 17 "path/filepath" 18 "runtime" 19 "strings" 20 "sync" 21 ) 22 23 // ptrTest is the tests without the boilerplate. 24 type ptrTest struct { 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 32 } 33 34 var ptrTests = []ptrTest{ 35 { 36 // Passing a pointer to a struct that contains a Go pointer. 37 name: "ptr1", 38 c: `typedef struct s { int *p; } s; void f(s *ps) {}`, 39 body: `C.f(&C.s{new(C.int)})`, 40 fail: true, 41 }, 42 { 43 // Passing a pointer to a struct that contains a Go pointer. 44 name: "ptr2", 45 c: `typedef struct s { int *p; } s; void f(s *ps) {}`, 46 body: `p := &C.s{new(C.int)}; C.f(p)`, 47 fail: true, 48 }, 49 { 50 // Passing a pointer to an int field of a Go struct 51 // that (irrelevantly) contains a Go pointer. 52 name: "ok1", 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)`, 55 fail: false, 56 }, 57 { 58 // Passing a pointer to a pointer field of a Go struct. 59 name: "ptr-field", 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)`, 62 fail: true, 63 }, 64 { 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. 68 name: "ptr-field-ok", 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)`, 71 fail: false, 72 }, 73 { 74 // Passing the address of a slice with no Go pointers. 75 name: "slice-ok-1", 76 c: `void f(void **p) {}`, 77 imports: []string{"unsafe"}, 78 body: `s := []unsafe.Pointer{nil}; C.f(&s[0])`, 79 fail: false, 80 }, 81 { 82 // Passing the address of a slice with a Go pointer. 83 name: "slice-ptr-1", 84 c: `void f(void **p) {}`, 85 imports: []string{"unsafe"}, 86 body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f(&s[0])`, 87 fail: true, 88 }, 89 { 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. 93 name: "slice-ptr-2", 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])`, 97 fail: true, 98 }, 99 { 100 // Passing the address of a slice that is an element 101 // in a struct only looks at the slice. 102 name: "slice-ok-2", 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])`, 107 fail: false, 108 }, 109 { 110 // Passing the address of a static variable with no 111 // pointers doesn't matter. 112 name: "varok", 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])`, 116 fail: false, 117 }, 118 { 119 // Passing the address of a static variable with 120 // pointers does matter. 121 name: "var", 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])`, 125 fail: true, 126 }, 127 { 128 // Storing a Go pointer into C memory should fail. 129 name: "barrier", 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)`, 134 fail: true, 135 expensive: true, 136 }, 137 { 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)`, 146 fail: true, 147 expensive: true, 148 }, 149 { 150 // Storing a Go pointer into C memory using a slice 151 // copy should fail. 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)`, 158 fail: true, 159 expensive: true, 160 }, 161 { 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)`, 170 fail: true, 171 expensive: true, 172 }, 173 { 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))`, 183 fail: true, 184 expensive: true, 185 }, 186 { 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)`, 195 fail: true, 196 expensive: true, 197 }, 198 { 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))`, 209 fail: true, 210 expensive: true, 211 }, 212 { 213 // Exported functions may not return Go pointers. 214 name: "export1", 215 c: `extern unsigned char *GoFn();`, 216 support: `//export GoFn 217 func GoFn() *byte { return new(byte) }`, 218 body: `C.GoFn()`, 219 fail: true, 220 }, 221 { 222 // Returning a C pointer is fine. 223 name: "exportok", 224 c: `#include <stdlib.h> 225 extern unsigned char *GoFn();`, 226 support: `//export GoFn 227 func GoFn() *byte { return (*byte)(C.malloc(1)) }`, 228 body: `C.GoFn()`, 229 }, 230 } 231 232 func main() { 233 os.Exit(doTests()) 234 } 235 236 func doTests() int { 237 dir, err := ioutil.TempDir("", "cgoerrors") 238 if err != nil { 239 fmt.Fprintln(os.Stderr, err) 240 return 2 241 } 242 defer os.RemoveAll(dir) 243 244 workers := runtime.NumCPU() + 1 245 246 var wg sync.WaitGroup 247 c := make(chan int) 248 errs := make(chan int) 249 for i := 0; i < workers; i++ { 250 wg.Add(1) 251 go func() { 252 worker(dir, c, errs) 253 wg.Done() 254 }() 255 } 256 257 for i := range ptrTests { 258 c <- i 259 } 260 close(c) 261 262 go func() { 263 wg.Wait() 264 close(errs) 265 }() 266 267 tot := 0 268 for e := range errs { 269 tot += e 270 } 271 return tot 272 } 273 274 func worker(dir string, c, errs chan int) { 275 e := 0 276 for i := range c { 277 if !doOne(dir, i) { 278 e++ 279 } 280 } 281 if e > 0 { 282 errs <- e 283 } 284 } 285 286 func doOne(dir string, i int) bool { 287 t := &ptrTests[i] 288 289 name := filepath.Join(dir, fmt.Sprintf("t%d.go", i)) 290 f, err := os.Create(name) 291 if err != nil { 292 fmt.Fprintln(os.Stderr, err) 293 return false 294 } 295 296 b := bufio.NewWriter(f) 297 fmt.Fprintln(b, `package main`) 298 fmt.Fprintln(b) 299 fmt.Fprintln(b, `/*`) 300 fmt.Fprintln(b, t.c) 301 fmt.Fprintln(b, `*/`) 302 fmt.Fprintln(b, `import "C"`) 303 fmt.Fprintln(b) 304 for _, imp := range t.imports { 305 fmt.Fprintln(b, `import "`+imp+`"`) 306 } 307 if len(t.imports) > 0 { 308 fmt.Fprintln(b) 309 } 310 if len(t.support) > 0 { 311 fmt.Fprintln(b, t.support) 312 fmt.Fprintln(b) 313 } 314 fmt.Fprintln(b, `func main() {`) 315 fmt.Fprintln(b, t.body) 316 fmt.Fprintln(b, `}`) 317 318 if err := b.Flush(); err != nil { 319 fmt.Fprintf(os.Stderr, "flushing %s: %v\n", name, err) 320 return false 321 } 322 if err := f.Close(); err != nil { 323 fmt.Fprintln(os.Stderr, "closing %s: %v\n", name, err) 324 return false 325 } 326 327 ok := true 328 329 cmd := exec.Command("go", "run", name) 330 cmd.Dir = dir 331 332 if t.expensive { 333 cmd.Env = cgocheckEnv("1") 334 buf, err := cmd.CombinedOutput() 335 if err != nil { 336 var errbuf bytes.Buffer 337 if t.fail { 338 fmt.Fprintf(&errbuf, "test %s marked expensive but failed when not expensive: %v\n", t.name, err) 339 } else { 340 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=1: %v\n", t.name, err) 341 } 342 reportTestOutput(&errbuf, t.name, buf) 343 os.Stderr.Write(errbuf.Bytes()) 344 ok = false 345 } 346 347 cmd = exec.Command("go", "run", name) 348 cmd.Dir = dir 349 } 350 351 if t.expensive { 352 cmd.Env = cgocheckEnv("2") 353 } 354 355 buf, err := cmd.CombinedOutput() 356 357 if t.fail { 358 if err == nil { 359 var errbuf bytes.Buffer 360 fmt.Fprintf(&errbuf, "test %s did not fail as expected\n", t.name) 361 reportTestOutput(&errbuf, t.name, buf) 362 os.Stderr.Write(errbuf.Bytes()) 363 ok = false 364 } else if !bytes.Contains(buf, []byte("Go pointer")) { 365 var errbuf bytes.Buffer 366 fmt.Fprintf(&errbuf, "test %s output does not contain expected error (failed with %v)\n", t.name, err) 367 reportTestOutput(&errbuf, t.name, buf) 368 os.Stderr.Write(errbuf.Bytes()) 369 ok = false 370 } 371 } else { 372 if err != nil { 373 var errbuf bytes.Buffer 374 fmt.Fprintf(&errbuf, "test %s failed unexpectedly: %v\n", t.name, err) 375 reportTestOutput(&errbuf, t.name, buf) 376 os.Stderr.Write(errbuf.Bytes()) 377 ok = false 378 } 379 380 if !t.expensive && ok { 381 // Make sure it passes with the expensive checks. 382 cmd := exec.Command("go", "run", name) 383 cmd.Dir = dir 384 cmd.Env = cgocheckEnv("2") 385 buf, err := cmd.CombinedOutput() 386 if err != nil { 387 var errbuf bytes.Buffer 388 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with expensive checks: %v\n", t.name, err) 389 reportTestOutput(&errbuf, t.name, buf) 390 os.Stderr.Write(errbuf.Bytes()) 391 ok = false 392 } 393 } 394 } 395 396 if t.fail && ok { 397 cmd = exec.Command("go", "run", name) 398 cmd.Dir = dir 399 cmd.Env = cgocheckEnv("0") 400 buf, err := cmd.CombinedOutput() 401 if err != nil { 402 var errbuf bytes.Buffer 403 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=0: %v\n", t.name, err) 404 reportTestOutput(&errbuf, t.name, buf) 405 os.Stderr.Write(errbuf.Bytes()) 406 ok = false 407 } 408 } 409 410 return ok 411 } 412 413 func reportTestOutput(w io.Writer, name string, buf []byte) { 414 fmt.Fprintf(w, "=== test %s output ===\n", name) 415 fmt.Fprintf(w, "%s", buf) 416 fmt.Fprintf(w, "=== end of test %s output ===\n", name) 417 } 418 419 func cgocheckEnv(val string) []string { 420 env := []string{"GODEBUG=cgocheck=" + val} 421 for _, e := range os.Environ() { 422 if !strings.HasPrefix(e, "GODEBUG=") { 423 env = append(env, e) 424 } 425 } 426 return env 427 }