github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/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 // Check a pointer to a union if the union has any 327 // pointer fields. 328 name: "union1", 329 c: `typedef union { char **p; unsigned long i; } u; void f(u *pu) {}`, 330 imports: []string{"unsafe"}, 331 body: `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`, 332 fail: true, 333 }, 334 { 335 // Don't check a pointer to a union if the union does 336 // not have any pointer fields. 337 // Like ptrdata1 above, the uintptr represents an 338 // integer that happens to have the same 339 // representation as a pointer. 340 name: "union2", 341 c: `typedef union { unsigned long i; } u; void f(u *pu) {}`, 342 imports: []string{"unsafe"}, 343 body: `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`, 344 fail: false, 345 }, 346 } 347 348 func main() { 349 os.Exit(doTests()) 350 } 351 352 func doTests() int { 353 gopath, err := ioutil.TempDir("", "cgoerrors") 354 if err != nil { 355 fmt.Fprintln(os.Stderr, err) 356 return 2 357 } 358 defer os.RemoveAll(gopath) 359 360 if err := os.MkdirAll(filepath.Join(gopath, "src"), 0777); err != nil { 361 fmt.Fprintln(os.Stderr, err) 362 return 2 363 } 364 365 workers := runtime.NumCPU() + 1 366 367 var wg sync.WaitGroup 368 c := make(chan int) 369 errs := make(chan int) 370 for i := 0; i < workers; i++ { 371 wg.Add(1) 372 go func() { 373 worker(gopath, c, errs) 374 wg.Done() 375 }() 376 } 377 378 for i := range ptrTests { 379 c <- i 380 } 381 close(c) 382 383 go func() { 384 wg.Wait() 385 close(errs) 386 }() 387 388 tot := 0 389 for e := range errs { 390 tot += e 391 } 392 return tot 393 } 394 395 func worker(gopath string, c, errs chan int) { 396 e := 0 397 for i := range c { 398 if !doOne(gopath, i) { 399 e++ 400 } 401 } 402 if e > 0 { 403 errs <- e 404 } 405 } 406 407 func doOne(gopath string, i int) bool { 408 t := &ptrTests[i] 409 410 dir := filepath.Join(gopath, "src", fmt.Sprintf("dir%d", i)) 411 if err := os.Mkdir(dir, 0777); err != nil { 412 fmt.Fprintln(os.Stderr, err) 413 return false 414 } 415 416 name := filepath.Join(dir, fmt.Sprintf("t%d.go", i)) 417 f, err := os.Create(name) 418 if err != nil { 419 fmt.Fprintln(os.Stderr, err) 420 return false 421 } 422 423 b := bufio.NewWriter(f) 424 fmt.Fprintln(b, `package main`) 425 fmt.Fprintln(b) 426 fmt.Fprintln(b, `/*`) 427 fmt.Fprintln(b, t.c) 428 fmt.Fprintln(b, `*/`) 429 fmt.Fprintln(b, `import "C"`) 430 fmt.Fprintln(b) 431 for _, imp := range t.imports { 432 fmt.Fprintln(b, `import "`+imp+`"`) 433 } 434 if len(t.imports) > 0 { 435 fmt.Fprintln(b) 436 } 437 if len(t.support) > 0 { 438 fmt.Fprintln(b, t.support) 439 fmt.Fprintln(b) 440 } 441 fmt.Fprintln(b, `func main() {`) 442 fmt.Fprintln(b, t.body) 443 fmt.Fprintln(b, `}`) 444 445 if err := b.Flush(); err != nil { 446 fmt.Fprintf(os.Stderr, "flushing %s: %v\n", name, err) 447 return false 448 } 449 if err := f.Close(); err != nil { 450 fmt.Fprintf(os.Stderr, "closing %s: %v\n", name, err) 451 return false 452 } 453 454 for _, e := range t.extra { 455 if err := ioutil.WriteFile(filepath.Join(dir, e.name), []byte(e.contents), 0644); err != nil { 456 fmt.Fprintf(os.Stderr, "writing %s: %v\n", e.name, err) 457 return false 458 } 459 } 460 461 ok := true 462 463 cmd := exec.Command("go", "build") 464 cmd.Dir = dir 465 cmd.Env = addEnv("GOPATH", gopath) 466 buf, err := cmd.CombinedOutput() 467 if err != nil { 468 fmt.Fprintf(os.Stderr, "test %s failed to build: %v\n%s", t.name, err, buf) 469 return false 470 } 471 472 exe := filepath.Join(dir, filepath.Base(dir)) 473 cmd = exec.Command(exe) 474 cmd.Dir = dir 475 476 if t.expensive { 477 cmd.Env = cgocheckEnv("1") 478 buf, err := cmd.CombinedOutput() 479 if err != nil { 480 var errbuf bytes.Buffer 481 if t.fail { 482 fmt.Fprintf(&errbuf, "test %s marked expensive but failed when not expensive: %v\n", t.name, err) 483 } else { 484 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=1: %v\n", t.name, err) 485 } 486 reportTestOutput(&errbuf, t.name, buf) 487 os.Stderr.Write(errbuf.Bytes()) 488 ok = false 489 } 490 491 cmd = exec.Command(exe) 492 cmd.Dir = dir 493 } 494 495 if t.expensive { 496 cmd.Env = cgocheckEnv("2") 497 } 498 499 buf, err = cmd.CombinedOutput() 500 501 if t.fail { 502 if err == nil { 503 var errbuf bytes.Buffer 504 fmt.Fprintf(&errbuf, "test %s did not fail as expected\n", t.name) 505 reportTestOutput(&errbuf, t.name, buf) 506 os.Stderr.Write(errbuf.Bytes()) 507 ok = false 508 } else if !bytes.Contains(buf, []byte("Go pointer")) { 509 var errbuf bytes.Buffer 510 fmt.Fprintf(&errbuf, "test %s output does not contain expected error (failed with %v)\n", t.name, err) 511 reportTestOutput(&errbuf, t.name, buf) 512 os.Stderr.Write(errbuf.Bytes()) 513 ok = false 514 } 515 } else { 516 if err != nil { 517 var errbuf bytes.Buffer 518 fmt.Fprintf(&errbuf, "test %s failed unexpectedly: %v\n", t.name, err) 519 reportTestOutput(&errbuf, t.name, buf) 520 os.Stderr.Write(errbuf.Bytes()) 521 ok = false 522 } 523 524 if !t.expensive && ok { 525 // Make sure it passes with the expensive checks. 526 cmd := exec.Command(exe) 527 cmd.Dir = dir 528 cmd.Env = cgocheckEnv("2") 529 buf, err := cmd.CombinedOutput() 530 if err != nil { 531 var errbuf bytes.Buffer 532 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with expensive checks: %v\n", t.name, err) 533 reportTestOutput(&errbuf, t.name, buf) 534 os.Stderr.Write(errbuf.Bytes()) 535 ok = false 536 } 537 } 538 } 539 540 if t.fail && ok { 541 cmd = exec.Command(exe) 542 cmd.Dir = dir 543 cmd.Env = cgocheckEnv("0") 544 buf, err := cmd.CombinedOutput() 545 if err != nil { 546 var errbuf bytes.Buffer 547 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=0: %v\n", t.name, err) 548 reportTestOutput(&errbuf, t.name, buf) 549 os.Stderr.Write(errbuf.Bytes()) 550 ok = false 551 } 552 } 553 554 return ok 555 } 556 557 func reportTestOutput(w io.Writer, name string, buf []byte) { 558 fmt.Fprintf(w, "=== test %s output ===\n", name) 559 fmt.Fprintf(w, "%s", buf) 560 fmt.Fprintf(w, "=== end of test %s output ===\n", name) 561 } 562 563 func cgocheckEnv(val string) []string { 564 return addEnv("GODEBUG", "cgocheck="+val) 565 } 566 567 func addEnv(key, val string) []string { 568 env := []string{key + "=" + val} 569 look := key + "=" 570 for _, e := range os.Environ() { 571 if !strings.HasPrefix(e, look) { 572 env = append(env, e) 573 } 574 } 575 return env 576 }