]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/cgo/internal/testerrors/ptr_test.go
runtime: implement Pinner API for object pinning
[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         "internal/testenv"
14         "os"
15         "os/exec"
16         "path/filepath"
17         "runtime"
18         "strings"
19         "sync/atomic"
20         "testing"
21 )
22
23 var tmp = flag.String("tmp", "", "use `dir` for temporary files and do not clean up")
24
25 // ptrTest is the tests without the boilerplate.
26 type 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
38 type extra struct {
39         name     string
40         contents string
41 }
42
43 var 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:    `extern unsigned char *GoFn21();`,
256                 support: `//export GoFn21
257                           func GoFn21() *byte { return new(byte) }`,
258                 body: `C.GoFn21()`,
259                 fail: true,
260         },
261         {
262                 // Returning a C pointer is fine.
263                 name: "exportok",
264                 c: `#include <stdlib.h>
265                     extern unsigned char *GoFn22();`,
266                 support: `//export GoFn22
267                           func GoFn22() *byte { return (*byte)(C.malloc(1)) }`,
268                 body: `C.GoFn22()`,
269         },
270         {
271                 // Passing a Go string is fine.
272                 name: "passstring",
273                 c: `#include <stddef.h>
274                     typedef struct { const char *p; ptrdiff_t n; } gostring23;
275                     gostring23 f23(gostring23 s) { return s; }`,
276                 imports: []string{"unsafe"},
277                 body:    `s := "a"; r := C.f23(*(*C.gostring23)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`,
278         },
279         {
280                 // Passing a slice of Go strings fails.
281                 name:    "passstringslice",
282                 c:       `void f24(void *p) {}`,
283                 imports: []string{"strings", "unsafe"},
284                 support: `type S24 struct { a [1]string }`,
285                 body:    `s := S24{a:[1]string{strings.Repeat("a", 2)}}; C.f24(unsafe.Pointer(&s.a[0]))`,
286                 fail:    true,
287         },
288         {
289                 // Exported functions may not return strings.
290                 name:    "retstring",
291                 c:       `extern void f25();`,
292                 imports: []string{"strings"},
293                 support: `//export GoStr25
294                           func GoStr25() string { return strings.Repeat("a", 2) }`,
295                 body: `C.f25()`,
296                 c1: `#include <stddef.h>
297                      typedef struct { const char *p; ptrdiff_t n; } gostring25;
298                      extern gostring25 GoStr25();
299                      void f25() { GoStr25(); }`,
300                 fail: true,
301         },
302         {
303                 // Don't check non-pointer data.
304                 // Uses unsafe code to get a pointer we shouldn't check.
305                 // Although we use unsafe, the uintptr represents an integer
306                 // that happens to have the same representation as a pointer;
307                 // that is, we are testing something that is not unsafe.
308                 name: "ptrdata1",
309                 c: `#include <stdlib.h>
310                     void f26(void* p) {}`,
311                 imports: []string{"unsafe"},
312                 support: `type S26 struct { p *int; a [8*8]byte; u uintptr }`,
313                 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))`,
314                 fail:    false,
315         },
316         {
317                 // Like ptrdata1, but with a type that uses a GC program.
318                 name: "ptrdata2",
319                 c: `#include <stdlib.h>
320                     void f27(void* p) {}`,
321                 imports: []string{"unsafe"},
322                 support: `type S27 struct { p *int; a [32769*8]byte; q *int; u uintptr }`,
323                 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))`,
324                 fail:    false,
325         },
326         {
327                 // Check deferred pointers when they are used, not
328                 // when the defer statement is run.
329                 name: "defer1",
330                 c:    `typedef struct s28 { int *p; } s28; void f28(s28 *ps) {}`,
331                 body: `p := &C.s28{}; defer C.f28(p); p.p = new(C.int)`,
332                 fail: true,
333         },
334         {
335                 // Check a pointer to a union if the union has any
336                 // pointer fields.
337                 name:    "union1",
338                 c:       `typedef union { char **p; unsigned long i; } u29; void f29(u29 *pu) {}`,
339                 imports: []string{"unsafe"},
340                 body:    `var b C.char; p := &b; C.f29((*C.u29)(unsafe.Pointer(&p)))`,
341                 fail:    true,
342         },
343         {
344                 // Don't check a pointer to a union if the union does
345                 // not have any pointer fields.
346                 // Like ptrdata1 above, the uintptr represents an
347                 // integer that happens to have the same
348                 // representation as a pointer.
349                 name:    "union2",
350                 c:       `typedef union { unsigned long i; } u39; void f39(u39 *pu) {}`,
351                 imports: []string{"unsafe"},
352                 body:    `var b C.char; p := &b; C.f39((*C.u39)(unsafe.Pointer(&p)))`,
353                 fail:    false,
354         },
355         {
356                 // Test preemption while entering a cgo call. Issue #21306.
357                 name:    "preemptduringcall",
358                 c:       `void f30() {}`,
359                 imports: []string{"runtime", "sync"},
360                 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()`,
361                 fail:    false,
362         },
363         {
364                 // Test poller deadline with cgocheck=2.  Issue #23435.
365                 name:    "deadline",
366                 c:       `#define US31 10`,
367                 imports: []string{"os", "time"},
368                 body:    `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US31 * time.Microsecond))`,
369                 fail:    false,
370         },
371         {
372                 // Test for double evaluation of channel receive.
373                 name:    "chanrecv",
374                 c:       `void f32(char** p) {}`,
375                 imports: []string{"time"},
376                 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]);`,
377                 fail:    false,
378         },
379         {
380                 // Test that converting the address of a struct field
381                 // to unsafe.Pointer still just checks that field.
382                 // Issue #25941.
383                 name:    "structfield",
384                 c:       `void f33(void* p) {}`,
385                 imports: []string{"unsafe"},
386                 support: `type S33 struct { p *int; a [8]byte; u uintptr }`,
387                 body:    `s := &S33{p: new(int)}; C.f33(unsafe.Pointer(&s.a))`,
388                 fail:    false,
389         },
390         {
391                 // Test that converting multiple struct field
392                 // addresses to unsafe.Pointer still just checks those
393                 // fields. Issue #25941.
394                 name:    "structfield2",
395                 c:       `void f34(void* p, int r, void* s) {}`,
396                 imports: []string{"unsafe"},
397                 support: `type S34 struct { a [8]byte; p *int; b int64; }`,
398                 body:    `s := &S34{p: new(int)}; C.f34(unsafe.Pointer(&s.a), 32, unsafe.Pointer(&s.b))`,
399                 fail:    false,
400         },
401         {
402                 // Test that second argument to cgoCheckPointer is
403                 // evaluated when a deferred function is deferred, not
404                 // when it is run.
405                 name:    "defer2",
406                 c:       `void f35(char **pc) {}`,
407                 support: `type S35a struct { s []*C.char }; type S35b struct { ps *S35a }`,
408                 body:    `p := &S35b{&S35a{[]*C.char{nil}}}; defer C.f35(&p.ps.s[0]); p.ps = nil`,
409                 fail:    false,
410         },
411         {
412                 // Test that indexing into a function call still
413                 // examines only the slice being indexed.
414                 name:    "buffer",
415                 c:       `void f36(void *p) {}`,
416                 imports: []string{"bytes", "unsafe"},
417                 body:    `var b bytes.Buffer; b.WriteString("a"); C.f36(unsafe.Pointer(&b.Bytes()[0]))`,
418                 fail:    false,
419         },
420         {
421                 // Test that bgsweep releasing a finalizer is OK.
422                 name:    "finalizer",
423                 c:       `// Nothing to declare.`,
424                 imports: []string{"os"},
425                 support: `func open37() { os.Open(os.Args[0]) }; var G37 [][]byte`,
426                 body:    `for i := 0; i < 10000; i++ { G37 = append(G37, make([]byte, 4096)); if i % 100 == 0 { G37 = nil; open37() } }`,
427                 fail:    false,
428         },
429         {
430                 // Test that converting generated struct to interface is OK.
431                 name:    "structof",
432                 c:       `// Nothing to declare.`,
433                 imports: []string{"reflect"},
434                 support: `type MyInt38 int; func (i MyInt38) Get() int { return int(i) }; type Getter38 interface { Get() int }`,
435                 body:    `t := reflect.StructOf([]reflect.StructField{{Name: "MyInt38", Type: reflect.TypeOf(MyInt38(0)), Anonymous: true}}); v := reflect.New(t).Elem(); v.Interface().(Getter38).Get()`,
436                 fail:    false,
437         },
438         {
439                 // Test that a converted address of a struct field results
440                 // in a check for just that field and not the whole struct.
441                 name:    "structfieldcast",
442                 c:       `struct S40i { int i; int* p; }; void f40(struct S40i* p) {}`,
443                 support: `type S40 struct { p *int; a C.struct_S40i }`,
444                 body:    `s := &S40{p: new(int)}; C.f40((*C.struct_S40i)(&s.a))`,
445                 fail:    false,
446         },
447 }
448
449 func TestPointerChecks(t *testing.T) {
450         testenv.MustHaveGoBuild(t)
451         testenv.MustHaveCGO(t)
452         if runtime.GOOS == "windows" {
453                 // TODO: Skip just the cases that fail?
454                 t.Skipf("some tests fail to build on %s", runtime.GOOS)
455         }
456
457         var gopath string
458         var dir string
459         if *tmp != "" {
460                 gopath = *tmp
461                 dir = ""
462         } else {
463                 d, err := os.MkdirTemp("", filepath.Base(t.Name()))
464                 if err != nil {
465                         t.Fatal(err)
466                 }
467                 dir = d
468                 gopath = d
469         }
470
471         exe := buildPtrTests(t, gopath, false)
472         exe2 := buildPtrTests(t, gopath, true)
473
474         // We (TestPointerChecks) return before the parallel subtest functions do,
475         // so we can't just defer os.RemoveAll(dir). Instead we have to wait for
476         // the parallel subtests to finish. This code looks racy but is not:
477         // the add +1 run in serial before testOne blocks. The -1 run in parallel
478         // after testOne finishes.
479         var pending int32
480         for _, pt := range ptrTests {
481                 pt := pt
482                 t.Run(pt.name, func(t *testing.T) {
483                         atomic.AddInt32(&pending, +1)
484                         defer func() {
485                                 if atomic.AddInt32(&pending, -1) == 0 {
486                                         os.RemoveAll(dir)
487                                 }
488                         }()
489                         testOne(t, pt, exe, exe2)
490                 })
491         }
492 }
493
494 func buildPtrTests(t *testing.T, gopath string, cgocheck2 bool) (exe string) {
495
496         src := filepath.Join(gopath, "src", "ptrtest")
497         if err := os.MkdirAll(src, 0777); err != nil {
498                 t.Fatal(err)
499         }
500         if err := os.WriteFile(filepath.Join(src, "go.mod"), []byte("module ptrtest"), 0666); err != nil {
501                 t.Fatal(err)
502         }
503
504         // Prepare two cgo inputs: one for standard cgo and one for //export cgo.
505         // (The latter cannot have C definitions, only declarations.)
506         var cgo1, cgo2 bytes.Buffer
507         fmt.Fprintf(&cgo1, "package main\n\n/*\n")
508         fmt.Fprintf(&cgo2, "package main\n\n/*\n")
509
510         // C code
511         for _, pt := range ptrTests {
512                 cgo := &cgo1
513                 if strings.Contains(pt.support, "//export") {
514                         cgo = &cgo2
515                 }
516                 fmt.Fprintf(cgo, "%s\n", pt.c)
517                 fmt.Fprintf(&cgo1, "%s\n", pt.c1)
518         }
519         fmt.Fprintf(&cgo1, "*/\nimport \"C\"\n\n")
520         fmt.Fprintf(&cgo2, "*/\nimport \"C\"\n\n")
521
522         // Imports
523         did1 := make(map[string]bool)
524         did2 := make(map[string]bool)
525         did1["os"] = true // for ptrTestMain
526         fmt.Fprintf(&cgo1, "import \"os\"\n")
527
528         for _, pt := range ptrTests {
529                 did := did1
530                 cgo := &cgo1
531                 if strings.Contains(pt.support, "//export") {
532                         did = did2
533                         cgo = &cgo2
534                 }
535                 for _, imp := range pt.imports {
536                         if !did[imp] {
537                                 did[imp] = true
538                                 fmt.Fprintf(cgo, "import %q\n", imp)
539                         }
540                 }
541         }
542
543         // Func support and bodies.
544         for _, pt := range ptrTests {
545                 cgo := &cgo1
546                 if strings.Contains(pt.support, "//export") {
547                         cgo = &cgo2
548                 }
549                 fmt.Fprintf(cgo, "%s\nfunc %s() {\n%s\n}\n", pt.support, pt.name, pt.body)
550         }
551
552         // Func list and main dispatch.
553         fmt.Fprintf(&cgo1, "var funcs = map[string]func() {\n")
554         for _, pt := range ptrTests {
555                 fmt.Fprintf(&cgo1, "\t%q: %s,\n", pt.name, pt.name)
556         }
557         fmt.Fprintf(&cgo1, "}\n\n")
558         fmt.Fprintf(&cgo1, "%s\n", ptrTestMain)
559
560         if err := os.WriteFile(filepath.Join(src, "cgo1.go"), cgo1.Bytes(), 0666); err != nil {
561                 t.Fatal(err)
562         }
563         if err := os.WriteFile(filepath.Join(src, "cgo2.go"), cgo2.Bytes(), 0666); err != nil {
564                 t.Fatal(err)
565         }
566
567         exeName := "ptrtest.exe"
568         if cgocheck2 {
569                 exeName = "ptrtest2.exe"
570         }
571         cmd := exec.Command("go", "build", "-o", exeName)
572         cmd.Dir = src
573         cmd.Env = append(os.Environ(), "GOPATH="+gopath)
574         if cgocheck2 {
575                 found := false
576                 for i, e := range cmd.Env {
577                         if strings.HasPrefix(e, "GOEXPERIMENT=") {
578                                 cmd.Env[i] = e + ",cgocheck2"
579                                 found = true
580                         }
581                 }
582                 if !found {
583                         cmd.Env = append(cmd.Env, "GOEXPERIMENT=cgocheck2")
584                 }
585         }
586         out, err := cmd.CombinedOutput()
587         if err != nil {
588                 t.Fatalf("go build: %v\n%s", err, out)
589         }
590
591         return filepath.Join(src, exeName)
592 }
593
594 const ptrTestMain = `
595 func main() {
596         for _, arg := range os.Args[1:] {
597                 f := funcs[arg]
598                 if f == nil {
599                         panic("missing func "+arg)
600                 }
601                 f()
602         }
603 }
604 `
605
606 var csem = make(chan bool, 16)
607
608 func testOne(t *testing.T, pt ptrTest, exe, exe2 string) {
609         t.Parallel()
610
611         // Run the tests in parallel, but don't run too many
612         // executions in parallel, to avoid overloading the system.
613         runcmd := func(cgocheck string) ([]byte, error) {
614                 csem <- true
615                 defer func() { <-csem }()
616                 x := exe
617                 if cgocheck == "2" {
618                         x = exe2
619                         cgocheck = "1"
620                 }
621                 cmd := exec.Command(x, pt.name)
622                 cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck)
623                 return cmd.CombinedOutput()
624         }
625
626         if pt.expensive {
627                 buf, err := runcmd("1")
628                 if err != nil {
629                         t.Logf("%s", buf)
630                         if pt.fail {
631                                 t.Fatalf("test marked expensive, but failed when not expensive: %v", err)
632                         } else {
633                                 t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err)
634                         }
635                 }
636
637         }
638
639         cgocheck := ""
640         if pt.expensive {
641                 cgocheck = "2"
642         }
643
644         buf, err := runcmd(cgocheck)
645         if pt.fail {
646                 if err == nil {
647                         t.Logf("%s", buf)
648                         t.Fatalf("did not fail as expected")
649                 } else if !bytes.Contains(buf, []byte("Go pointer")) {
650                         t.Logf("%s", buf)
651                         t.Fatalf("did not print expected error (failed with %v)", err)
652                 }
653         } else {
654                 if err != nil {
655                         t.Logf("%s", buf)
656                         t.Fatalf("failed unexpectedly: %v", err)
657                 }
658
659                 if !pt.expensive {
660                         // Make sure it passes with the expensive checks.
661                         buf, err := runcmd("2")
662                         if err != nil {
663                                 t.Logf("%s", buf)
664                                 t.Fatalf("failed unexpectedly with expensive checks: %v", err)
665                         }
666                 }
667         }
668
669         if pt.fail {
670                 buf, err := runcmd("0")
671                 if err != nil {
672                         t.Logf("%s", buf)
673                         t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err)
674                 }
675         }
676 }