-
Notifications
You must be signed in to change notification settings - Fork 1
/
walk_ssa.go
160 lines (137 loc) · 3.64 KB
/
walk_ssa.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
package taint
import (
"fmt"
"golang.org/x/tools/go/ssa"
)
var ErrStopWalk = fmt.Errorf("taint: stop walk")
// WalkSSA walks the SSA IR recursively with a visitor function that
// can be used to inspect each node in the graph. The visitor function
// should return an error if it wants to stop the walk.
func WalkSSA(v ssa.Value, visit func(v ssa.Value) error) error {
visited := make(valueSet)
return walkSSA(v, visit, visited)
}
func walkSSA(v ssa.Value, visit func(v ssa.Value) error, visited valueSet) error {
if visited == nil {
visited = make(valueSet)
}
if visited.includes(v) {
return nil
}
visited.add(v)
// fmt.Printf("walk SSA: %s: %[1]T\n", v)
if err := visit(v); err != nil {
return err
}
switch v := v.(type) {
case *ssa.Call:
// Check the operands of the call instruction.
for _, opr := range v.Operands(nil) {
if err := walkSSA(*opr, visit, visited); err != nil {
return err
}
}
// Check the arguments of the call instruction.
for _, arg := range v.Common().Args {
if err := walkSSA(arg, visit, visited); err != nil {
return err
}
}
// Check the function being called.
if err := walkSSA(v.Call.Value, visit, visited); err != nil {
return err
}
// Check the return value of the call instruction.
if v.Common().IsInvoke() {
if err := walkSSA(v.Common().Value, visit, visited); err != nil {
return err
}
}
// Check the return value of the call instruction.
if err := walkSSA(v.Common().Value, visit, visited); err != nil {
return err
}
case *ssa.ChangeInterface:
if err := walkSSA(v.X, visit, visited); err != nil {
return err
}
case *ssa.Convert:
if err := walkSSA(v.X, visit, visited); err != nil {
return err
}
case *ssa.MakeInterface:
if err := walkSSA(v.X, visit, visited); err != nil {
return err
}
case *ssa.Phi:
for _, edge := range v.Edges {
if err := walkSSA(edge, visit, visited); err != nil {
return err
}
}
case *ssa.UnOp:
if err := walkSSA(v.X, visit, visited); err != nil {
return err
}
case *ssa.Function:
for _, block := range v.Blocks {
for _, instr := range block.Instrs {
for _, opr := range instr.Operands(nil) {
if err := walkSSA(*opr, visit, visited); err != nil {
return err
}
}
}
}
default:
// fmt.Printf("? walk SSA %s: %[1]T\n", v)
}
refs := v.Referrers()
if refs == nil {
return nil
}
for _, instr := range *refs {
switch instr := instr.(type) {
case *ssa.Store:
// Store instructions need to be checked for both the value being stored,
// and the address being stored to.
if err := walkSSA(instr.Val, visit, visited); err != nil {
return err
}
if err := walkSSA(instr.Addr, visit, visited); err != nil {
return err
}
case *ssa.Call:
// Check the operands of the call instruction.
for _, opr := range instr.Operands(nil) {
if err := walkSSA(*opr, visit, visited); err != nil {
return err
}
}
// Check the arguments of the call instruction.
for _, arg := range instr.Common().Args {
if err := walkSSA(arg, visit, visited); err != nil {
return err
}
}
// Check the function being called.
if err := walkSSA(instr.Call.Value, visit, visited); err != nil {
return err
}
// Check the return value of the call instruction.
if instr.Common().IsInvoke() {
if err := walkSSA(instr.Common().Value, visit, visited); err != nil {
return err
}
}
// Check the return value of the call instruction.
if err := walkSSA(instr.Common().Value, visit, visited); err != nil {
return err
}
default:
// fmt.Printf("? check SSA instr %s: %[1]T\n", i)
continue
}
}
return nil
}