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