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