1
2
3
4
5
6
7 package errorstest
8
9 import (
10 "bytes"
11 "flag"
12 "fmt"
13 "os"
14 "os/exec"
15 "path/filepath"
16 "strings"
17 "sync/atomic"
18 "testing"
19 )
20
21 var tmp = flag.String("tmp", "", "use `dir` for temporary files and do not clean up")
22
23
24 type ptrTest struct {
25 name string
26 c string
27 c1 string
28 imports []string
29 support string
30 body string
31 extra []extra
32 fail bool
33 expensive bool
34 }
35
36 type extra struct {
37 name string
38 contents string
39 }
40
41 var ptrTests = []ptrTest{
42 {
43
44 name: "ptr1",
45 c: `typedef struct s1 { int *p; } s1; void f1(s1 *ps) {}`,
46 body: `C.f1(&C.s1{new(C.int)})`,
47 fail: true,
48 },
49 {
50
51 name: "ptr2",
52 c: `typedef struct s2 { int *p; } s2; void f2(s2 *ps) {}`,
53 body: `p := &C.s2{new(C.int)}; C.f2(p)`,
54 fail: true,
55 },
56 {
57
58
59 name: "ok1",
60 c: `struct s3 { int i; int *p; }; void f3(int *p) {}`,
61 body: `p := &C.struct_s3{i: 0, p: new(C.int)}; C.f3(&p.i)`,
62 fail: false,
63 },
64 {
65
66 name: "ptrfield",
67 c: `struct s4 { int i; int *p; }; void f4(int **p) {}`,
68 body: `p := &C.struct_s4{i: 0, p: new(C.int)}; C.f4(&p.p)`,
69 fail: true,
70 },
71 {
72
73
74
75 name: "ptrfieldok",
76 c: `struct s5 { int *p1; int *p2; }; void f5(int **p) {}`,
77 body: `p := &C.struct_s5{p1: nil, p2: new(C.int)}; C.f5(&p.p1)`,
78 fail: false,
79 },
80 {
81
82 name: "sliceok1",
83 c: `void f6(void **p) {}`,
84 imports: []string{"unsafe"},
85 body: `s := []unsafe.Pointer{nil}; C.f6(&s[0])`,
86 fail: false,
87 },
88 {
89
90 name: "sliceptr1",
91 c: `void f7(void **p) {}`,
92 imports: []string{"unsafe"},
93 body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f7(&s[0])`,
94 fail: true,
95 },
96 {
97
98
99
100 name: "sliceptr2",
101 c: `void f8(void **p) {}`,
102 imports: []string{"unsafe"},
103 body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f8(&s[0])`,
104 fail: true,
105 },
106 {
107
108
109 name: "sliceok2",
110 c: `void f9(void **p) {}`,
111 imports: []string{"unsafe"},
112 support: `type S9 struct { p *int; s []unsafe.Pointer }`,
113 body: `i := 0; p := &S9{p:&i, s:[]unsafe.Pointer{nil}}; C.f9(&p.s[0])`,
114 fail: false,
115 },
116 {
117
118
119 name: "sliceok3",
120 c: `void f10(void* p) {}`,
121 imports: []string{"unsafe"},
122 support: `type S10 struct { p *int; a [4]byte }`,
123 body: `i := 0; p := &S10{p:&i}; s := p.a[:]; C.f10(unsafe.Pointer(&s[0]))`,
124 fail: false,
125 },
126 {
127
128
129 name: "sliceok4",
130 c: `typedef void* PV11; void f11(PV11 p) {}`,
131 imports: []string{"unsafe"},
132 support: `type S11 struct { p *int; a [4]byte }`,
133 body: `i := 0; p := &S11{p:&i}; C.f11(C.PV11(unsafe.Pointer(&p.a[0])))`,
134 fail: false,
135 },
136 {
137
138
139 name: "varok",
140 c: `void f12(char** parg) {}`,
141 support: `var hello12 = [...]C.char{'h', 'e', 'l', 'l', 'o'}`,
142 body: `parg := [1]*C.char{&hello12[0]}; C.f12(&parg[0])`,
143 fail: false,
144 },
145 {
146
147
148 name: "var1",
149 c: `void f13(char*** parg) {}`,
150 support: `var hello13 = [...]*C.char{new(C.char)}`,
151 body: `parg := [1]**C.char{&hello13[0]}; C.f13(&parg[0])`,
152 fail: true,
153 },
154 {
155
156 name: "barrier",
157 c: `#include <stdlib.h>
158 char **f14a() { return malloc(sizeof(char*)); }
159 void f14b(char **p) {}`,
160 body: `p := C.f14a(); *p = new(C.char); C.f14b(p)`,
161 fail: true,
162 expensive: true,
163 },
164 {
165
166
167 name: "barrierstruct",
168 c: `#include <stdlib.h>
169 struct s15 { char *a[10]; };
170 struct s15 *f15() { return malloc(sizeof(struct s15)); }
171 void f15b(struct s15 *p) {}`,
172 body: `p := C.f15(); p.a = [10]*C.char{new(C.char)}; C.f15b(p)`,
173 fail: true,
174 expensive: true,
175 },
176 {
177
178
179 name: "barrierslice",
180 c: `#include <stdlib.h>
181 struct s16 { char *a[10]; };
182 struct s16 *f16() { return malloc(sizeof(struct s16)); }
183 void f16b(struct s16 *p) {}`,
184 body: `p := C.f16(); copy(p.a[:], []*C.char{new(C.char)}); C.f16b(p)`,
185 fail: true,
186 expensive: true,
187 },
188 {
189
190
191 name: "barriergcprogarray",
192 c: `#include <stdlib.h>
193 struct s17 { char *a[32769]; };
194 struct s17 *f17() { return malloc(sizeof(struct s17)); }
195 void f17b(struct s17 *p) {}`,
196 body: `p := C.f17(); p.a = [32769]*C.char{new(C.char)}; C.f17b(p)`,
197 fail: true,
198 expensive: true,
199 },
200 {
201
202 name: "barriergcprogarrayheap",
203 c: `#include <stdlib.h>
204 struct s18 { char *a[32769]; };
205 struct s18 *f18() { return malloc(sizeof(struct s18)); }
206 void f18b(struct s18 *p) {}
207 void f18c(void *p) {}`,
208 imports: []string{"unsafe"},
209 body: `p := C.f18(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f18b(p); n[0] = nil; C.f18c(unsafe.Pointer(n))`,
210 fail: true,
211 expensive: true,
212 },
213 {
214
215 name: "barriergcprogstruct",
216 c: `#include <stdlib.h>
217 struct s19a { char *a[32769]; };
218 struct s19b { struct s19a f; };
219 struct s19b *f19() { return malloc(sizeof(struct s19b)); }
220 void f19b(struct s19b *p) {}`,
221 body: `p := C.f19(); p.f = C.struct_s19a{[32769]*C.char{new(C.char)}}; C.f19b(p)`,
222 fail: true,
223 expensive: true,
224 },
225 {
226
227 name: "barriergcprogstructheap",
228 c: `#include <stdlib.h>
229 struct s20a { char *a[32769]; };
230 struct s20b { struct s20a f; };
231 struct s20b *f20() { return malloc(sizeof(struct s20b)); }
232 void f20b(struct s20b *p) {}
233 void f20c(void *p) {}`,
234 imports: []string{"unsafe"},
235 body: `p := C.f20(); n := &C.struct_s20a{[32769]*C.char{new(C.char)}}; p.f = *n; C.f20b(p); n.a[0] = nil; C.f20c(unsafe.Pointer(n))`,
236 fail: true,
237 expensive: true,
238 },
239 {
240
241 name: "export1",
242 c: `extern unsigned char *GoFn21();`,
243 support: `//export GoFn21
244 func GoFn21() *byte { return new(byte) }`,
245 body: `C.GoFn21()`,
246 fail: true,
247 },
248 {
249
250 name: "exportok",
251 c: `#include <stdlib.h>
252 extern unsigned char *GoFn22();`,
253 support: `//export GoFn22
254 func GoFn22() *byte { return (*byte)(C.malloc(1)) }`,
255 body: `C.GoFn22()`,
256 },
257 {
258
259 name: "passstring",
260 c: `#include <stddef.h>
261 typedef struct { const char *p; ptrdiff_t n; } gostring23;
262 gostring23 f23(gostring23 s) { return s; }`,
263 imports: []string{"unsafe"},
264 body: `s := "a"; r := C.f23(*(*C.gostring23)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`,
265 },
266 {
267
268 name: "passstringslice",
269 c: `void f24(void *p) {}`,
270 imports: []string{"strings", "unsafe"},
271 support: `type S24 struct { a [1]string }`,
272 body: `s := S24{a:[1]string{strings.Repeat("a", 2)}}; C.f24(unsafe.Pointer(&s.a[0]))`,
273 fail: true,
274 },
275 {
276
277 name: "retstring",
278 c: `extern void f25();`,
279 imports: []string{"strings"},
280 support: `//export GoStr25
281 func GoStr25() string { return strings.Repeat("a", 2) }`,
282 body: `C.f25()`,
283 c1: `#include <stddef.h>
284 typedef struct { const char *p; ptrdiff_t n; } gostring25;
285 extern gostring25 GoStr25();
286 void f25() { GoStr25(); }`,
287 fail: true,
288 },
289 {
290
291
292
293
294
295 name: "ptrdata1",
296 c: `#include <stdlib.h>
297 void f26(void* p) {}`,
298 imports: []string{"unsafe"},
299 support: `type S26 struct { p *int; a [8*8]byte; u uintptr }`,
300 body: `i := 0; p := &S26{u:uintptr(unsafe.Pointer(&i))}; q := (*S26)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f26(unsafe.Pointer(q))`,
301 fail: false,
302 },
303 {
304
305 name: "ptrdata2",
306 c: `#include <stdlib.h>
307 void f27(void* p) {}`,
308 imports: []string{"unsafe"},
309 support: `type S27 struct { p *int; a [32769*8]byte; q *int; u uintptr }`,
310 body: `i := 0; p := S27{u:uintptr(unsafe.Pointer(&i))}; q := (*S27)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f27(unsafe.Pointer(q))`,
311 fail: false,
312 },
313 {
314
315
316 name: "defer1",
317 c: `typedef struct s28 { int *p; } s28; void f28(s28 *ps) {}`,
318 body: `p := &C.s28{}; defer C.f28(p); p.p = new(C.int)`,
319 fail: true,
320 },
321 {
322
323
324 name: "union1",
325 c: `typedef union { char **p; unsigned long i; } u29; void f29(u29 *pu) {}`,
326 imports: []string{"unsafe"},
327 body: `var b C.char; p := &b; C.f29((*C.u29)(unsafe.Pointer(&p)))`,
328 fail: true,
329 },
330 {
331
332
333
334
335
336 name: "union2",
337 c: `typedef union { unsigned long i; } u39; void f39(u39 *pu) {}`,
338 imports: []string{"unsafe"},
339 body: `var b C.char; p := &b; C.f39((*C.u39)(unsafe.Pointer(&p)))`,
340 fail: false,
341 },
342 {
343
344 name: "preemptduringcall",
345 c: `void f30() {}`,
346 imports: []string{"runtime", "sync"},
347 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.f30(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`,
348 fail: false,
349 },
350 {
351
352 name: "deadline",
353 c: `#define US31 10`,
354 imports: []string{"os", "time"},
355 body: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US31 * time.Microsecond))`,
356 fail: false,
357 },
358 {
359
360 name: "chanrecv",
361 c: `void f32(char** p) {}`,
362 imports: []string{"time"},
363 body: `c := make(chan []*C.char, 2); c <- make([]*C.char, 1); go func() { time.Sleep(10 * time.Second); panic("received twice from chan") }(); C.f32(&(<-c)[0]);`,
364 fail: false,
365 },
366 {
367
368
369
370 name: "structfield",
371 c: `void f33(void* p) {}`,
372 imports: []string{"unsafe"},
373 support: `type S33 struct { p *int; a [8]byte; u uintptr }`,
374 body: `s := &S33{p: new(int)}; C.f33(unsafe.Pointer(&s.a))`,
375 fail: false,
376 },
377 {
378
379
380
381 name: "structfield2",
382 c: `void f34(void* p, int r, void* s) {}`,
383 imports: []string{"unsafe"},
384 support: `type S34 struct { a [8]byte; p *int; b int64; }`,
385 body: `s := &S34{p: new(int)}; C.f34(unsafe.Pointer(&s.a), 32, unsafe.Pointer(&s.b))`,
386 fail: false,
387 },
388 {
389
390
391
392 name: "defer2",
393 c: `void f35(char **pc) {}`,
394 support: `type S35a struct { s []*C.char }; type S35b struct { ps *S35a }`,
395 body: `p := &S35b{&S35a{[]*C.char{nil}}}; defer C.f35(&p.ps.s[0]); p.ps = nil`,
396 fail: false,
397 },
398 {
399
400
401 name: "buffer",
402 c: `void f36(void *p) {}`,
403 imports: []string{"bytes", "unsafe"},
404 body: `var b bytes.Buffer; b.WriteString("a"); C.f36(unsafe.Pointer(&b.Bytes()[0]))`,
405 fail: false,
406 },
407 {
408
409 name: "finalizer",
410 c: `// Nothing to declare.`,
411 imports: []string{"os"},
412 support: `func open37() { os.Open(os.Args[0]) }; var G37 [][]byte`,
413 body: `for i := 0; i < 10000; i++ { G37 = append(G37, make([]byte, 4096)); if i % 100 == 0 { G37 = nil; open37() } }`,
414 fail: false,
415 },
416 {
417
418 name: "structof",
419 c: `// Nothing to declare.`,
420 imports: []string{"reflect"},
421 support: `type MyInt38 int; func (i MyInt38) Get() int { return int(i) }; type Getter38 interface { Get() int }`,
422 body: `t := reflect.StructOf([]reflect.StructField{{Name: "MyInt38", Type: reflect.TypeOf(MyInt38(0)), Anonymous: true}}); v := reflect.New(t).Elem(); v.Interface().(Getter38).Get()`,
423 fail: false,
424 },
425 {
426
427
428 name: "structfieldcast",
429 c: `struct S40i { int i; int* p; }; void f40(struct S40i* p) {}`,
430 support: `type S40 struct { p *int; a C.struct_S40i }`,
431 body: `s := &S40{p: new(int)}; C.f40((*C.struct_S40i)(&s.a))`,
432 fail: false,
433 },
434 }
435
436 func TestPointerChecks(t *testing.T) {
437 dir, exe := buildPtrTests(t)
438
439
440
441
442
443
444 var pending int32
445 for _, pt := range ptrTests {
446 pt := pt
447 t.Run(pt.name, func(t *testing.T) {
448 atomic.AddInt32(&pending, +1)
449 defer func() {
450 if atomic.AddInt32(&pending, -1) == 0 {
451 os.RemoveAll(dir)
452 }
453 }()
454 testOne(t, pt, exe)
455 })
456 }
457 }
458
459 func buildPtrTests(t *testing.T) (dir, exe string) {
460 var gopath string
461 if *tmp != "" {
462 gopath = *tmp
463 dir = ""
464 } else {
465 d, err := os.MkdirTemp("", filepath.Base(t.Name()))
466 if err != nil {
467 t.Fatal(err)
468 }
469 dir = d
470 gopath = d
471 }
472
473 src := filepath.Join(gopath, "src", "ptrtest")
474 if err := os.MkdirAll(src, 0777); err != nil {
475 t.Fatal(err)
476 }
477 if err := os.WriteFile(filepath.Join(src, "go.mod"), []byte("module ptrtest"), 0666); err != nil {
478 t.Fatal(err)
479 }
480
481
482
483 var cgo1, cgo2 bytes.Buffer
484 fmt.Fprintf(&cgo1, "package main\n\n/*\n")
485 fmt.Fprintf(&cgo2, "package main\n\n/*\n")
486
487
488 for _, pt := range ptrTests {
489 cgo := &cgo1
490 if strings.Contains(pt.support, "//export") {
491 cgo = &cgo2
492 }
493 fmt.Fprintf(cgo, "%s\n", pt.c)
494 fmt.Fprintf(&cgo1, "%s\n", pt.c1)
495 }
496 fmt.Fprintf(&cgo1, "*/\nimport \"C\"\n\n")
497 fmt.Fprintf(&cgo2, "*/\nimport \"C\"\n\n")
498
499
500 did1 := make(map[string]bool)
501 did2 := make(map[string]bool)
502 did1["os"] = true
503 fmt.Fprintf(&cgo1, "import \"os\"\n")
504
505 for _, pt := range ptrTests {
506 did := did1
507 cgo := &cgo1
508 if strings.Contains(pt.support, "//export") {
509 did = did2
510 cgo = &cgo2
511 }
512 for _, imp := range pt.imports {
513 if !did[imp] {
514 did[imp] = true
515 fmt.Fprintf(cgo, "import %q\n", imp)
516 }
517 }
518 }
519
520
521 for _, pt := range ptrTests {
522 cgo := &cgo1
523 if strings.Contains(pt.support, "//export") {
524 cgo = &cgo2
525 }
526 fmt.Fprintf(cgo, "%s\nfunc %s() {\n%s\n}\n", pt.support, pt.name, pt.body)
527 }
528
529
530 fmt.Fprintf(&cgo1, "var funcs = map[string]func() {\n")
531 for _, pt := range ptrTests {
532 fmt.Fprintf(&cgo1, "\t%q: %s,\n", pt.name, pt.name)
533 }
534 fmt.Fprintf(&cgo1, "}\n\n")
535 fmt.Fprintf(&cgo1, "%s\n", ptrTestMain)
536
537 if err := os.WriteFile(filepath.Join(src, "cgo1.go"), cgo1.Bytes(), 0666); err != nil {
538 t.Fatal(err)
539 }
540 if err := os.WriteFile(filepath.Join(src, "cgo2.go"), cgo2.Bytes(), 0666); err != nil {
541 t.Fatal(err)
542 }
543
544 cmd := exec.Command("go", "build", "-o", "ptrtest.exe")
545 cmd.Dir = src
546 cmd.Env = append(os.Environ(), "GOPATH="+gopath)
547 out, err := cmd.CombinedOutput()
548 if err != nil {
549 t.Fatalf("go build: %v\n%s", err, out)
550 }
551
552 return dir, filepath.Join(src, "ptrtest.exe")
553 }
554
555 const ptrTestMain = `
556 func main() {
557 for _, arg := range os.Args[1:] {
558 f := funcs[arg]
559 if f == nil {
560 panic("missing func "+arg)
561 }
562 f()
563 }
564 }
565 `
566
567 var csem = make(chan bool, 16)
568
569 func testOne(t *testing.T, pt ptrTest, exe string) {
570 t.Parallel()
571
572
573
574 runcmd := func(cgocheck string) ([]byte, error) {
575 csem <- true
576 defer func() { <-csem }()
577 cmd := exec.Command(exe, pt.name)
578 cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck)
579 return cmd.CombinedOutput()
580 }
581
582 if pt.expensive {
583 buf, err := runcmd("1")
584 if err != nil {
585 t.Logf("%s", buf)
586 if pt.fail {
587 t.Fatalf("test marked expensive, but failed when not expensive: %v", err)
588 } else {
589 t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err)
590 }
591 }
592
593 }
594
595 cgocheck := ""
596 if pt.expensive {
597 cgocheck = "2"
598 }
599
600 buf, err := runcmd(cgocheck)
601 if pt.fail {
602 if err == nil {
603 t.Logf("%s", buf)
604 t.Fatalf("did not fail as expected")
605 } else if !bytes.Contains(buf, []byte("Go pointer")) {
606 t.Logf("%s", buf)
607 t.Fatalf("did not print expected error (failed with %v)", err)
608 }
609 } else {
610 if err != nil {
611 t.Logf("%s", buf)
612 t.Fatalf("failed unexpectedly: %v", err)
613 }
614
615 if !pt.expensive {
616
617 buf, err := runcmd("2")
618 if err != nil {
619 t.Logf("%s", buf)
620 t.Fatalf("failed unexpectedly with expensive checks: %v", err)
621 }
622 }
623 }
624
625 if pt.fail {
626 buf, err := runcmd("0")
627 if err != nil {
628 t.Logf("%s", buf)
629 t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err)
630 }
631 }
632 }
633
View as plain text