github.com/guyezi/gofrontend@v0.0.0-20200228202240-7a62a49e62c0/libgo/misc/cgo/errors/ptr_test.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 errorstest 8 9 import ( 10 "bytes" 11 "flag" 12 "fmt" 13 "io/ioutil" 14 "os" 15 "os/exec" 16 "path/filepath" 17 "strings" 18 "sync/atomic" 19 "testing" 20 ) 21 22 var tmp = flag.String("tmp", "", "use `dir` for temporary files and do not clean up") 23 24 // ptrTest is the tests without the boilerplate. 25 type ptrTest struct { 26 name string // for reporting 27 c string // the cgo comment 28 c1 string // cgo comment forced into non-export cgo file 29 imports []string // a list of imports 30 support string // supporting functions 31 body string // the body of the main function 32 extra []extra // extra files 33 fail bool // whether the test should fail 34 expensive bool // whether the test requires the expensive check 35 } 36 37 type extra struct { 38 name string 39 contents string 40 } 41 42 var ptrTests = []ptrTest{ 43 { 44 // Passing a pointer to a struct that contains a Go pointer. 45 name: "ptr1", 46 c: `typedef struct s1 { int *p; } s1; void f1(s1 *ps) {}`, 47 body: `C.f1(&C.s1{new(C.int)})`, 48 fail: true, 49 }, 50 { 51 // Passing a pointer to a struct that contains a Go pointer. 52 name: "ptr2", 53 c: `typedef struct s2 { int *p; } s2; void f2(s2 *ps) {}`, 54 body: `p := &C.s2{new(C.int)}; C.f2(p)`, 55 fail: true, 56 }, 57 { 58 // Passing a pointer to an int field of a Go struct 59 // that (irrelevantly) contains a Go pointer. 60 name: "ok1", 61 c: `struct s3 { int i; int *p; }; void f3(int *p) {}`, 62 body: `p := &C.struct_s3{i: 0, p: new(C.int)}; C.f3(&p.i)`, 63 fail: false, 64 }, 65 { 66 // Passing a pointer to a pointer field of a Go struct. 67 name: "ptrfield", 68 c: `struct s4 { int i; int *p; }; void f4(int **p) {}`, 69 body: `p := &C.struct_s4{i: 0, p: new(C.int)}; C.f4(&p.p)`, 70 fail: true, 71 }, 72 { 73 // Passing a pointer to a pointer field of a Go 74 // struct, where the field does not contain a Go 75 // pointer, but another field (irrelevantly) does. 76 name: "ptrfieldok", 77 c: `struct s5 { int *p1; int *p2; }; void f5(int **p) {}`, 78 body: `p := &C.struct_s5{p1: nil, p2: new(C.int)}; C.f5(&p.p1)`, 79 fail: false, 80 }, 81 { 82 // Passing the address of a slice with no Go pointers. 83 name: "sliceok1", 84 c: `void f6(void **p) {}`, 85 imports: []string{"unsafe"}, 86 body: `s := []unsafe.Pointer{nil}; C.f6(&s[0])`, 87 fail: false, 88 }, 89 { 90 // Passing the address of a slice with a Go pointer. 91 name: "sliceptr1", 92 c: `void f7(void **p) {}`, 93 imports: []string{"unsafe"}, 94 body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f7(&s[0])`, 95 fail: true, 96 }, 97 { 98 // Passing the address of a slice with a Go pointer, 99 // where we are passing the address of an element that 100 // is not a Go pointer. 101 name: "sliceptr2", 102 c: `void f8(void **p) {}`, 103 imports: []string{"unsafe"}, 104 body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f8(&s[0])`, 105 fail: true, 106 }, 107 { 108 // Passing the address of a slice that is an element 109 // in a struct only looks at the slice. 110 name: "sliceok2", 111 c: `void f9(void **p) {}`, 112 imports: []string{"unsafe"}, 113 support: `type S9 struct { p *int; s []unsafe.Pointer }`, 114 body: `i := 0; p := &S9{p:&i, s:[]unsafe.Pointer{nil}}; C.f9(&p.s[0])`, 115 fail: false, 116 }, 117 { 118 // Passing the address of a slice of an array that is 119 // an element in a struct, with a type conversion. 120 name: "sliceok3", 121 c: `void f10(void* p) {}`, 122 imports: []string{"unsafe"}, 123 support: `type S10 struct { p *int; a [4]byte }`, 124 body: `i := 0; p := &S10{p:&i}; s := p.a[:]; C.f10(unsafe.Pointer(&s[0]))`, 125 fail: false, 126 }, 127 { 128 // Passing the address of a slice of an array that is 129 // an element in a struct, with a type conversion. 130 name: "sliceok4", 131 c: `typedef void* PV11; void f11(PV11 p) {}`, 132 imports: []string{"unsafe"}, 133 support: `type S11 struct { p *int; a [4]byte }`, 134 body: `i := 0; p := &S11{p:&i}; C.f11(C.PV11(unsafe.Pointer(&p.a[0])))`, 135 fail: false, 136 }, 137 { 138 // Passing the address of a static variable with no 139 // pointers doesn't matter. 140 name: "varok", 141 c: `void f12(char** parg) {}`, 142 support: `var hello12 = [...]C.char{'h', 'e', 'l', 'l', 'o'}`, 143 body: `parg := [1]*C.char{&hello12[0]}; C.f12(&parg[0])`, 144 fail: false, 145 }, 146 { 147 // Passing the address of a static variable with 148 // pointers does matter. 149 name: "var1", 150 c: `void f13(char*** parg) {}`, 151 support: `var hello13 = [...]*C.char{new(C.char)}`, 152 body: `parg := [1]**C.char{&hello13[0]}; C.f13(&parg[0])`, 153 fail: true, 154 }, 155 { 156 // Storing a Go pointer into C memory should fail. 157 name: "barrier", 158 c: `#include <stdlib.h> 159 char **f14a() { return malloc(sizeof(char*)); } 160 void f14b(char **p) {}`, 161 body: `p := C.f14a(); *p = new(C.char); C.f14b(p)`, 162 fail: true, 163 expensive: true, 164 }, 165 { 166 // Storing a Go pointer into C memory by assigning a 167 // large value should fail. 168 name: "barrierstruct", 169 c: `#include <stdlib.h> 170 struct s15 { char *a[10]; }; 171 struct s15 *f15() { return malloc(sizeof(struct s15)); } 172 void f15b(struct s15 *p) {}`, 173 body: `p := C.f15(); p.a = [10]*C.char{new(C.char)}; C.f15b(p)`, 174 fail: true, 175 expensive: true, 176 }, 177 { 178 // Storing a Go pointer into C memory using a slice 179 // copy should fail. 180 name: "barrierslice", 181 c: `#include <stdlib.h> 182 struct s16 { char *a[10]; }; 183 struct s16 *f16() { return malloc(sizeof(struct s16)); } 184 void f16b(struct s16 *p) {}`, 185 body: `p := C.f16(); copy(p.a[:], []*C.char{new(C.char)}); C.f16b(p)`, 186 fail: true, 187 expensive: true, 188 }, 189 { 190 // A very large value uses a GC program, which is a 191 // different code path. 192 name: "barriergcprogarray", 193 c: `#include <stdlib.h> 194 struct s17 { char *a[32769]; }; 195 struct s17 *f17() { return malloc(sizeof(struct s17)); } 196 void f17b(struct s17 *p) {}`, 197 body: `p := C.f17(); p.a = [32769]*C.char{new(C.char)}; C.f17b(p)`, 198 fail: true, 199 expensive: true, 200 }, 201 { 202 // Similar case, with a source on the heap. 203 name: "barriergcprogarrayheap", 204 c: `#include <stdlib.h> 205 struct s18 { char *a[32769]; }; 206 struct s18 *f18() { return malloc(sizeof(struct s18)); } 207 void f18b(struct s18 *p) {} 208 void f18c(void *p) {}`, 209 imports: []string{"unsafe"}, 210 body: `p := C.f18(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f18b(p); n[0] = nil; C.f18c(unsafe.Pointer(n))`, 211 fail: true, 212 expensive: true, 213 }, 214 { 215 // A GC program with a struct. 216 name: "barriergcprogstruct", 217 c: `#include <stdlib.h> 218 struct s19a { char *a[32769]; }; 219 struct s19b { struct s19a f; }; 220 struct s19b *f19() { return malloc(sizeof(struct s19b)); } 221 void f19b(struct s19b *p) {}`, 222 body: `p := C.f19(); p.f = C.struct_s19a{[32769]*C.char{new(C.char)}}; C.f19b(p)`, 223 fail: true, 224 expensive: true, 225 }, 226 { 227 // Similar case, with a source on the heap. 228 name: "barriergcprogstructheap", 229 c: `#include <stdlib.h> 230 struct s20a { char *a[32769]; }; 231 struct s20b { struct s20a f; }; 232 struct s20b *f20() { return malloc(sizeof(struct s20b)); } 233 void f20b(struct s20b *p) {} 234 void f20c(void *p) {}`, 235 imports: []string{"unsafe"}, 236 body: `p := C.f20(); n := &C.struct_s20a{[32769]*C.char{new(C.char)}}; p.f = *n; C.f20b(p); n.a[0] = nil; C.f20c(unsafe.Pointer(n))`, 237 fail: true, 238 expensive: true, 239 }, 240 { 241 // Exported functions may not return Go pointers. 242 name: "export1", 243 c: `extern unsigned char *GoFn21();`, 244 support: `//export GoFn21 245 func GoFn21() *byte { return new(byte) }`, 246 body: `C.GoFn21()`, 247 fail: true, 248 }, 249 { 250 // Returning a C pointer is fine. 251 name: "exportok", 252 c: `#include <stdlib.h> 253 extern unsigned char *GoFn22();`, 254 support: `//export GoFn22 255 func GoFn22() *byte { return (*byte)(C.malloc(1)) }`, 256 body: `C.GoFn22()`, 257 }, 258 { 259 // Passing a Go string is fine. 260 name: "passstring", 261 c: `#include <stddef.h> 262 typedef struct { const char *p; ptrdiff_t n; } gostring23; 263 gostring23 f23(gostring23 s) { return s; }`, 264 imports: []string{"unsafe"}, 265 body: `s := "a"; r := C.f23(*(*C.gostring23)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`, 266 }, 267 { 268 // Passing a slice of Go strings fails. 269 name: "passstringslice", 270 c: `void f24(void *p) {}`, 271 imports: []string{"strings", "unsafe"}, 272 support: `type S24 struct { a [1]string }`, 273 body: `s := S24{a:[1]string{strings.Repeat("a", 2)}}; C.f24(unsafe.Pointer(&s.a[0]))`, 274 fail: true, 275 }, 276 { 277 // Exported functions may not return strings. 278 name: "retstring", 279 c: `extern void f25();`, 280 imports: []string{"strings"}, 281 support: `//export GoStr25 282 func GoStr25() string { return strings.Repeat("a", 2) }`, 283 body: `C.f25()`, 284 c1: `#include <stddef.h> 285 typedef struct { const char *p; ptrdiff_t n; } gostring25; 286 extern gostring25 GoStr25(); 287 void f25() { GoStr25(); }`, 288 fail: true, 289 }, 290 { 291 // Don't check non-pointer data. 292 // Uses unsafe code to get a pointer we shouldn't check. 293 // Although we use unsafe, the uintptr represents an integer 294 // that happens to have the same representation as a pointer; 295 // that is, we are testing something that is not unsafe. 296 name: "ptrdata1", 297 c: `#include <stdlib.h> 298 void f26(void* p) {}`, 299 imports: []string{"unsafe"}, 300 support: `type S26 struct { p *int; a [8*8]byte; u uintptr }`, 301 body: `i := 0; p := &S26{u:uintptr(unsafe.Pointer(&i))}; q := (*S26)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f26(unsafe.Pointer(q))`, 302 fail: false, 303 }, 304 { 305 // Like ptrdata1, but with a type that uses a GC program. 306 name: "ptrdata2", 307 c: `#include <stdlib.h> 308 void f27(void* p) {}`, 309 imports: []string{"unsafe"}, 310 support: `type S27 struct { p *int; a [32769*8]byte; q *int; u uintptr }`, 311 body: `i := 0; p := S27{u:uintptr(unsafe.Pointer(&i))}; q := (*S27)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f27(unsafe.Pointer(q))`, 312 fail: false, 313 }, 314 { 315 // Check deferred pointers when they are used, not 316 // when the defer statement is run. 317 name: "defer1", 318 c: `typedef struct s28 { int *p; } s28; void f28(s28 *ps) {}`, 319 body: `p := &C.s28{}; defer C.f28(p); p.p = new(C.int)`, 320 fail: true, 321 }, 322 { 323 // Check a pointer to a union if the union has any 324 // pointer fields. 325 name: "union1", 326 c: `typedef union { char **p; unsigned long i; } u29; void f29(u29 *pu) {}`, 327 imports: []string{"unsafe"}, 328 body: `var b C.char; p := &b; C.f29((*C.u29)(unsafe.Pointer(&p)))`, 329 fail: true, 330 }, 331 { 332 // Don't check a pointer to a union if the union does 333 // not have any pointer fields. 334 // Like ptrdata1 above, the uintptr represents an 335 // integer that happens to have the same 336 // representation as a pointer. 337 name: "union2", 338 c: `typedef union { unsigned long i; } u39; void f39(u39 *pu) {}`, 339 imports: []string{"unsafe"}, 340 body: `var b C.char; p := &b; C.f39((*C.u39)(unsafe.Pointer(&p)))`, 341 fail: false, 342 }, 343 { 344 // Test preemption while entering a cgo call. Issue #21306. 345 name: "preemptduringcall", 346 c: `void f30() {}`, 347 imports: []string{"runtime", "sync"}, 348 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.f30(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`, 349 fail: false, 350 }, 351 { 352 // Test poller deadline with cgocheck=2. Issue #23435. 353 name: "deadline", 354 c: `#define US31 10`, 355 imports: []string{"os", "time"}, 356 body: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US31 * time.Microsecond))`, 357 fail: false, 358 }, 359 { 360 // Test for double evaluation of channel receive. 361 name: "chanrecv", 362 c: `void f32(char** p) {}`, 363 imports: []string{"time"}, 364 body: `c := make(chan []*C.char, 2); c <- make([]*C.char, 1); go func() { time.Sleep(10 * time.Second); panic("received twice from chan") }(); C.f32(&(<-c)[0]);`, 365 fail: false, 366 }, 367 { 368 // Test that converting the address of a struct field 369 // to unsafe.Pointer still just checks that field. 370 // Issue #25941. 371 name: "structfield", 372 c: `void f33(void* p) {}`, 373 imports: []string{"unsafe"}, 374 support: `type S33 struct { p *int; a [8]byte; u uintptr }`, 375 body: `s := &S33{p: new(int)}; C.f33(unsafe.Pointer(&s.a))`, 376 fail: false, 377 }, 378 { 379 // Test that converting multiple struct field 380 // addresses to unsafe.Pointer still just checks those 381 // fields. Issue #25941. 382 name: "structfield2", 383 c: `void f34(void* p, int r, void* s) {}`, 384 imports: []string{"unsafe"}, 385 support: `type S34 struct { a [8]byte; p *int; b int64; }`, 386 body: `s := &S34{p: new(int)}; C.f34(unsafe.Pointer(&s.a), 32, unsafe.Pointer(&s.b))`, 387 fail: false, 388 }, 389 { 390 // Test that second argument to cgoCheckPointer is 391 // evaluated when a deferred function is deferred, not 392 // when it is run. 393 name: "defer2", 394 c: `void f35(char **pc) {}`, 395 support: `type S35a struct { s []*C.char }; type S35b struct { ps *S35a }`, 396 body: `p := &S35b{&S35a{[]*C.char{nil}}}; defer C.f35(&p.ps.s[0]); p.ps = nil`, 397 fail: false, 398 }, 399 { 400 // Test that indexing into a function call still 401 // examines only the slice being indexed. 402 name: "buffer", 403 c: `void f36(void *p) {}`, 404 imports: []string{"bytes", "unsafe"}, 405 body: `var b bytes.Buffer; b.WriteString("a"); C.f36(unsafe.Pointer(&b.Bytes()[0]))`, 406 fail: false, 407 }, 408 { 409 // Test that bgsweep releasing a finalizer is OK. 410 name: "finalizer", 411 c: `// Nothing to declare.`, 412 imports: []string{"os"}, 413 support: `func open37() { os.Open(os.Args[0]) }; var G37 [][]byte`, 414 body: `for i := 0; i < 10000; i++ { G37 = append(G37, make([]byte, 4096)); if i % 100 == 0 { G37 = nil; open37() } }`, 415 fail: false, 416 }, 417 { 418 // Test that converting generated struct to interface is OK. 419 name: "structof", 420 c: `// Nothing to declare.`, 421 imports: []string{"reflect"}, 422 support: `type MyInt38 int; func (i MyInt38) Get() int { return int(i) }; type Getter38 interface { Get() int }`, 423 body: `t := reflect.StructOf([]reflect.StructField{{Name: "MyInt38", Type: reflect.TypeOf(MyInt38(0)), Anonymous: true}}); v := reflect.New(t).Elem(); v.Interface().(Getter38).Get()`, 424 fail: false, 425 }, 426 { 427 // Test that a converted address of a struct field results 428 // in a check for just that field and not the whole struct. 429 name: "structfieldcast", 430 c: `struct S40i { int i; int* p; }; void f40(struct S40i* p) {}`, 431 support: `type S40 struct { p *int; a C.struct_S40i }`, 432 body: `s := &S40{p: new(int)}; C.f40((*C.struct_S40i)(&s.a))`, 433 fail: false, 434 }, 435 } 436 437 func TestPointerChecks(t *testing.T) { 438 dir, exe := buildPtrTests(t) 439 440 // We (TestPointerChecks) return before the parallel subtest functions do, 441 // so we can't just defer os.RemoveAll(dir). Instead we have to wait for 442 // the parallel subtests to finish. This code looks racy but is not: 443 // the add +1 run in serial before testOne blocks. The -1 run in parallel 444 // after testOne finishes. 445 var pending int32 446 for _, pt := range ptrTests { 447 pt := pt 448 t.Run(pt.name, func(t *testing.T) { 449 atomic.AddInt32(&pending, +1) 450 defer func() { 451 if atomic.AddInt32(&pending, -1) == 0 { 452 os.RemoveAll(dir) 453 } 454 }() 455 testOne(t, pt, exe) 456 }) 457 } 458 } 459 460 func buildPtrTests(t *testing.T) (dir, exe string) { 461 var gopath string 462 if *tmp != "" { 463 gopath = *tmp 464 dir = "" 465 } else { 466 d, err := ioutil.TempDir("", filepath.Base(t.Name())) 467 if err != nil { 468 t.Fatal(err) 469 } 470 dir = d 471 gopath = d 472 } 473 474 src := filepath.Join(gopath, "src", "ptrtest") 475 if err := os.MkdirAll(src, 0777); err != nil { 476 t.Fatal(err) 477 } 478 if err := ioutil.WriteFile(filepath.Join(src, "go.mod"), []byte("module ptrtest"), 0666); err != nil { 479 t.Fatal(err) 480 } 481 482 // Prepare two cgo inputs: one for standard cgo and one for //export cgo. 483 // (The latter cannot have C definitions, only declarations.) 484 var cgo1, cgo2 bytes.Buffer 485 fmt.Fprintf(&cgo1, "package main\n\n/*\n") 486 fmt.Fprintf(&cgo2, "package main\n\n/*\n") 487 488 // C code 489 for _, pt := range ptrTests { 490 cgo := &cgo1 491 if strings.Contains(pt.support, "//export") { 492 cgo = &cgo2 493 } 494 fmt.Fprintf(cgo, "%s\n", pt.c) 495 fmt.Fprintf(&cgo1, "%s\n", pt.c1) 496 } 497 fmt.Fprintf(&cgo1, "*/\nimport \"C\"\n\n") 498 fmt.Fprintf(&cgo2, "*/\nimport \"C\"\n\n") 499 500 // Imports 501 did1 := make(map[string]bool) 502 did2 := make(map[string]bool) 503 did1["os"] = true // for ptrTestMain 504 fmt.Fprintf(&cgo1, "import \"os\"\n") 505 506 for _, pt := range ptrTests { 507 did := did1 508 cgo := &cgo1 509 if strings.Contains(pt.support, "//export") { 510 did = did2 511 cgo = &cgo2 512 } 513 for _, imp := range pt.imports { 514 if !did[imp] { 515 did[imp] = true 516 fmt.Fprintf(cgo, "import %q\n", imp) 517 } 518 } 519 } 520 521 // Func support and bodies. 522 for _, pt := range ptrTests { 523 cgo := &cgo1 524 if strings.Contains(pt.support, "//export") { 525 cgo = &cgo2 526 } 527 fmt.Fprintf(cgo, "%s\nfunc %s() {\n%s\n}\n", pt.support, pt.name, pt.body) 528 } 529 530 // Func list and main dispatch. 531 fmt.Fprintf(&cgo1, "var funcs = map[string]func() {\n") 532 for _, pt := range ptrTests { 533 fmt.Fprintf(&cgo1, "\t%q: %s,\n", pt.name, pt.name) 534 } 535 fmt.Fprintf(&cgo1, "}\n\n") 536 fmt.Fprintf(&cgo1, "%s\n", ptrTestMain) 537 538 if err := ioutil.WriteFile(filepath.Join(src, "cgo1.go"), cgo1.Bytes(), 0666); err != nil { 539 t.Fatal(err) 540 } 541 if err := ioutil.WriteFile(filepath.Join(src, "cgo2.go"), cgo2.Bytes(), 0666); err != nil { 542 t.Fatal(err) 543 } 544 545 cmd := exec.Command("go", "build", "-o", "ptrtest.exe") 546 cmd.Dir = src 547 cmd.Env = append(os.Environ(), "GOPATH="+gopath) 548 out, err := cmd.CombinedOutput() 549 if err != nil { 550 t.Fatalf("go build: %v\n%s", err, out) 551 } 552 553 return dir, filepath.Join(src, "ptrtest.exe") 554 } 555 556 const ptrTestMain = ` 557 func main() { 558 for _, arg := range os.Args[1:] { 559 f := funcs[arg] 560 if f == nil { 561 panic("missing func "+arg) 562 } 563 f() 564 } 565 } 566 ` 567 568 var csem = make(chan bool, 16) 569 570 func testOne(t *testing.T, pt ptrTest, exe string) { 571 t.Parallel() 572 573 // Run the tests in parallel, but don't run too many 574 // executions in parallel, to avoid overloading the system. 575 runcmd := func(cgocheck string) ([]byte, error) { 576 csem <- true 577 defer func() { <-csem }() 578 cmd := exec.Command(exe, pt.name) 579 cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck) 580 return cmd.CombinedOutput() 581 } 582 583 if pt.expensive { 584 buf, err := runcmd("1") 585 if err != nil { 586 t.Logf("%s", buf) 587 if pt.fail { 588 t.Fatalf("test marked expensive, but failed when not expensive: %v", err) 589 } else { 590 t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err) 591 } 592 } 593 594 } 595 596 cgocheck := "" 597 if pt.expensive { 598 cgocheck = "2" 599 } 600 601 buf, err := runcmd(cgocheck) 602 if pt.fail { 603 if err == nil { 604 t.Logf("%s", buf) 605 t.Fatalf("did not fail as expected") 606 } else if !bytes.Contains(buf, []byte("Go pointer")) { 607 t.Logf("%s", buf) 608 t.Fatalf("did not print expected error (failed with %v)", err) 609 } 610 } else { 611 if err != nil { 612 t.Logf("%s", buf) 613 t.Fatalf("failed unexpectedly: %v", err) 614 } 615 616 if !pt.expensive { 617 // Make sure it passes with the expensive checks. 618 buf, err := runcmd("2") 619 if err != nil { 620 t.Logf("%s", buf) 621 t.Fatalf("failed unexpectedly with expensive checks: %v", err) 622 } 623 } 624 } 625 626 if pt.fail { 627 buf, err := runcmd("0") 628 if err != nil { 629 t.Logf("%s", buf) 630 t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err) 631 } 632 } 633 }