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