...

Source file src/cmd/cgo/internal/testerrors/ptr_test.go

Documentation: cmd/cgo/internal/testerrors

     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  	"slices"
    18  	"strings"
    19  	"sync/atomic"
    20  	"testing"
    21  )
    22  
    23  var tmp = flag.String("tmp", "", "use `dir` for temporary files and do not clean up")
    24  
    25  // ptrTest is the tests without the boilerplate.
    26  type ptrTest struct {
    27  	name      string   // for reporting
    28  	c         string   // the cgo comment
    29  	c1        string   // cgo comment forced into non-export cgo file
    30  	imports   []string // a list of imports
    31  	support   string   // supporting functions
    32  	body      string   // the body of the main function
    33  	extra     []extra  // extra files
    34  	fail      bool     // whether the test should fail
    35  	expensive bool     // whether the test requires the expensive check
    36  }
    37  
    38  type extra struct {
    39  	name     string
    40  	contents string
    41  }
    42  
    43  var ptrTests = []ptrTest{
    44  	{
    45  		// Passing a pointer to a struct that contains a Go pointer.
    46  		name: "ptr1",
    47  		c:    `typedef struct s1 { int *p; } s1; void f1(s1 *ps) {}`,
    48  		body: `C.f1(&C.s1{new(C.int)})`,
    49  		fail: true,
    50  	},
    51  	{
    52  		// Passing a pointer to a struct that contains a Go pointer.
    53  		name: "ptr2",
    54  		c:    `typedef struct s2 { int *p; } s2; void f2(s2 *ps) {}`,
    55  		body: `p := &C.s2{new(C.int)}; C.f2(p)`,
    56  		fail: true,
    57  	},
    58  	{
    59  		// Passing a pointer to an int field of a Go struct
    60  		// that (irrelevantly) contains a Go pointer.
    61  		name: "ok1",
    62  		c:    `struct s3 { int i; int *p; }; void f3(int *p) {}`,
    63  		body: `p := &C.struct_s3{i: 0, p: new(C.int)}; C.f3(&p.i)`,
    64  		fail: false,
    65  	},
    66  	{
    67  		// Passing a pointer to a pointer field of a Go struct.
    68  		name: "ptrfield",
    69  		c:    `struct s4 { int i; int *p; }; void f4(int **p) {}`,
    70  		body: `p := &C.struct_s4{i: 0, p: new(C.int)}; C.f4(&p.p)`,
    71  		fail: true,
    72  	},
    73  	{
    74  		// Passing a pointer to a pointer field of a Go
    75  		// struct, where the field does not contain a Go
    76  		// pointer, but another field (irrelevantly) does.
    77  		name: "ptrfieldok",
    78  		c:    `struct s5 { int *p1; int *p2; }; void f5(int **p) {}`,
    79  		body: `p := &C.struct_s5{p1: nil, p2: new(C.int)}; C.f5(&p.p1)`,
    80  		fail: false,
    81  	},
    82  	{
    83  		// Passing the address of a slice with no Go pointers.
    84  		name:    "sliceok1",
    85  		c:       `void f6(void **p) {}`,
    86  		imports: []string{"unsafe"},
    87  		body:    `s := []unsafe.Pointer{nil}; C.f6(&s[0])`,
    88  		fail:    false,
    89  	},
    90  	{
    91  		// Passing the address of a slice with a Go pointer.
    92  		name:    "sliceptr1",
    93  		c:       `void f7(void **p) {}`,
    94  		imports: []string{"unsafe"},
    95  		body:    `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f7(&s[0])`,
    96  		fail:    true,
    97  	},
    98  	{
    99  		// Passing the address of a slice with a Go pointer,
   100  		// where we are passing the address of an element that
   101  		// is not a Go pointer.
   102  		name:    "sliceptr2",
   103  		c:       `void f8(void **p) {}`,
   104  		imports: []string{"unsafe"},
   105  		body:    `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f8(&s[0])`,
   106  		fail:    true,
   107  	},
   108  	{
   109  		// Passing the address of a slice that is an element
   110  		// in a struct only looks at the slice.
   111  		name:    "sliceok2",
   112  		c:       `void f9(void **p) {}`,
   113  		imports: []string{"unsafe"},
   114  		support: `type S9 struct { p *int; s []unsafe.Pointer }`,
   115  		body:    `i := 0; p := &S9{p:&i, s:[]unsafe.Pointer{nil}}; C.f9(&p.s[0])`,
   116  		fail:    false,
   117  	},
   118  	{
   119  		// Passing the address of a slice of an array that is
   120  		// an element in a struct, with a type conversion.
   121  		name:    "sliceok3",
   122  		c:       `void f10(void* p) {}`,
   123  		imports: []string{"unsafe"},
   124  		support: `type S10 struct { p *int; a [4]byte }`,
   125  		body:    `i := 0; p := &S10{p:&i}; s := p.a[:]; C.f10(unsafe.Pointer(&s[0]))`,
   126  		fail:    false,
   127  	},
   128  	{
   129  		// Passing the address of a slice of an array that is
   130  		// an element in a struct, with a type conversion.
   131  		name:    "sliceok4",
   132  		c:       `typedef void* PV11; void f11(PV11 p) {}`,
   133  		imports: []string{"unsafe"},
   134  		support: `type S11 struct { p *int; a [4]byte }`,
   135  		body:    `i := 0; p := &S11{p:&i}; C.f11(C.PV11(unsafe.Pointer(&p.a[0])))`,
   136  		fail:    false,
   137  	},
   138  	{
   139  		// Passing the address of a static variable with no
   140  		// pointers doesn't matter.
   141  		name:    "varok",
   142  		c:       `void f12(char** parg) {}`,
   143  		support: `var hello12 = [...]C.char{'h', 'e', 'l', 'l', 'o'}`,
   144  		body:    `parg := [1]*C.char{&hello12[0]}; C.f12(&parg[0])`,
   145  		fail:    false,
   146  	},
   147  	{
   148  		// Passing the address of a static variable with
   149  		// pointers does matter.
   150  		name:    "var1",
   151  		c:       `void f13(char*** parg) {}`,
   152  		support: `var hello13 = [...]*C.char{new(C.char)}`,
   153  		body:    `parg := [1]**C.char{&hello13[0]}; C.f13(&parg[0])`,
   154  		fail:    true,
   155  	},
   156  	{
   157  		// Storing a Go pointer into C memory should fail.
   158  		name: "barrier",
   159  		c: `#include <stdlib.h>
   160  		    char **f14a() { return malloc(sizeof(char*)); }
   161  		    void f14b(char **p) {}`,
   162  		body:      `p := C.f14a(); *p = new(C.char); C.f14b(p)`,
   163  		fail:      true,
   164  		expensive: true,
   165  	},
   166  	{
   167  		// Storing a pinned Go pointer into C memory should succeed.
   168  		name: "barrierpinnedok",
   169  		c: `#include <stdlib.h>
   170  		    char **f14a2() { return malloc(sizeof(char*)); }
   171  		    void f14b2(char **p) {}`,
   172  		imports:   []string{"runtime"},
   173  		body:      `var pinr runtime.Pinner; p := C.f14a2(); x := new(C.char); pinr.Pin(x); *p = x; C.f14b2(p); pinr.Unpin()`,
   174  		fail:      false,
   175  		expensive: true,
   176  	},
   177  	{
   178  		// Storing a Go pointer into C memory by assigning a
   179  		// large value should fail.
   180  		name: "barrierstruct",
   181  		c: `#include <stdlib.h>
   182  		    struct s15 { char *a[10]; };
   183  		    struct s15 *f15() { return malloc(sizeof(struct s15)); }
   184  		    void f15b(struct s15 *p) {}`,
   185  		body:      `p := C.f15(); p.a = [10]*C.char{new(C.char)}; C.f15b(p)`,
   186  		fail:      true,
   187  		expensive: true,
   188  	},
   189  	{
   190  		// Storing a Go pointer into C memory using a slice
   191  		// copy should fail.
   192  		name: "barrierslice",
   193  		c: `#include <stdlib.h>
   194  		    struct s16 { char *a[10]; };
   195  		    struct s16 *f16() { return malloc(sizeof(struct s16)); }
   196  		    void f16b(struct s16 *p) {}`,
   197  		body:      `p := C.f16(); copy(p.a[:], []*C.char{new(C.char)}); C.f16b(p)`,
   198  		fail:      true,
   199  		expensive: true,
   200  	},
   201  	{
   202  		// A very large value uses a GC program, which is a
   203  		// different code path.
   204  		name: "barriergcprogarray",
   205  		c: `#include <stdlib.h>
   206  		    struct s17 { char *a[32769]; };
   207  		    struct s17 *f17() { return malloc(sizeof(struct s17)); }
   208  		    void f17b(struct s17 *p) {}`,
   209  		body:      `p := C.f17(); p.a = [32769]*C.char{new(C.char)}; C.f17b(p)`,
   210  		fail:      true,
   211  		expensive: true,
   212  	},
   213  	{
   214  		// Similar case, with a source on the heap.
   215  		name: "barriergcprogarrayheap",
   216  		c: `#include <stdlib.h>
   217  		    struct s18 { char *a[32769]; };
   218  		    struct s18 *f18() { return malloc(sizeof(struct s18)); }
   219  		    void f18b(struct s18 *p) {}
   220  		    void f18c(void *p) {}`,
   221  		imports:   []string{"unsafe"},
   222  		body:      `p := C.f18(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f18b(p); n[0] = nil; C.f18c(unsafe.Pointer(n))`,
   223  		fail:      true,
   224  		expensive: true,
   225  	},
   226  	{
   227  		// A GC program with a struct.
   228  		name: "barriergcprogstruct",
   229  		c: `#include <stdlib.h>
   230  		    struct s19a { char *a[32769]; };
   231  		    struct s19b { struct s19a f; };
   232  		    struct s19b *f19() { return malloc(sizeof(struct s19b)); }
   233  		    void f19b(struct s19b *p) {}`,
   234  		body:      `p := C.f19(); p.f = C.struct_s19a{[32769]*C.char{new(C.char)}}; C.f19b(p)`,
   235  		fail:      true,
   236  		expensive: true,
   237  	},
   238  	{
   239  		// Similar case, with a source on the heap.
   240  		name: "barriergcprogstructheap",
   241  		c: `#include <stdlib.h>
   242  		    struct s20a { char *a[32769]; };
   243  		    struct s20b { struct s20a f; };
   244  		    struct s20b *f20() { return malloc(sizeof(struct s20b)); }
   245  		    void f20b(struct s20b *p) {}
   246  		    void f20c(void *p) {}`,
   247  		imports:   []string{"unsafe"},
   248  		body:      `p := C.f20(); n := &C.struct_s20a{[32769]*C.char{new(C.char)}}; p.f = *n; C.f20b(p); n.a[0] = nil; C.f20c(unsafe.Pointer(n))`,
   249  		fail:      true,
   250  		expensive: true,
   251  	},
   252  	{
   253  		// Exported functions may not return Go pointers.
   254  		name: "export1",
   255  		c: `#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  
   477  func 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  
   518  func 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  
   625  const ptrTestMain = `
   626  func 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  
   637  var csem = make(chan bool, 16)
   638  
   639  func 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  

View as plain text