github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/misc/cgo/errors/ptr_test.go (about)

     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  	"bufio"
    11  	"bytes"
    12  	"fmt"
    13  	"io/ioutil"
    14  	"os"
    15  	"os/exec"
    16  	"path/filepath"
    17  	"strings"
    18  	"testing"
    19  )
    20  
    21  // ptrTest is the tests without the boilerplate.
    22  type ptrTest struct {
    23  	name      string   // for reporting
    24  	c         string   // the cgo comment
    25  	imports   []string // a list of imports
    26  	support   string   // supporting functions
    27  	body      string   // the body of the main function
    28  	extra     []extra  // extra files
    29  	fail      bool     // whether the test should fail
    30  	expensive bool     // whether the test requires the expensive check
    31  }
    32  
    33  type extra struct {
    34  	name     string
    35  	contents string
    36  }
    37  
    38  var ptrTests = []ptrTest{
    39  	{
    40  		// Passing a pointer to a struct that contains a Go pointer.
    41  		name: "ptr1",
    42  		c:    `typedef struct s { int *p; } s; void f(s *ps) {}`,
    43  		body: `C.f(&C.s{new(C.int)})`,
    44  		fail: true,
    45  	},
    46  	{
    47  		// Passing a pointer to a struct that contains a Go pointer.
    48  		name: "ptr2",
    49  		c:    `typedef struct s { int *p; } s; void f(s *ps) {}`,
    50  		body: `p := &C.s{new(C.int)}; C.f(p)`,
    51  		fail: true,
    52  	},
    53  	{
    54  		// Passing a pointer to an int field of a Go struct
    55  		// that (irrelevantly) contains a Go pointer.
    56  		name: "ok1",
    57  		c:    `struct s { int i; int *p; }; void f(int *p) {}`,
    58  		body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.i)`,
    59  		fail: false,
    60  	},
    61  	{
    62  		// Passing a pointer to a pointer field of a Go struct.
    63  		name: "ptr-field",
    64  		c:    `struct s { int i; int *p; }; void f(int **p) {}`,
    65  		body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.p)`,
    66  		fail: true,
    67  	},
    68  	{
    69  		// Passing a pointer to a pointer field of a Go
    70  		// struct, where the field does not contain a Go
    71  		// pointer, but another field (irrelevantly) does.
    72  		name: "ptr-field-ok",
    73  		c:    `struct s { int *p1; int *p2; }; void f(int **p) {}`,
    74  		body: `p := &C.struct_s{p1: nil, p2: new(C.int)}; C.f(&p.p1)`,
    75  		fail: false,
    76  	},
    77  	{
    78  		// Passing the address of a slice with no Go pointers.
    79  		name:    "slice-ok-1",
    80  		c:       `void f(void **p) {}`,
    81  		imports: []string{"unsafe"},
    82  		body:    `s := []unsafe.Pointer{nil}; C.f(&s[0])`,
    83  		fail:    false,
    84  	},
    85  	{
    86  		// Passing the address of a slice with a Go pointer.
    87  		name:    "slice-ptr-1",
    88  		c:       `void f(void **p) {}`,
    89  		imports: []string{"unsafe"},
    90  		body:    `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f(&s[0])`,
    91  		fail:    true,
    92  	},
    93  	{
    94  		// Passing the address of a slice with a Go pointer,
    95  		// where we are passing the address of an element that
    96  		// is not a Go pointer.
    97  		name:    "slice-ptr-2",
    98  		c:       `void f(void **p) {}`,
    99  		imports: []string{"unsafe"},
   100  		body:    `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f(&s[0])`,
   101  		fail:    true,
   102  	},
   103  	{
   104  		// Passing the address of a slice that is an element
   105  		// in a struct only looks at the slice.
   106  		name:    "slice-ok-2",
   107  		c:       `void f(void **p) {}`,
   108  		imports: []string{"unsafe"},
   109  		support: `type S struct { p *int; s []unsafe.Pointer }`,
   110  		body:    `i := 0; p := &S{p:&i, s:[]unsafe.Pointer{nil}}; C.f(&p.s[0])`,
   111  		fail:    false,
   112  	},
   113  	{
   114  		// Passing the address of a slice of an array that is
   115  		// an element in a struct, with a type conversion.
   116  		name:    "slice-ok-3",
   117  		c:       `void f(void* p) {}`,
   118  		imports: []string{"unsafe"},
   119  		support: `type S struct { p *int; a [4]byte }`,
   120  		body:    `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`,
   121  		fail:    false,
   122  	},
   123  	{
   124  		// Passing the address of a slice of an array that is
   125  		// an element in a struct, with a type conversion.
   126  		name:    "slice-ok-4",
   127  		c:       `typedef void* PV; void f(PV p) {}`,
   128  		imports: []string{"unsafe"},
   129  		support: `type S struct { p *int; a [4]byte }`,
   130  		body:    `i := 0; p := &S{p:&i}; C.f(C.PV(unsafe.Pointer(&p.a[0])))`,
   131  		fail:    false,
   132  	},
   133  	{
   134  		// Passing the address of a static variable with no
   135  		// pointers doesn't matter.
   136  		name:    "varok",
   137  		c:       `void f(char** parg) {}`,
   138  		support: `var hello = [...]C.char{'h', 'e', 'l', 'l', 'o'}`,
   139  		body:    `parg := [1]*C.char{&hello[0]}; C.f(&parg[0])`,
   140  		fail:    false,
   141  	},
   142  	{
   143  		// Passing the address of a static variable with
   144  		// pointers does matter.
   145  		name:    "var",
   146  		c:       `void f(char*** parg) {}`,
   147  		support: `var hello = [...]*C.char{new(C.char)}`,
   148  		body:    `parg := [1]**C.char{&hello[0]}; C.f(&parg[0])`,
   149  		fail:    true,
   150  	},
   151  	{
   152  		// Storing a Go pointer into C memory should fail.
   153  		name: "barrier",
   154  		c: `#include <stdlib.h>
   155                      char **f1() { return malloc(sizeof(char*)); }
   156                      void f2(char **p) {}`,
   157  		body:      `p := C.f1(); *p = new(C.char); C.f2(p)`,
   158  		fail:      true,
   159  		expensive: true,
   160  	},
   161  	{
   162  		// Storing a Go pointer into C memory by assigning a
   163  		// large value should fail.
   164  		name: "barrier-struct",
   165  		c: `#include <stdlib.h>
   166                      struct s { char *a[10]; };
   167                      struct s *f1() { return malloc(sizeof(struct s)); }
   168                      void f2(struct s *p) {}`,
   169  		body:      `p := C.f1(); p.a = [10]*C.char{new(C.char)}; C.f2(p)`,
   170  		fail:      true,
   171  		expensive: true,
   172  	},
   173  	{
   174  		// Storing a Go pointer into C memory using a slice
   175  		// copy should fail.
   176  		name: "barrier-slice",
   177  		c: `#include <stdlib.h>
   178                      struct s { char *a[10]; };
   179                      struct s *f1() { return malloc(sizeof(struct s)); }
   180                      void f2(struct s *p) {}`,
   181  		body:      `p := C.f1(); copy(p.a[:], []*C.char{new(C.char)}); C.f2(p)`,
   182  		fail:      true,
   183  		expensive: true,
   184  	},
   185  	{
   186  		// A very large value uses a GC program, which is a
   187  		// different code path.
   188  		name: "barrier-gcprog-array",
   189  		c: `#include <stdlib.h>
   190                      struct s { char *a[32769]; };
   191                      struct s *f1() { return malloc(sizeof(struct s)); }
   192                      void f2(struct s *p) {}`,
   193  		body:      `p := C.f1(); p.a = [32769]*C.char{new(C.char)}; C.f2(p)`,
   194  		fail:      true,
   195  		expensive: true,
   196  	},
   197  	{
   198  		// Similar case, with a source on the heap.
   199  		name: "barrier-gcprog-array-heap",
   200  		c: `#include <stdlib.h>
   201                      struct s { char *a[32769]; };
   202                      struct s *f1() { return malloc(sizeof(struct s)); }
   203                      void f2(struct s *p) {}
   204                      void f3(void *p) {}`,
   205  		imports:   []string{"unsafe"},
   206  		body:      `p := C.f1(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f2(p); n[0] = nil; C.f3(unsafe.Pointer(n))`,
   207  		fail:      true,
   208  		expensive: true,
   209  	},
   210  	{
   211  		// A GC program with a struct.
   212  		name: "barrier-gcprog-struct",
   213  		c: `#include <stdlib.h>
   214                      struct s { char *a[32769]; };
   215                      struct s2 { struct s f; };
   216                      struct s2 *f1() { return malloc(sizeof(struct s2)); }
   217                      void f2(struct s2 *p) {}`,
   218  		body:      `p := C.f1(); p.f = C.struct_s{[32769]*C.char{new(C.char)}}; C.f2(p)`,
   219  		fail:      true,
   220  		expensive: true,
   221  	},
   222  	{
   223  		// Similar case, with a source on the heap.
   224  		name: "barrier-gcprog-struct-heap",
   225  		c: `#include <stdlib.h>
   226                      struct s { char *a[32769]; };
   227                      struct s2 { struct s f; };
   228                      struct s2 *f1() { return malloc(sizeof(struct s2)); }
   229                      void f2(struct s2 *p) {}
   230                      void f3(void *p) {}`,
   231  		imports:   []string{"unsafe"},
   232  		body:      `p := C.f1(); n := &C.struct_s{[32769]*C.char{new(C.char)}}; p.f = *n; C.f2(p); n.a[0] = nil; C.f3(unsafe.Pointer(n))`,
   233  		fail:      true,
   234  		expensive: true,
   235  	},
   236  	{
   237  		// Exported functions may not return Go pointers.
   238  		name: "export1",
   239  		c:    `extern unsigned char *GoFn();`,
   240  		support: `//export GoFn
   241                            func GoFn() *byte { return new(byte) }`,
   242  		body: `C.GoFn()`,
   243  		fail: true,
   244  	},
   245  	{
   246  		// Returning a C pointer is fine.
   247  		name: "exportok",
   248  		c: `#include <stdlib.h>
   249                      extern unsigned char *GoFn();`,
   250  		support: `//export GoFn
   251                            func GoFn() *byte { return (*byte)(C.malloc(1)) }`,
   252  		body: `C.GoFn()`,
   253  	},
   254  	{
   255  		// Passing a Go string is fine.
   256  		name: "pass-string",
   257  		c: `#include <stddef.h>
   258                      typedef struct { const char *p; ptrdiff_t n; } gostring;
   259                      gostring f(gostring s) { return s; }`,
   260  		imports: []string{"unsafe"},
   261  		body:    `s := "a"; r := C.f(*(*C.gostring)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`,
   262  	},
   263  	{
   264  		// Passing a slice of Go strings fails.
   265  		name:    "pass-string-slice",
   266  		c:       `void f(void *p) {}`,
   267  		imports: []string{"strings", "unsafe"},
   268  		support: `type S struct { a [1]string }`,
   269  		body:    `s := S{a:[1]string{strings.Repeat("a", 2)}}; C.f(unsafe.Pointer(&s.a[0]))`,
   270  		fail:    true,
   271  	},
   272  	{
   273  		// Exported functions may not return strings.
   274  		name:    "ret-string",
   275  		c:       `extern void f();`,
   276  		imports: []string{"strings"},
   277  		support: `//export GoStr
   278                            func GoStr() string { return strings.Repeat("a", 2) }`,
   279  		body: `C.f()`,
   280  		extra: []extra{
   281  			{
   282  				"call.c",
   283  				`#include <stddef.h>
   284                                   typedef struct { const char *p; ptrdiff_t n; } gostring;
   285                                   extern gostring GoStr();
   286                                   void f() { GoStr(); }`,
   287  			},
   288  		},
   289  		fail: true,
   290  	},
   291  	{
   292  		// Don't check non-pointer data.
   293  		// Uses unsafe code to get a pointer we shouldn't check.
   294  		// Although we use unsafe, the uintptr represents an integer
   295  		// that happens to have the same representation as a pointer;
   296  		// that is, we are testing something that is not unsafe.
   297  		name: "ptrdata1",
   298  		c: `#include <stdlib.h>
   299                      void f(void* p) {}`,
   300  		imports: []string{"unsafe"},
   301  		support: `type S struct { p *int; a [8*8]byte; u uintptr }`,
   302  		body:    `i := 0; p := &S{u:uintptr(unsafe.Pointer(&i))}; q := (*S)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f(unsafe.Pointer(q))`,
   303  		fail:    false,
   304  	},
   305  	{
   306  		// Like ptrdata1, but with a type that uses a GC program.
   307  		name: "ptrdata2",
   308  		c: `#include <stdlib.h>
   309                      void f(void* p) {}`,
   310  		imports: []string{"unsafe"},
   311  		support: `type S struct { p *int; a [32769*8]byte; q *int; u uintptr }`,
   312  		body:    `i := 0; p := S{u:uintptr(unsafe.Pointer(&i))}; q := (*S)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f(unsafe.Pointer(q))`,
   313  		fail:    false,
   314  	},
   315  	{
   316  		// Check deferred pointers when they are used, not
   317  		// when the defer statement is run.
   318  		name: "defer",
   319  		c:    `typedef struct s { int *p; } s; void f(s *ps) {}`,
   320  		body: `p := &C.s{}; defer C.f(p); p.p = new(C.int)`,
   321  		fail: true,
   322  	},
   323  	{
   324  		// Check a pointer to a union if the union has any
   325  		// pointer fields.
   326  		name:    "union1",
   327  		c:       `typedef union { char **p; unsigned long i; } u; void f(u *pu) {}`,
   328  		imports: []string{"unsafe"},
   329  		body:    `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`,
   330  		fail:    true,
   331  	},
   332  	{
   333  		// Don't check a pointer to a union if the union does
   334  		// not have any pointer fields.
   335  		// Like ptrdata1 above, the uintptr represents an
   336  		// integer that happens to have the same
   337  		// representation as a pointer.
   338  		name:    "union2",
   339  		c:       `typedef union { unsigned long i; } u; void f(u *pu) {}`,
   340  		imports: []string{"unsafe"},
   341  		body:    `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`,
   342  		fail:    false,
   343  	},
   344  	{
   345  		// Test preemption while entering a cgo call. Issue #21306.
   346  		name:    "preempt-during-call",
   347  		c:       `void f() {}`,
   348  		imports: []string{"runtime", "sync"},
   349  		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.f(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`,
   350  		fail:    false,
   351  	},
   352  	{
   353  		// Test poller deadline with cgocheck=2.  Issue #23435.
   354  		name:    "deadline",
   355  		c:       `#define US 10`,
   356  		imports: []string{"os", "time"},
   357  		body:    `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US * time.Microsecond))`,
   358  		fail:    false,
   359  	},
   360  }
   361  
   362  func TestPointerChecks(t *testing.T) {
   363  	for _, pt := range ptrTests {
   364  		pt := pt
   365  		t.Run(pt.name, func(t *testing.T) {
   366  			testOne(t, pt)
   367  		})
   368  	}
   369  }
   370  
   371  func testOne(t *testing.T, pt ptrTest) {
   372  	t.Parallel()
   373  
   374  	gopath, err := ioutil.TempDir("", filepath.Base(t.Name()))
   375  	if err != nil {
   376  		t.Fatal(err)
   377  	}
   378  	defer os.RemoveAll(gopath)
   379  
   380  	src := filepath.Join(gopath, "src")
   381  	if err := os.Mkdir(src, 0777); err != nil {
   382  		t.Fatal(err)
   383  	}
   384  
   385  	name := filepath.Join(src, fmt.Sprintf("%s.go", filepath.Base(t.Name())))
   386  	f, err := os.Create(name)
   387  	if err != nil {
   388  		t.Fatal(err)
   389  	}
   390  
   391  	b := bufio.NewWriter(f)
   392  	fmt.Fprintln(b, `package main`)
   393  	fmt.Fprintln(b)
   394  	fmt.Fprintln(b, `/*`)
   395  	fmt.Fprintln(b, pt.c)
   396  	fmt.Fprintln(b, `*/`)
   397  	fmt.Fprintln(b, `import "C"`)
   398  	fmt.Fprintln(b)
   399  	for _, imp := range pt.imports {
   400  		fmt.Fprintln(b, `import "`+imp+`"`)
   401  	}
   402  	if len(pt.imports) > 0 {
   403  		fmt.Fprintln(b)
   404  	}
   405  	if len(pt.support) > 0 {
   406  		fmt.Fprintln(b, pt.support)
   407  		fmt.Fprintln(b)
   408  	}
   409  	fmt.Fprintln(b, `func main() {`)
   410  	fmt.Fprintln(b, pt.body)
   411  	fmt.Fprintln(b, `}`)
   412  
   413  	if err := b.Flush(); err != nil {
   414  		t.Fatalf("flushing %s: %v", name, err)
   415  	}
   416  	if err := f.Close(); err != nil {
   417  		t.Fatalf("closing %s: %v", name, err)
   418  	}
   419  
   420  	for _, e := range pt.extra {
   421  		if err := ioutil.WriteFile(filepath.Join(src, e.name), []byte(e.contents), 0644); err != nil {
   422  			t.Fatalf("writing %s: %v", e.name, err)
   423  		}
   424  	}
   425  
   426  	args := func(cmd *exec.Cmd) string {
   427  		return strings.Join(cmd.Args, " ")
   428  	}
   429  
   430  	cmd := exec.Command("go", "build")
   431  	cmd.Dir = src
   432  	cmd.Env = addEnv("GOPATH", gopath)
   433  	buf, err := cmd.CombinedOutput()
   434  	if err != nil {
   435  		t.Logf("%#q:\n%s", args(cmd), buf)
   436  		t.Fatalf("failed to build: %v", err)
   437  	}
   438  
   439  	exe := filepath.Join(src, filepath.Base(src))
   440  	cmd = exec.Command(exe)
   441  	cmd.Dir = src
   442  
   443  	if pt.expensive {
   444  		cmd.Env = cgocheckEnv("1")
   445  		buf, err := cmd.CombinedOutput()
   446  		if err != nil {
   447  			t.Logf("%#q:\n%s", args(cmd), buf)
   448  			if pt.fail {
   449  				t.Fatalf("test marked expensive, but failed when not expensive: %v", err)
   450  			} else {
   451  				t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err)
   452  			}
   453  		}
   454  
   455  		cmd = exec.Command(exe)
   456  		cmd.Dir = src
   457  	}
   458  
   459  	if pt.expensive {
   460  		cmd.Env = cgocheckEnv("2")
   461  	}
   462  
   463  	buf, err = cmd.CombinedOutput()
   464  	if pt.fail {
   465  		if err == nil {
   466  			t.Logf("%#q:\n%s", args(cmd), buf)
   467  			t.Fatalf("did not fail as expected")
   468  		} else if !bytes.Contains(buf, []byte("Go pointer")) {
   469  			t.Logf("%#q:\n%s", args(cmd), buf)
   470  			t.Fatalf("did not print expected error (failed with %v)", err)
   471  		}
   472  	} else {
   473  		if err != nil {
   474  			t.Logf("%#q:\n%s", args(cmd), buf)
   475  			t.Fatalf("failed unexpectedly: %v", err)
   476  		}
   477  
   478  		if !pt.expensive {
   479  			// Make sure it passes with the expensive checks.
   480  			cmd := exec.Command(exe)
   481  			cmd.Dir = src
   482  			cmd.Env = cgocheckEnv("2")
   483  			buf, err := cmd.CombinedOutput()
   484  			if err != nil {
   485  				t.Logf("%#q:\n%s", args(cmd), buf)
   486  				t.Fatalf("failed unexpectedly with expensive checks: %v", err)
   487  			}
   488  		}
   489  	}
   490  
   491  	if pt.fail {
   492  		cmd = exec.Command(exe)
   493  		cmd.Dir = src
   494  		cmd.Env = cgocheckEnv("0")
   495  		buf, err := cmd.CombinedOutput()
   496  		if err != nil {
   497  			t.Logf("%#q:\n%s", args(cmd), buf)
   498  			t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err)
   499  		}
   500  	}
   501  }
   502  
   503  func cgocheckEnv(val string) []string {
   504  	return addEnv("GODEBUG", "cgocheck="+val)
   505  }
   506  
   507  func addEnv(key, val string) []string {
   508  	env := []string{key + "=" + val}
   509  	look := key + "="
   510  	for _, e := range os.Environ() {
   511  		if !strings.HasPrefix(e, look) {
   512  			env = append(env, e)
   513  		}
   514  	}
   515  	return env
   516  }