github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/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 for _, arg := range x.Args { 42 if !typeOKForCgoCall(cgoBaseType(f, arg)) { 43 f.Badf(arg.Pos(), "possibly passing Go type with embedded pointer to C") 44 } 45 46 // Check for passing the address of a bad type. 47 if conv, ok := arg.(*ast.CallExpr); ok && len(conv.Args) == 1 && f.hasBasicType(conv.Fun, types.UnsafePointer) { 48 arg = conv.Args[0] 49 } 50 if u, ok := arg.(*ast.UnaryExpr); ok && u.Op == token.AND { 51 if !typeOKForCgoCall(cgoBaseType(f, u.X)) { 52 f.Badf(arg.Pos(), "possibly passing Go type with embedded pointer to C") 53 } 54 } 55 } 56 } 57 58 // cgoBaseType tries to look through type conversions involving 59 // unsafe.Pointer to find the real type. It converts: 60 // unsafe.Pointer(x) => x 61 // *(*unsafe.Pointer)(unsafe.Pointer(&x)) => x 62 func cgoBaseType(f *File, arg ast.Expr) types.Type { 63 switch arg := arg.(type) { 64 case *ast.CallExpr: 65 if len(arg.Args) == 1 && f.hasBasicType(arg.Fun, types.UnsafePointer) { 66 return cgoBaseType(f, arg.Args[0]) 67 } 68 case *ast.StarExpr: 69 call, ok := arg.X.(*ast.CallExpr) 70 if !ok || len(call.Args) != 1 { 71 break 72 } 73 // Here arg is *f(v). 74 t := f.pkg.types[call.Fun].Type 75 if t == nil { 76 break 77 } 78 ptr, ok := t.Underlying().(*types.Pointer) 79 if !ok { 80 break 81 } 82 // Here arg is *(*p)(v) 83 elem, ok := ptr.Elem().Underlying().(*types.Basic) 84 if !ok || elem.Kind() != types.UnsafePointer { 85 break 86 } 87 // Here arg is *(*unsafe.Pointer)(v) 88 call, ok = call.Args[0].(*ast.CallExpr) 89 if !ok || len(call.Args) != 1 { 90 break 91 } 92 // Here arg is *(*unsafe.Pointer)(f(v)) 93 if !f.hasBasicType(call.Fun, types.UnsafePointer) { 94 break 95 } 96 // Here arg is *(*unsafe.Pointer)(unsafe.Pointer(v)) 97 u, ok := call.Args[0].(*ast.UnaryExpr) 98 if !ok || u.Op != token.AND { 99 break 100 } 101 // Here arg is *(*unsafe.Pointer)(unsafe.Pointer(&v)) 102 return cgoBaseType(f, u.X) 103 } 104 105 return f.pkg.types[arg].Type 106 } 107 108 // typeOKForCgoCall returns true if the type of arg is OK to pass to a 109 // C function using cgo. This is not true for Go types with embedded 110 // pointers. 111 func typeOKForCgoCall(t types.Type) bool { 112 if t == nil { 113 return true 114 } 115 switch t := t.Underlying().(type) { 116 case *types.Chan, *types.Map, *types.Signature, *types.Slice: 117 return false 118 case *types.Pointer: 119 return typeOKForCgoCall(t.Elem()) 120 case *types.Array: 121 return typeOKForCgoCall(t.Elem()) 122 case *types.Struct: 123 for i := 0; i < t.NumFields(); i++ { 124 if !typeOKForCgoCall(t.Field(i).Type()) { 125 return false 126 } 127 } 128 } 129 return true 130 }