]> Cypherpunks.ru repositories - gostls13.git/blob - misc/cgo/errors/ptr.go
cmd/compile: fix value range check for complex constants
[gostls13.git] / misc / cgo / errors / ptr.go
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
214 func main() {
215         os.Exit(doTests())
216 }
217
218 func doTests() int {
219         dir, err := ioutil.TempDir("", "cgoerrors")
220         if err != nil {
221                 fmt.Fprintln(os.Stderr, err)
222                 return 2
223         }
224         defer os.RemoveAll(dir)
225
226         workers := runtime.NumCPU() + 1
227
228         var wg sync.WaitGroup
229         c := make(chan int)
230         errs := make(chan int)
231         for i := 0; i < workers; i++ {
232                 wg.Add(1)
233                 go func() {
234                         worker(dir, c, errs)
235                         wg.Done()
236                 }()
237         }
238
239         for i := range ptrTests {
240                 c <- i
241         }
242         close(c)
243
244         go func() {
245                 wg.Wait()
246                 close(errs)
247         }()
248
249         tot := 0
250         for e := range errs {
251                 tot += e
252         }
253         return tot
254 }
255
256 func worker(dir string, c, errs chan int) {
257         e := 0
258         for i := range c {
259                 if !doOne(dir, i) {
260                         e++
261                 }
262         }
263         if e > 0 {
264                 errs <- e
265         }
266 }
267
268 func doOne(dir string, i int) bool {
269         t := &ptrTests[i]
270
271         name := filepath.Join(dir, fmt.Sprintf("t%d.go", i))
272         f, err := os.Create(name)
273         if err != nil {
274                 fmt.Fprintln(os.Stderr, err)
275                 return false
276         }
277
278         b := bufio.NewWriter(f)
279         fmt.Fprintln(b, `package main`)
280         fmt.Fprintln(b)
281         fmt.Fprintln(b, `/*`)
282         fmt.Fprintln(b, t.c)
283         fmt.Fprintln(b, `*/`)
284         fmt.Fprintln(b, `import "C"`)
285         fmt.Fprintln(b)
286         for _, imp := range t.imports {
287                 fmt.Fprintln(b, `import "`+imp+`"`)
288         }
289         if len(t.imports) > 0 {
290                 fmt.Fprintln(b)
291         }
292         if len(t.support) > 0 {
293                 fmt.Fprintln(b, t.support)
294                 fmt.Fprintln(b)
295         }
296         fmt.Fprintln(b, `func main() {`)
297         fmt.Fprintln(b, t.body)
298         fmt.Fprintln(b, `}`)
299
300         if err := b.Flush(); err != nil {
301                 fmt.Fprintf(os.Stderr, "flushing %s: %v\n", name, err)
302                 return false
303         }
304         if err := f.Close(); err != nil {
305                 fmt.Fprintln(os.Stderr, "closing %s: %v\n", name, err)
306                 return false
307         }
308
309         ok := true
310
311         cmd := exec.Command("go", "run", name)
312         cmd.Dir = dir
313
314         if t.expensive {
315                 cmd.Env = cgocheckEnv("1")
316                 buf, err := cmd.CombinedOutput()
317                 if err != nil {
318                         var errbuf bytes.Buffer
319                         if t.fail {
320                                 fmt.Fprintf(&errbuf, "test %s marked expensive but failed when not expensive: %v\n", t.name, err)
321                         } else {
322                                 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=1: %v\n", t.name, err)
323                         }
324                         reportTestOutput(&errbuf, t.name, buf)
325                         os.Stderr.Write(errbuf.Bytes())
326                         ok = false
327                 }
328
329                 cmd = exec.Command("go", "run", name)
330                 cmd.Dir = dir
331         }
332
333         if t.expensive {
334                 cmd.Env = cgocheckEnv("2")
335         }
336
337         buf, err := cmd.CombinedOutput()
338
339         if t.fail {
340                 if err == nil {
341                         var errbuf bytes.Buffer
342                         fmt.Fprintf(&errbuf, "test %s did not fail as expected\n", t.name)
343                         reportTestOutput(&errbuf, t.name, buf)
344                         os.Stderr.Write(errbuf.Bytes())
345                         ok = false
346                 } else if !bytes.Contains(buf, []byte("Go pointer")) {
347                         var errbuf bytes.Buffer
348                         fmt.Fprintf(&errbuf, "test %s output does not contain expected error (failed with %v)\n", t.name, err)
349                         reportTestOutput(&errbuf, t.name, buf)
350                         os.Stderr.Write(errbuf.Bytes())
351                         ok = false
352                 }
353         } else {
354                 if err != nil {
355                         var errbuf bytes.Buffer
356                         fmt.Fprintf(&errbuf, "test %s failed unexpectedly: %v\n", t.name, err)
357                         reportTestOutput(&errbuf, t.name, buf)
358                         os.Stderr.Write(errbuf.Bytes())
359                         ok = false
360                 }
361
362                 if !t.expensive && ok {
363                         // Make sure it passes with the expensive checks.
364                         cmd := exec.Command("go", "run", name)
365                         cmd.Dir = dir
366                         cmd.Env = cgocheckEnv("2")
367                         buf, err := cmd.CombinedOutput()
368                         if err != nil {
369                                 var errbuf bytes.Buffer
370                                 fmt.Fprintf(&errbuf, "test %s failed unexpectedly with expensive checks: %v\n", t.name, err)
371                                 reportTestOutput(&errbuf, t.name, buf)
372                                 os.Stderr.Write(errbuf.Bytes())
373                                 ok = false
374                         }
375                 }
376         }
377
378         if t.fail && ok {
379                 cmd = exec.Command("go", "run", name)
380                 cmd.Dir = dir
381                 cmd.Env = cgocheckEnv("0")
382                 buf, err := cmd.CombinedOutput()
383                 if err != nil {
384                         var errbuf bytes.Buffer
385                         fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=0: %v\n", t.name, err)
386                         reportTestOutput(&errbuf, t.name, buf)
387                         os.Stderr.Write(errbuf.Bytes())
388                         ok = false
389                 }
390         }
391
392         return ok
393 }
394
395 func reportTestOutput(w io.Writer, name string, buf []byte) {
396         fmt.Fprintf(w, "=== test %s output ===\n", name)
397         fmt.Fprintf(w, "%s", buf)
398         fmt.Fprintf(w, "=== end of test %s output ===\n", name)
399 }
400
401 func cgocheckEnv(val string) []string {
402         env := []string{"GODEBUG=cgocheck=" + val}
403         for _, e := range os.Environ() {
404                 if !strings.HasPrefix(e, "GODEBUG=") {
405                         env = append(env, e)
406                 }
407         }
408         return env
409 }