Commit c631bcfe by Enkelmann Committed by Thomas Barabosch

Type inference (#18)

Initial version of type inference. It is still very rudimentary at the moment since it just tracks pointer but it's a very solid start! 
parent 2d0fdfc6
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
- Improved check for CWE-476 (NULL Pointer Dereference) using data flow analysis (PR #11) - Improved check for CWE-476 (NULL Pointer Dereference) using data flow analysis (PR #11)
- Added cwe_checker_emulation plugin based on BAP's Primus to detect CWE-125, CWE-415, and CWE-416 (PR #15) - Added cwe_checker_emulation plugin based on BAP's Primus to detect CWE-125, CWE-415, and CWE-416 (PR #15)
- Switched C build system from make to scons (PR #16) - Switched C build system from make to scons (PR #16)
- Added type inference pass (PR #14) - Added type inference pass (PR #14, #18)
- Added unit tests to test suite (PR #14) - Added unit tests to test suite (PR #14)
0.1 (2018-10-08) 0.1 (2018-10-08)
......
(* This file contains analysis passes for type recognition *) (* This file contains analysis passes for type recognition.
It can annotate whether registers or values on the stack hold data or pointers
to memory. For the latter the target memory location is also tracked if known.
Pointers to the heap are tracked by tracking calls to malloc, calloc and realloc.
This analysis does not check whether the return values of these calls are checked
for NULL values (see cwe_476 for that). *)
open Bap.Std open Bap.Std
open Core_kernel open Core_kernel
(** The PointerTargetInfo contains knowledge about the offset and the alignment of
a pointer into a memory area. Here the alignment is always considered relative
offset zero of the target memory area. *)
module PointerTargetInfo : sig
type t = {
offset: (Bitvector.t, unit) Result.t Option.t;
alignment: (int, unit) Result.t Option.t;
} [@@deriving bin_io, compare, sexp]
end
(** The register type. *) (** The Register.t type. A register holds either arbitrary data or a pointer to some
memory region. We do track possible targets of the pointer as follows:
- heap objects: tid of corresponding call instruction to malloc, calloc, etc.
- current stack frame: sub_tid of current function
- some other stack frame: tid of corresponding call that left the stack frame.
This way we can distinguish between current stack pointers and pointers to the
stack frame of the same function coming from recursive calls. *)
module Register : sig module Register : sig
type t = type t =
| Pointer | Pointer of PointerTargetInfo.t Tid.Map.t
| Data | Data
[@@deriving bin_io, compare, sexp] [@@deriving bin_io, compare, sexp]
end end
(** The TypeInfo module. A TypeInfo.t structure holds a list of registers with known
type information (see Register.t type) and known type information for values
on the stack. *)
module TypeInfo : sig module TypeInfo : sig
type reg_state = (Register.t, unit) Result.t Var.Map.t [@@deriving bin_io, compare, sexp] type reg_state = (Register.t, unit) Result.t Var.Map.t [@@deriving bin_io, compare, sexp]
type t = { type t = {
stack: Register.t Mem_region.t; stack: Register.t Mem_region.t;
stack_offset: (Bitvector.t, unit) Result.t Option.t;
reg: reg_state; reg: reg_state;
} [@@deriving bin_io, compare, sexp] } [@@deriving bin_io, compare, sexp]
...@@ -26,6 +47,8 @@ module TypeInfo : sig ...@@ -26,6 +47,8 @@ module TypeInfo : sig
val pp: Format.formatter -> t -> unit val pp: Format.formatter -> t -> unit
end end
(** A tag for TypeInfo.t, so that we can annotate basic blocks with known type information
using bap tags. *)
val type_info_tag: TypeInfo.t Value.tag val type_info_tag: TypeInfo.t Value.tag
(** Computes TypeInfo for the given project. Adds tags to each block containing the (** Computes TypeInfo for the given project. Adds tags to each block containing the
...@@ -38,10 +61,21 @@ val print_type_info_tags: project:Project.t -> tid_map:word Tid.Map.t -> unit ...@@ -38,10 +61,21 @@ val print_type_info_tags: project:Project.t -> tid_map:word Tid.Map.t -> unit
(** Updates the type info for a single element (Phi/Def/Jmp) of a block. Input (** Updates the type info for a single element (Phi/Def/Jmp) of a block. Input
is the type info before execution of the element, output is the type info is the type info before execution of the element, output is the type info
after execution of the element. *) after execution of the element. sub_tid is the Tid of the current function
val update_type_info: Blk.elt -> TypeInfo.t -> project:Project.t -> TypeInfo.t which is internally used to mark which pointers point to the current stack frame.*)
val update_type_info: Blk.elt -> TypeInfo.t -> sub_tid:Tid.t -> project:Project.t -> TypeInfo.t
(* functions made available for unit tests: *)
module Private : sig
val update_block_analysis: Blk.t -> TypeInfo.t -> sub_tid:Tid.t -> project:Project.t -> TypeInfo.t
val function_start_state: Tid.t -> Project.t -> TypeInfo.t
val compute_stack_offset: TypeInfo.t -> Exp.t -> sub_tid:Tid.t -> project:Project.t -> Bitvector.t Option.t
val only_stack_pointer_and_flags: Tid.t -> Project.t -> TypeInfo.t
val merge_type_infos: TypeInfo.t -> TypeInfo.t -> TypeInfo.t
(* functions made public for unit tests: *) val type_info_equal: TypeInfo.t -> TypeInfo.t -> bool
module Test : sig
val update_block_analysis: Blk.t -> TypeInfo.t -> project:Project.t -> TypeInfo.t
end end
...@@ -48,8 +48,9 @@ let parse_dyn_sym_line line = ...@@ -48,8 +48,9 @@ let parse_dyn_sym_line line =
line := String.strip left; line := String.strip left;
str_list := right :: !str_list; str_list := right :: !str_list;
done; done;
str_list := !line :: !str_list;
match !str_list with match !str_list with
| _ :: value :: _ :: "FUNC" :: _ :: _ :: _ :: name :: [] -> begin | _ :: value :: _ :: "FUNC" :: _ :: _ :: _ :: name :: _ -> begin
match ( String.strip ~drop:(fun x -> x = '0') value, String.lsplit2 name ~on:'@') with match ( String.strip ~drop:(fun x -> x = '0') value, String.lsplit2 name ~on:'@') with
| ("", Some(left, _)) -> Some(left) | ("", Some(left, _)) -> Some(left)
| ("", None) -> Some(name) | ("", None) -> Some(name)
......
all: all:
bapbundle remove cwe_checker_unit_tests.plugin bapbundle remove cwe_checker_unit_tests.plugin
bapbuild -r -Is analysis cwe_checker_unit_tests.plugin -pkgs core,alcotest,yojson,unix,ppx_jane,cwe_checker_core bapbuild -r -Is analysis,utils cwe_checker_unit_tests.plugin -pkgs core,alcotest,yojson,unix,ppx_jane,cwe_checker_core
bapbundle install cwe_checker_unit_tests.plugin bapbundle install cwe_checker_unit_tests.plugin
bap ../artificial_samples/build/arrays_x64.out --pass=cwe-checker-unit-tests bap ../artificial_samples/build/arrays_x64.out --pass=cwe-checker-unit-tests
bapbundle remove cwe_checker_unit_tests.plugin bapbundle remove cwe_checker_unit_tests.plugin
......
...@@ -4,9 +4,11 @@ open Cwe_checker_core ...@@ -4,9 +4,11 @@ open Cwe_checker_core
let run_tests project = let run_tests project =
Type_inference_test.example_project := Some(project); Type_inference_test.example_project := Some(project);
Cconv_test.example_project := Some(project);
Alcotest.run "Unit tests" ~argv:[|"DoNotComplainWhenRunAsABapPlugin";"--color=always";|] [ Alcotest.run "Unit tests" ~argv:[|"DoNotComplainWhenRunAsABapPlugin";"--color=always";|] [
"Mem_region_tests", Mem_region_test.tests; "Mem_region_tests", Mem_region_test.tests;
"Type_inference_tests", Type_inference_test.tests; "Type_inference_tests", Type_inference_test.tests;
"Cconv_tests", Cconv_test.tests;
] ]
let () = let () =
......
open Bap.Std
open Core_kernel
open Cwe_checker_core
open Cconv
let check msg x = Alcotest.(check bool) msg true x
let example_project = ref None
let test_callee_saved () =
(* this test assumes, that the example project is a x64 binary *)
let project = Option.value_exn !example_project in
let register = Var.create "RBX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "callee_saved_register" (is_callee_saved register project) in
let register = Var.create "RAX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "caller_saved_register" (is_callee_saved register project = false) in
()
let test_parse_dyn_syms () =
(* this test assumes, that the example project is the arrays_x64.out binary from the artificial samples. *)
let project = Option.value_exn !example_project in
let () = check "free_as_dyn_sym" (String.Set.mem (parse_dyn_syms project) "free") in
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 tests = [
"Callee saved register", `Quick, test_callee_saved;
"Parse dynamic symbols", `Quick, test_parse_dyn_syms;
]
open Bap.Std
open Core_kernel
val example_project: Project.t option ref
val tests: unit Alcotest.test_case list
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