Commit eaf51726 by Enkelmann Committed by Thomas Barabosch

Corrected dune linter warnings, linter warnings are now handled like errors. (#20)

parent 09f6dbb0
.PHONY: all clean test uninstall
all:
dune build --profile release
dune build
dune install
cd plugins/cwe_checker; make all; cd ../..
cd plugins/cwe_checker_emulation; make all; cd ../..
......@@ -8,7 +8,7 @@ all:
cd plugins/cwe_checker_type_inference_print; make all; cd ../..
test:
dune runtest --profile release # TODO: correct all dune linter warnings so that we can remove --profile release
dune runtest
cd test/artificial_samples; scons; cd ../..
pytest -v
......
......@@ -52,7 +52,7 @@ If you plan to develop cwe_checker, it is recommended to build it using the prov
- Opam 2.0.2
- dune >= 1.6
- BAP 1.6 (and its dependencies)
- yojson >= 1.4.1
- yojson >= 1.6.0
- alcotest >= 0.8.3
- Sark (latest) for IDA Pro annotations
- pytest >= 3.5.1
......
......@@ -14,7 +14,7 @@ dev-repo: "git+https://github.com/fkie-cad/cwe_checker"
depends: [
"ocaml" {>= "4.05"}
"dune" {>= "1.6"}
"yojson" {>= "1.4.1"}
"yojson" {>= "1.6.0"}
"bap" {>= "1.6"}
"alcotest" {>= "0.8.3"}
"core_kernel" {>= "v0.11" & < "v0.12"}
......
......@@ -57,13 +57,13 @@ let partial_run project config modules =
let tid_address_map = Address_translation.generate_tid_map program in
let json = Yojson.Basic.from_file config in
Log_utils.info "[cwe_checker] Just running the following analyses: %s." modules;
List.iter (String.split modules ~on: ',') ~f:(fun cwe -> try
begin
let cwe_mod = List.find_exn known_modules ~f:(fun x -> x.name = cwe) in
let program = Project.program project in
execute_cwe_module cwe_mod json program project tid_address_map
end
with Not_found -> failwith "[CWE_CHECKER] Unknown CWE module")
List.iter (String.split modules ~on: ',') ~f:(fun cwe ->
let cwe_mod = match List.find known_modules ~f:(fun x -> x.name = cwe) with
| Some(module_) -> module_
| None -> failwith "[CWE_CHECKER] Unknown CWE module" in
let program = Project.program project in
execute_cwe_module cwe_mod json program project tid_address_map
)
let full_run project config =
let program = Project.program project in
......
......@@ -6,7 +6,7 @@ let (+), (-) = Bitvector.(+), Bitvector.(-)
let (>) x y = Bitvector.(>) (Bitvector.signed x) (Bitvector.signed y)
let (<) x y = Bitvector.(<) (Bitvector.signed x) (Bitvector.signed y)
let (>=) x y = Bitvector.(>=) (Bitvector.signed x) (Bitvector.signed y)
(* let (>=) x y = Bitvector.(>=) (Bitvector.signed x) (Bitvector.signed y) *)
let (<=) x y = Bitvector.(<=) (Bitvector.signed x) (Bitvector.signed y)
let (=) x y = Bitvector.(=) x y
......@@ -74,16 +74,6 @@ let rec get mem_region pos =
else
Some(Error(())) (* pos intersects some data, but does not equal its starting address*)
(* Helper function. Removes all elements with position <= pos. *)
let rec remove_until mem_region pos =
match mem_region with
| [] -> []
| hd :: tl ->
if hd.pos <= pos then
remove_until tl pos
else
mem_region
let rec remove mem_region ~pos ~size =
let () = if pos + size < pos then failwith "[CWE-checker] element out of bounds for mem_region" in
......@@ -91,11 +81,11 @@ let rec remove mem_region ~pos ~size =
| [] -> []
| hd :: tl ->
if hd.pos + hd.size <= pos then
hd :: remove tl pos size
hd :: remove tl ~pos ~size
else if pos + size <= hd.pos then
mem_region
else
let mem_region = remove tl pos size in
let mem_region = remove tl ~pos ~size in
let mem_region =
if hd.pos + hd.size > pos + size then
error_elem ~pos:(pos + size) ~size:(hd.pos + hd.size - (pos + size)) :: mem_region
......@@ -111,12 +101,12 @@ let rec remove mem_region ~pos ~size =
let rec mark_error mem_region ~pos ~size =
let () = if pos + size < pos then failwith "[CWE-checker] element out of bounds for mem_region" in
match mem_region with
| [] -> (error_elem pos size) :: []
| [] -> (error_elem ~pos ~size) :: []
| hd :: tl ->
if hd.pos + hd.size <= pos then
hd :: (mark_error tl pos size)
hd :: (mark_error tl ~pos ~size)
else if pos + size <= hd.pos then
(error_elem pos size) :: mem_region
(error_elem ~pos ~size) :: mem_region
else
let start_pos = min pos hd.pos in
let end_pos_plus_one = max (pos + size) (hd.pos + hd.size) in
......@@ -130,22 +120,22 @@ let rec merge mem_region1 mem_region2 ~data_merge =
| ([], value) -> value
| (hd1 :: tl1, hd2 :: tl2) ->
if hd1.pos + hd1.size <= hd2.pos then
hd1 :: merge tl1 mem_region2 data_merge
hd1 :: merge tl1 mem_region2 ~data_merge
else if hd2.pos + hd2.size <= hd1.pos then
hd2 :: merge mem_region1 tl2 data_merge
hd2 :: merge mem_region1 tl2 ~data_merge
else if hd1.pos = hd2.pos && hd1.size = hd2.size then
match (hd1.data, hd2.data) with
| (Ok(data1), Ok(data2)) -> begin
match data_merge data1 data2 with
| Some(Ok(value)) -> { hd1 with data = Ok(value) } :: merge tl1 tl2 ~data_merge
| Some(Error(_)) -> {hd1 with data = Error(())} :: merge tl1 tl2 ~data_merge
| None -> merge tl1 tl2 data_merge
| None -> merge tl1 tl2 ~data_merge
end
| _ -> { hd1 with data = Error(()) } :: merge tl1 tl2 ~data_merge
else
let start_pos = min hd1.pos hd2.pos in
let end_pos_plus_one = max (hd1.pos + hd1.size) (hd2.pos + hd2.size) in
let mem_region = merge tl1 tl2 data_merge in
let mem_region = merge tl1 tl2 ~data_merge in
mark_error mem_region ~pos:start_pos ~size:(end_pos_plus_one - start_pos)
......@@ -156,8 +146,8 @@ let rec equal (mem_region1:'a t) (mem_region2:'a t) ~data_equal : bool =
if hd1.pos = hd2.pos && hd1.size = hd2.size then
match (hd1.data, hd2.data) with
| (Ok(data1), Ok(data2)) when data_equal data1 data2 ->
equal tl1 tl2 data_equal
| (Error(()), Error(())) -> equal tl1 tl2 data_equal
equal tl1 tl2 ~data_equal
| (Error(()), Error(())) -> equal tl1 tl2 ~data_equal
| _ -> false
else
false
......
......@@ -37,7 +37,7 @@ let binop_result_option val1 val2 ~op =
(* generic merge of two ('a, unit) Result.t Map.t*)
let merge_result_map val1 val2 ~value_merge =
Map.merge val1 val2 ~f:(fun ~key values ->
Map.merge val1 val2 ~f:(fun ~key:_ values ->
match values with
| `Left(x)
| `Right(x) -> Some(x)
......@@ -78,7 +78,7 @@ module Register = struct
let merge reg1 reg2 =
match (reg1, reg2) with
| (Pointer(target_info1), Pointer(target_info2)) ->
Ok(Pointer(Map.merge target_info1 target_info2 ~f:(fun ~key values ->
Ok(Pointer(Map.merge target_info1 target_info2 ~f:(fun ~key:_ values ->
match values with
| `Left(info)
| `Right(info) -> Some(info)
......@@ -118,7 +118,7 @@ module Register = struct
let set_unknown_offsets register =
match register with
| Pointer(targets) ->
let new_targets = Map.map targets ~f:(fun target -> { PointerTargetInfo.offset = None; alignment = None }) in
let new_targets = Map.map targets ~f:(fun _target -> { PointerTargetInfo.offset = None; alignment = None }) in
Pointer(new_targets)
| Data -> Data
......@@ -172,7 +172,7 @@ module TypeInfo = struct
| None -> None in
let stack_info = { PointerTargetInfo.offset = offset; alignment = alignment;} in
let stack_target_map = Map.set Tid.Map.empty ~key:sub_tid ~data:stack_info in
{ state with reg = Map.set state.reg stack_register (Ok(Register.Pointer(stack_target_map))); }
{ state with reg = Map.set state.reg ~key:stack_register ~data:(Ok(Register.Pointer(stack_target_map))); }
(** Returns a TypeInfo.t with only the stack pointer as pointer register (with
unknown offset) and only the flag registers as data registers. The stack is empty. *)
......@@ -196,19 +196,6 @@ module TypeInfo = struct
let remove_virtual_registers state =
{ state with reg = Map.filter_keys state.reg ~f:(fun var -> Var.is_physical var) }
(** udate offsets of all possible targets of the register by adding the given value *)
(* TODO: also implement correct offset for AND and OR if the alignment is known *)
let register_offset_add state register (value:Bitvector.t) =
match Map.find state.reg register with
| Some(Ok(Pointer(targets))) ->
let updated_targets = Map.map targets ~f:(fun type_info ->
match type_info.offset with
| Some(Ok(x)) -> { type_info with offset = Some(Ok(Bitvector.(+) x value ))}
| _ -> type_info
) in
{ state with reg = Map.set state.reg ~key:register ~data:(Ok(Pointer(updated_targets))) }
| _ -> state
(** if the addr_exp is a (computable) stack offset, return the offset. In cases where addr_expr
may or may not be a stack offset (i.e. offset of a register which may point to the stack or
to some other memory region), it still returns an offset. *)
......@@ -251,8 +238,8 @@ let rec nested_exp_list exp : Exp.t list =
let nested_exp = match exp with
| Bil.Load(exp1, exp2, _, _) -> exp :: (nested_exp_list exp1) @ (nested_exp_list exp2)
| Bil.Store(exp1, exp2, exp3, _, _) -> nested_exp_list exp1 @ nested_exp_list exp2 @ nested_exp_list exp3
| Bil.BinOp(op, exp1, exp2) -> nested_exp_list exp1 @ nested_exp_list exp2
| Bil.UnOp(op, exp1) -> nested_exp_list exp1
| Bil.BinOp(_op, exp1, exp2) -> nested_exp_list exp1 @ nested_exp_list exp2
| Bil.UnOp(_op, exp1) -> nested_exp_list exp1
| Bil.Var(_) -> []
| Bil.Int(_) -> []
| Bil.Cast(_, _, exp1) -> nested_exp_list exp1
......@@ -270,8 +257,8 @@ let rec nested_exp_list exp : Exp.t list =
TODO: Bil.AND and Bil.OR are ignored, because we do not track alignment yet. *)
let get_stack_elem state exp ~sub_tid ~project =
match exp with
| Bil.Load(_, addr, endian, size) -> begin (* TODO: add a test for correct endianess *)
match TypeInfo.compute_stack_offset state addr sub_tid project with
| Bil.Load(_, addr, _endian, size) -> begin (* TODO: add a test for correct endianess *)
match TypeInfo.compute_stack_offset state addr ~sub_tid ~project with
| Some(offset) -> begin
match Mem_region.get state.TypeInfo.stack offset with
| Some(Ok(elem, elem_size)) ->
......@@ -301,26 +288,26 @@ let rec type_of_exp exp (state: TypeInfo.t) ~sub_tid ~project =
get_stack_elem state exp ~sub_tid ~project
| Bil.Store(_) -> None (* Stores are handled in another function. *)
| Bil.BinOp(binop, exp1, exp2) -> begin
match (binop, type_of_exp exp1 state sub_tid project, type_of_exp exp2 state sub_tid project) with
match (binop, type_of_exp exp1 state ~sub_tid ~project, type_of_exp exp2 state ~sub_tid ~project) with
(* pointer arithmetics *)
| (Bil.PLUS, Some(Ok(Pointer(_))), Some(Ok(Pointer(_)))) -> Some(Error(()))
| (Bil.PLUS, Some(Ok(Pointer(targets))), summand) -> Some(Ok(Register.add_to_offsets (Pointer(targets)) (value_of_exp exp2)))
| (Bil.PLUS, summand, Some(Ok(Pointer(targets)))) -> Some(Ok(Register.add_to_offsets (Pointer(targets)) (value_of_exp exp1)))
| (Bil.PLUS, Some(Ok(Pointer(targets))), _summand) -> Some(Ok(Register.add_to_offsets (Pointer(targets)) (value_of_exp exp2)))
| (Bil.PLUS, _summand, Some(Ok(Pointer(targets)))) -> Some(Ok(Register.add_to_offsets (Pointer(targets)) (value_of_exp exp1)))
| (Bil.PLUS, Some(Ok(Data)), Some(Ok(Data))) -> Some(Ok(Data))
| (Bil.PLUS, _, _) -> None
| (Bil.MINUS, Some(Ok(Pointer(_))), Some(Ok(Pointer(_)))) -> Some(Ok(Data)) (* Pointer subtraction to determine offset is CWE-469, this should be logged. *)
| (Bil.MINUS, Some(Ok(Pointer(targets))), other) -> Some(Ok(Register.sub_from_offsets (Pointer(targets)) (value_of_exp exp2))) (* We assume that other is not a pointer. This can only generate errors in the presence of CWE-469 *)
| (Bil.MINUS, Some(Ok(Pointer(targets))), _other) -> Some(Ok(Register.sub_from_offsets (Pointer(targets)) (value_of_exp exp2))) (* We assume that other is not a pointer. This can only generate errors in the presence of CWE-469 *)
| (Bil.MINUS, Some(Ok(Data)), Some(Ok(Data))) -> Some(Ok(Data))
| (Bil.MINUS, _, _) -> None
(* bitwise AND and OR can be used as addition and subtraction if some alignment of the pointer is known *)
| (Bil.AND, Some(Ok(Pointer(_))), Some(Ok(Pointer(_)))) -> Some(Error(())) (* TODO: This could be a pointer, but is there any case where this is used in practice? *)
| (Bil.AND, Some(Ok(Pointer(targets))), other)
| (Bil.AND, other, Some(Ok(Pointer(targets)))) -> Some(Ok(Register.set_unknown_offsets (Pointer(targets))))
| (Bil.AND, Some(Ok(Pointer(targets))), _other)
| (Bil.AND, _other, Some(Ok(Pointer(targets)))) -> Some(Ok(Register.set_unknown_offsets (Pointer(targets))))
| (Bil.AND, Some(Ok(Data)), Some(Ok(Data))) -> Some(Ok(Data))
| (Bil.AND, _, _) -> None
| (Bil.OR, Some(Ok(Pointer(_))), Some(Ok(Pointer(_)))) -> Some(Error(())) (* TODO: This could be a pointer, but is there any case where this is used in practice? *)
| (Bil.OR, Some(Ok(Pointer(targets))), other)
| (Bil.OR, other, Some(Ok(Pointer(targets)))) -> Some(Ok(Register.set_unknown_offsets (Pointer(targets))))
| (Bil.OR, Some(Ok(Pointer(targets))), _other)
| (Bil.OR, _other, Some(Ok(Pointer(targets)))) -> Some(Ok(Register.set_unknown_offsets (Pointer(targets))))
| (Bil.OR, Some(Ok(Data)), Some(Ok(Data))) -> Some(Ok(Data))
| (Bil.OR, _, _) -> None
| _ -> Some(Ok(Data)) (* every other operation should not yield valid pointers *)
......@@ -330,11 +317,11 @@ let rec type_of_exp exp (state: TypeInfo.t) ~sub_tid ~project =
| Bil.Int(_) -> None (* TODO: For non-relocateable binaries this could be a pointer to a function/global variable *)
| Bil.Cast(Bil.SIGNED, _, _) -> Some(Ok(Data))
| Bil.Cast(_, size, exp) ->
if size = (Symbol_utils.arch_pointer_size_in_bytes project * 8) then type_of_exp exp state sub_tid project else Some(Ok(Data)) (* TODO: There is probably a special case when 64bit addresses are converted to 32bit addresses here, which can yield pointers *)
if size = (Symbol_utils.arch_pointer_size_in_bytes project * 8) then type_of_exp exp state ~sub_tid ~project else Some(Ok(Data)) (* TODO: There is probably a special case when 64bit addresses are converted to 32bit addresses here, which can yield pointers *)
| Bil.Let(_) -> None
| Bil.Unknown(_) -> None
| Bil.Ite(if_, then_, else_) -> begin
match (type_of_exp then_ state sub_tid project, type_of_exp else_ state sub_tid project) with
| Bil.Ite(_if_, then_, else_) -> begin
match (type_of_exp then_ state ~sub_tid ~project, type_of_exp else_ state ~sub_tid ~project) with
| (Some(value1), Some(value2)) -> if value1 = value2 then Some(value1) else None
| _ -> None
end
......@@ -352,7 +339,7 @@ let pointer_size_as_bitvector project =
is unclear, whether it really was a store onto the stack or to somewhere else. *)
let set_stack_elem state exp ~sub_tid ~project =
match exp with
| Bil.Store(_, addr_exp, value_exp, endian, size) ->
| Bil.Store(_, addr_exp, value_exp, _endian, size) ->
let stack_offset = TypeInfo.compute_stack_offset state addr_exp ~sub_tid ~project in
let value = type_of_exp value_exp state ~sub_tid ~project in
let addr_type = type_of_exp addr_exp state ~sub_tid ~project in
......@@ -406,7 +393,7 @@ let add_mem_address_registers state exp ~sub_tid ~project =
| Bil.BinOp(Bil.OR, Bil.Int(_), Bil.Var(addr)) ->
begin match Map.find state.TypeInfo.reg addr with
| Some(Ok(Pointer(_))) -> state
| _ -> { state with TypeInfo.reg = Map.set state.TypeInfo.reg addr (Ok(Register.Pointer(Tid.Map.empty))) } (* TODO: there are some false positives here for indices in global data arrays, where the immediate is the pointer. Maybe remove all cases with potential false positives? *)
| _ -> { state with TypeInfo.reg = Map.set state.TypeInfo.reg ~key:addr ~data:(Ok(Register.Pointer(Tid.Map.empty))) } (* TODO: there are some false positives here for indices in global data arrays, where the immediate is the pointer. Maybe remove all cases with potential false positives? *)
end
| Bil.BinOp(Bil.PLUS, Bil.Var(addr), exp2)
| Bil.BinOp(Bil.PLUS, exp2, Bil.Var(addr))
......@@ -415,10 +402,10 @@ let add_mem_address_registers state exp ~sub_tid ~project =
| Bil.BinOp(Bil.AND, exp2, Bil.Var(addr))
| Bil.BinOp(Bil.OR, Bil.Var(addr), exp2)
| Bil.BinOp(Bil.OR, exp2, Bil.Var(addr)) ->
if type_of_exp exp2 state sub_tid project = Some(Ok(Register.Data)) then
if type_of_exp exp2 state ~sub_tid ~project = Some(Ok(Register.Data)) then
begin match Map.find state.TypeInfo.reg addr with
| Some(Ok(Pointer(_))) -> state
| _ -> { state with TypeInfo.reg = Map.set state.TypeInfo.reg addr (Ok(Register.Pointer(Tid.Map.empty))) }
| _ -> { state with TypeInfo.reg = Map.set state.TypeInfo.reg ~key:addr ~data:(Ok(Register.Pointer(Tid.Map.empty))) }
end
else
state
......@@ -433,16 +420,16 @@ let keep_only_stack_register state ~sub_tid ~project =
let stack_pointer_value = Map.find state.TypeInfo.reg (Symbol_utils.stack_register project) in
let new_state = TypeInfo.only_stack_pointer_and_flags sub_tid project in
match stack_pointer_value with
| Some(value) -> { new_state with TypeInfo.reg = Map.set state.reg (Symbol_utils.stack_register project) value }
| Some(value) -> { new_state with TypeInfo.reg = Map.set state.reg ~key:(Symbol_utils.stack_register project) ~data:value }
| None -> new_state
let update_state_def state def ~sub_tid ~project =
(* add all registers that are used as address registers in load/store expressions to the state *)
let state = add_mem_address_registers state (Def.rhs def) sub_tid project in
let state = add_mem_address_registers state (Def.rhs def) ~sub_tid ~project in
(* update the lhs of the definition with its new type *)
let state = match type_of_exp (Def.rhs def) state sub_tid project with
let state = match type_of_exp (Def.rhs def) state ~sub_tid ~project with
| Some(value) ->
let reg = Map.set state.TypeInfo.reg (Def.lhs def) value in
let reg = Map.set state.TypeInfo.reg ~key:(Def.lhs def) ~data:value in
{ state with TypeInfo.reg = reg }
| None -> (* We don't know the type of the new value *)
let reg = Map.remove state.TypeInfo.reg (Def.lhs def) in
......@@ -490,7 +477,7 @@ let update_state_malloc_call state malloc_like_tid jmp_term ~project =
let return_reg = match Bap.Std.Arg.rhs return_arg with
| Bil.Var(var) -> var
| _ -> failwith "[CWE-checker] Return register of malloc-like function wasn't a register." in
let target_map = Map.set Tid.Map.empty (Term.tid jmp_term) { PointerTargetInfo.offset = Some(Ok(Bitvector.of_int 0 ~width:(Symbol_utils.arch_pointer_size_in_bytes project * 8))); alignment = None} in
let target_map = Map.set Tid.Map.empty ~key:(Term.tid jmp_term) ~data:{ PointerTargetInfo.offset = Some(Ok(Bitvector.of_int 0 ~width:(Symbol_utils.arch_pointer_size_in_bytes project * 8))); alignment = None} in
{ state with TypeInfo.reg = Var.Map.set state.reg ~key:return_reg ~data:(Ok(Pointer(target_map))) }
......@@ -508,25 +495,25 @@ let update_state_jmp state jmp ~sub_tid ~project =
| None -> Tid.name tid in
if String.Set.mem (Cconv.parse_dyn_syms project) func_name then
begin if List.exists (malloc_like_function_list ()) ~f:(fun elem -> elem = func_name) then
update_state_malloc_call state tid jmp project
update_state_malloc_call state tid jmp ~project
else
let empty_state = TypeInfo.empty () in (* TODO: to preserve stack information we need to be sure that the callee does not write on the stack. Can we already check that? *)
{ empty_state with
TypeInfo.reg = Var.Map.filter_keys state.TypeInfo.reg ~f:(fun var -> Cconv.is_callee_saved var project) }
end
else
keep_only_stack_register state sub_tid project (* TODO: add interprocedural analysis here. *)
| Indirect(_) -> keep_only_stack_register state sub_tid project in (* TODO: when we have value tracking and interprocedural analysis, we can add indirect calls to the regular analysis. *)
keep_only_stack_register state ~sub_tid ~project (* TODO: add interprocedural analysis here. *)
| Indirect(_) -> keep_only_stack_register state ~sub_tid ~project in (* TODO: when we have value tracking and interprocedural analysis, we can add indirect calls to the regular analysis. *)
(* The callee is responsible for removing the return address from the stack, so we have to adjust the stack offset accordingly. *)
(* TODO: x86/x64, arm, mips and ppc all use descending stacks and we assume here that a descending stack is used. Can this be checked by some info given from bap? Is there an architecture with an upward growing stack? *)
add_to_stack_offset return_state (Symbol_utils.arch_pointer_size_in_bytes project) project
add_to_stack_offset return_state (Symbol_utils.arch_pointer_size_in_bytes project) ~project
| Int(_, _) -> (* TODO: We need stubs and/or interprocedural analysis here *)
keep_only_stack_register state sub_tid project (* TODO: Are there cases where the stack offset has to be adjusted here? *)
keep_only_stack_register state ~sub_tid ~project (* TODO: Are there cases where the stack offset has to be adjusted here? *)
| Goto(Indirect(Bil.Var(var))) (* TODO: warn when jumping to something that is marked as data. *)
| Ret(Indirect(Bil.Var(var))) ->
begin match Map.find state.TypeInfo.reg var with
| Some(Ok(Pointer(_))) -> state
| _ -> { state with TypeInfo.reg = Map.set state.TypeInfo.reg var (Ok(Register.Pointer(Tid.Map.empty))) }
| _ -> { state with TypeInfo.reg = Map.set state.TypeInfo.reg ~key:var ~data:(Ok(Register.Pointer(Tid.Map.empty))) }
end
| Goto(_)
| Ret(_) -> state
......@@ -535,7 +522,7 @@ let update_state_jmp state jmp ~sub_tid ~project =
let update_type_info block_elem state ~sub_tid ~project =
match block_elem with
| `Def def -> update_state_def state def ~sub_tid ~project
| `Phi phi -> state (* We ignore phi terms for this analysis. *)
| `Phi _phi -> state (* We ignore phi terms for this analysis. *)
| `Jmp jmp -> update_state_jmp state jmp ~sub_tid ~project
(** updates a block analysis. *)
......@@ -560,7 +547,7 @@ let intraprocedural_fixpoint func ~project =
let fn_start_state = update_block_analysis fn_start_block fn_start_state ~sub_tid ~project in
let fn_start_node = Seq.find_exn (Graphs.Ir.nodes cfg) ~f:(fun node -> (Term.tid fn_start_block) = (Term.tid (Graphs.Ir.Node.label node))) in
let empty = Map.empty (module Graphs.Ir.Node) in
let with_start_node = Map.set empty fn_start_node fn_start_state in
let with_start_node = Map.set empty ~key:fn_start_node ~data:fn_start_state in
let init = Graphlib.Std.Solution.create with_start_node only_sp in
let equal = TypeInfo.equal in
let merge = TypeInfo.merge in
......@@ -585,28 +572,16 @@ let extract_start_state node ~cfg ~solution ~sub_tid ~project =
TypeInfo.merge state (Graphlib.Std.Solution.get solution node)
)
(** Returns a list of pairs (tid, state) for each def in a (blk_t-)node. The state
is the state _after execution of the node. *)
let state_list_def node ~cfg ~solution ~sub_tid ~project =
let input_state = extract_start_state node ~cfg ~solution ~sub_tid ~project in
let block = Graphs.Ir.Node.label node in
let defs = Term.enum def_t block in
let (output, _) = Seq.fold defs ~init:([], input_state) ~f:(fun (list_, state) def ->
let state = update_state_def state def sub_tid project in
( (Term.tid def, state) :: list_, state)
) in
output
let compute_pointer_register project =
let program = Project.program project in
let program_with_tags = Term.map sub_t program ~f:(fun func ->
let cfg = Sub.to_cfg func in
let sub_tid = Term.tid func in
let solution = intraprocedural_fixpoint func project in
let solution = intraprocedural_fixpoint func ~project in
Seq.fold (Graphs.Ir.nodes cfg) ~init:func ~f:(fun func node ->
let block = Graphs.Ir.Node.label node in
let start_state = extract_start_state node cfg solution sub_tid project in
let start_state = extract_start_state node ~cfg ~solution ~sub_tid ~project in
let tagged_block = Term.set_attr block type_info_tag start_state in
Term.update blk_t func tagged_block
)
......@@ -619,7 +594,7 @@ let print_type_info_to_debug state block_tid ~tid_map ~sub_tid ~project =
match reg with
| Ok(Register.Pointer(targets)) ->
(Var.name var ^ ":Pointer(targets: " ^
(Map.fold targets ~init:"" ~f:(fun ~key ~data accum_string -> (Tid.name key) ^ "," ^ accum_string)) ^
(Map.fold targets ~init:"" ~f:(fun ~key ~data:_ accum_string -> (Tid.name key) ^ "," ^ accum_string)) ^
")") :: str_list
| Ok(Register.Data) -> (Var.name var ^ ":Data, ") :: str_list
| Error(_) -> (Var.name var ^ ":Error, ") :: str_list ) in
......
......@@ -7,7 +7,7 @@ let version = "0.1"
let collect_muliplications = Exp.fold ~init:0 (object
inherit [Int.t] Exp.visitor
method! enter_binop op o1 o2 binops = match op with
method! enter_binop op _o1 _o2 binops = match op with
| Bil.TIMES | Bil.LSHIFT -> binops + 1
| _ -> binops
end)
......@@ -17,7 +17,7 @@ let contains_multiplication d =
let binops = collect_muliplications rhs in
binops > 0
let check_multiplication_before_symbol proj prog sub blk jmp tid_map symbols =
let check_multiplication_before_symbol _proj _prog _sub blk jmp tid_map symbols =
Seq.iter (Term.enum def_t blk)
~f:(fun d -> if contains_multiplication d then
Log_utils.warn
......
open Core_kernel
open Bap.Std
open Unix
(* TODO: IVG via gitter:
I see, so you need the CU information, and yes BAP doesn't provide this.
......@@ -19,16 +19,6 @@ but in general case it is better to use the approach described above. *)
let name = "CWE215"
let version = "0.1"
let read_lines in_chan =
let lines = ref [] in
try
while true; do
lines := input_line in_chan :: !lines
done; !lines
with End_of_file ->
In_channel.close in_chan;
List.rev !lines
(* TODO: check if program contains strings like "DEBUG"*)
let check_cwe _ project _ _ _ =
match Project.get project filename with
......@@ -36,7 +26,7 @@ let check_cwe _ project _ _ _ =
let cmd = Format.sprintf "readelf --debug-dump=decodedline %s | grep CU" fname in
try
let in_chan = Unix.open_process_in cmd in
read_lines in_chan |> List.iter ~f:(fun l -> Log_utils.warn "[%s] {%s} (Information Exposure Through Debug Information) %s" name version l)
In_channel.input_lines in_chan |> List.iter ~f:(fun l -> Log_utils.warn "[%s] {%s} (Information Exposure Through Debug Information) %s" name version l)
with
Unix.Unix_error (e,fm,argm) ->
Log_utils.error "[%s] {%s} %s %s %s" name version (Unix.error_message e) fm argm
......
......@@ -50,7 +50,7 @@ let check_route sub symbols =
if res then res else res
(** Checks one possible valid path (combination of APIs) of chroot. *)
let check_path prog tid_map sub path =
let check_path prog _tid_map sub path =
let symbols = build_symbols path prog in
if List.length symbols = List.length path then
begin
......@@ -81,7 +81,7 @@ let check_subfunction prog tid_map sub pathes =
(Term.name sub)
end
let check_cwe prog proj tid_map pathes _ =
let check_cwe prog _proj tid_map pathes _ =
let chroot_symbol = find_symbol prog "chroot" in
match chroot_symbol with
| Some _ ->
......
......@@ -27,7 +27,7 @@ let contains_symbol block symbol_name =
(* Checks whether a subfunction contains a catch block. *)
let contains_catch subfunction =
let blocks = Term.enum blk_t subfunction in
Seq.exists blocks (fun block -> contains_symbol block "@__cxa_begin_catch")
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. *)
......@@ -59,7 +59,7 @@ let rec find_uncaught_exceptions subfunction already_checked_functions 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 check_cwe program _project tid_map _symbol_pairs _ =
let entry_points = Symbol_utils.get_program_entry_points program in
let _ = Seq.fold entry_points ~init:[] ~f:(fun already_checked_functions sub -> find_uncaught_exceptions ~tid_map:tid_map sub already_checked_functions program) in
()
open Core_kernel
open Bap.Std
open Graph_utils
open Symbol_utils
let name = "CWE332"
let version = "0.1"
let check_cwe program proj tid_map symbol_pairs _ =
let check_cwe program _proj _tid_map _symbol_pairs _ =
match Option.both (find_symbol program "srand") (find_symbol program "rand") with
| None -> begin
match (find_symbol program "rand") with
| None -> ()
| Some _ -> Log_utils.warn "[%s] {%s} (Insufficient Entropy in PRNG) program uses rand without calling srand before" name version
end
| Some (srand_tid, rand_tid) -> ()
| Some (_srand_tid, _rand_tid) -> ()
......@@ -34,7 +34,7 @@ let is_reachable sub source sink =
let sink_blk = get_blk_tid_of_tid sub sink_tid in
Graphlib.Std.Graphlib.is_reachable (module Graphs.Tid) cfg source_blk sink_blk
let handle_sub sub program tid_map symbols source sink =
let handle_sub sub program tid_map _symbols source sink =
if (Symbol_utils.sub_calls_symbol program sub source) && (Symbol_utils.sub_calls_symbol program sub sink) then
begin
let calls = Symbol_utils.get_direct_callsites_of_sub sub in
......@@ -57,6 +57,6 @@ let handle_sub sub program tid_map symbols source sink =
else
()
let check_cwe program proj tid_map symbol_pairs _ =
let check_cwe program _proj tid_map _symbol_pairs _ =
let symbols = Symbol_utils.build_symbols ["access"; "open";] in
Seq.iter (Term.enum sub_t program) ~f:(fun s -> handle_sub s program tid_map symbols "access" "open")
......@@ -21,7 +21,7 @@ let handle_sub sub program tid_map symbols =
end
else ()
let check_cwe program proj tid_map symbols _ =
let check_cwe program _proj tid_map symbols _ =
match symbols with
| hd::[] ->
Seq.iter (Term.enum sub_t program) ~f:(fun s -> handle_sub s program tid_map hd)
......
......@@ -10,7 +10,7 @@ let get_defs sub_ssa =
let collect_stores_of_exp = Exp.fold ~init:0 (object
inherit [int] Exp.visitor
method! enter_store ~mem:_ ~addr:addr ~exp:exp _ _ stores =
method! enter_store ~mem:_ ~addr:_ ~exp:_ _ _ stores =
stores + 1
end)
......@@ -62,7 +62,7 @@ let is_interesting_load_store def fp_pointer =
(*TODO: implement real filtering*)
let filter_mem_address i min_fp_offset = Set.filter i ~f:(fun elem -> (Word.of_int ~width:32 min_fp_offset) < elem)
let check_subfunction prog proj tid_map sub =
let check_subfunction _prog proj tid_map sub =
let fp_pointer = get_fp_of_arch (Project.arch proj) in
let min_fp_offset = get_min_fp_offset (Project.arch proj) in
let stores = ref [||] in
......@@ -92,5 +92,5 @@ let check_subfunction prog proj tid_map sub =
end
end)
let check_cwe prog proj tid_map symbol_names _ =
let check_cwe prog proj tid_map _symbol_names _ =
Seq.iter (Term.enum sub_t prog) ~f:(fun sub -> check_subfunction prog proj tid_map sub)
......@@ -8,7 +8,7 @@ let version = "0.1"
let get_pointer_size arch =
Size.in_bytes @@ Arch.addr_size arch
let check_input_is_pointer_size proj prog sub blk jmp tid_map symbols =
let check_input_is_pointer_size proj _prog _sub blk jmp tid_map symbols =
Seq.iter (Term.enum def_t blk) ~f:(fun d -> match Exp.eval @@ Def.rhs d with
| Imm w ->
begin
......
......@@ -53,7 +53,7 @@ module State = struct
(** two states are equal if they contain the same set of tainted registers*)
let equal state1 state2 =
(List.length state1) = (List.length state2) &&
not (List.exists state1 ~f:(fun (var, tid) -> Option.is_none (find state2 var) ))
not (List.exists state1 ~f:(fun (var, _tid) -> Option.is_none (find state2 var) ))
(** The union of two states is the union of the tainted registers*)
let union state1 state2 =
......@@ -66,14 +66,14 @@ module State = struct
(** remove virtual registers from the state (useful at the end of a block) *)
let remove_virtual_registers state =
List.filter state ~f:(fun (var, tid) -> Var.is_physical var)
List.filter state ~f:(fun (var, _tid) -> Var.is_physical var)
end
(* check whether an expression contains an unchecked value. *)
let rec contains_unchecked exp state : access_type =
match exp with
| Bil.Load(mem, addr, _, _)->
| Bil.Load(_mem, addr, _, _)->
begin
let acc = contains_unchecked addr state in
match acc with
......@@ -81,7 +81,7 @@ let rec contains_unchecked exp state : access_type =
| Access(var) -> MemAccess(var)
| NoAccess -> NoAccess
end
| Bil.Store(mem, addr, val_expression, _,_) ->
| Bil.Store(_mem, addr, val_expression, _,_) ->
begin
let acc = union_access (contains_unchecked addr state) (contains_unchecked val_expression state) in
match acc with
......@@ -111,7 +111,7 @@ let rec contains_unchecked exp state : access_type =
to the source of this return value from state. *)
let checks_value exp state : State.t =
match exp with
| Bil.Ite(if_, then_, else_) -> begin
| Bil.Ite(if_, _then_, _else_) -> begin
match contains_unchecked if_ state with
| Access(var) ->
(* We filter out all registers with the same generating tid, since we have checked
......@@ -138,7 +138,7 @@ let flag_any_access exp state ~cwe_hits =
(** flag all unchecked registers as cwe_hits, return empty state *)
let flag_all_unchecked_registers state ~cwe_hits =
let () = List.iter state ~f:(fun (var, tid) ->
let () = List.iter state ~f:(fun (_var, tid) ->
append_to_hits cwe_hits tid) in
[]
......@@ -190,30 +190,30 @@ let update_state_jmp jmp state ~cwe_hits ~function_names ~program ~block ~strict
| NoAccess -> state
end in
match Jmp.kind jmp with
| Goto(Indirect(exp)) -> flag_any_access exp state cwe_hits
| Goto(Indirect(exp)) -> flag_any_access exp state ~cwe_hits
| Goto(Direct(_)) -> state
| Ret(_) -> if strict_call_policy then
flag_all_unchecked_registers state cwe_hits
flag_all_unchecked_registers state ~cwe_hits
else
state
| Int(_, _) -> flag_all_unchecked_registers state cwe_hits
| Int(_, _) -> flag_all_unchecked_registers state ~cwe_hits
| Call(call) ->
let state = match Call.return call with
| Some(Indirect(exp)) -> flag_any_access exp state cwe_hits
| Some(Indirect(exp)) -> flag_any_access exp state ~cwe_hits
| _ -> state in
let state = match Call.target call with
| Indirect(exp) -> flag_any_access exp state cwe_hits
| Indirect(exp) -> flag_any_access exp state ~cwe_hits
| _ -> state in
let state = match strict_call_policy with
| true -> (* all unchecked registers get flagged as hits *)
flag_all_unchecked_registers state cwe_hits
flag_all_unchecked_registers state ~cwe_hits
| false -> (* we assume that the callee will check all remaining unchecked values *)
[] in
match Call.target call with
| Indirect(_) -> state (* already handled above *)
| Direct(tid) ->
if List.exists function_names ~f:(fun elem -> String.(=) elem (Tid.name tid)) then
taint_return_registers tid state program block
taint_return_registers tid state ~program ~block
else
state
......@@ -227,7 +227,7 @@ let update_block_analysis block register_state ~cwe_hits ~function_names ~progra
let register_state = Seq.fold elements ~init:register_state ~f:(fun state element ->
match element with
| `Def def -> update_state_def def state ~cwe_hits
| `Phi phi -> state (* We ignore phi terms for this analysis. *)
| `Phi _phi -> state (* We ignore phi terms for this analysis. *)
| `Jmp jmp -> update_state_jmp jmp state ~cwe_hits ~function_names ~program ~block ~strict_call_policy
) in
State.remove_virtual_registers register_state (* virtual registers should not be accessed outside of the block where they are defined. *)
......@@ -255,7 +255,7 @@ let print_hit tid ~sub ~function_names ~tid_map =
| _ -> false
) in ()
let check_cwe prog proj tid_map symbol_names parameters =
let check_cwe prog _proj tid_map symbol_names parameters =
let symbols = match symbol_names with
| hd :: _ -> hd
| _ -> failwith "[CWE476] symbol_names not as expected" in
......
......@@ -4,7 +4,7 @@ open Bap.Std
let name = "CWE676"
let version = "0.1"
let get_call_to_target cg callee target =
let get_call_to_target _cg callee target =
Term.enum blk_t callee |>
Seq.concat_map ~f:(fun blk ->
Term.enum jmp_t blk |> Seq.filter_map ~f:(fun j ->
......@@ -35,7 +35,7 @@ let resolve_symbols prog symbols =
Seq.filter ~f:(fun s -> List.exists ~f:(fun x -> x = Sub.name s) symbols)
let check_cwe prog proj tid_map symbol_names _ =
let check_cwe prog _proj tid_map symbol_names _ =
match symbol_names with
| hd::[] ->
let subfunctions = Term.enum sub_t prog in
......
open Core_kernel
open Bap.Std
let name = "CWE782"
let version = "0.1"
(*TODO: check if binary is setuid*)
let handle_sub sub program tid_map symbols =
let handle_sub sub program tid_map _symbols =
if Symbol_utils.sub_calls_symbol program sub "ioctl" then
Log_utils.warn "[%s] {%s} (Exposed IOCTL with Insufficient Access Control) Program uses ioctl at %s (%s). Be sure to double check the program and the corresponding driver."
name
......@@ -15,5 +14,5 @@ let handle_sub sub program tid_map symbols =
else
()
let check_cwe program proj tid_map symbols _ =
let check_cwe program _proj tid_map symbols _ =
Seq.iter (Term.enum sub_t program) ~f:(fun s -> handle_sub s program tid_map symbols)
......@@ -14,7 +14,7 @@ dev-repo: "git+https://github.com/fkie-cad/cwe_checker"
depends: [
"ocaml" {>= "4.05"}
"dune" {>= "1.6"}
"yojson" {>= "1.4.1"}
"yojson" {>= "1.6.0"}
"bap" {>= "1.6"}
"core_kernel" {>= "v0.11" & < "v0.12"}
"ppx_jane" {>= "v0.11" & < "v0.12"}
......
......@@ -9,7 +9,7 @@ let translate_tid_to_assembler_address_string tid tid_map =
let generate_tid_map prog =
(object
inherit [addr Tid.Map.t] Term.visitor
method enter_term _ t addrs = match Term.get_attr t address with
method! enter_term _ t addrs = match Term.get_attr t address with
| None -> addrs
| Some addr -> Map.add_exn addrs ~key:(Term.tid t) ~data:addr
end)#run prog Tid.Map.empty
......@@ -12,9 +12,11 @@ let callee_saved_register_list project =
let arch = Project.arch project in
match arch with
| `x86_64 -> (* System V ABI *)
"RBX" :: "RSP" :: "RBP" :: "R12" :: "R13" :: "R14" :: "R15" :: []
| `x86_64 -> (* Microsoft x64 calling convention *) (* TODO: How to distinguish from System V? For the time being, only use the System V ABI, since it saves less registers. *)
"RBX" :: "RBP" :: "RDI" :: "RSI" :: "RSP" :: "R12" :: "R13" :: "R14" :: "R15" :: []
"RBX" :: "RSP" :: "RBP" :: "R12" :: "R13" :: "R14" :: "R15" :: []
(* Microsoft x64 calling convention. Unused at the moment, since Windows binaries are not yet supported.
| `x86_64 -> (* Microsoft x64 calling convention *)
"RBX" :: "RBP" :: "RDI" :: "RSI" :: "RSP" :: "R12" :: "R13" :: "R14" :: "R15" :: []
*)
| `x86 -> (* Both Windows and Linux save the same registers *)
"EBX" :: "ESI" :: "EDI" :: "EBP" :: []
| `armv4 | `armv5 | `armv6 | `armv7
......
open Core_kernel
open Bap.Std
open Graphlib.Std
type path = {
start_node: Bap.Std.tid;
nodes: Bap.Std.tid array;
end_node: Bap.Std.tid;
}
let get_entry_blk_of_sub sub =
match Term.first blk_t sub with
| Some blk -> blk
| _ -> failwith "Could not determine first block of sub."
let print_path p =
Format.printf "%s\n" (Array.fold p.nodes ~init:"" ~f:(fun acc n -> acc ^ " -> " ^ (Tid.to_string n)))
let print_path_length p =
Format.printf "%d\n" (Array.length p.nodes)
(* ToDo: remove *)
let print_current_edge a b =
Format.printf "\t%s -> %s\n" (Tid.to_string a) (Tid.to_string b)
let fork_path current_path current_node =
let new_path = Array.append (Array.copy current_path.nodes) [|current_node|] in
{start_node = current_path.start_node; nodes = new_path; end_node = current_path.end_node;}
let node_already_visited_on_path node path =
node = path.start_node || Array.exists path.nodes ~f:(fun n -> n = node)
let rec get_all_paths_from_node node g current_path =
match Seq.to_list (Graphs.Tid.Node.succs node g) with
| [] -> [current_path]
| succs -> List.concat_map succs
~f:(fun succ ->
if node_already_visited_on_path succ current_path then
[]
else
get_all_paths_from_node succ g (fork_path current_path node))
(* Please mind the path explosion !!! *)
let enumerate_paths_between_blks sub blk_start_tid blk_end_tid limit =
let g = Sub.to_graph sub in
let pathes = get_all_paths_from_node blk_start_tid g {start_node = blk_start_tid; nodes = [||]; end_node = blk_end_tid} in
Format.printf "\tFound %d pathes.\n" (List.length pathes); []
(** This module implements functionality that works on graphs like the CFG.
Most of its functionality is implemented by using BAP's Graphlib.Std. *)
(* This module implements functionality related to parsing the JSON configuration file. *)
val get_symbol_lists_from_json : Yojson.Basic.json -> string -> string list list
val get_symbols_from_json : Yojson.Basic.json -> string -> string list
val get_parameter_list_from_json : Yojson.Basic.json -> string -> string list
val get_symbol_lists_from_json : Yojson.Basic.t -> string -> string list list
val get_symbols_from_json : Yojson.Basic.t -> string -> string list
val get_parameter_list_from_json : Yojson.Basic.t -> string -> string list
......@@ -166,7 +166,7 @@ module Make (S: SECTION) = struct
!prefix
(* example for a shorter timestamp string *)
let short_timestamp_str lvl =
let _short_timestamp_str lvl =
sprintf "%.3f %s: " (Unix.gettimeofday()) (string_of_level lvl)
let log lvl fmt =
......
......@@ -48,7 +48,7 @@ Term.enum blk_t sub |>
match Jmp.kind j with
| Goto _ | Ret _ | Int (_,_) -> None
| Call destination -> begin match Call.target destination with
| Direct tid -> Some j
| Direct _tid -> Some j
| _ -> None
end))
......
open Bap.Std
open Core_kernel
(* open Core_kernel *)
open Cwe_checker_core
let check msg x = Alcotest.(check bool) msg true x
......
open Bap.Std
open Core_kernel
val tests: unit Alcotest.test_case list
......@@ -29,16 +29,16 @@ let test_preamble () =
(project, stack_register, sub, sub_tid, fn_start_state)
let test_update_stack_offset () =
let (project, stack_register, sub, sub_tid, fn_start_state) = test_preamble () in
let (project, stack_register, _sub, sub_tid, fn_start_state) = test_preamble () in
let def1 = Def.create stack_register (Bil.binop Bil.plus (Bil.var stack_register) (Bil.int (bv 8))) in
let def2 = Def.create stack_register (Bil.binop Bil.minus (Bil.var stack_register) (Bil.int (bv 16))) in
let block = create_block_from_defs [def1; def2] in
let state = update_block_analysis block fn_start_state sub_tid project in
let () = check "update_stack_offset" ( (compute_stack_offset state (Bil.var stack_register) sub_tid project) = Some(Bitvector.unsigned (bv (-8)))) in
let state = update_block_analysis block fn_start_state ~sub_tid ~project in
let () = check "update_stack_offset" ( (compute_stack_offset state (Bil.var stack_register) ~sub_tid ~project) = Some(Bitvector.unsigned (bv (-8)))) in
()
let test_preserve_stack_offset_on_stubs () =
let (project, stack_register, sub, sub_tid, fn_start_state) = test_preamble () in
let (project, stack_register, _sub, sub_tid, fn_start_state) = test_preamble () in
let register1 = Var.create "Register1" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let mem_reg = Var.create "Mem_reg" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let def1 = Def.create register1 (Bil.unop Bil.NEG (Bil.var register1)) in
......@@ -49,9 +49,9 @@ let test_preserve_stack_offset_on_stubs () =
let () = Blk.Builder.add_def block def2 in
let () = Blk.Builder.add_jmp block call_term in
let block = Blk.Builder.result block in
let state = update_block_analysis block fn_start_state sub_tid project in
let state = update_block_analysis block fn_start_state ~sub_tid ~project in
let pointer_size = Symbol_utils.arch_pointer_size_in_bytes project in (* since the callee removes the return address from the stack, the stack offset is adjusted accordingly. *)
let () = check "preserve_stack_offset_inner_call" ( (compute_stack_offset state (Bil.var stack_register) sub_tid project) = Some(Bitvector.unsigned (bv pointer_size))) in
let () = check "preserve_stack_offset_inner_call" ( (compute_stack_offset state (Bil.var stack_register) ~sub_tid ~project) = Some(Bitvector.unsigned (bv pointer_size))) in
let () = check "delete_stack_info_inner_call" (Mem_region.get state.TypeInfo.stack (bv (-8)) = None) in
(* find the malloc extern call. This fails if the example project does not contain a call to malloc. *)
let malloc_sub = Seq.find_exn (Term.enum sub_t (Project.program project)) ~f:(fun sub -> Sub.name sub = "malloc") in
......@@ -61,8 +61,8 @@ let test_preserve_stack_offset_on_stubs () =
let () = Blk.Builder.add_def block def2 in
let () = Blk.Builder.add_jmp block call_term in
let block = Blk.Builder.result block in
let state = update_block_analysis block fn_start_state sub_tid project in
let () = check "preserve_stack_offset_extern_malloc_call" ( (compute_stack_offset state (Bil.var stack_register) sub_tid project) = Some(Bitvector.unsigned (bv pointer_size))) in
let state = update_block_analysis block fn_start_state ~sub_tid ~project in
let () = check "preserve_stack_offset_extern_malloc_call" ( (compute_stack_offset state (Bil.var stack_register) ~sub_tid ~project) = Some(Bitvector.unsigned (bv pointer_size))) in
let () = check "preserve_stack_info_extern_malloc_call" (Mem_region.get state.TypeInfo.stack (bv (-8)) = Some(Ok((Data, bv 8)))) in
(* find the "free" extern call. This fails if the example project does not contain a call to "free". *)
let extern_sub = Seq.find_exn (Term.enum sub_t (Project.program project)) ~f:(fun sub -> Sub.name sub = "free") in
......@@ -72,19 +72,19 @@ let test_preserve_stack_offset_on_stubs () =
let () = Blk.Builder.add_def block def2 in
let () = Blk.Builder.add_jmp block call_term in
let block = Blk.Builder.result block in
let state = update_block_analysis block fn_start_state sub_tid project in
let () = check "preserve_stack_offset_extern_call" ( (compute_stack_offset state (Bil.var stack_register) sub_tid project) = Some(Bitvector.unsigned (bv pointer_size))) in
let state = update_block_analysis block fn_start_state ~sub_tid ~project in
let () = check "preserve_stack_offset_extern_call" ( (compute_stack_offset state (Bil.var stack_register) ~sub_tid ~project) = Some(Bitvector.unsigned (bv pointer_size))) in
let () = check "delete_stack_info_extern_call" (Mem_region.get state.TypeInfo.stack (bv (-8)) <> Some(Ok((Data, bv 8)))) in
()
let test_update_reg () =
let (project, stack_register, sub, sub_tid, fn_start_state) = test_preamble () in
let (project, stack_register, _sub, sub_tid, fn_start_state) = test_preamble () in
let register1 = Var.create "Register1" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let register2 = Var.create "Register2" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let def1 = Def.create register1 (Bil.binop Bil.AND (Bil.var stack_register) (Bil.int (bv 8))) in
let def2 = Def.create register2 (Bil.binop Bil.XOR (Bil.var register1) (Bil.var stack_register)) in
let block = create_block_from_defs [def1; def2] in
let state = update_block_analysis block fn_start_state sub_tid project in
let state = update_block_analysis block fn_start_state ~sub_tid ~project in
let () = check "update_pointer_register" (
match Var.Map.find state.TypeInfo.reg register1 with
| Some(Ok(Pointer(_))) -> true
......@@ -93,7 +93,7 @@ let test_update_reg () =
let () = check "update_data_register" (Var.Map.find state.TypeInfo.reg register2 = Some(Ok(Data))) in
let def1 = Def.create register1 (Bil.Load (Bil.var register1, Bil.var register2, Bitvector.LittleEndian, `r64) ) in
let block = create_block_from_defs [def1;] in
let state = update_block_analysis block fn_start_state sub_tid project in
let state = update_block_analysis block fn_start_state ~sub_tid ~project in
let () = check "add_mem_address_registers" (
match Var.Map.find state.TypeInfo.reg register2 with
| Some(Ok(Pointer(_))) -> true
......@@ -102,7 +102,7 @@ let test_update_reg () =
()
let test_update_stack () =
let (project, stack_register, sub, sub_tid, fn_start_state) = test_preamble () in
let (project, stack_register, _sub, sub_tid, fn_start_state) = test_preamble () in
let register1 = Var.create "Register1" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let register2 = Var.create "Register2" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let mem_reg = Var.create "Mem_reg" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
......@@ -110,10 +110,10 @@ let test_update_stack () =
let def2 = Def.create mem_reg (Bil.Store ((Bil.var mem_reg), (Bil.binop Bil.PLUS (Bil.var stack_register) (Bil.int (bv (-8)))), (Bil.var stack_register), Bitvector.LittleEndian, `r64)) in
let def3 = Def.create register2 (Bil.Load (Bil.var mem_reg, (Bil.binop Bil.MINUS (Bil.var stack_register) (Bil.int (bv 8))), Bitvector.LittleEndian, `r64) ) in
let block = create_block_from_defs [def1; def2; def3;] in
let state = update_block_analysis block fn_start_state sub_tid project in
let state = update_block_analysis block fn_start_state ~sub_tid ~project in
let () = check "write_to_stack" (
match Mem_region.get state.TypeInfo.stack (bv (-8)) with
| Some(Ok(Pointer(targets), size )) when size = bv (Symbol_utils.arch_pointer_size_in_bytes project) -> true
| Some(Ok(Pointer(_targets), size )) when size = bv (Symbol_utils.arch_pointer_size_in_bytes project) -> true
| _ -> false
) in
let () = check "load_from_stack" (
......@@ -124,7 +124,7 @@ let test_update_stack () =
()
let test_address_registers_on_load_and_store () =
let (project, stack_register, sub, sub_tid, fn_start_state) = test_preamble () in
let (project, stack_register, _sub, sub_tid, fn_start_state) = test_preamble () in
let register1 = Var.create "Register1" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let register2 = Var.create "Register2" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let mem_reg = Var.create "Mem_reg" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
......@@ -132,52 +132,52 @@ let test_address_registers_on_load_and_store () =
let def2 = Def.create mem_reg (Bil.Store ((Bil.var mem_reg), (Bil.var register1) , (Bil.var stack_register), Bitvector.LittleEndian, `r64)) in
let def3 = Def.create register2 (Bil.Load (Bil.var mem_reg, (Bil.binop Bil.MINUS (Bil.var stack_register) (Bil.int (bv 8))), Bitvector.LittleEndian, `r64) ) in
let block = create_block_from_defs [def1; def2; def3;] in
let state = update_block_analysis block fn_start_state sub_tid project in
let state = update_block_analysis block fn_start_state ~sub_tid ~project in
let () = check "mark_store_address_as_pointer" (
match Map.find_exn state.TypeInfo.reg register1 with
| Ok(Pointer(targets)) -> (Map.is_empty targets)
| _ -> false
) in
let () = check "dont_change_offsets_on_address_register" (compute_stack_offset state (Bil.var stack_register) sub_tid project = Some(bv 0)) in
let () = check "dont_change_offsets_on_address_register" (compute_stack_offset state (Bil.var stack_register) ~sub_tid ~project = Some(bv 0)) in
()
let test_merge_type_infos () =
let (project, stack_register, sub, sub_tid, fn_start_state) = test_preamble () in
let (project, stack_register, _sub, sub_tid, fn_start_state) = test_preamble () in
let generic_empty_state = only_stack_pointer_and_flags sub_tid project in
let def1 = Def.create stack_register (Bil.binop Bil.plus (Bil.var stack_register) (Bil.int (bv 8))) in
let block = create_block_from_defs [def1;] in
let state1 = update_block_analysis block fn_start_state sub_tid project in
let state2 = update_block_analysis block generic_empty_state sub_tid project in
let state1 = update_block_analysis block fn_start_state ~sub_tid ~project in
let state2 = update_block_analysis block generic_empty_state ~sub_tid ~project in
let merged_state = merge_type_infos state1 state1 in
let () = check "merge_same_stack_offset" (compute_stack_offset merged_state (Bil.var stack_register) sub_tid project = Some(Bitvector.unsigned (bv 8))) in
let () = check "merge_same_stack_offset" (compute_stack_offset merged_state (Bil.var stack_register) ~sub_tid ~project = Some(Bitvector.unsigned (bv 8))) in
let merged_state = merge_type_infos fn_start_state state1 in
let () = check "merge_different_stack_offsets" (compute_stack_offset merged_state (Bil.var stack_register) sub_tid project = None) in
let () = check "merge_different_stack_offsets" (compute_stack_offset merged_state (Bil.var stack_register) ~sub_tid ~project = None) in
let merged_state = merge_type_infos generic_empty_state state1 in
let () = check "merge_with_unknown_stack_offset" (compute_stack_offset merged_state (Bil.var stack_register) sub_tid project = Some(Bitvector.unsigned (bv 8))) in
let () = check "merge_with_unknown_stack_offset" (compute_stack_offset merged_state (Bil.var stack_register) ~sub_tid ~project = Some(Bitvector.unsigned (bv 8))) in
let merged_state = merge_type_infos generic_empty_state state2 in
let () = check "merge_empty_stack_offsets" (compute_stack_offset merged_state (Bil.var stack_register) sub_tid project = None) in
let () = check "merge_empty_stack_offsets" (compute_stack_offset merged_state (Bil.var stack_register) ~sub_tid ~project = None) in
()
let test_type_info_equal () =
let (project, stack_register, sub, sub_tid, fn_start_state) = test_preamble () in
let (project, _stack_register, _sub, sub_tid, fn_start_state) = test_preamble () in
let generic_empty_state = only_stack_pointer_and_flags sub_tid project in
let () = check "empty_state_neq_fn_start_state" (false = (type_info_equal fn_start_state generic_empty_state)) in
()
let test_malloc_call_return_reg () =
let (project, stack_register, sub, sub_tid, fn_start_state) = test_preamble () in
let (project, _stack_register, _sub, sub_tid, fn_start_state) = test_preamble () in
(* find the malloc extern call. This fails if the example project does not contain a call to malloc. *)
let malloc_sub = Seq.find_exn (Term.enum sub_t (Project.program project)) ~f:(fun sub -> Sub.name sub = "malloc") in
let call_term = Jmp.create (Call (Call.create ~target:(Label.direct (Term.tid malloc_sub)) () )) in
let block = Blk.Builder.create () in
let () = Blk.Builder.add_jmp block call_term in
let block = Blk.Builder.result block in
let state = update_block_analysis block fn_start_state sub_tid project in
let state = update_block_analysis block fn_start_state ~sub_tid ~project in
(* test whether the return register is marked as a pointer register. This fails if the example project is not a x64 binary. *)
let state_reg_list = Map.to_alist state.TypeInfo.reg in
let () = String.Set.iter (Cconv.parse_dyn_syms project) ~f:(fun elem -> print_endline elem) in
let () = check "malloc_return_register_marked" (match List.find state_reg_list ~f:(fun (var, register_info) -> Var.name var = "RAX") with
| Some((var, register_info)) -> (* TODO: test whether the target is set correctly. *)
let () = check "malloc_return_register_marked" (match List.find state_reg_list ~f:(fun (var, _register_info) -> Var.name var = "RAX") with
| Some((_var, register_info)) -> (* TODO: test whether the target is set correctly. *)
begin match register_info with
| Ok(Pointer(targets)) ->
begin match Map.to_alist targets with
......
open Bap.Std
open Core_kernel
open Cwe_checker_core
let run_tests project =
Type_inference_test.example_project := Some(project);
......
......@@ -24,7 +24,7 @@ let test_parse_dyn_syms () =
let () = check "__libc_start_main_as_dyn_sym" (String.Set.mem (parse_dyn_syms project) "__libc_start_main") in
let () = check "malloc_as_dyn_sym" (String.Set.mem (parse_dyn_syms project) "malloc") in
let () = check "__cxa_finalize_as_dyn_sym" (String.Set.mem (parse_dyn_syms project) "__cxa_finalize") in
let () = check "dyn_sym_count" (String.Set.count (parse_dyn_syms project) ~f:(fun elem -> true) = 4) in
let () = check "dyn_sym_count" (String.Set.count (parse_dyn_syms project) ~f:(fun _elem -> true) = 4) in
()
let tests = [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment