flaw_detector 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +7 -0
- data/.travis.yml +18 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/README.md +4 -0
- data/Rakefile +4 -0
- data/bin/flaw_detector +38 -0
- data/ext/insns_ext/extconf.rb +2 -0
- data/ext/insns_ext/insn_ext.rb +7 -0
- data/ext/insns_ext/insns.inc +179 -0
- data/ext/insns_ext/insns_ext.c +36 -0
- data/ext/insns_ext/insns_info.inc +695 -0
- data/flaw_detector.gemspec +26 -0
- data/lib/flaw_detector/code_model/cfg_node.rb +190 -0
- data/lib/flaw_detector/code_model/code_document.rb +63 -0
- data/lib/flaw_detector/code_model/insns_frame.rb +341 -0
- data/lib/flaw_detector/controller.rb +27 -0
- data/lib/flaw_detector/detector/abstract_detector.rb +10 -0
- data/lib/flaw_detector/detector/nil_false_path_flow.rb +179 -0
- data/lib/flaw_detector/formatter/csv_formatter.rb +24 -0
- data/lib/flaw_detector/message.rb +45 -0
- data/lib/flaw_detector/version.rb +3 -0
- data/lib/flaw_detector.rb +169 -0
- data/sample/flaw_in_code.rb +9 -0
- data/spec/lib/flaw_detector_spec.rb +526 -0
- data/spec/spec_helper.rb +7 -0
- metadata +92 -0
@@ -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
|