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) |
| 37 | if !ok || id.Name != "C" { |
| 38 | return |
| 39 | } |
| 40 | |
Alan Donovan | 07a22db | 2016-10-24 11:00:19 -0400 | [diff] [blame] | 41 | // A call to C.CBytes passes a pointer but is always safe. |
| 42 | if sel.Sel.Name == "CBytes" { |
| 43 | return |
| 44 | } |
| 45 | |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 46 | for _, arg := range x.Args { |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame^] | 47 | if !typeOKForCgoCall(cgoBaseType(f, arg), make(map[types.Type]bool)) { |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 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 { |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame^] | 56 | if !typeOKForCgoCall(cgoBaseType(f, u.X), make(map[types.Type]bool)) { |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 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 |
Brad Fitzpatrick | 5fea2cc | 2016-03-01 23:21:55 +0000 | [diff] [blame] | 64 | // unsafe.Pointer to find the real type. It converts: |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 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 |
Ian Lance Taylor | 182a9db | 2016-02-02 13:23:32 -0800 | [diff] [blame] | 80 | if t == nil { |
| 81 | break |
| 82 | } |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 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 | |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame^] | 113 | // 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] | 114 | // 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^] | 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] { |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 118 | return true |
| 119 | } |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame^] | 120 | m[t] = true |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 121 | switch t := t.Underlying().(type) { |
| 122 | case *types.Chan, *types.Map, *types.Signature, *types.Slice: |
| 123 | return false |
| 124 | case *types.Pointer: |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame^] | 125 | return typeOKForCgoCall(t.Elem(), m) |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 126 | case *types.Array: |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame^] | 127 | return typeOKForCgoCall(t.Elem(), m) |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 128 | case *types.Struct: |
| 129 | for i := 0; i < t.NumFields(); i++ { |
Ian Lance Taylor | 27fb26c | 2016-12-20 18:16:17 -0800 | [diff] [blame^] | 130 | if !typeOKForCgoCall(t.Field(i).Type(), m) { |
Ian Lance Taylor | ebf64bc | 2015-11-18 11:09:43 -0800 | [diff] [blame] | 131 | return false |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | return true |
| 136 | } |