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