Home | History | Annotate | Download | only in 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