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