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