Brad Fitzpatrick | 5194744 | 2016-03-01 22:57:46 +0000 | [diff] [blame] | 1 | // Copyright 2015 The Go Authors. All rights reserved. |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 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) |
Daniel Martà | bef0055 | 2017-06-13 16:22:06 +0100 | [diff] [blame] | 37 | if !ok { |
| 38 | return |
| 39 | } |
| 40 | |
| 41 | pkgname, ok := f.pkg.uses[id].(*types.PkgName) |
| 42 | if !ok || pkgname.Imported().Path() != "C" { |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 43 | return |
| 44 | } |
| 45 | |
Alan Donovan | 07a22db | 2016-10-24 11:00:19 -0400 | [diff] [blame] | 46 | // A call to C.CBytes passes a pointer but is always safe. |
| 47 | if sel.Sel.Name == "CBytes" { |
| 48 | return |
| 49 | } |
| 50 | |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 51 | for _, arg := range x.Args { |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame] | 52 | if !typeOKForCgoCall(cgoBaseType(f, arg), make(map[types.Type]bool)) { |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 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 { |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame] | 61 | if !typeOKForCgoCall(cgoBaseType(f, u.X), make(map[types.Type]bool)) { |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 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 |
Brad Fitzpatrick | 5fea2cc | 2016-03-01 23:21:55 +0000 | [diff] [blame] | 69 | // unsafe.Pointer to find the real type. It converts: |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 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 |
Ian Lance Taylor | 182a9db | 2016-02-02 13:23:32 -0800 | [diff] [blame] | 85 | if t == nil { |
| 86 | break |
| 87 | } |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 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 | |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame] | 118 | // typeOKForCgoCall reports whether the type of arg is OK to pass to a |
Brad Fitzpatrick | 5fea2cc | 2016-03-01 23:21:55 +0000 | [diff] [blame] | 119 | // C function using cgo. This is not true for Go types with embedded |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame] | 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] { |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 123 | return true |
| 124 | } |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame] | 125 | m[t] = true |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 126 | switch t := t.Underlying().(type) { |
| 127 | case *types.Chan, *types.Map, *types.Signature, *types.Slice: |
| 128 | return false |
| 129 | case *types.Pointer: |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame] | 130 | return typeOKForCgoCall(t.Elem(), m) |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 131 | case *types.Array: |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame] | 132 | return typeOKForCgoCall(t.Elem(), m) |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 133 | case *types.Struct: |
| 134 | for i := 0; i < t.NumFields(); i++ { |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame] | 135 | if !typeOKForCgoCall(t.Field(i).Type(), m) { |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 136 | return false |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | return true |
| 141 | } |