...
Run Format

Source file src/cmd/vet/cgo.go

Documentation: cmd/vet

     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  }
   142  

View as plain text