This repository has been archived by the owner on Feb 23, 2023. It is now read-only.
forked from golang/tools
/
unsafeptr.go
168 lines (151 loc) · 5.09 KB
/
unsafeptr.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package unsafeptr defines an Analyzer that checks for invalid
// conversions of uintptr to unsafe.Pointer.
package unsafeptr
import (
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
)
const Doc = `check for invalid conversions of uintptr to unsafe.Pointer
The unsafeptr analyzer reports likely incorrect uses of unsafe.Pointer
to convert integers to pointers. A conversion from uintptr to
unsafe.Pointer is invalid if it implies that there is a uintptr-typed
word in memory that holds a pointer value, because that word will be
invisible to stack copying and to the garbage collector.`
var Analyzer = &analysis.Analyzer{
Name: "unsafeptr",
Doc: Doc,
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run,
}
func run(pass *analysis.Pass) (interface{}, error) {
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{
(*ast.CallExpr)(nil),
(*ast.StarExpr)(nil),
(*ast.UnaryExpr)(nil),
}
inspect.Preorder(nodeFilter, func(n ast.Node) {
switch x := n.(type) {
case *ast.CallExpr:
if len(x.Args) == 1 &&
hasBasicType(pass.TypesInfo, x.Fun, types.UnsafePointer) &&
hasBasicType(pass.TypesInfo, x.Args[0], types.Uintptr) &&
!isSafeUintptr(pass.TypesInfo, x.Args[0]) {
pass.ReportRangef(x, "possible misuse of unsafe.Pointer")
}
case *ast.StarExpr:
if t := pass.TypesInfo.Types[x].Type; isReflectHeader(t) {
pass.ReportRangef(x, "possible misuse of %s", t)
}
case *ast.UnaryExpr:
if x.Op != token.AND {
return
}
if t := pass.TypesInfo.Types[x.X].Type; isReflectHeader(t) {
pass.ReportRangef(x, "possible misuse of %s", t)
}
}
})
return nil, nil
}
// isSafeUintptr reports whether x - already known to be a uintptr -
// is safe to convert to unsafe.Pointer.
func isSafeUintptr(info *types.Info, x ast.Expr) bool {
// Check unsafe.Pointer safety rules according to
// https://golang.org/pkg/unsafe/#Pointer.
switch x := analysisutil.Unparen(x).(type) {
case *ast.SelectorExpr:
// "(6) Conversion of a reflect.SliceHeader or
// reflect.StringHeader Data field to or from Pointer."
if x.Sel.Name != "Data" {
break
}
// reflect.SliceHeader and reflect.StringHeader are okay,
// but only if they are pointing at a real slice or string.
// It's not okay to do:
// var x SliceHeader
// x.Data = uintptr(unsafe.Pointer(...))
// ... use x ...
// p := unsafe.Pointer(x.Data)
// because in the middle the garbage collector doesn't
// see x.Data as a pointer and so x.Data may be dangling
// by the time we get to the conversion at the end.
// For now approximate by saying that *Header is okay
// but Header is not.
pt, ok := info.Types[x.X].Type.(*types.Pointer)
if ok && isReflectHeader(pt.Elem()) {
return true
}
case *ast.CallExpr:
// "(5) Conversion of the result of reflect.Value.Pointer or
// reflect.Value.UnsafeAddr from uintptr to Pointer."
if len(x.Args) != 0 {
break
}
sel, ok := x.Fun.(*ast.SelectorExpr)
if !ok {
break
}
switch sel.Sel.Name {
case "Pointer", "UnsafeAddr":
t, ok := info.Types[sel.X].Type.(*types.Named)
if ok && t.Obj().Pkg().Path() == "reflect" && t.Obj().Name() == "Value" {
return true
}
}
}
// "(3) Conversion of a Pointer to a uintptr and back, with arithmetic."
return isSafeArith(info, x)
}
// isSafeArith reports whether x is a pointer arithmetic expression that is safe
// to convert to unsafe.Pointer.
func isSafeArith(info *types.Info, x ast.Expr) bool {
switch x := analysisutil.Unparen(x).(type) {
case *ast.CallExpr:
// Base case: initial conversion from unsafe.Pointer to uintptr.
return len(x.Args) == 1 &&
hasBasicType(info, x.Fun, types.Uintptr) &&
hasBasicType(info, x.Args[0], types.UnsafePointer)
case *ast.BinaryExpr:
// "It is valid both to add and to subtract offsets from a
// pointer in this way. It is also valid to use &^ to round
// pointers, usually for alignment."
switch x.Op {
case token.ADD, token.SUB, token.AND_NOT:
// TODO(mdempsky): Match compiler
// semantics. ADD allows a pointer on either
// side; SUB and AND_NOT don't care about RHS.
return isSafeArith(info, x.X) && !isSafeArith(info, x.Y)
}
}
return false
}
// hasBasicType reports whether x's type is a types.Basic with the given kind.
func hasBasicType(info *types.Info, x ast.Expr, kind types.BasicKind) bool {
t := info.Types[x].Type
if t != nil {
t = t.Underlying()
}
b, ok := t.(*types.Basic)
return ok && b.Kind() == kind
}
// isReflectHeader reports whether t is reflect.SliceHeader or reflect.StringHeader.
func isReflectHeader(t types.Type) bool {
if named, ok := t.(*types.Named); ok {
if obj := named.Obj(); obj.Pkg() != nil && obj.Pkg().Path() == "reflect" {
switch obj.Name() {
case "SliceHeader", "StringHeader":
return true
}
}
}
return false
}