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

View as plain text