2016-03-01 15:57:46 -07:00
|
|
|
// Copyright 2015 The Go Authors. All rights reserved.
|
2015-11-18 12:09:43 -07:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// Check for invalid cgo pointer passing.
|
|
|
|
// This looks for code that uses cgo to call C code passing values
|
|
|
|
// whose types are almost always invalid according to the cgo pointer
|
|
|
|
// sharing rules.
|
|
|
|
// Specifically, it warns about attempts to pass a Go chan, map, func,
|
|
|
|
// or slice to C, either directly, or via a pointer, array, or struct.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"go/ast"
|
|
|
|
"go/token"
|
|
|
|
"go/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
register("cgocall",
|
|
|
|
"check for types that may not be passed to cgo calls",
|
|
|
|
checkCgoCall,
|
|
|
|
callExpr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkCgoCall(f *File, node ast.Node) {
|
|
|
|
x := node.(*ast.CallExpr)
|
|
|
|
|
|
|
|
// We are only looking for calls to functions imported from
|
|
|
|
// the "C" package.
|
|
|
|
sel, ok := x.Fun.(*ast.SelectorExpr)
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
id, ok := sel.X.(*ast.Ident)
|
|
|
|
if !ok || id.Name != "C" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-10-24 09:00:19 -06:00
|
|
|
// A call to C.CBytes passes a pointer but is always safe.
|
|
|
|
if sel.Sel.Name == "CBytes" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-11-18 12:09:43 -07:00
|
|
|
for _, arg := range x.Args {
|
|
|
|
if !typeOKForCgoCall(cgoBaseType(f, arg)) {
|
|
|
|
f.Badf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for passing the address of a bad type.
|
|
|
|
if conv, ok := arg.(*ast.CallExpr); ok && len(conv.Args) == 1 && f.hasBasicType(conv.Fun, types.UnsafePointer) {
|
|
|
|
arg = conv.Args[0]
|
|
|
|
}
|
|
|
|
if u, ok := arg.(*ast.UnaryExpr); ok && u.Op == token.AND {
|
|
|
|
if !typeOKForCgoCall(cgoBaseType(f, u.X)) {
|
|
|
|
f.Badf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// cgoBaseType tries to look through type conversions involving
|
2016-03-01 16:21:55 -07:00
|
|
|
// unsafe.Pointer to find the real type. It converts:
|
2015-11-18 12:09:43 -07:00
|
|
|
// unsafe.Pointer(x) => x
|
|
|
|
// *(*unsafe.Pointer)(unsafe.Pointer(&x)) => x
|
|
|
|
func cgoBaseType(f *File, arg ast.Expr) types.Type {
|
|
|
|
switch arg := arg.(type) {
|
|
|
|
case *ast.CallExpr:
|
|
|
|
if len(arg.Args) == 1 && f.hasBasicType(arg.Fun, types.UnsafePointer) {
|
|
|
|
return cgoBaseType(f, arg.Args[0])
|
|
|
|
}
|
|
|
|
case *ast.StarExpr:
|
|
|
|
call, ok := arg.X.(*ast.CallExpr)
|
|
|
|
if !ok || len(call.Args) != 1 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Here arg is *f(v).
|
|
|
|
t := f.pkg.types[call.Fun].Type
|
2016-02-02 14:23:32 -07:00
|
|
|
if t == nil {
|
|
|
|
break
|
|
|
|
}
|
2015-11-18 12:09:43 -07:00
|
|
|
ptr, ok := t.Underlying().(*types.Pointer)
|
|
|
|
if !ok {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Here arg is *(*p)(v)
|
|
|
|
elem, ok := ptr.Elem().Underlying().(*types.Basic)
|
|
|
|
if !ok || elem.Kind() != types.UnsafePointer {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Here arg is *(*unsafe.Pointer)(v)
|
|
|
|
call, ok = call.Args[0].(*ast.CallExpr)
|
|
|
|
if !ok || len(call.Args) != 1 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Here arg is *(*unsafe.Pointer)(f(v))
|
|
|
|
if !f.hasBasicType(call.Fun, types.UnsafePointer) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Here arg is *(*unsafe.Pointer)(unsafe.Pointer(v))
|
|
|
|
u, ok := call.Args[0].(*ast.UnaryExpr)
|
|
|
|
if !ok || u.Op != token.AND {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Here arg is *(*unsafe.Pointer)(unsafe.Pointer(&v))
|
|
|
|
return cgoBaseType(f, u.X)
|
|
|
|
}
|
|
|
|
|
|
|
|
return f.pkg.types[arg].Type
|
|
|
|
}
|
|
|
|
|
|
|
|
// typeOKForCgoCall returns true if the type of arg is OK to pass to a
|
2016-03-01 16:21:55 -07:00
|
|
|
// C function using cgo. This is not true for Go types with embedded
|
2015-11-18 12:09:43 -07:00
|
|
|
// pointers.
|
|
|
|
func typeOKForCgoCall(t types.Type) bool {
|
|
|
|
if t == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
switch t := t.Underlying().(type) {
|
|
|
|
case *types.Chan, *types.Map, *types.Signature, *types.Slice:
|
|
|
|
return false
|
|
|
|
case *types.Pointer:
|
|
|
|
return typeOKForCgoCall(t.Elem())
|
|
|
|
case *types.Array:
|
|
|
|
return typeOKForCgoCall(t.Elem())
|
|
|
|
case *types.Struct:
|
|
|
|
for i := 0; i < t.NumFields(); i++ {
|
|
|
|
if !typeOKForCgoCall(t.Field(i).Type()) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|