Home | History | Annotate | Download | only in errors
      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 }
    517