github.com/euank/go@v0.0.0-20160829210321-495514729181/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 extra []extra // extra files 31 fail bool // whether the test should fail 32 expensive bool // whether the test requires the expensive check 33 } 34 35 type extra struct { 36 name string 37 contents string 38 } 39 40 var ptrTests = []ptrTest{ 41 { 42 // Passing a pointer to a struct that contains a Go pointer. 43 name: "ptr1", 44 c: `typedef struct s { int *p; } s; void f(s *ps) {}`, 45 body: `C.f(&C.s{new(C.int)})`, 46 fail: true, 47 }, 48 { 49 // Passing a pointer to a struct that contains a Go pointer. 50 name: "ptr2", 51 c: `typedef struct s { int *p; } s; void f(s *ps) {}`, 52 body: `p := &C.s{new(C.int)}; C.f(p)`, 53 fail: true, 54 }, 55 { 56 // Passing a pointer to an int field of a Go struct 57 // that (irrelevantly) contains a Go pointer. 58 name: "ok1", 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)`, 61 fail: false, 62 }, 63 { 64 // Passing a pointer to a pointer field of a Go struct. 65 name: "ptr-field", 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)`, 68 fail: true, 69 }, 70 { 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. 74 name: "ptr-field-ok", 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)`, 77 fail: false, 78 }, 79 { 80 // Passing the address of a slice with no Go pointers. 81 name: "slice-ok-1", 82 c: `void f(void **p) {}`, 83 imports: []string{"unsafe"}, 84 body: `s := []unsafe.Pointer{nil}; C.f(&s[0])`, 85 fail: false, 86 }, 87 { 88 // Passing the address of a slice with a Go pointer. 89 name: "slice-ptr-1", 90 c: `void f(void **p) {}`, 91 imports: []string{"unsafe"}, 92 body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f(&s[0])`, 93 fail: true, 94 }, 95 { 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. 99 name: "slice-ptr-2", 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])`, 103 fail: true, 104 }, 105 { 106 // Passing the address of a slice that is an element 107 // in a struct only looks at the slice. 108 name: "slice-ok-2", 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])`, 113 fail: false, 114 }, 115 { 116 // Passing the address of a slice of an array that is 117 // an element in a struct, with a type conversion. 118 name: "slice-ok-3", 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]))`, 123 fail: false, 124 }, 125 { 126 // Passing the address of a slice of an array that is 127 // an element in a struct, with a type conversion. 128 name: "slice-ok-4", 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])))`, 133 fail: false, 134 }, 135 { 136 // Passing the address of a static variable with no 137 // pointers doesn't matter. 138 name: "varok", 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])`, 142 fail: false, 143 }, 144 { 145 // Passing the address of a static variable with 146 // pointers does matter. 147 name: "var", 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])`, 151 fail: true, 152 }, 153 { 154 // Storing a Go pointer into C memory should fail. 155 name: "barrier", 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)`, 160 fail: true, 161 expensive: true, 162 }, 163 { 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)`, 172 fail: true, 173 expensive: true, 174 }, 175 { 176 // Storing a Go pointer into C memory using a slice 177 // copy should fail. 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)`, 184 fail: true, 185 expensive: true, 186 }, 187 { 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)`, 196 fail: true, 197 expensive: true, 198 }, 199 { 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))`, 209 fail: true, 210 expensive: true, 211 }, 212 { 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)`, 221 fail: true, 222 expensive: true, 223 }, 224 { 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))`, 235 fail: true, 236 expensive: true, 237 }, 238 { 239 // Exported functions may not return Go pointers. 240 name: "export1", 241 c: `extern unsigned char *GoFn();`, 242 support: `//export GoFn 243 func GoFn() *byte { return new(byte) }`, 244 body: `C.GoFn()`, 245 fail: true, 246 }, 247 { 248 // Returning a C pointer is fine. 249 name: "exportok", 250 c: `#include <stdlib.h> 251 extern unsigned char *GoFn();`, 252 support: `//export GoFn 253 func GoFn() *byte { return (*byte)(C.malloc(1)) }`, 254 body: `C.GoFn()`, 255 }, 256 { 257 // Passing a Go string is fine. 258 name: "pass-string", 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) }`, 264 }, 265 { 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]))`, 272 fail: true, 273 }, 274 { 275 // Exported functions may not return strings. 276 name: "ret-string", 277 c: `extern void f();`, 278 imports: []string{"strings"}, 279 support: `//export GoStr 280 func GoStr() string { return strings.Repeat("a", 2) }`, 281 body: `C.f()`, 282 extra: []extra{ 283 { 284 "call.c", 285 `#include <stddef.h> 286 typedef struct { const char *p; ptrdiff_t n; } gostring; 287 extern gostring GoStr(); 288 void f() { GoStr(); }`, 289 }, 290 }, 291 fail: true, 292 }, 293 { 294 // Don't check non-pointer data. 295 // Uses unsafe code to get a pointer we shouldn't check. 296 // Although we use unsafe, the uintptr represents an integer 297 // that happens to have the same representation as a pointer; 298 // that is, we are testing something that is not unsafe. 299 name: "ptrdata1", 300 c: `#include <stdlib.h> 301 void f(void* p) {}`, 302 imports: []string{"unsafe"}, 303 support: `type S struct { p *int; a [8*8]byte; u uintptr }`, 304 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))`, 305 fail: false, 306 }, 307 { 308 // Like ptrdata1, but with a type that uses a GC program. 309 name: "ptrdata2", 310 c: `#include <stdlib.h> 311 void f(void* p) {}`, 312 imports: []string{"unsafe"}, 313 support: `type S struct { p *int; a [32769*8]byte; q *int; u uintptr }`, 314 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))`, 315 fail: false, 316 }, 317 { 318 // Check deferred pointers when they are used, not 319 // when the defer statement is run. 320 name: "defer", 321 c: `typedef struct s { int *p; } s; void f(s *ps) {}`, 322 body: `p := &C.s{}; defer C.f(p); p.p = new(C.int)`, 323 fail: true, 324 }, 325 } 326 327 func main() { 328 os.Exit(doTests()) 329 } 330 331 func doTests() int { 332 gopath, err := ioutil.TempDir("", "cgoerrors") 333 if err != nil { 334 fmt.Fprintln(os.Stderr, err) 335 return 2 336 } 337 defer os.RemoveAll(gopath) 338 339 if err := os.MkdirAll(filepath.Join(gopath, "src"), 0777); err != nil { 340 fmt.Fprintln(os.Stderr, err) 341 return 2 342 } 343 344 workers := runtime.NumCPU() + 1 345 346 var wg sync.WaitGroup 347 c := make(chan int) 348 errs := make(chan int) 349 for i := 0; i < workers; i++ { 350 wg.Add(1) 351 go func() { 352 worker(gopath, c, errs) 353 wg.Done() 354 }() 355 } 356 357 for i := range ptrTests { 358 c <- i 359 } 360 close(c) 361 362 go func() { 363 wg.Wait() 364 close(errs) 365 }() 366 367 tot := 0 368 for e := range errs { 369 tot += e 370 } 371 return tot 372 } 373 374 func worker(gopath string, c, errs chan int) { 375 e := 0 376 for i := range c { 377 if !doOne(gopath, i) { 378 e++ 379 } 380 } 381 if e > 0 { 382 errs <- e 383 } 384 } 385 386 func doOne(gopath string, i int) bool { 387 t := &ptrTests[i] 388 389 dir := filepath.Join(gopath, "src", fmt.Sprintf("dir%d", i)) 390 if err := os.Mkdir(dir, 0777); err != nil { 391 fmt.Fprintln(os.Stderr, err) 392 return false 393 } 394 395 name := filepath.Join(dir, fmt.Sprintf("t%d.go", i)) 396 f, err := os.Create(name) 397 if err != nil { 398 fmt.Fprintln(os.Stderr, err) 399 return false 400 } 401 402 b := bufio.NewWriter(f) 403 fmt.Fprintln(b, `package main`) 404 fmt.Fprintln(b) 405 fmt.Fprintln(b, `/*`) 406 fmt.Fprintln(b, t.c) 407 fmt.Fprintln(b, `*/`) 408 fmt.Fprintln(b, `import "C"`) 409 fmt.Fprintln(b) 410 for _, imp := range t.imports { 411 fmt.Fprintln(b, `import "`+imp+`"`) 412 } 413 if len(t.imports) > 0 { 414 fmt.Fprintln(b) 415 } 416 if len(t.support) > 0 { 417 fmt.Fprintln(b, t.support) 418 fmt.Fprintln(b) 419 } 420 fmt.Fprintln(b, `func main() {`) 421 fmt.Fprintln(b, t.body) 422 fmt.Fprintln(b, `}`) 423 424 if err := b.Flush(); err != nil { 425 fmt.Fprintf(os.Stderr, "flushing %s: %v\n", name, err) 426 return false 427 } 428 if err := f.Close(); err != nil { 429 fmt.Fprintf(os.Stderr, "closing %s: %v\n", name, err) 430 return false 431 } 432 433 for _, e := range t.extra { 434 if err := ioutil.WriteFile(filepath.Join(dir, e.name), []byte(e.contents), 0644); err != nil { 435 fmt.Fprintf(os.Stderr, "writing %s: %v\n", e.name, err) 436 return false 437 } 438 } 439 440 ok := true 441 442 cmd := exec.Command("go", "build") 443 cmd.Dir = dir 444 cmd.Env = addEnv("GOPATH", gopath) 445 buf, err := cmd.CombinedOutput() 446 if err != nil { 447 fmt.Fprintf(os.Stderr, "test %s failed to build: %v\n%s", t.name, err, buf) 448 return false 449 } 450 451 exe := filepath.Join(dir, filepath.Base(dir)) 452 cmd = exec.Command(exe) 453 cmd.Dir = dir 454 455 if t.expensive { 456 cmd.Env = cgocheckEnv("1") 457 buf, err := cmd.CombinedOutput() 458 if err != nil { 459 var errbuf bytes.Buffer 460 if t.fail { 461 fmt.Fprintf(&errbuf, "test %s marked expensive but failed when not expensive: %v\n", t.name, err) 462 } else { 463 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=1: %v\n", t.name, err) 464 } 465 reportTestOutput(&errbuf, t.name, buf) 466 os.Stderr.Write(errbuf.Bytes()) 467 ok = false 468 } 469 470 cmd = exec.Command(exe) 471 cmd.Dir = dir 472 } 473 474 if t.expensive { 475 cmd.Env = cgocheckEnv("2") 476 } 477 478 buf, err = cmd.CombinedOutput() 479 480 if t.fail { 481 if err == nil { 482 var errbuf bytes.Buffer 483 fmt.Fprintf(&errbuf, "test %s did not fail as expected\n", t.name) 484 reportTestOutput(&errbuf, t.name, buf) 485 os.Stderr.Write(errbuf.Bytes()) 486 ok = false 487 } else if !bytes.Contains(buf, []byte("Go pointer")) { 488 var errbuf bytes.Buffer 489 fmt.Fprintf(&errbuf, "test %s output does not contain expected error (failed with %v)\n", t.name, err) 490 reportTestOutput(&errbuf, t.name, buf) 491 os.Stderr.Write(errbuf.Bytes()) 492 ok = false 493 } 494 } else { 495 if err != nil { 496 var errbuf bytes.Buffer 497 fmt.Fprintf(&errbuf, "test %s failed unexpectedly: %v\n", t.name, err) 498 reportTestOutput(&errbuf, t.name, buf) 499 os.Stderr.Write(errbuf.Bytes()) 500 ok = false 501 } 502 503 if !t.expensive && ok { 504 // Make sure it passes with the expensive checks. 505 cmd := exec.Command(exe) 506 cmd.Dir = dir 507 cmd.Env = cgocheckEnv("2") 508 buf, err := cmd.CombinedOutput() 509 if err != nil { 510 var errbuf bytes.Buffer 511 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with expensive checks: %v\n", t.name, err) 512 reportTestOutput(&errbuf, t.name, buf) 513 os.Stderr.Write(errbuf.Bytes()) 514 ok = false 515 } 516 } 517 } 518 519 if t.fail && ok { 520 cmd = exec.Command(exe) 521 cmd.Dir = dir 522 cmd.Env = cgocheckEnv("0") 523 buf, err := cmd.CombinedOutput() 524 if err != nil { 525 var errbuf bytes.Buffer 526 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=0: %v\n", t.name, err) 527 reportTestOutput(&errbuf, t.name, buf) 528 os.Stderr.Write(errbuf.Bytes()) 529 ok = false 530 } 531 } 532 533 return ok 534 } 535 536 func reportTestOutput(w io.Writer, name string, buf []byte) { 537 fmt.Fprintf(w, "=== test %s output ===\n", name) 538 fmt.Fprintf(w, "%s", buf) 539 fmt.Fprintf(w, "=== end of test %s output ===\n", name) 540 } 541 542 func cgocheckEnv(val string) []string { 543 return addEnv("GODEBUG", "cgocheck="+val) 544 } 545 546 func addEnv(key, val string) []string { 547 env := []string{key + "=" + val} 548 look := key + "=" 549 for _, e := range os.Environ() { 550 if !strings.HasPrefix(e, look) { 551 env = append(env, e) 552 } 553 } 554 return env 555 }