• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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