github.com/sanprasirt/go@v0.0.0-20170607001320-a027466e4b6d/src/cmd/vet/cgo.go (about) 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 // 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 12 package main 13 14 import ( 15 "go/ast" 16 "go/token" 17 "go/types" 18 ) 19 20 func init() { 21 register("cgocall", 22 "check for types that may not be passed to cgo calls", 23 checkCgoCall, 24 callExpr) 25 } 26 27 func 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 41 // A call to C.CBytes passes a pointer but is always safe. 42 if sel.Sel.Name == "CBytes" { 43 return 44 } 45 46 for _, arg := range x.Args { 47 if !typeOKForCgoCall(cgoBaseType(f, arg), make(map[types.Type]bool)) { 48 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 { 56 if !typeOKForCgoCall(cgoBaseType(f, u.X), make(map[types.Type]bool)) { 57 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 64 // unsafe.Pointer to find the real type. It converts: 65 // unsafe.Pointer(x) => x 66 // *(*unsafe.Pointer)(unsafe.Pointer(&x)) => x 67 func 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 80 if t == nil { 81 break 82 } 83 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 113 // typeOKForCgoCall reports whether the type of arg is OK to pass to a 114 // C function using cgo. This is not true for Go types with embedded 115 // pointers. m is used to avoid infinite recursion on recursive types. 116 func typeOKForCgoCall(t types.Type, m map[types.Type]bool) bool { 117 if t == nil || m[t] { 118 return true 119 } 120 m[t] = true 121 switch t := t.Underlying().(type) { 122 case *types.Chan, *types.Map, *types.Signature, *types.Slice: 123 return false 124 case *types.Pointer: 125 return typeOKForCgoCall(t.Elem(), m) 126 case *types.Array: 127 return typeOKForCgoCall(t.Elem(), m) 128 case *types.Struct: 129 for i := 0; i < t.NumFields(); i++ { 130 if !typeOKForCgoCall(t.Field(i).Type(), m) { 131 return false 132 } 133 } 134 } 135 return true 136 }