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