]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/cgo/internal/testerrors/ptr_test.go
misc/cgo: move easy tests to cmd/cgo/internal
[gostls13.git] / src / cmd / cgo / internal / testerrors / ptr_test.go
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         var gopath string
438         var dir string
439         if *tmp != "" {
440                 gopath = *tmp
441                 dir = ""
442         } else {
443                 d, err := os.MkdirTemp("", filepath.Base(t.Name()))
444                 if err != nil {
445                         t.Fatal(err)
446                 }
447                 dir = d
448                 gopath = d
449         }
450
451         exe := buildPtrTests(t, gopath, false)
452         exe2 := buildPtrTests(t, gopath, true)
453
454         // We (TestPointerChecks) return before the parallel subtest functions do,
455         // so we can't just defer os.RemoveAll(dir). Instead we have to wait for
456         // the parallel subtests to finish. This code looks racy but is not:
457         // the add +1 run in serial before testOne blocks. The -1 run in parallel
458         // after testOne finishes.
459         var pending int32
460         for _, pt := range ptrTests {
461                 pt := pt
462                 t.Run(pt.name, func(t *testing.T) {
463                         atomic.AddInt32(&pending, +1)
464                         defer func() {
465                                 if atomic.AddInt32(&pending, -1) == 0 {
466                                         os.RemoveAll(dir)
467                                 }
468                         }()
469                         testOne(t, pt, exe, exe2)
470                 })
471         }
472 }
473
474 func buildPtrTests(t *testing.T, gopath string, cgocheck2 bool) (exe string) {
475
476         src := filepath.Join(gopath, "src", "ptrtest")
477         if err := os.MkdirAll(src, 0777); err != nil {
478                 t.Fatal(err)
479         }
480         if err := os.WriteFile(filepath.Join(src, "go.mod"), []byte("module ptrtest"), 0666); err != nil {
481                 t.Fatal(err)
482         }
483
484         // Prepare two cgo inputs: one for standard cgo and one for //export cgo.
485         // (The latter cannot have C definitions, only declarations.)
486         var cgo1, cgo2 bytes.Buffer
487         fmt.Fprintf(&cgo1, "package main\n\n/*\n")
488         fmt.Fprintf(&cgo2, "package main\n\n/*\n")
489
490         // C code
491         for _, pt := range ptrTests {
492                 cgo := &cgo1
493                 if strings.Contains(pt.support, "//export") {
494                         cgo = &cgo2
495                 }
496                 fmt.Fprintf(cgo, "%s\n", pt.c)
497                 fmt.Fprintf(&cgo1, "%s\n", pt.c1)
498         }
499         fmt.Fprintf(&cgo1, "*/\nimport \"C\"\n\n")
500         fmt.Fprintf(&cgo2, "*/\nimport \"C\"\n\n")
501
502         // Imports
503         did1 := make(map[string]bool)
504         did2 := make(map[string]bool)
505         did1["os"] = true // for ptrTestMain
506         fmt.Fprintf(&cgo1, "import \"os\"\n")
507
508         for _, pt := range ptrTests {
509                 did := did1
510                 cgo := &cgo1
511                 if strings.Contains(pt.support, "//export") {
512                         did = did2
513                         cgo = &cgo2
514                 }
515                 for _, imp := range pt.imports {
516                         if !did[imp] {
517                                 did[imp] = true
518                                 fmt.Fprintf(cgo, "import %q\n", imp)
519                         }
520                 }
521         }
522
523         // Func support and bodies.
524         for _, pt := range ptrTests {
525                 cgo := &cgo1
526                 if strings.Contains(pt.support, "//export") {
527                         cgo = &cgo2
528                 }
529                 fmt.Fprintf(cgo, "%s\nfunc %s() {\n%s\n}\n", pt.support, pt.name, pt.body)
530         }
531
532         // Func list and main dispatch.
533         fmt.Fprintf(&cgo1, "var funcs = map[string]func() {\n")
534         for _, pt := range ptrTests {
535                 fmt.Fprintf(&cgo1, "\t%q: %s,\n", pt.name, pt.name)
536         }
537         fmt.Fprintf(&cgo1, "}\n\n")
538         fmt.Fprintf(&cgo1, "%s\n", ptrTestMain)
539
540         if err := os.WriteFile(filepath.Join(src, "cgo1.go"), cgo1.Bytes(), 0666); err != nil {
541                 t.Fatal(err)
542         }
543         if err := os.WriteFile(filepath.Join(src, "cgo2.go"), cgo2.Bytes(), 0666); err != nil {
544                 t.Fatal(err)
545         }
546
547         exeName := "ptrtest.exe"
548         if cgocheck2 {
549                 exeName = "ptrtest2.exe"
550         }
551         cmd := exec.Command("go", "build", "-o", exeName)
552         cmd.Dir = src
553         cmd.Env = append(os.Environ(), "GOPATH="+gopath)
554         if cgocheck2 {
555                 found := false
556                 for i, e := range cmd.Env {
557                         if strings.HasPrefix(e, "GOEXPERIMENT=") {
558                                 cmd.Env[i] = e + ",cgocheck2"
559                                 found = true
560                         }
561                 }
562                 if !found {
563                         cmd.Env = append(cmd.Env, "GOEXPERIMENT=cgocheck2")
564                 }
565         }
566         out, err := cmd.CombinedOutput()
567         if err != nil {
568                 t.Fatalf("go build: %v\n%s", err, out)
569         }
570
571         return filepath.Join(src, exeName)
572 }
573
574 const ptrTestMain = `
575 func main() {
576         for _, arg := range os.Args[1:] {
577                 f := funcs[arg]
578                 if f == nil {
579                         panic("missing func "+arg)
580                 }
581                 f()
582         }
583 }
584 `
585
586 var csem = make(chan bool, 16)
587
588 func testOne(t *testing.T, pt ptrTest, exe, exe2 string) {
589         t.Parallel()
590
591         // Run the tests in parallel, but don't run too many
592         // executions in parallel, to avoid overloading the system.
593         runcmd := func(cgocheck string) ([]byte, error) {
594                 csem <- true
595                 defer func() { <-csem }()
596                 x := exe
597                 if cgocheck == "2" {
598                         x = exe2
599                         cgocheck = "1"
600                 }
601                 cmd := exec.Command(x, pt.name)
602                 cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck)
603                 return cmd.CombinedOutput()
604         }
605
606         if pt.expensive {
607                 buf, err := runcmd("1")
608                 if err != nil {
609                         t.Logf("%s", buf)
610                         if pt.fail {
611                                 t.Fatalf("test marked expensive, but failed when not expensive: %v", err)
612                         } else {
613                                 t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err)
614                         }
615                 }
616
617         }
618
619         cgocheck := ""
620         if pt.expensive {
621                 cgocheck = "2"
622         }
623
624         buf, err := runcmd(cgocheck)
625         if pt.fail {
626                 if err == nil {
627                         t.Logf("%s", buf)
628                         t.Fatalf("did not fail as expected")
629                 } else if !bytes.Contains(buf, []byte("Go pointer")) {
630                         t.Logf("%s", buf)
631                         t.Fatalf("did not print expected error (failed with %v)", err)
632                 }
633         } else {
634                 if err != nil {
635                         t.Logf("%s", buf)
636                         t.Fatalf("failed unexpectedly: %v", err)
637                 }
638
639                 if !pt.expensive {
640                         // Make sure it passes with the expensive checks.
641                         buf, err := runcmd("2")
642                         if err != nil {
643                                 t.Logf("%s", buf)
644                                 t.Fatalf("failed unexpectedly with expensive checks: %v", err)
645                         }
646                 }
647         }
648
649         if pt.fail {
650                 buf, err := runcmd("0")
651                 if err != nil {
652                         t.Logf("%s", buf)
653                         t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err)
654                 }
655         }
656 }