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

View as plain text