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