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/inspector"
19 )
20
21
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
66
67 func isSafeUintptr(info *types.Info, x ast.Expr) bool {
68
69
70
71 switch x := ast.Unparen(x).(type) {
72 case *ast.SelectorExpr:
73
74
75 if x.Sel.Name != "Data" {
76 break
77 }
78
79
80
81
82
83
84
85
86
87
88
89
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
97
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
114 return isSafeArith(info, x)
115 }
116
117
118
119 func isSafeArith(info *types.Info, x ast.Expr) bool {
120 switch x := ast.Unparen(x).(type) {
121 case *ast.CallExpr:
122
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
129
130
131 switch x.Op {
132 case token.ADD, token.SUB, token.AND_NOT:
133
134
135
136 return isSafeArith(info, x.X) && !isSafeArith(info, x.Y)
137 }
138 }
139
140 return false
141 }
142
143
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
154 func isReflectHeader(t types.Type) bool {
155 return analysisutil.IsNamedType(t, "reflect", "SliceHeader", "StringHeader")
156 }
157
View as plain text