flaw_detector 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,190 @@
1
+ module FlawDetector
2
+ module CodeModel
3
+ class CfgNode
4
+ def create_sibling(bb_name)
5
+ cfg = CfgNode.new(bb_name, @insns_block)
6
+ add_list(cfg)
7
+ end
8
+
9
+ def initialize(bb_name, insns_block)
10
+ @next_nodes = []
11
+ @prev_nodes = []
12
+ @name = bb_name
13
+ @insns_block = insns_block
14
+ @leave = nil
15
+ @dfst_parent = nil
16
+ @dominators = nil
17
+ end
18
+
19
+ def inspect
20
+ return "#{self.class.name}:#{self.object_id} @name='#{self.name}',@insns_block='#{self.insns_block}'>"
21
+ end
22
+
23
+ def add_next(node)
24
+ @next_nodes << node
25
+ end
26
+
27
+ def add_prev(node)
28
+ @prev_nodes << node
29
+ end
30
+
31
+ def set_leave(node)
32
+ if entry == self
33
+ @leave = node
34
+ else
35
+ entry.set_leave(node)
36
+ end
37
+ end
38
+
39
+ def leave
40
+ if entry == self
41
+ @leave
42
+ else
43
+ entry.leave
44
+ end
45
+ end
46
+
47
+ # @todo exclude...
48
+ def last_line
49
+ insns_frame.index2line(bb[0].last-1)
50
+ end
51
+
52
+ def each(&block)
53
+ each_node_list do |name, node|
54
+ block.call(name, node)
55
+ node.next_nodes.each do |nnd|
56
+ next unless nnd.name == :entry
57
+ nnd.each_node_list(&block)
58
+ end
59
+ end
60
+ end
61
+
62
+ def each_dfst_node_by_preorder(&block)
63
+ each_dfst_node(:preorder, &block)
64
+ end
65
+
66
+ def each_dfst_node_by_postorder(&block)
67
+ each_dfst_node(:postorder, &block)
68
+ end
69
+
70
+ def each_dfst_node_by_reverse_postorder(&block)
71
+ nodes = []
72
+ each_dfst_node_by_postorder do |node|
73
+ nodes << node
74
+ end
75
+
76
+ nodes.reverse_each(&block)
77
+ end
78
+
79
+ def each_node_list(&block)
80
+ node_list.each(&block)
81
+ end
82
+
83
+ def entry
84
+ node_list[:entry]
85
+ end
86
+
87
+ def node(bb_name)
88
+ node_list[bb_name]
89
+ end
90
+
91
+ def total_node_count
92
+ self.node_list.count
93
+ end
94
+
95
+ def bb
96
+ @insns_block.raw_block_data[:extra][:basic_blocks][name]
97
+ end
98
+
99
+ def last_lineno
100
+ @insns_block.index2line(bb[0].last+1)
101
+ end
102
+
103
+ # @todo support ISeqHeader::TYPE_BLOCK
104
+ def dominate?(node)
105
+ return false unless @insns_block == node.insns_block
106
+ return false if insns_frame.type == ISeqHeader::TYPE_BLOCK
107
+ return true if self.name == node.name
108
+ idom = node
109
+ while idom = @insns_block.domtree[idom.name] do
110
+ return true if self.name == idom.name
111
+ end
112
+ return false
113
+ end
114
+
115
+ def is_dominance_frontier_of?(node)
116
+ if node.prev_nodes.first == node.prev_nodes.last
117
+ pfpl = plpf = []
118
+ else
119
+ pfpl = node.prev_nodes.first.dominators - node.prev_nodes.last.dominators
120
+ plpf = node.prev_nodes.last.dominators - node.prev_nodes.first.dominators
121
+ end
122
+ pfpl.include?(self) || plpf.include?(self)
123
+ end
124
+
125
+ def dominators
126
+ return @dominators if @dominators
127
+ @dominators = []
128
+ each_node_list do |name, node|
129
+ @dominators << node if node.dominate?(self)
130
+ end
131
+ return @dominators
132
+ end
133
+
134
+ def visit_specified_dominators(&block)
135
+ checked_nodes = []
136
+ stack_of_nodes = [self]
137
+ until stack_of_nodes.empty? do
138
+ node = stack_of_nodes.pop
139
+ next unless self.dominate?(node)
140
+ next if checked_nodes.include?(node)
141
+ checked_nodes << node
142
+ n = block.call(node)
143
+ case n
144
+ when :first, :last
145
+ next_nodes = [node.next_nodes.send(n)]
146
+ when :all
147
+ next_nodes = node.next_nodes
148
+ when :none
149
+ next_nodes = []
150
+ else
151
+ raise "unknown state"
152
+ end
153
+ next_nodes.each do |nnd|
154
+ next if nnd.name == :entry
155
+ stack_of_nodes << nnd
156
+ end
157
+ end
158
+ end
159
+
160
+ attr_reader :next_nodes,:prev_nodes,:name,:leave,:insns_block,:dfst_parent
161
+ alias :insns_frame :insns_block
162
+
163
+ protected
164
+ attr_writer :dfst_parent
165
+
166
+ def add_list(node)
167
+ node_list[node.name] = node
168
+ end
169
+
170
+ def node_list
171
+ @insns_block.cfg_node_list.keys
172
+ @insns_block.cfg_node_list
173
+ end
174
+
175
+ def each_dfst_node(type = :preorder, position = :entry, found = [], &block)
176
+ node = node_list[position]
177
+ found << node
178
+
179
+ block.call(node) if type == :preorder
180
+ node.next_nodes.each do |nnd|
181
+ next if nnd.name == :entry #skip another frame
182
+ next if found.include?(nnd)
183
+ nnd.dfst_parent = node
184
+ each_dfst_node(type, nnd.name, found, &block)
185
+ end
186
+ block.call(node) if type == :postorder
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,63 @@
1
+ module FlawDetector
2
+ module CodeModel
3
+ class CodeDocument
4
+ def self.create(doc, filepath = "<compiled>")
5
+ return CodeDocument.new(doc, filepath)
6
+ end
7
+
8
+ def top
9
+ root
10
+ end
11
+
12
+ def select_methods(name=nil)
13
+ select_some_type(ISeqHeader::TYPE_METHOD, name)
14
+ end
15
+
16
+ def select_classes(name=nil)
17
+ class_name = nil
18
+ class_name = "<class:#{name}>" if name
19
+ select_some_type(ISeqHeader::TYPE_CLASS, class_name)
20
+ end
21
+
22
+ def select_class(name)
23
+ root.each do |block|
24
+ next unless block.type == ISeqHeader::TYPE_CLASS
25
+ next unless block.name == "<class:#{name}>"
26
+ return block
27
+ end
28
+ return nil
29
+ end
30
+
31
+ attr_reader :root
32
+ attr_reader :filepath
33
+ private
34
+ def initialize(str, filepath)
35
+ @root = InsnsFrame.new(str)
36
+ @filepath = filepath
37
+ end
38
+
39
+ def select_some_type(type, name=nil)
40
+ selects = []
41
+ b = proc do |parent|
42
+ parent.child_blocks.each do |frame|
43
+ unless frame.type == type
44
+ b.call(frame)
45
+ next
46
+ end
47
+ if name.nil? || frame.name == name
48
+ selects << frame
49
+ end
50
+ end
51
+ end
52
+
53
+ if root.type == type
54
+ if name.nil? || root.name == name
55
+ selects << frame
56
+ end
57
+ end
58
+ b.call(root)
59
+ selects
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,341 @@
1
+ module FlawDetector
2
+ module CodeModel
3
+ class InsnsFrame
4
+ def initialize(iseq_data,pblock=nil, insns_pos=nil)
5
+ @raw_block_data = iseq_data
6
+ @child_blocks = []
7
+ @parent = pblock
8
+ @insns_pos = insns_pos
9
+ @cfg_node_list = {}
10
+ @domtree = nil
11
+
12
+ if @raw_block_data
13
+ @raw_block_data[:extra][:insns_pos_to_operand_iseq].each do |insns_pos, raw_block|
14
+ if raw_block
15
+ @child_blocks << InsnsFrame.new(raw_block, self, insns_pos)
16
+ else
17
+ @child_blocks << InsnsFrame.new(nil, self, insns_pos)
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def inspect
24
+ return "#{self.class.name}:#{self.object_id} @name='#{self.name}',@type='#{self.type}',@insns_pos='#{@insns_pos}',@child_frames=#{@child_blocks}>"
25
+ end
26
+
27
+ def insns_pos_in_parent_body
28
+ @insns_pos
29
+ end
30
+
31
+ def name
32
+ if @raw_block_data
33
+ @raw_block_data[:header][:name].to_s
34
+ else
35
+ ""
36
+ end
37
+ end
38
+
39
+ def type
40
+ if @raw_block_data
41
+ @raw_block_data[:header][:type]
42
+ else
43
+ nil
44
+ end
45
+ end
46
+
47
+ def insns
48
+ if @raw_block_data
49
+ s = @raw_block_data[:body][:insns].clone
50
+ else
51
+ s = []
52
+ end
53
+ s.freeze
54
+ end
55
+
56
+ def each(&block)
57
+ block.call(self)
58
+ @child_blocks.each(&block)
59
+ end
60
+
61
+ def basic_blocks
62
+ raw_block_data[:extra][:basic_blocks].dup.freeze
63
+ end
64
+
65
+ # == Arguments & Return
66
+ # _varname_ :: [Symbol] variable name
67
+ # *Return* :: [Integer] idx which is operand for getlocal, setlocal and so on
68
+ def varname2idx(varsym)
69
+ cnt = raw_block_data[:header][:locals].count
70
+ index = raw_block_data[:header][:locals].find_index(varsym)
71
+ if index
72
+ return cnt - index + 1
73
+ else
74
+ return nil
75
+ end
76
+ end
77
+
78
+ # == Arguments & Return
79
+ # _idx_ :: [Integer] idx which is operand for getlocal, setlocal and so on
80
+ # *Return* :: [Symbol] variable name
81
+ def idx2varname(idx)
82
+ cnt = raw_block_data[:header][:locals].count
83
+ raw_block_data[:header][:locals][cnt+1-idx]
84
+ end
85
+
86
+ def insn2variable(insn)
87
+ frame = nil
88
+ varname = nil
89
+ case insn[0]
90
+ when :getlocal,:setlocal
91
+ idx = insn[1]
92
+ frame = self
93
+ varname = frame.idx2varname(idx)
94
+ when :getdynamic,:setdynamic
95
+ idx = insn[1]
96
+ up_frame = insn[2]
97
+ frame = self
98
+ insn[2].times{frame = frame.parent}
99
+ varname = frame.idx2varname(idx)
100
+ else
101
+ raise 'unknown state'
102
+ end
103
+ return frame, varname
104
+ end
105
+
106
+ def entry_cfg_node
107
+ unless @cfg_node_list[:entry]
108
+ top_of_block = self
109
+ while top_of_block.raw_block_data[:header][:type] == ISeqHeader::TYPE_BLOCK do
110
+ top_of_block = top_of_block.parent
111
+ unless top_of_block
112
+ raise "unknown state"
113
+ end
114
+ end
115
+ top_of_block.make_cfg
116
+ unless @cfg_node_list[:entry]
117
+ #ASSERT: make_cfg method creates cfg nodes of top/method and child blocks
118
+ raise "unknown state"
119
+ end
120
+ end
121
+ @cfg_node_list[:entry]
122
+ end
123
+
124
+ def find_cfg_node_by_insn_index(index)
125
+ entry_cfg_node
126
+ @cfg_node_list.each do |key, val|
127
+ return val if val.bb[0].include?(index)
128
+ end
129
+ end
130
+
131
+ def find_insn_index_of_send_obj(index, insn)
132
+ find_def_insn_index_of_arg(index, insn)
133
+ end
134
+
135
+ def find_use_insn_index_of_ret(index, insn, retnum=0)
136
+ if retnum >= InsnExt::insn_ret_num(InsnExt::insn_num(insn[0]))
137
+ raise "wrong argument: retnum=#{retnum}"
138
+ end
139
+
140
+ depth = retnum
141
+ obj_index = nil
142
+ start_index = index+1
143
+ cfg_node = find_cfg_node_by_insn_index(index)
144
+ unless cfg_node.bb[0].include?(start_index)
145
+ if cfg_node.next_nodes.count == 1 #TODO: exclude different frame
146
+ cfg_node = cfg_node.next_nodes.first
147
+ start_index = cfg_node.bb[0].first
148
+ else
149
+ cfg_node = nil
150
+ end
151
+ end
152
+ while cfg_node do
153
+ (start_index...(cfg_node.bb[0].last)).each do |tmp_index|
154
+ tmp_insn = self.raw_block_data[:body][:insns][tmp_index]
155
+ tmp_ret = InsnExt::insn_ret_num(InsnExt::insn_num(tmp_insn[0]))
156
+ tmp_inc = InsnExt::insn_stack_increase(InsnExt::insn_num(tmp_insn[0]), tmp_insn[1..-1])
157
+ depth += tmp_inc - tmp_ret
158
+ if depth < 0
159
+ obj_index = tmp_index
160
+ break
161
+ end
162
+ depth += tmp_ret
163
+ end
164
+ break if obj_index
165
+ if cfg_node.next_nodes.count == 1 #TODO: exclude different frame
166
+ cfg_node = cfg_node.next_nodes.first
167
+ start_index = cfg_node.bb[0].first
168
+ else
169
+ cfg_node = nil
170
+ end
171
+ end
172
+
173
+ obj_argnum = nil
174
+ if obj_index
175
+ obj_argnum = -(depth + 1)
176
+ end
177
+ return obj_index, obj_argnum
178
+ end
179
+
180
+ def find_def_insn_index_of_arg(index, insn, argnum=0)
181
+ inc = InsnExt::insn_stack_increase(InsnExt::insn_num(insn[0]), insn[1..-1])
182
+ ret = InsnExt::insn_ret_num(InsnExt::insn_num(insn[0]))
183
+ depth = (inc - ret) + argnum
184
+ obj_index = nil
185
+ end_index = index
186
+ cfg_node = find_cfg_node_by_insn_index(index)
187
+ unless cfg_node.bb[0].include?(index-1)
188
+ if cfg_node.prev_nodes.count == 1 #TODO: exclude different frame
189
+ cfg_node = cfg_node.prev_nodes.first
190
+ end_index = cfg_node.bb[0].last
191
+ else
192
+ cfg_node = nil
193
+ end
194
+ end
195
+ while cfg_node
196
+ ((cfg_node.bb[0].first)...end_index).reverse_each do |tmp_index|
197
+ rinsn = self.raw_block_data[:body][:insns][tmp_index]
198
+ tmp_ret = InsnExt::insn_ret_num(InsnExt::insn_num(rinsn[0]))
199
+ if (depth + tmp_ret) >= 0
200
+ obj_index = tmp_index
201
+ break
202
+ end
203
+ depth += InsnExt::insn_stack_increase(InsnExt::insn_num(rinsn[0]), rinsn[1..-1])
204
+ end
205
+ break if obj_index
206
+ if cfg_node.prev_nodes.count == 1 #TODO: exclude different frame
207
+ cfg_node = cfg_node.prev_nodes.first
208
+ end_index = cfg_node.bb[0].last
209
+ else
210
+ cfg_node = nil
211
+ end
212
+ end
213
+
214
+ obj_retnum = nil
215
+ if obj_index
216
+ tmp_insn = self.raw_block_data[:body][:insns][obj_index]
217
+ tmp_ret = InsnExt::insn_ret_num(InsnExt::insn_num(tmp_insn[0]))
218
+ obj_retnum = depth + tmp_ret
219
+ end
220
+
221
+ return obj_index, obj_retnum
222
+ end
223
+
224
+ def index2line(index)
225
+ i = self.raw_block_data[:extra][:insns_pos_to_lineno].find_index{|elem| elem[0].include?(index)}
226
+ if i
227
+ self.raw_block_data[:extra][:insns_pos_to_lineno][i][1]
228
+ else
229
+ nil
230
+ end
231
+ end
232
+
233
+ def domtree
234
+ @domtree if @domtree
235
+ e = entry_cfg_node
236
+ idom = {}
237
+ dfsn = {}
238
+ val = 0
239
+ e.each_dfst_node_by_reverse_postorder do |node|
240
+ dfsn[node.name] = val
241
+ val += 1
242
+ end
243
+
244
+ e.each_node_list do |name, node|
245
+ idom[node.name] = node.dfst_parent
246
+ end
247
+
248
+ nca = lambda do |x,y|
249
+ while (x != y) do
250
+ x_dfsn = if x then dfsn[x.name] else nil end
251
+ y_dfsn = if y then dfsn[y.name] else nil end
252
+ if x_dfsn > y_dfsn
253
+ x = idom[x.name]
254
+ elsif x_dfsn < y_dfsn
255
+ y = idom[y.name]
256
+ end
257
+ end
258
+ x
259
+ end
260
+
261
+ change_flag = true
262
+ while(change_flag) do
263
+ change_flag = false
264
+ e.each_dfst_node_by_postorder do |v|
265
+ v.prev_nodes.each do |succ|
266
+ next if succ.name == :leave
267
+ tmp_idom = nca.call(idom[v.name], succ)
268
+ if tmp_idom != idom[v.name]
269
+ change_flag = true
270
+ idom[v.name] = tmp_idom
271
+ end
272
+ end
273
+ end
274
+ end
275
+ @domtree = idom
276
+ end
277
+
278
+ attr_reader :parent, :child_blocks, :raw_block_data, :cfg_node_list
279
+
280
+ protected
281
+ def make_cfg
282
+ bb_name = :entry
283
+ pcfg = CfgNode.new(bb_name, self)
284
+ @cfg_node_list[bb_name] = pcfg
285
+ make_cfg_inter(pcfg)
286
+
287
+ node = pcfg.node(:leave)
288
+ unless node
289
+ node = pcfg.create_sibling(:leave)
290
+ end
291
+ pcfg.set_leave(node)
292
+
293
+ return pcfg
294
+ end
295
+
296
+ private
297
+ def make_cfg_inter(pcfg)
298
+ new_node = []
299
+ data = self.raw_block_data
300
+ bbs = data[:extra][:basic_blocks]
301
+ pcfg.bb[1..-1].each do |pos|
302
+ next if [:exception].include?(pos)
303
+ key, val = bbs.find{|key,val|
304
+ if pos == :leave && key == :leave
305
+ true
306
+ else
307
+ val[0].include?(pos)
308
+ end
309
+ }
310
+
311
+ unless key
312
+ raise "unknown state"
313
+ end
314
+ node = pcfg.node(key)
315
+ unless node
316
+ node = pcfg.create_sibling(key)
317
+ new_node << node
318
+ end
319
+ pcfg.add_next(node)
320
+ node.add_prev(pcfg)
321
+ end
322
+
323
+ @child_blocks.each do |b|
324
+ next unless b.raw_block_data
325
+ next unless b.raw_block_data[:header][:type] == ISeqHeader::TYPE_BLOCK
326
+ if pcfg.bb[0].include?(b.insns_pos_in_parent_body)
327
+ node = b.make_cfg
328
+ pcfg.add_next(node)
329
+ node.add_prev(pcfg)
330
+
331
+ #TODO: is this ok? (or should use :break exception?)
332
+ pcfg.next_nodes.first.add_prev(node.leave)
333
+ node.leave.add_next(pcfg.next_nodes.first)
334
+ break
335
+ end
336
+ end
337
+ new_node.each {|n| make_cfg_inter(n)}
338
+ end
339
+ end
340
+ end
341
+ end
@@ -0,0 +1,27 @@
1
+ module FlawDetector
2
+ class BasicController
3
+ def initialize(formatter = Formatter::CsvFormatter.new)
4
+ @detectors = []
5
+ @formatter = formatter
6
+ end
7
+
8
+ # @return [Boolean] true if no flaw found, otherwise false
9
+ def run(file_or_ary)
10
+ result = []
11
+ ary = [file_or_ary] if file_or_ary.is_a?(String)
12
+ ary ||= file_or_ary
13
+ ary.each do |file|
14
+ File.open(file) do |fp|
15
+ dom = FlawDetector::parse_file(fp)
16
+ @detectors.each do |analyzer|
17
+ result += analyzer.analyze(dom)
18
+ end
19
+ end
20
+ end
21
+ @formatter.render(result)
22
+ result.empty?
23
+ end
24
+
25
+ attr_accessor :detectors
26
+ end
27
+ end
@@ -0,0 +1,10 @@
1
+ module FlawDetector
2
+ module Detector
3
+ class AbstractDetector
4
+ def detect
5
+ raise "not yet implemented."
6
+ end
7
+ end
8
+ end
9
+ end
10
+