This repository was archived by the owner on Oct 22, 2021. It is now read-only.
forked from IagoAbal/eba
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patheba.ml
More file actions
279 lines (233 loc) · 8.77 KB
/
Copy patheba.ml
File metadata and controls
279 lines (233 loc) · 8.77 KB
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
open Batteries
open Cmdliner
open Dolog
open Abs
module L = LazyList
type checks = {
chk_uninit : bool
; chk_dlock : bool
; chk_uaf : bool
; chk_birq : bool
; chk_automata_double_unlock : bool
; chk_automata_double_lock : bool
; chk_automata_uaf : bool
}
let run_checks checks file fileAbs :unit =
let print_bugs =
let with_warn_out print =
if Opts.Get.warn_output()
then File.with_file_out (Cil.(file.fileName) ^ ".warn") print
else print IO.stdout
in
L.iter (fun errmsg ->
with_warn_out (fun out ->
Printf.fprintf out "\nPotential BUG found:\n%s\n\n" errmsg
)
)
in
let run_check_fun fd in_func = in_func fileAbs fd |> print_bugs
in
let fds = Cil.(file.globals) |> List.filter_map (function
| Cil.GFun(fd,_) -> Some fd
| ______________ -> None
)
in
(* THINK: Too much if-ery? *)
fds |> List.iter (fun fd ->
Log.debug "Analyzing function %s\n" Cil.(fd.svar.vname);
if checks.chk_uninit
then run_check_fun fd CheckUninitFlow1.in_func;
if checks.chk_uaf
then run_check_fun fd CheckUAF.in_func;
if checks.chk_dlock
then run_check_fun fd CheckDLockFlow2.in_func;
if checks.chk_birq
then run_check_fun fd CheckBhOnIrqFlow2.in_func;
);
if checks.chk_automata_double_unlock
then
List.map (fun fd -> CheckAutomataDoubleUnlock.check fileAbs fd (Opts.Get.no_static())) fds
|> List.flatten
|> CheckAutomataDoubleUnlock.filter_results
|> CheckAutomataDoubleUnlock.stringify_results
|> L.of_list
|> print_bugs;
if checks.chk_automata_double_lock
then
List.map (fun fd -> CheckAutomataDoubleLock.check fileAbs fd (Opts.Get.no_static())) fds
|> List.flatten
|> CheckAutomataDoubleLock.filter_results
|> CheckAutomataDoubleLock.stringify_results
|> L.of_list
|> print_bugs;
if checks.chk_automata_uaf
then
List.map (fun fd -> CheckAutomataUseAfterFree.check fileAbs fd (Opts.Get.no_static())) fds
|> List.flatten
|> CheckAutomataUseAfterFree.filter_results
|> CheckAutomataUseAfterFree.stringify_results
|> L.of_list
|> print_bugs
let infer_file checks fn =
let file = Frontc.parse fn () in
let fileAbs = Infer.of_file file in
if Opts.Get.save_abs()
then begin
let fn_abs = fn ^ ".abs" in
File.with_file_out fn_abs (fun out -> AFile.fprint out fileAbs)
end;
run_checks checks file fileAbs;
if Opts.Get.gc_stats()
then begin
Printf.fprintf stderr "======= GC stats =======\n";
Gc.print_stat stderr;
Printf.fprintf stderr "========================\n"
end
let infer_file_gcc checks args =
let fn = Gcc.gcc args in
infer_file checks fn
(* CLI *)
let log_level_of_int = function
| x when x <= 0 -> Log.ERROR
| 1 -> Log.WARN
| 2 -> Log.INFO
| _ -> Log.DEBUG (* x >= 3 *)
let infer_files verbosity
flag_gcstats flag_saveabs flag_warn_output flag_fake_gcc flag_no_static
flag_no_dce flag_no_dfe flag_safe_casts flag_externs_do_nothing
opt_inline_limit opt_loop_limit opt_branch_limit flag_no_path_check
flag_all_lock_types flag_no_match_lock_exp flag_ignore_writes
chk_uninit chk_dlock chk_uaf chk_birq chk_automata_double_unlock chk_automata_double_lock chk_automata_uaf
files =
(* CIL: do not print #line directives. *)
Cil.lineDirectiveStyle := None;
Log.color_on();
Log.set_log_level (log_level_of_int verbosity);
Opts.Set.gc_stats flag_gcstats;
Opts.Set.save_abs flag_saveabs;
Opts.Set.no_static flag_no_static;
Opts.Set.warn_output flag_warn_output;
Opts.Set.dce (not flag_no_dce);
Opts.Set.dfe (not flag_no_dfe);
Opts.Set.unsafe_casts (not flag_safe_casts);
Opts.Set.externs_do_nothing flag_externs_do_nothing;
Opts.Set.inline_limit opt_inline_limit;
Opts.Set.loop_limit opt_loop_limit;
Opts.Set.branch_limit opt_branch_limit;
Opts.Set.path_check (not flag_no_path_check);
Opts.Set.all_lock_types flag_all_lock_types;
Opts.Set.match_lock_exp (not flag_no_match_lock_exp);
Opts.Set.ignore_writes flag_ignore_writes;
let checks = { chk_uninit; chk_dlock; chk_uaf; chk_birq; chk_automata_double_unlock; chk_automata_double_lock; chk_automata_uaf } in
Axioms.load_axioms();
if flag_fake_gcc
then infer_file_gcc checks files
else begin
List.iter Utils.check_if_file_exists files;
List.iter (infer_file checks) files
end
let files = Arg.(non_empty & pos_all string [] & info [] ~docv:"FILE")
(* General *)
(* TODO: Write a Cmdliner.converter for Log.log_level *)
let verbose =
let doc = "Set the verbosity level." in
Arg.(value & opt int 0 & info ["v"; "verbose"] ~docv:"LEVEL" ~doc)
let flag_gcstats =
let doc = "Print GC stats after analyzing a C file." in
Arg.(value & flag & info ["gc-stats"] ~doc)
let flag_saveabs =
let doc = "Save effect abstraction to an .abs file." in
Arg.(value & flag & info ["save-abs"] ~doc)
let flag_warn_output =
let doc = "Save warns into a .warn file." in
Arg.(value & flag & info ["warn-output"] ~doc)
let flag_fake_gcc =
let doc = "Fake GCC and preprocess input file." in
Arg.(value & flag & info ["fake-gcc"] ~doc)
let flag_no_static =
let doc = "Explore non-static functions only." in
Arg.(value & flag & info ["no-static"] ~doc)
(* Type inferrer*)
let flag_no_dce =
let doc = "Do not eliminate dead code." in
Arg.(value & flag & info ["no-dce"] ~doc)
let flag_no_dfe =
let doc = "Do not ignore unused fields in structure types (aka dead field elimination)." in
Arg.(value & flag & info ["no-dfe"] ~doc)
let flag_safe_casts =
let doc = "Fail on potentially unsafe casts." in
Arg.(value & flag & info ["safe-casts"] ~doc)
let flag_externs_do_nothing =
let doc = "Ignore potential side-effects of extern functions." in
Arg.(value & flag & info ["externs-do-nothing"] ~doc)
(* Model checker *)
let opt_inline_limit =
let doc = "Inline function calls up to $(docv) times. Provide -1 to prevent inlining but accept some false positives." in
let def = Opts.Get.inline_limit() in
Arg.(value & opt int def & info ["inline-limit"] ~docv:"N" ~doc)
let opt_loop_limit =
let doc = "Take up to $(docv) loop iterations." in
let def = Opts.Get.loop_limit() in
Arg.(value & opt int def & info ["loop-limit"] ~docv:"N" ~doc)
let opt_branch_limit =
let doc = "Take up to $(docv) branch decisions." in
let def = Opts.Get.branch_limit() in
Arg.(value & opt int def & info ["branch-limit"] ~docv:"N" ~doc)
let flag_no_path_check =
let doc = "Do not check path consistency." in
Arg.(value & flag & info ["no-path-check"] ~doc)
(* Double-Lock bug cheker *)
let flag_all_lock_types =
let doc = "[Double-Lock] Check all lock types (not only spin locks)." in
Arg.(value & flag & info ["all-lock-types"] ~doc)
let flag_no_match_lock_exp =
let doc = "[Double-Lock] Do not use heuristics to match lock object expressions." in
Arg.(value & flag & info ["no-match-lock-exp"] ~doc)
let flag_ignore_writes =
let doc = "[Double-Lock] Ignore writes that may affect the lock object." in
Arg.(value & flag & info ["ignore-writes"] ~doc)
(* Bug chekers *)
let check_uninit =
let doc = "Check for uses of variables before initialization" in
Arg.(value & flag & info ["U"; "uninit"] ~doc)
let check_dlock =
let doc = "Check for double locking" in
Arg.(value & flag & info ["L"; "dlock"] ~doc)
let check_automata_double_unlock =
let doc = "Check for double unlocking using automata" in
Arg.(value & flag & info ["dUa"; "dunlockaut"] ~doc)
let check_automata_double_lock =
let doc = "Check for double locking using automata" in
Arg.(value & flag & info ["La"; "dlockaut"] ~doc)
let check_automata_double_unlock =
let doc = "Check for double unlocking using automata" in
Arg.(value & flag & info ["dUa"; "dunlockaut"] ~doc)
let check_uaf =
let doc = "Check for use-after-free" in
Arg.(value & flag & info ["F"; "uaf"] ~doc)
let check_automata_uaf =
let doc = "Check for use-after-free using automata" in
Arg.(value & flag & info ["Fa"; "uafaut"] ~doc)
let check_birq =
let doc = "Check for BH-enabling while IRQs are off" in
Arg.(value & flag & info ["B"; "bh-irq"] ~doc)
let cmd =
let doc = "Effect-based analysis of C programs" in
let man =
[
`S "DESCRIPTION";
`P "Author: Iago Abal <mail@iagoabal.eu>.";
`P "To preprocess the input use `--fake-gcc' and pass the necessary arguments after `--', as in:";
`P "eba --fake-gcc -- -Iinclude/ foo.c";
`P "EBA will extract the `-D', `-include', and `-I' arguments and invoke GCC, any other option will be ignored."
] in
Term.(pure infer_files
$ verbose
$ flag_gcstats $ flag_saveabs $ flag_warn_output $ flag_fake_gcc $ flag_no_static
$ flag_no_dce $ flag_no_dfe $ flag_safe_casts $ flag_externs_do_nothing
$ opt_inline_limit $ opt_loop_limit $ opt_branch_limit $ flag_no_path_check
$ flag_all_lock_types $ flag_no_match_lock_exp $ flag_ignore_writes
$ check_uninit $ check_dlock $ check_uaf $ check_birq $ check_automata_double_unlock $ check_automata_double_lock $ check_automata_uaf
$ files),
Term.info "eba" ~version:"0.1" ~doc ~man
let () = match Term.eval cmd with `Error _ -> exit 1 | _ -> exit 0