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.
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
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.
22 "check for types that may not be passed to cgo calls",
27 func checkCgoCall(f
*File
, node ast
.Node
) {
28 x
:= node
.(*ast
.CallExpr
)
30 // We are only looking for calls to functions imported from
32 sel
, ok
:= x
.Fun
.(*ast
.SelectorExpr
)
36 id
, ok
:= sel
.X
.(*ast
.Ident
)
41 pkgname
, ok
:= f
.pkg
.uses
[id
].(*types
.PkgName
)
42 if !ok || pkgname
.Imported().Path() != "C" {
46 // A call to C.CBytes passes a pointer but is always safe.
47 if sel
.Sel
.Name
== "CBytes" {
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")
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
) {
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")
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) {
75 if len(arg
.Args
) == 1 && f
.hasBasicType(arg
.Fun
, types
.UnsafePointer
) {
76 return cgoBaseType(f
, arg
.Args
[0])
79 call
, ok
:= arg
.X
.(*ast
.CallExpr
)
80 if !ok ||
len(call
.Args
) != 1 {
84 t
:= f
.pkg
.types
[call
.Fun
].Type
88 ptr
, ok
:= t
.Underlying().(*types
.Pointer
)
92 // Here arg is *(*p)(v)
93 elem
, ok
:= ptr
.Elem().Underlying().(*types
.Basic
)
94 if !ok || elem
.Kind() != types
.UnsafePointer
{
97 // Here arg is *(*unsafe.Pointer)(v)
98 call
, ok
= call
.Args
[0].(*ast
.CallExpr
)
99 if !ok ||
len(call
.Args
) != 1 {
102 // Here arg is *(*unsafe.Pointer)(f(v))
103 if !f
.hasBasicType(call
.Fun
, types
.UnsafePointer
) {
106 // Here arg is *(*unsafe.Pointer)(unsafe.Pointer(v))
107 u
, ok
:= call
.Args
[0].(*ast
.UnaryExpr
)
108 if !ok || u
.Op
!= token
.AND
{
111 // Here arg is *(*unsafe.Pointer)(unsafe.Pointer(&v))
112 return cgoBaseType(f
, u
.X
)
115 return f
.pkg
.types
[arg
].Type
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
] {
126 switch t
:= t
.Underlying().(type) {
127 case *types
.Chan
, *types
.Map
, *types
.Signature
, *types
.Slice
:
130 return typeOKForCgoCall(t
.Elem(), m
)
132 return typeOKForCgoCall(t
.Elem(), m
)
134 for i
:= 0; i
< t
.NumFields(); i
++ {
135 if !typeOKForCgoCall(t
.Field(i
).Type(), m
) {