blob: 984911c489f9d70c8c71e371ccbc6c1ac452146f [file] [log] [blame]
Brad Fitzpatrick51947442016-03-01 22:57:46 +00001// Copyright 2015 The Go Authors. All rights reserved.
Ian Lance Taylorebf64bc2015-11-18 11:09:43 -08002// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Check for invalid cgo pointer passing.
6// This looks for code that uses cgo to call C code passing values
7// whose types are almost always invalid according to the cgo pointer
8// sharing rules.
9// Specifically, it warns about attempts to pass a Go chan, map, func,
10// or slice to C, either directly, or via a pointer, array, or struct.
11
12package main
13
14import (
15 "go/ast"
16 "go/token"
17 "go/types"
18)
19
20func init() {
21 register("cgocall",
22 "check for types that may not be passed to cgo calls",
23 checkCgoCall,
24 callExpr)
25}
26
27func checkCgoCall(f *File, node ast.Node) {
28 x := node.(*ast.CallExpr)
29
30 // We are only looking for calls to functions imported from
31 // the "C" package.
32 sel, ok := x.Fun.(*ast.SelectorExpr)
33 if !ok {
34 return
35 }
36 id, ok := sel.X.(*ast.Ident)
37 if !ok || id.Name != "C" {
38 return
39 }
40
Alan Donovan07a22db2016-10-24 11:00:19 -040041 // A call to C.CBytes passes a pointer but is always safe.
42 if sel.Sel.Name == "CBytes" {
43 return
44 }
45
Ian Lance Taylorebf64bc2015-11-18 11:09:43 -080046 for _, arg := range x.Args {
Ian Lance Taylor27fb26c2016-12-20 18:16:17 -080047 if !typeOKForCgoCall(cgoBaseType(f, arg), make(map[types.Type]bool)) {
Ian Lance Taylorebf64bc2015-11-18 11:09:43 -080048 f.Badf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
49 }
50
51 // Check for passing the address of a bad type.
52 if conv, ok := arg.(*ast.CallExpr); ok && len(conv.Args) == 1 && f.hasBasicType(conv.Fun, types.UnsafePointer) {
53 arg = conv.Args[0]
54 }
55 if u, ok := arg.(*ast.UnaryExpr); ok && u.Op == token.AND {
Ian Lance Taylor27fb26c2016-12-20 18:16:17 -080056 if !typeOKForCgoCall(cgoBaseType(f, u.X), make(map[types.Type]bool)) {
Ian Lance Taylorebf64bc2015-11-18 11:09:43 -080057 f.Badf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
58 }
59 }
60 }
61}
62
63// cgoBaseType tries to look through type conversions involving
Brad Fitzpatrick5fea2cc2016-03-01 23:21:55 +000064// unsafe.Pointer to find the real type. It converts:
Ian Lance Taylorebf64bc2015-11-18 11:09:43 -080065// unsafe.Pointer(x) => x
66// *(*unsafe.Pointer)(unsafe.Pointer(&x)) => x
67func cgoBaseType(f *File, arg ast.Expr) types.Type {
68 switch arg := arg.(type) {
69 case *ast.CallExpr:
70 if len(arg.Args) == 1 && f.hasBasicType(arg.Fun, types.UnsafePointer) {
71 return cgoBaseType(f, arg.Args[0])
72 }
73 case *ast.StarExpr:
74 call, ok := arg.X.(*ast.CallExpr)
75 if !ok || len(call.Args) != 1 {
76 break
77 }
78 // Here arg is *f(v).
79 t := f.pkg.types[call.Fun].Type
Ian Lance Taylor182a9db2016-02-02 13:23:32 -080080 if t == nil {
81 break
82 }
Ian Lance Taylorebf64bc2015-11-18 11:09:43 -080083 ptr, ok := t.Underlying().(*types.Pointer)
84 if !ok {
85 break
86 }
87 // Here arg is *(*p)(v)
88 elem, ok := ptr.Elem().Underlying().(*types.Basic)
89 if !ok || elem.Kind() != types.UnsafePointer {
90 break
91 }
92 // Here arg is *(*unsafe.Pointer)(v)
93 call, ok = call.Args[0].(*ast.CallExpr)
94 if !ok || len(call.Args) != 1 {
95 break
96 }
97 // Here arg is *(*unsafe.Pointer)(f(v))
98 if !f.hasBasicType(call.Fun, types.UnsafePointer) {
99 break
100 }
101 // Here arg is *(*unsafe.Pointer)(unsafe.Pointer(v))
102 u, ok := call.Args[0].(*ast.UnaryExpr)
103 if !ok || u.Op != token.AND {
104 break
105 }
106 // Here arg is *(*unsafe.Pointer)(unsafe.Pointer(&v))
107 return cgoBaseType(f, u.X)
108 }
109
110 return f.pkg.types[arg].Type
111}
112
Ian Lance Taylor27fb26c2016-12-20 18:16:17 -0800113// typeOKForCgoCall reports whether the type of arg is OK to pass to a
Brad Fitzpatrick5fea2cc2016-03-01 23:21:55 +0000114// C function using cgo. This is not true for Go types with embedded
Ian Lance Taylor27fb26c2016-12-20 18:16:17 -0800115// pointers. m is used to avoid infinite recursion on recursive types.
116func typeOKForCgoCall(t types.Type, m map[types.Type]bool) bool {
117 if t == nil || m[t] {
Ian Lance Taylorebf64bc2015-11-18 11:09:43 -0800118 return true
119 }
Ian Lance Taylor27fb26c2016-12-20 18:16:17 -0800120 m[t] = true
Ian Lance Taylorebf64bc2015-11-18 11:09:43 -0800121 switch t := t.Underlying().(type) {
122 case *types.Chan, *types.Map, *types.Signature, *types.Slice:
123 return false
124 case *types.Pointer:
Ian Lance Taylor27fb26c2016-12-20 18:16:17 -0800125 return typeOKForCgoCall(t.Elem(), m)
Ian Lance Taylorebf64bc2015-11-18 11:09:43 -0800126 case *types.Array:
Ian Lance Taylor27fb26c2016-12-20 18:16:17 -0800127 return typeOKForCgoCall(t.Elem(), m)
Ian Lance Taylorebf64bc2015-11-18 11:09:43 -0800128 case *types.Struct:
129 for i := 0; i < t.NumFields(); i++ {
Ian Lance Taylor27fb26c2016-12-20 18:16:17 -0800130 if !typeOKForCgoCall(t.Field(i).Type(), m) {
Ian Lance Taylorebf64bc2015-11-18 11:09:43 -0800131 return false
132 }
133 }
134 }
135 return true
136}