1
2
3
4
5
6
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/astutil"
19 "golang.org/x/tools/go/ast/inspector"
20 "golang.org/x/tools/internal/aliases"
21 )
22
23
24 var doc string
25
26 var Analyzer = &analysis.Analyzer{
27 Name: "unsafeptr",
28 Doc: analysisutil.MustExtractDoc(doc, "unsafeptr"),
29 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unsafeptr",
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 (*ast.StarExpr)(nil),
40 (*ast.UnaryExpr)(nil),
41 }
42 inspect.Preorder(nodeFilter, func(n ast.Node) {
43 switch x := n.(type) {
44 case *ast.CallExpr:
45 if len(x.Args) == 1 &&
46 hasBasicType(pass.TypesInfo, x.Fun, types.UnsafePointer) &&
47 hasBasicType(pass.TypesInfo, x.Args[0], types.Uintptr) &&
48 !isSafeUintptr(pass.TypesInfo, x.Args[0]) {
49 pass.ReportRangef(x, "possible misuse of unsafe.Pointer")
50 }
51 case *ast.StarExpr:
52 if t := pass.TypesInfo.Types[x].Type; isReflectHeader(t) {
53 pass.ReportRangef(x, "possible misuse of %s", t)
54 }
55 case *ast.UnaryExpr:
56 if x.Op != token.AND {
57 return
58 }
59 if t := pass.TypesInfo.Types[x.X].Type; isReflectHeader(t) {
60 pass.ReportRangef(x, "possible misuse of %s", t)
61 }
62 }
63 })
64 return nil, nil
65 }
66
67
68
69 func isSafeUintptr(info *types.Info, x ast.Expr) bool {
70
71
72
73 switch x := astutil.Unparen(x).(type) {
74 case *ast.SelectorExpr:
75
76
77 if x.Sel.Name != "Data" {
78 break
79 }
80
81
82
83
84
85
86
87
88
89
90
91
92 pt, ok := aliases.Unalias(info.Types[x.X].Type).(*types.Pointer)
93 if ok && isReflectHeader(pt.Elem()) {
94 return true
95 }
96
97 case *ast.CallExpr:
98
99
100 if len(x.Args) != 0 {
101 break
102 }
103 sel, ok := x.Fun.(*ast.SelectorExpr)
104 if !ok {
105 break
106 }
107 switch sel.Sel.Name {
108 case "Pointer", "UnsafeAddr":
109 if analysisutil.IsNamedType(info.Types[sel.X].Type, "reflect", "Value") {
110 return true
111 }
112 }
113 }
114
115
116 return isSafeArith(info, x)
117 }
118
119
120
121 func isSafeArith(info *types.Info, x ast.Expr) bool {
122 switch x := astutil.Unparen(x).(type) {
123 case *ast.CallExpr:
124
125 return len(x.Args) == 1 &&
126 hasBasicType(info, x.Fun, types.Uintptr) &&
127 hasBasicType(info, x.Args[0], types.UnsafePointer)
128
129 case *ast.BinaryExpr:
130
131
132
133 switch x.Op {
134 case token.ADD, token.SUB, token.AND_NOT:
135
136
137
138 return isSafeArith(info, x.X) && !isSafeArith(info, x.Y)
139 }
140 }
141
142 return false
143 }
144
145
146 func hasBasicType(info *types.Info, x ast.Expr, kind types.BasicKind) bool {
147 t := info.Types[x].Type
148 if t != nil {
149 t = t.Underlying()
150 }
151 b, ok := t.(*types.Basic)
152 return ok && b.Kind() == kind
153 }
154
155
156 func isReflectHeader(t types.Type) bool {
157 return analysisutil.IsNamedType(t, "reflect", "SliceHeader", "StringHeader")
158 }
159
View as plain text