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
open Core_kernel
open Bap.Std
open Log_utils
let name = "CWE248"
let version = "0.1"
(* Print the findings to the log *)
let print_uncatched_exception block_tid ~tid_map =
let address = (Address_translation.translate_tid_to_assembler_address_string block_tid tid_map) in
let description = sprintf "(Possibly Uncaught Exception) (Exception thrown at %s)." address in
let cwe_warning = cwe_warning_factory name version description ~addresses:[address] in
collect_cwe_warning cwe_warning
(* Extract the name of a direct call, if the block contains a direct call. *)
let extract_direct_call_symbol block =
match Symbol_utils.extract_direct_call_tid_from_block block with
| Some(tid) -> Some(Tid.name tid)
| None -> None
(* check whether block contains a direct call to a symbol with name symbol_name *)
let contains_symbol block symbol_name =
match extract_direct_call_symbol block with
| Some(symb) -> symb = symbol_name
| None -> false
(* Checks whether a subfunction contains a catch block. *)
let contains_catch subfunction =
let blocks = Term.enum blk_t subfunction in
Seq.exists blocks ~f:(fun block -> contains_symbol block "@__cxa_begin_catch")
(* Find all calls to subfunctions that are reachable from this subfunction. The calls are returned
as a list, except for calls to "@__cxa_throw", which are logged as possibly uncaught exceptions. *)
let find_calls_and_throws subfunction ~tid_map =
let blocks = Term.enum blk_t subfunction in
Seq.fold blocks ~init:[] ~f:(fun call_list block ->
if contains_symbol block "@__cxa_throw" then
let () = print_uncatched_exception (Term.tid block) ~tid_map:tid_map in
call_list
else
match Symbol_utils.extract_direct_call_tid_from_block block with
| Some(tid) -> tid :: call_list
| None -> call_list
)
(* find exception throws with for which an exception handler was not necessarily allocated beforehand.
The return value is a list of all already checked functions.*)
let rec find_uncaught_exceptions subfunction already_checked_functions program ~tid_map =
if contains_catch subfunction then
(* This function contains a catch so we assume every throw reachable from here is catched. *)
already_checked_functions
else
let subfunction_calls = find_calls_and_throws subfunction ~tid_map:tid_map in
List.fold subfunction_calls ~init:already_checked_functions ~f:(fun already_checked subfunc ->
match List.exists ~f:(fun a -> a = subfunc) already_checked with
| true -> already_checked
| false -> find_uncaught_exceptions ~tid_map:tid_map (Core_kernel.Option.value_exn (Term.find sub_t program subfunc)) (subfunc :: already_checked) program)
(* Search for uncatched exceptions for each entry point into the binary.
TODO: Exceptions, that are catched when starting from one entry point, but not from another, are masked this
way. We should check whether this produces a lot of false negatives. *)
let check_cwe program _project tid_map _symbol_pairs _ =
let entry_points = Symbol_utils.get_program_entry_points program in
let _ = List.fold entry_points ~init:[] ~f:(fun already_checked_functions sub -> find_uncaught_exceptions ~tid_map:tid_map sub already_checked_functions program) in
()