...

Source file src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unsafeptr/unsafeptr.go

     1	// Copyright 2014 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	// Package unsafeptr defines an Analyzer that checks for invalid
     6	// conversions of uintptr to unsafe.Pointer.
     7	package unsafeptr
     8	
     9	import (
    10		"go/ast"
    11		"go/token"
    12		"go/types"
    13	
    14		"golang.org/x/tools/go/analysis"
    15		"golang.org/x/tools/go/analysis/passes/inspect"
    16		"golang.org/x/tools/go/ast/inspector"
    17	)
    18	
    19	const Doc = `check for invalid conversions of uintptr to unsafe.Pointer
    20	
    21	The unsafeptr analyzer reports likely incorrect uses of unsafe.Pointer
    22	to convert integers to pointers. A conversion from uintptr to
    23	unsafe.Pointer is invalid if it implies that there is a uintptr-typed
    24	word in memory that holds a pointer value, because that word will be
    25	invisible to stack copying and to the garbage collector.`
    26	
    27	var Analyzer = &analysis.Analyzer{
    28		Name:     "unsafeptr",
    29		Doc:      Doc,
    30		Requires: []*analysis.Analyzer{inspect.Analyzer},
    31		Run:      run,
    32	}
    33	
    34	func run(pass *analysis.Pass) (interface{}, error) {
    35		inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    36	
    37		nodeFilter := []ast.Node{
    38			(*ast.CallExpr)(nil),
    39		}
    40		inspect.Preorder(nodeFilter, func(n ast.Node) {
    41			x := n.(*ast.CallExpr)
    42			if len(x.Args) != 1 {
    43				return
    44			}
    45			if hasBasicType(pass.TypesInfo, x.Fun, types.UnsafePointer) &&
    46				hasBasicType(pass.TypesInfo, x.Args[0], types.Uintptr) &&
    47				!isSafeUintptr(pass.TypesInfo, x.Args[0]) {
    48				pass.Reportf(x.Pos(), "possible misuse of unsafe.Pointer")
    49			}
    50		})
    51		return nil, nil
    52	}
    53	
    54	// isSafeUintptr reports whether x - already known to be a uintptr -
    55	// is safe to convert to unsafe.Pointer. It is safe if x is itself derived
    56	// directly from an unsafe.Pointer via conversion and pointer arithmetic
    57	// or if x is the result of reflect.Value.Pointer or reflect.Value.UnsafeAddr
    58	// or obtained from the Data field of a *reflect.SliceHeader or *reflect.StringHeader.
    59	func isSafeUintptr(info *types.Info, x ast.Expr) bool {
    60		switch x := x.(type) {
    61		case *ast.ParenExpr:
    62			return isSafeUintptr(info, x.X)
    63	
    64		case *ast.SelectorExpr:
    65			if x.Sel.Name != "Data" {
    66				break
    67			}
    68			// reflect.SliceHeader and reflect.StringHeader are okay,
    69			// but only if they are pointing at a real slice or string.
    70			// It's not okay to do:
    71			//	var x SliceHeader
    72			//	x.Data = uintptr(unsafe.Pointer(...))
    73			//	... use x ...
    74			//	p := unsafe.Pointer(x.Data)
    75			// because in the middle the garbage collector doesn't
    76			// see x.Data as a pointer and so x.Data may be dangling
    77			// by the time we get to the conversion at the end.
    78			// For now approximate by saying that *Header is okay
    79			// but Header is not.
    80			pt, ok := info.Types[x.X].Type.(*types.Pointer)
    81			if ok {
    82				t, ok := pt.Elem().(*types.Named)
    83				if ok && t.Obj().Pkg().Path() == "reflect" {
    84					switch t.Obj().Name() {
    85					case "StringHeader", "SliceHeader":
    86						return true
    87					}
    88				}
    89			}
    90	
    91		case *ast.CallExpr:
    92			switch len(x.Args) {
    93			case 0:
    94				// maybe call to reflect.Value.Pointer or reflect.Value.UnsafeAddr.
    95				sel, ok := x.Fun.(*ast.SelectorExpr)
    96				if !ok {
    97					break
    98				}
    99				switch sel.Sel.Name {
   100				case "Pointer", "UnsafeAddr":
   101					t, ok := info.Types[sel.X].Type.(*types.Named)
   102					if ok && t.Obj().Pkg().Path() == "reflect" && t.Obj().Name() == "Value" {
   103						return true
   104					}
   105				}
   106	
   107			case 1:
   108				// maybe conversion of uintptr to unsafe.Pointer
   109				return hasBasicType(info, x.Fun, types.Uintptr) &&
   110					hasBasicType(info, x.Args[0], types.UnsafePointer)
   111			}
   112	
   113		case *ast.BinaryExpr:
   114			switch x.Op {
   115			case token.ADD, token.SUB, token.AND_NOT:
   116				return isSafeUintptr(info, x.X) && !isSafeUintptr(info, x.Y)
   117			}
   118		}
   119		return false
   120	}
   121	
   122	// hasBasicType reports whether x's type is a types.Basic with the given kind.
   123	func hasBasicType(info *types.Info, x ast.Expr, kind types.BasicKind) bool {
   124		t := info.Types[x].Type
   125		if t != nil {
   126			t = t.Underlying()
   127		}
   128		b, ok := t.(*types.Basic)
   129		return ok && b.Kind() == kind
   130	}
   131	

View as plain text