github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/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  	fail      bool     // whether the test should fail
    31  	expensive bool     // whether the test requires the expensive check
    32  }
    33  
    34  var ptrTests = []ptrTest{
    35  	{
    36  		// Passing a pointer to a struct that contains a Go pointer.
    37  		name: "ptr1",
    38  		c:    `typedef struct s { int *p; } s; void f(s *ps) {}`,
    39  		body: `C.f(&C.s{new(C.int)})`,
    40  		fail: true,
    41  	},
    42  	{
    43  		// Passing a pointer to a struct that contains a Go pointer.
    44  		name: "ptr2",
    45  		c:    `typedef struct s { int *p; } s; void f(s *ps) {}`,
    46  		body: `p := &C.s{new(C.int)}; C.f(p)`,
    47  		fail: true,
    48  	},
    49  	{
    50  		// Passing a pointer to an int field of a Go struct
    51  		// that (irrelevantly) contains a Go pointer.
    52  		name: "ok1",
    53  		c:    `struct s { int i; int *p; }; void f(int *p) {}`,
    54  		body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.i)`,
    55  		fail: false,
    56  	},
    57  	{
    58  		// Passing a pointer to a pointer field of a Go struct.
    59  		name: "ptr-field",
    60  		c:    `struct s { int i; int *p; }; void f(int **p) {}`,
    61  		body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.p)`,
    62  		fail: true,
    63  	},
    64  	{
    65  		// Passing a pointer to a pointer field of a Go
    66  		// struct, where the field does not contain a Go
    67  		// pointer, but another field (irrelevantly) does.
    68  		name: "ptr-field-ok",
    69  		c:    `struct s { int *p1; int *p2; }; void f(int **p) {}`,
    70  		body: `p := &C.struct_s{p1: nil, p2: new(C.int)}; C.f(&p.p1)`,
    71  		fail: false,
    72  	},
    73  	{
    74  		// Passing the address of a slice with no Go pointers.
    75  		name:    "slice-ok-1",
    76  		c:       `void f(void **p) {}`,
    77  		imports: []string{"unsafe"},
    78  		body:    `s := []unsafe.Pointer{nil}; C.f(&s[0])`,
    79  		fail:    false,
    80  	},
    81  	{
    82  		// Passing the address of a slice with a Go pointer.
    83  		name:    "slice-ptr-1",
    84  		c:       `void f(void **p) {}`,
    85  		imports: []string{"unsafe"},
    86  		body:    `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f(&s[0])`,
    87  		fail:    true,
    88  	},
    89  	{
    90  		// Passing the address of a slice with a Go pointer,
    91  		// where we are passing the address of an element that
    92  		// is not a Go pointer.
    93  		name:    "slice-ptr-2",
    94  		c:       `void f(void **p) {}`,
    95  		imports: []string{"unsafe"},
    96  		body:    `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f(&s[0])`,
    97  		fail:    true,
    98  	},
    99  	{
   100  		// Passing the address of a slice that is an element
   101  		// in a struct only looks at the slice.
   102  		name:    "slice-ok-2",
   103  		c:       `void f(void **p) {}`,
   104  		imports: []string{"unsafe"},
   105  		support: `type S struct { p *int; s []unsafe.Pointer }`,
   106  		body:    `i := 0; p := &S{p:&i, s:[]unsafe.Pointer{nil}}; C.f(&p.s[0])`,
   107  		fail:    false,
   108  	},
   109  	{
   110  		// Passing the address of a static variable with no
   111  		// pointers doesn't matter.
   112  		name:    "varok",
   113  		c:       `void f(char** parg) {}`,
   114  		support: `var hello = [...]C.char{'h', 'e', 'l', 'l', 'o'}`,
   115  		body:    `parg := [1]*C.char{&hello[0]}; C.f(&parg[0])`,
   116  		fail:    false,
   117  	},
   118  	{
   119  		// Passing the address of a static variable with
   120  		// pointers does matter.
   121  		name:    "var",
   122  		c:       `void f(char*** parg) {}`,
   123  		support: `var hello = [...]*C.char{new(C.char)}`,
   124  		body:    `parg := [1]**C.char{&hello[0]}; C.f(&parg[0])`,
   125  		fail:    true,
   126  	},
   127  	{
   128  		// Storing a Go pointer into C memory should fail.
   129  		name: "barrier",
   130  		c: `#include <stdlib.h>
   131                      char **f1() { return malloc(sizeof(char*)); }
   132                      void f2(char **p) {}`,
   133  		body:      `p := C.f1(); *p = new(C.char); C.f2(p)`,
   134  		fail:      true,
   135  		expensive: true,
   136  	},
   137  	{
   138  		// Storing a Go pointer into C memory by assigning a
   139  		// large value should fail.
   140  		name: "barrier-struct",
   141  		c: `#include <stdlib.h>
   142                      struct s { char *a[10]; };
   143                      struct s *f1() { return malloc(sizeof(struct s)); }
   144                      void f2(struct s *p) {}`,
   145  		body:      `p := C.f1(); p.a = [10]*C.char{new(C.char)}; C.f2(p)`,
   146  		fail:      true,
   147  		expensive: true,
   148  	},
   149  	{
   150  		// Storing a Go pointer into C memory using a slice
   151  		// copy should fail.
   152  		name: "barrier-slice",
   153  		c: `#include <stdlib.h>
   154                      struct s { char *a[10]; };
   155                      struct s *f1() { return malloc(sizeof(struct s)); }
   156                      void f2(struct s *p) {}`,
   157  		body:      `p := C.f1(); copy(p.a[:], []*C.char{new(C.char)}); C.f2(p)`,
   158  		fail:      true,
   159  		expensive: true,
   160  	},
   161  	{
   162  		// A very large value uses a GC program, which is a
   163  		// different code path.
   164  		name: "barrier-gcprog-array",
   165  		c: `#include <stdlib.h>
   166                      struct s { char *a[32769]; };
   167                      struct s *f1() { return malloc(sizeof(struct s)); }
   168                      void f2(struct s *p) {}`,
   169  		body:      `p := C.f1(); p.a = [32769]*C.char{new(C.char)}; C.f2(p)`,
   170  		fail:      true,
   171  		expensive: true,
   172  	},
   173  	{
   174  		// Similar case, with a source on the heap.
   175  		name: "barrier-gcprog-array-heap",
   176  		c: `#include <stdlib.h>
   177                      struct s { char *a[32769]; };
   178                      struct s *f1() { return malloc(sizeof(struct s)); }
   179                      void f2(struct s *p) {}
   180                      void f3(void *p) {}`,
   181  		imports:   []string{"unsafe"},
   182  		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))`,
   183  		fail:      true,
   184  		expensive: true,
   185  	},
   186  	{
   187  		// A GC program with a struct.
   188  		name: "barrier-gcprog-struct",
   189  		c: `#include <stdlib.h>
   190                      struct s { char *a[32769]; };
   191                      struct s2 { struct s f; };
   192                      struct s2 *f1() { return malloc(sizeof(struct s2)); }
   193                      void f2(struct s2 *p) {}`,
   194  		body:      `p := C.f1(); p.f = C.struct_s{[32769]*C.char{new(C.char)}}; C.f2(p)`,
   195  		fail:      true,
   196  		expensive: true,
   197  	},
   198  	{
   199  		// Similar case, with a source on the heap.
   200  		name: "barrier-gcprog-struct-heap",
   201  		c: `#include <stdlib.h>
   202                      struct s { char *a[32769]; };
   203                      struct s2 { struct s f; };
   204                      struct s2 *f1() { return malloc(sizeof(struct s2)); }
   205                      void f2(struct s2 *p) {}
   206                      void f3(void *p) {}`,
   207  		imports:   []string{"unsafe"},
   208  		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))`,
   209  		fail:      true,
   210  		expensive: true,
   211  	},
   212  	{
   213  		// Exported functions may not return Go pointers.
   214  		name: "export1",
   215  		c:    `extern unsigned char *GoFn();`,
   216  		support: `//export GoFn
   217                            func GoFn() *byte { return new(byte) }`,
   218  		body: `C.GoFn()`,
   219  		fail: true,
   220  	},
   221  	{
   222  		// Returning a C pointer is fine.
   223  		name: "exportok",
   224  		c: `#include <stdlib.h>
   225                      extern unsigned char *GoFn();`,
   226  		support: `//export GoFn
   227                            func GoFn() *byte { return (*byte)(C.malloc(1)) }`,
   228  		body: `C.GoFn()`,
   229  	},
   230  }
   231  
   232  func main() {
   233  	os.Exit(doTests())
   234  }
   235  
   236  func doTests() int {
   237  	dir, err := ioutil.TempDir("", "cgoerrors")
   238  	if err != nil {
   239  		fmt.Fprintln(os.Stderr, err)
   240  		return 2
   241  	}
   242  	defer os.RemoveAll(dir)
   243  
   244  	workers := runtime.NumCPU() + 1
   245  
   246  	var wg sync.WaitGroup
   247  	c := make(chan int)
   248  	errs := make(chan int)
   249  	for i := 0; i < workers; i++ {
   250  		wg.Add(1)
   251  		go func() {
   252  			worker(dir, c, errs)
   253  			wg.Done()
   254  		}()
   255  	}
   256  
   257  	for i := range ptrTests {
   258  		c <- i
   259  	}
   260  	close(c)
   261  
   262  	go func() {
   263  		wg.Wait()
   264  		close(errs)
   265  	}()
   266  
   267  	tot := 0
   268  	for e := range errs {
   269  		tot += e
   270  	}
   271  	return tot
   272  }
   273  
   274  func worker(dir string, c, errs chan int) {
   275  	e := 0
   276  	for i := range c {
   277  		if !doOne(dir, i) {
   278  			e++
   279  		}
   280  	}
   281  	if e > 0 {
   282  		errs <- e
   283  	}
   284  }
   285  
   286  func doOne(dir string, i int) bool {
   287  	t := &ptrTests[i]
   288  
   289  	name := filepath.Join(dir, fmt.Sprintf("t%d.go", i))
   290  	f, err := os.Create(name)
   291  	if err != nil {
   292  		fmt.Fprintln(os.Stderr, err)
   293  		return false
   294  	}
   295  
   296  	b := bufio.NewWriter(f)
   297  	fmt.Fprintln(b, `package main`)
   298  	fmt.Fprintln(b)
   299  	fmt.Fprintln(b, `/*`)
   300  	fmt.Fprintln(b, t.c)
   301  	fmt.Fprintln(b, `*/`)
   302  	fmt.Fprintln(b, `import "C"`)
   303  	fmt.Fprintln(b)
   304  	for _, imp := range t.imports {
   305  		fmt.Fprintln(b, `import "`+imp+`"`)
   306  	}
   307  	if len(t.imports) > 0 {
   308  		fmt.Fprintln(b)
   309  	}
   310  	if len(t.support) > 0 {
   311  		fmt.Fprintln(b, t.support)
   312  		fmt.Fprintln(b)
   313  	}
   314  	fmt.Fprintln(b, `func main() {`)
   315  	fmt.Fprintln(b, t.body)
   316  	fmt.Fprintln(b, `}`)
   317  
   318  	if err := b.Flush(); err != nil {
   319  		fmt.Fprintf(os.Stderr, "flushing %s: %v\n", name, err)
   320  		return false
   321  	}
   322  	if err := f.Close(); err != nil {
   323  		fmt.Fprintln(os.Stderr, "closing %s: %v\n", name, err)
   324  		return false
   325  	}
   326  
   327  	ok := true
   328  
   329  	cmd := exec.Command("go", "run", name)
   330  	cmd.Dir = dir
   331  
   332  	if t.expensive {
   333  		cmd.Env = cgocheckEnv("1")
   334  		buf, err := cmd.CombinedOutput()
   335  		if err != nil {
   336  			var errbuf bytes.Buffer
   337  			if t.fail {
   338  				fmt.Fprintf(&errbuf, "test %s marked expensive but failed when not expensive: %v\n", t.name, err)
   339  			} else {
   340  				fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=1: %v\n", t.name, err)
   341  			}
   342  			reportTestOutput(&errbuf, t.name, buf)
   343  			os.Stderr.Write(errbuf.Bytes())
   344  			ok = false
   345  		}
   346  
   347  		cmd = exec.Command("go", "run", name)
   348  		cmd.Dir = dir
   349  	}
   350  
   351  	if t.expensive {
   352  		cmd.Env = cgocheckEnv("2")
   353  	}
   354  
   355  	buf, err := cmd.CombinedOutput()
   356  
   357  	if t.fail {
   358  		if err == nil {
   359  			var errbuf bytes.Buffer
   360  			fmt.Fprintf(&errbuf, "test %s did not fail as expected\n", t.name)
   361  			reportTestOutput(&errbuf, t.name, buf)
   362  			os.Stderr.Write(errbuf.Bytes())
   363  			ok = false
   364  		} else if !bytes.Contains(buf, []byte("Go pointer")) {
   365  			var errbuf bytes.Buffer
   366  			fmt.Fprintf(&errbuf, "test %s output does not contain expected error (failed with %v)\n", t.name, err)
   367  			reportTestOutput(&errbuf, t.name, buf)
   368  			os.Stderr.Write(errbuf.Bytes())
   369  			ok = false
   370  		}
   371  	} else {
   372  		if err != nil {
   373  			var errbuf bytes.Buffer
   374  			fmt.Fprintf(&errbuf, "test %s failed unexpectedly: %v\n", t.name, err)
   375  			reportTestOutput(&errbuf, t.name, buf)
   376  			os.Stderr.Write(errbuf.Bytes())
   377  			ok = false
   378  		}
   379  
   380  		if !t.expensive && ok {
   381  			// Make sure it passes with the expensive checks.
   382  			cmd := exec.Command("go", "run", name)
   383  			cmd.Dir = dir
   384  			cmd.Env = cgocheckEnv("2")
   385  			buf, err := cmd.CombinedOutput()
   386  			if err != nil {
   387  				var errbuf bytes.Buffer
   388  				fmt.Fprintf(&errbuf, "test %s failed unexpectedly with expensive checks: %v\n", t.name, err)
   389  				reportTestOutput(&errbuf, t.name, buf)
   390  				os.Stderr.Write(errbuf.Bytes())
   391  				ok = false
   392  			}
   393  		}
   394  	}
   395  
   396  	if t.fail && ok {
   397  		cmd = exec.Command("go", "run", name)
   398  		cmd.Dir = dir
   399  		cmd.Env = cgocheckEnv("0")
   400  		buf, err := cmd.CombinedOutput()
   401  		if err != nil {
   402  			var errbuf bytes.Buffer
   403  			fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=0: %v\n", t.name, err)
   404  			reportTestOutput(&errbuf, t.name, buf)
   405  			os.Stderr.Write(errbuf.Bytes())
   406  			ok = false
   407  		}
   408  	}
   409  
   410  	return ok
   411  }
   412  
   413  func reportTestOutput(w io.Writer, name string, buf []byte) {
   414  	fmt.Fprintf(w, "=== test %s output ===\n", name)
   415  	fmt.Fprintf(w, "%s", buf)
   416  	fmt.Fprintf(w, "=== end of test %s output ===\n", name)
   417  }
   418  
   419  func cgocheckEnv(val string) []string {
   420  	env := []string{"GODEBUG=cgocheck=" + val}
   421  	for _, e := range os.Environ() {
   422  		if !strings.HasPrefix(e, "GODEBUG=") {
   423  			env = append(env, e)
   424  		}
   425  	}
   426  	return env
   427  }