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  	_ "embed"
    11  	"go/ast"
    12  	"go/token"
    13  	"go/types"
    14  
    15  	"golang.org/x/tools/go/analysis"
    16  	"golang.org/x/tools/go/analysis/passes/inspect"
    17  	"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
    18  	"golang.org/x/tools/go/ast/inspector"
    19  )
    20  
    21  //go:embed doc.go
    22  var doc string
    23  
    24  var Analyzer = &analysis.Analyzer{
    25  	Name:     "unsafeptr",
    26  	Doc:      analysisutil.MustExtractDoc(doc, "unsafeptr"),
    27  	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unsafeptr",
    28  	Requires: []*analysis.Analyzer{inspect.Analyzer},
    29  	Run:      run,
    30  }
    31  
    32  func run(pass *analysis.Pass) (interface{}, error) {
    33  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    34  
    35  	nodeFilter := []ast.Node{
    36  		(*ast.CallExpr)(nil),
    37  		(*ast.StarExpr)(nil),
    38  		(*ast.UnaryExpr)(nil),
    39  	}
    40  	inspect.Preorder(nodeFilter, func(n ast.Node) {
    41  		switch x := n.(type) {
    42  		case *ast.CallExpr:
    43  			if len(x.Args) == 1 &&
    44  				hasBasicType(pass.TypesInfo, x.Fun, types.UnsafePointer) &&
    45  				hasBasicType(pass.TypesInfo, x.Args[0], types.Uintptr) &&
    46  				!isSafeUintptr(pass.TypesInfo, x.Args[0]) {
    47  				pass.ReportRangef(x, "possible misuse of unsafe.Pointer")
    48  			}
    49  		case *ast.StarExpr:
    50  			if t := pass.TypesInfo.Types[x].Type; isReflectHeader(t) {
    51  				pass.ReportRangef(x, "possible misuse of %s", t)
    52  			}
    53  		case *ast.UnaryExpr:
    54  			if x.Op != token.AND {
    55  				return
    56  			}
    57  			if t := pass.TypesInfo.Types[x.X].Type; isReflectHeader(t) {
    58  				pass.ReportRangef(x, "possible misuse of %s", t)
    59  			}
    60  		}
    61  	})
    62  	return nil, nil
    63  }
    64  
    65  // isSafeUintptr reports whether x - already known to be a uintptr -
    66  // is safe to convert to unsafe.Pointer.
    67  func isSafeUintptr(info *types.Info, x ast.Expr) bool {
    68  	// Check unsafe.Pointer safety rules according to
    69  	// https://golang.org/pkg/unsafe/#Pointer.
    70  
    71  	switch x := ast.Unparen(x).(type) {
    72  	case *ast.SelectorExpr:
    73  		// "(6) Conversion of a reflect.SliceHeader or
    74  		// reflect.StringHeader Data field to or from Pointer."
    75  		if x.Sel.Name != "Data" {
    76  			break
    77  		}
    78  		// reflect.SliceHeader and reflect.StringHeader are okay,
    79  		// but only if they are pointing at a real slice or string.
    80  		// It's not okay to do:
    81  		//	var x SliceHeader
    82  		//	x.Data = uintptr(unsafe.Pointer(...))
    83  		//	... use x ...
    84  		//	p := unsafe.Pointer(x.Data)
    85  		// because in the middle the garbage collector doesn't
    86  		// see x.Data as a pointer and so x.Data may be dangling
    87  		// by the time we get to the conversion at the end.
    88  		// For now approximate by saying that *Header is okay
    89  		// but Header is not.
    90  		pt, ok := types.Unalias(info.Types[x.X].Type).(*types.Pointer)
    91  		if ok && isReflectHeader(pt.Elem()) {
    92  			return true
    93  		}
    94  
    95  	case *ast.CallExpr:
    96  		// "(5) Conversion of the result of reflect.Value.Pointer or
    97  		// reflect.Value.UnsafeAddr from uintptr to Pointer."
    98  		if len(x.Args) != 0 {
    99  			break
   100  		}
   101  		sel, ok := x.Fun.(*ast.SelectorExpr)
   102  		if !ok {
   103  			break
   104  		}
   105  		switch sel.Sel.Name {
   106  		case "Pointer", "UnsafeAddr":
   107  			if analysisutil.IsNamedType(info.Types[sel.X].Type, "reflect", "Value") {
   108  				return true
   109  			}
   110  		}
   111  	}
   112  
   113  	// "(3) Conversion of a Pointer to a uintptr and back, with arithmetic."
   114  	return isSafeArith(info, x)
   115  }
   116  
   117  // isSafeArith reports whether x is a pointer arithmetic expression that is safe
   118  // to convert to unsafe.Pointer.
   119  func isSafeArith(info *types.Info, x ast.Expr) bool {
   120  	switch x := ast.Unparen(x).(type) {
   121  	case *ast.CallExpr:
   122  		// Base case: initial conversion from unsafe.Pointer to uintptr.
   123  		return len(x.Args) == 1 &&
   124  			hasBasicType(info, x.Fun, types.Uintptr) &&
   125  			hasBasicType(info, x.Args[0], types.UnsafePointer)
   126  
   127  	case *ast.BinaryExpr:
   128  		// "It is valid both to add and to subtract offsets from a
   129  		// pointer in this way. It is also valid to use &^ to round
   130  		// pointers, usually for alignment."
   131  		switch x.Op {
   132  		case token.ADD, token.SUB, token.AND_NOT:
   133  			// TODO(mdempsky): Match compiler
   134  			// semantics. ADD allows a pointer on either
   135  			// side; SUB and AND_NOT don't care about RHS.
   136  			return isSafeArith(info, x.X) && !isSafeArith(info, x.Y)
   137  		}
   138  	}
   139  
   140  	return false
   141  }
   142  
   143  // hasBasicType reports whether x's type is a types.Basic with the given kind.
   144  func hasBasicType(info *types.Info, x ast.Expr, kind types.BasicKind) bool {
   145  	t := info.Types[x].Type
   146  	if t != nil {
   147  		t = t.Underlying()
   148  	}
   149  	b, ok := t.(*types.Basic)
   150  	return ok && b.Kind() == kind
   151  }
   152  
   153  // isReflectHeader reports whether t is reflect.SliceHeader or reflect.StringHeader.
   154  func isReflectHeader(t types.Type) bool {
   155  	return analysisutil.IsNamedType(t, "reflect", "SliceHeader", "StringHeader")
   156  }
   157  

View as plain text