Unverified Commit 11ca1728 by Enkelmann Committed by GitHub

Remove old ocaml code (#148)

* Remove old Ocaml files

* adjust makefile

* Remove BAP build instructions from Readme

* minor documentation fix

* Remove old Ocaml documentation

* Remove unused Travis CI files

* Move Rust and Java code into src directory

* adjust build scripts to new folder structure
parent 02864348
#!/bin/bash
pat=".*\.ml(i|l|y)?$"
if [[ $1 =~ $pat ]];
then
s1=$(cat $1)
s2=$(ocp-indent $1)
if [ "$s1" == "$s2" ]
then
exit 0
else
echo "$1: ocp-indent"
exit 1
fi
fi
exit 0
JaneStreet
match_clause = 4
\ No newline at end of file
default_stages: [commit, push]
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.1.0
hooks:
- id: check-added-large-files
args: [--maxkb=10000]
- id: check-json
- id: check-merge-conflict
- id: check-yaml
- id: end-of-file-fixer
types: [python]
- id: fix-encoding-pragma
args: [--remove]
- id: flake8
args: [--ignore=E501]
- id: forbid-new-submodules
- id: no-commit-to-branch
- id: pretty-format-json
args: [--autofix]
- id: trailing-whitespace
types: [python]
- repo: https://github.com/pryorda/dockerfilelint-precommit-hooks
rev: v0.1.0
hooks:
- id: dockerfilelint
- repo: local
hooks:
- id: ocp-indent
name: ocp-indent
language: system
verbose: true
entry: .hooks/ocp-indent.sh
dist: xenial
language: python
python: 3.6
services:
- docker
install:
- sudo apt-get update
- pip install scons
before_script:
- ./.travis_prepare.sh
script:
- ./.travis_run_tests.sh
notifications:
email:
- nils-edvin.enkelmann@fkie.fraunhofer.de
#!/bin/bash
cd test/artificial_samples/
./install_cross_compilers.sh
scons
cd ../unit/
./specify_test_files_for_compilation.sh
cd ../..
docker build -t cwe-checker .
docker build -t cwe-checker-ghidra -f ghidra.Dockerfile .
\ No newline at end of file
#!/bin/bash
docker run --rm -t cwe-checker make codestyle-check \
&& docker run --rm -t cwe-checker cargo test \
&& docker run --rm -t cwe-checker dune runtest \
&& pytest \
&& docker run --rm -t cwe-checker-ghidra cargo test --no-fail-fast -p acceptance_tests_ghidra -- --show-output --ignored --test-threads 1
[workspace] [workspace]
members = ["cwe_checker_rs", "caller", "test"] members = ["src/cwe_checker_lib", "src/caller", "test"]
...@@ -18,7 +18,7 @@ USER cwe ...@@ -18,7 +18,7 @@ USER cwe
# Install all necessary files from the builder stage # Install all necessary files from the builder stage
COPY --from=builder /cwe_checker/target/release/cwe_checker /home/cwe/cwe_checker COPY --from=builder /cwe_checker/target/release/cwe_checker /home/cwe/cwe_checker
COPY --from=builder /cwe_checker/src/config.json /home/cwe/.config/cwe_checker/config.json COPY --from=builder /cwe_checker/src/config.json /home/cwe/.config/cwe_checker/config.json
COPY --from=builder /cwe_checker/ghidra/p_code_extractor /home/cwe/.local/share/cwe_checker/ghidra/p_code_extractor COPY --from=builder /cwe_checker/src/ghidra/p_code_extractor /home/cwe/.local/share/cwe_checker/ghidra/p_code_extractor
RUN echo "{ \"ghidra_path\": \"/opt/ghidra\" }" | sudo tee /home/cwe/.config/cwe_checker/ghidra.json RUN echo "{ \"ghidra_path\": \"/opt/ghidra\" }" | sudo tee /home/cwe/.config/cwe_checker/ghidra.json
WORKDIR / WORKDIR /
......
...@@ -6,40 +6,19 @@ all: ...@@ -6,40 +6,19 @@ all:
ifdef GHIDRA_PATH ifdef GHIDRA_PATH
mkdir -p ${HOME}/.config/cwe_checker mkdir -p ${HOME}/.config/cwe_checker
cp src/config.json ${HOME}/.config/cwe_checker/config.json cp src/config.json ${HOME}/.config/cwe_checker/config.json
cargo install --path caller --locked cargo install --path src/caller --locked
echo "{ \"ghidra_path\": \"${GHIDRA_PATH}\" }" > ${HOME}/.config/cwe_checker/ghidra.json echo "{ \"ghidra_path\": \"${GHIDRA_PATH}\" }" > ${HOME}/.config/cwe_checker/ghidra.json
mkdir -p ${HOME}/.local/share/cwe_checker mkdir -p ${HOME}/.local/share/cwe_checker
cp -r ghidra ${HOME}/.local/share/cwe_checker/ghidra cp -r src/ghidra ${HOME}/.local/share/cwe_checker/ghidra
else else
echo "GHIDRA_PATH not specified. Please set it to the path to your local Ghidra installation." echo "GHIDRA_PATH not specified. Please set it to the path to your local Ghidra installation."
false false
endif endif
with_bap_backend:
cargo build --release
mkdir -p ${HOME}/.config/cwe_checker
cp src/config.json ${HOME}/.config/cwe_checker/config.json
cp target/release/libcwe_checker_rs.a src/libcwe_checker_rs.a
cp target/release/libcwe_checker_rs.so src/dllcwe_checker_rs.so
dune build
dune install
cd plugins/cwe_checker && make all
cd plugins/cwe_checker_emulation && make all
cd plugins/cwe_checker_type_inference && make all
cd plugins/cwe_checker_type_inference_print && make all
cd plugins/cwe_checker_pointer_inference_debug && make all
test: test:
cargo test cargo test
ifeq (,$(wildcard ${HOME}/.config/cwe_checker/ghidra.json))
cd test/unit/ && ./specify_test_files_for_compilation.sh
dune runtest
cd test/artificial_samples; scons; cd ../..
pytest -v --ignore=_build
else
cd test/artificial_samples; scons; cd ../.. cd test/artificial_samples; scons; cd ../..
cargo test --no-fail-fast -p acceptance_tests_ghidra -- --show-output --ignored cargo test --no-fail-fast -p acceptance_tests_ghidra -- --show-output --ignored
endif
compile_test_files: compile_test_files:
cd test/artificial_samples \ cd test/artificial_samples \
...@@ -52,28 +31,12 @@ codestyle-check: ...@@ -52,28 +31,12 @@ codestyle-check:
clean: clean:
cargo clean cargo clean
rm -f src/libcwe_checker_rs.a
rm -f src/dllcwe_checker_rs.so
dune clean
bapbuild -clean
rm -f -r doc/html rm -f -r doc/html
cd test/unit; make clean; cd ../..
cd plugins/cwe_checker; make clean; cd ../..
cd plugins/cwe_checker_emulation; make clean; cd ../..
cd plugins/cwe_checker_type_inference; make clean; cd ../..
cd plugins/cwe_checker_type_inference_print; make clean; cd ../..
cd plugins/cwe_checker_pointer_inference_debug; make clean; cd ../..
uninstall: uninstall:
rm -f -r ${HOME}/.config/cwe_checker rm -f -r ${HOME}/.config/cwe_checker
rm -f -r ${HOME}/.local/share/cwe_checker rm -f -r ${HOME}/.local/share/cwe_checker
cargo uninstall cwe_checker; echo "" cargo uninstall cwe_checker
dune uninstall
cd plugins/cwe_checker; make uninstall; cd ../..
cd plugins/cwe_checker_emulation; make uninstall; cd ../..
cd plugins/cwe_checker_type_inference; make uninstall; cd ../..
cd plugins/cwe_checker_type_inference_print; make uninstall; cd ../..
cd plugins/cwe_checker_pointer_inference_debug; make uninstall; cd ../..
documentation: documentation:
cargo doc --open cargo doc --open
......
...@@ -54,24 +54,6 @@ The following dependencies must be installed in order to build and install the * ...@@ -54,24 +54,6 @@ The following dependencies must be installed in order to build and install the *
Run `make all GHIDRA_PATH=path/to/ghidra_folder` (with the correct path to the local Ghidra installation inserted) to compile and install the *cwe_checker*. Run `make all GHIDRA_PATH=path/to/ghidra_folder` (with the correct path to the local Ghidra installation inserted) to compile and install the *cwe_checker*.
### Local installation with BAP as backend ###
If you want to use the older [BAP](https://github.com/BinaryAnalysisPlatform/bap) backend instead of Ghidra, you must ensure that the following dependencies are fulfilled:
- Ocaml 4.08.0
- Opam 2.0.2
- dune >= 2.0
- BAP 2.2.0 (and its dependencies).
- yojson >= 1.6.0
- ppx_deriving_yojson >= 3.5.1
- alcotest >= 0.8.3 (for tests)
- Sark (latest) for IDA Pro annotations
- pytest >= 3.5.1 (for tests)
- SCons >= 3.0.5 (for tests)
- odoc >= 1.4 (for documentation)
- [Rust](https://www.rust-lang.org) >= 1.49
Just run `make with_bap_backend` to compile and register the plugin with BAP.
## Usage ## ## Usage ##
The *cwe_checker* takes a binary as input, The *cwe_checker* takes a binary as input,
......
FROM fkiecad/cwe_checker_travis_docker_image:bap
COPY . /home/cwe/cwe_checker/
RUN sudo chown -R cwe:cwe /home/cwe/cwe_checker \
&& cd /home/cwe/cwe_checker \
&& make with_bap_backend \
&& cargo clean \
&& dune clean
WORKDIR /home/cwe/cwe_checker
ENTRYPOINT ["opam", "config", "exec", "--"]
CMD cwe_checker /tmp/input
opam-version: "2.0"
name: "cwe_checker"
version: "0.4"
synopsis: "BAP plugin collection to detect common bug classes"
description: """
cwe_checker is a suite of tools to detect common bug classes such as use of dangerous functions and simple integer overflows. These bug classes are formally known as Common Weakness Enumerations (CWEs).
"""
maintainer: "CWE_checker Team <nils-edvin.enkelmann@fkie.fraunhofer.de>"
authors: [ "Thomas Barabosch <thomas.barabosch@fkie.fraunhofer.de>" "Nils-Edvin Enkelmann <nils-edvin.enkelmann@fkie.fraunhofer.de>" ]
license: "LGPL-3.0"
homepage: "https://github.com/fkie-cad/cwe_checker"
bug-reports: "https://github.com/fkie-cad/cwe_checker/issues"
dev-repo: "git+https://github.com/fkie-cad/cwe_checker"
depends: [
"ocaml" {>= "4.08.0"}
"dune" {>= "2.0"}
"yojson" {>= "1.6.0"}
"bap" {>= "2.2.0"}
"alcotest" {>= "0.8.3"}
"core_kernel" {>= "v0.14"}
"ppx_jane" {>= "v0.14"}
"ppx_deriving_yojson" {>= "3.5.1"}
"odoc" {>= "1.4"}
]
depexts: [
"binutils"
]
conflicts: [
"fkie-cad-cwe-checker" {!= "0.2"}
]
build: [
[ "dune" "build" "--profile" "release" ]
]
install: [
[ make "uninstall" ]
[ make "clean" ]
[ make "all" ]
]
(install
(section etc)
(files
(src/config.json as config.json)
(src/utils/registers.json as registers.json)
)
)
(documentation)
(lang dune 2.0)
(name cwe_checker)
{0 cwe_checker}
{1 Contents}
- {!section:Basics}
- {!section:CmdLineOptions}
- {!section:ToolIntegration}
- {!section:HackingHowto}
{1:Basics Basic usage}
{i cwe_checker} is implemented as a plugin for the {{: https://github.com/BinaryAnalysisPlatform/bap} Binary Analysis Platform} (BAP).
To use it, just run BAP with {i cwe_checker} as a pass:
{[bap [BINARY_FILE] --pass=cwe-checker]}
This runs all static analysis based checks.
You can find more documentation on these checks at their {{: ../cwe_checker_core/Cwe_checker_core/index.html} module documentation pages}.
The behaviour of these checks can be modified through an optional configuration file.
Just edit the [src/config.json] file and then add [--cwe-checker-config=src/config.json] as a command line option.
You can find more on the available command line options {{!section:CmdLineOptions} here}.
Alternatively, you can run the {i cwe_checker} through the command
{[cwe_checker [BINARY_FILE] ]}
Internally, this also calls BAP as above, but enables shorter {{!section:CmdLineOptions} command line options}.
The symbolic execution based checks can be run with the emulation recipe in the recipes folder.
{[bap PATH_TO_BINARY --recipe=recipes/emulation]}
Note that these checks are rather slow and should only be applied to small binaries.
{e Deprecation warning:} We recently decided to deprecate support for the symbolic execution based checks and they will be removed in a future version.
Users of these checks should take a look at the {{: https://github.com/BinaryAnalysisPlatform/bap-toolkit} BAP toolkit},
which provides better-maintained versions of these checks.
{2 How to use the docker images}
There are two docker images containing preinstalled versions of the {i cwe_checker}:
- [docker pull fkiecad/cwe_checker:latest] pulls the image based on the current master branch.
- [docker pull fkiecad/cwe_checker:stable] pulls the image based on the current stable release version.
To use them, mount the target binary inside the docker container and call {i bap} with {i cwe_checker} as a pass as usual:
{[docker run --rm -v [BINARY]:/tmp/input fkiecad/cwe_checker bap /tmp/input --pass=cwe-checker]}
If you are using a customized [config.json] file, don't forget to mount it inside your container as well!
If you want to print the output to a file with [--cwe-checker-out], you also need to mount the output file to the docker container,
or else the file will be lost once the container gets destroyed.
{1:CmdLineOptions Command line options}
If you run the {i cwe_checker} as a BAP plugin, all command line options have to be prefixed with [--cwe-checker] (so that BAP knows to forward them to the {i cwe_checker} plugin).
If you run the {i cwe_checker} directly, do not prefix the command line options.
The available command line options are:
- [-version] Print the version number of the {i cwe_checker} and exit.
- [-check-path] Find paths between input functions (configurable in the configuration file) and CWE hits.
Should be used together with the [-partial] command line option if you are only interested in paths to specific CWEs.
- [-config=[FILE]] Use [[FILE]] as the configuration file.
If you omit this option, {i cwe_checker} uses a standard configuration file located at [src/config.json].
- [-module-versions] Prints the version numbers of each check.
- [-json] Format the CWE-warnings as JSON.
If you print to {i stdout}, note that debug, info and error messages are not formatted as JSON and may pollute the output.
Use [-no-logging] to suppress these messages.
- [-no-logging] Suppress printing of debug, info and error messages.
This is useful if you want to print to {i stdout} with the [-json] flag to prevent these messages polluting the JSON output.
- [-out=[FILE]] Print the CWE-warnings to the file located at [[FILE]].
Note that debug, info and error messages are still printed to stdout and not to the file.
- [-partial=[MODULE_LIST]] Only run the checks given in [[MODULE_LIST]], where [[MODULE_LIST]] is a comma separated list of module names.
E.g. [-partial=CWE190,CWE476] would only run the checks for CWE-190 and CWE-476.
The names of all available modules can be printed with the [-module-versions] command line option.
{1:ToolIntegration Integration with IDA Pro and Ghidra}
To annotate CWE-hits in IDA Pro or Ghidra, first run {i cwe_checker} and save the JSON-formatted output to a file.
{[bap [BINARY] --pass=cwe-checker --cwe-checker-json --cwe-checker-out=cwe_hits.json]}
After that execute the tool-specific script to import the results:
- For IDA Pro run the [cwe_checker_to_ida.py] script located in the [cwe_checker_to_ida] folder.
{[python3 cwe_checker_to_ida.py -i cwe_hits.json -o cwe_hits.py]}
Now open the binary file in IDA Pro and execute the generated [cwe_hits.py] script from within IDA Pro (Alt+F7).
- For Ghidra copy the [cwe_checker_ghidra_plugin.py] script located in the [ghidra_plugin] folder into the script folder of Ghidra.
Now open the binary in Ghidra and run the [cwe_checker_ghidra_plugin.py] script through the script manager and select the generated [cwe_hits.json] file when prompted.
{1:HackingHowto How to write your own check}
{2 Step 1: Get acquainted with BAP}
{i cwe_checker} interfaces via OCaml with the {{: https://github.com/BinaryAnalysisPlatform/bap} Binary Analysis Platform} as a backend and you should read its documentation.
All information about a binary file is gathered through BAP.
{2 Step 2: Write the check}
You need a [your_check.ml] and a [your_check.mli] file that should be located in the [src/checkers] folder.
The [your_check.mli] file should look like this:
{[
(** This module implements your_check.
Some more documentation about your_check.
*)
val name : string (* The name of your check *)
val version : string (* The version of your check (e.g. "0.1"). *)
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
]}
A corresponding example [your_check.ml] file, which just prints a "hello world" message, would look like this:
{[
open Core_kernel
open Bap.Std (* To interface with BAP *)
let name = "your_check"
let version = "0.1"
let check_cwe program project tid_map symbol_pairs parameters =
Log_utils.info "Hello world!"
]}
In practice you would use the parameters of {i check_cwe}-function to gather the necessary information for the computation of your check.
These parameters are:
- [program: Bap.Std.program Bap.Std.term] The program term of the binary
- [project: Bap.Std.project] The BAP-project term of the binary
- [Tid_map: Bap.Std.word Bap.Std.Tid.Map.t] A map from the Tids of basic blocks to concrete addresses in the binary file
- [symbols_pairs: string list list] Symbols read from the {i config.json} file
- [parameters: string list] Parameters read from the config.json file
The results of your check should be reported back to the user via the functions in the {{: ../cwe_checker_core/Cwe_checker_core/Log_utils/index.html} Log_utils}-module.
See its module-level documentation for more details.
{2 Step 3: Add your check to the {i cwe_checker} plugin}
The main file of the {i cwe_checker} plugin is located at [plugins/cwe_checker/cwe_checker.ml].
In there you have to add your check to the list of {i known_modules}.
Here the {i requires_pairs} flag controls whether the symbols in the [config.json] file are a string list or a list of string lists.
The {i has_parameters} flag controls whether the [config.json] file contains parameters to control the behaviour of the check.
Now just recompile {i cwe_checker} via [make all] and your check will be available for use.
all:
bapbuild -pkgs yojson,unix,ppx_jane,cwe_checker_core cwe_checker.plugin
bapbundle install cwe_checker.plugin
clean:
bapbuild -clean
uninstall:
bapbundle remove cwe_checker.plugin
open Cwe_checker_core.Main
open Bap.Std
open Core_kernel
include Self()
let generate_bap_flags flags =
List.map flags (fun (name, docstring) -> (name, Config.flag name ~doc:docstring))
let generate_bap_params params =
List.map params (fun (name, docstring) -> (name, Config.param Config.string name ~doc:docstring))
let () =
let cmdline_flags = generate_bap_flags cmdline_flags in
let cmdline_params = generate_bap_params cmdline_params in
let () = Config.when_ready (fun ({get=(!!)}) ->
let flags: Bool.t String.Map.t = List.fold cmdline_flags ~init:String.Map.empty ~f:(fun flag_map (name, bap_flag) ->
String.Map.set flag_map ~key:name ~data:(!!bap_flag)
) in
let params: String.t String.Map.t = List.fold cmdline_params ~init:String.Map.empty ~f:(fun param_map (name, bap_param) ->
String.Map.set param_map ~key:name ~data:(!!bap_param)
) in
Project.register_pass' ~deps:["callsites"] (main flags params)
) in
let () = Config.manpage [
`S "DESCRIPTION";
`P "This plugin checks various CWEs such as Insufficient Entropy in PRNG (CWE-332) or Use of Potentially Dangerous Function (CWE-676)"
] in
()
all:
bapbuild -pkgs yojson,unix,ppx_jane,bap-primus,monads,graphlib,str,cwe_checker_core cwe_checker_emulation.plugin
bapbundle install cwe_checker_emulation.plugin
clean:
bapbuild -clean
uninstall:
bapbundle remove cwe_checker_emulation.plugin
open Core_kernel
open Bap.Std
open Bap_primus.Std
open Bap_future.Std
open Graphlib.Std
open Monads.Std
open Format
open Ppx_jane
open Cwe_checker_core
include Self()
let pp_id = Monad.State.Multi.Id.pp
module Machine = struct
type 'a m = 'a
include Primus.Machine.Make(Monad.Ident)
end
open Machine.Syntax
module Main = Primus.Machine.Main(Machine)
module Interpreter = Primus.Interpreter.Make(Machine)
module Linker = Primus.Linker.Make(Machine)
module Env = Primus.Env.Make(Machine)
module Lisp = Primus.Lisp.Make(Machine)
module Eval = Primus.Interpreter.Make(Machine)
(** this array collects the observed primus events*)
let collected_events = ref ([||])
(** Converts a hexadecimal string representation of
an address to an integer. *)
let convert_location loc =
match (Str.split (Str.regexp ":") loc) with
| fst::snd::[] -> Int.of_string ("0x" ^ snd)
| _ -> failwith "Could not parse location"
(** Converts a list of hexadecimal strings to a
list of integers. *)
let convert_location_list loc_list =
let locs = ref [] in
Sexplib__Sexp_with_layout.List.iter loc_list ~f:(fun x -> locs := (convert_location @@ Sexp.to_string x)::(!locs));
!locs
(** Analyze events and report to the user. *)
let analyze_events _ =
let location_tbl = Hashtbl.create (module String) in
Array.iter ~f:(fun (p, ev) ->
begin
match ev with
| Sexp.Atom _ -> failwith "Sexp.Atom not expected in report_events."
| Sexp.List [Sexp.Atom location_id; Sexp.List location_list] -> Hashtbl.add_exn location_tbl location_id (convert_location_list location_list)
| Sexp.List incident -> Incident_reporter.report incident location_tbl
end) !collected_events
(** Just adds the observed Primus events to the collected_events array. *)
let collect_events p ev =
collected_events := Array.append !collected_events [|(p, ev)|]
(* Most functions beyond here have been taken and adjusted from BAP's Primus plugins*)
let string_of_name = function
| `symbol s -> s
| `tid t -> Tid.to_string t
| `addr x -> Addr.string_of_value x
(** Executes/forks another Primus machine. *)
let exec x =
Machine.current () >>= fun cid ->
info "Fork %a: starting from the %s entry point"
pp_id cid (string_of_name x);
Machine.catch (Linker.exec x)
(fun exn ->
info "execution from %s terminated with: %s "
(string_of_name x)
(Primus.Exn.to_string exn);
Machine.return ())
let rec run = function
| [] ->
info "all toplevel machines done, halting";
Eval.halt >>=
never_returns
| x :: xs ->
Machine.current () >>= fun pid ->
Machine.fork () >>= fun () ->
Machine.current () >>= fun cid ->
if Poly.(=) pid cid
then run xs
else
exec x >>= fun () ->
Eval.halt >>=
never_returns
(** Checks if a certain Primus.Observation.Provider is equal
to a string like 'incident'. *)
let has_name name p =
Poly.(=) (Primus.Observation.Provider.name p) name
(** Register a monitor. *)
let monitor_provider name ps =
Primus.Observation.list_providers () |>
List.find ~f:(has_name name) |> function
| None -> invalid_argf "An unknown observation provider `%s'" name ()
| Some p -> p :: ps
let parse_monitors =
List.fold ~init:[] ~f:(fun ps name -> monitor_provider name ps)
(** Register monitors for 'incident' related events. *)
module Monitor(Machine : Primus.Machine.S) = struct
open Machine.Syntax
let init () =
parse_monitors ["incident"; "incident-location"] |>
List.iter ~f:(fun m ->
info "monitoring %s" (Primus.Observation.Provider.name m);
Stream.observe (Primus.Observation.Provider.data m) (collect_events m));
Machine.return ()
end
(** Main logic of program:
- we monitor all 'incident' related events
- for all subroutines we fork a Primus machine
- all monitored events are collected globally
- after the last Primus machine has terminated we report all observed incidents *)
let main json_output file_output proj =
print_endline "INFO: The emulation based checks in this plugin have been deprecated. Please look at https://github.com/BinaryAnalysisPlatform/bap-toolkit for an alternative." ;
Primus.Machine.add_component (module Monitor);
begin
let prog = (Project.program proj) in
let targets = Seq.to_list @@ Seq.map (Term.enum sub_t prog) ~f:(fun x -> `tid (Term.tid x)) in
Main.run ~envp:[||] ~args:[||] proj (run targets) |> function
| (Primus.Normal,proj)
| (Primus.Exn Primus.Interpreter.Halt,proj) ->
info "Ok, we've terminated normally";
| (Primus.Exn exn,proj) ->
info "program terminated by a signal: %s" (Primus.Exn.to_string exn);
end;
analyze_events ();
Incident_reporter.parse_reports ();
Incident_reporter.report_cwe ();
Incident_reporter.report_unknown_incidents ();
if json_output then
begin
match Project.get proj filename with
| Some fname -> Log_utils.emit_json fname file_output
| None -> Log_utils.emit_json "" file_output
end
else
Log_utils.emit_native file_output
module Cmdline = struct
open Config
let json_output = flag "json" ~doc:"Outputs the result as JSON."
let file_output = param string "out" ~doc:"Path to output file."
let () = when_ready (fun ({get=(!!)}) -> Project.register_pass' ~deps:["trivial-condition-form"] (main !!json_output !!file_output))
let () = manpage [
`S "DESCRIPTION";
`P "This plugin utilizes symbolic execution to find CWEs like Double Free (CWE415) or Use After Free (CWE416)."]
end
open Core_kernel
open Cwe_checker_core
open Log_utils
let version = "0.1"
(** Keeps track of reported events so that events are not reported multiple times. *)
let reported_events = ref (String.Set.empty)
(** We may want to get the number of emulated CWEs from a central point for scalability *)
let collected_locations = Hashtbl.create (module Int) ~size:4
let known_incidents = Hashtbl.of_alist_exn (module Int) [(125, "(Out-of-bounds Read)"); (787, "(Out-of-bounds Write)"); (415, "(Double Free)"); (416, "(Use After Free)")]
let cwe_incidents = ref [||]
let unknown_cwe_incidents = ref [||]
(** Builds a string of a path of addresses. *)
let build_location_path locations =
let rec internal locations path_str =
match locations with
| [] -> path_str
| hd::[] -> internal [] (path_str ^ (Printf.sprintf "0x%x" hd))
| hd::tl -> internal tl (path_str ^ (Printf.sprintf "0x%x -> " hd)) in
internal locations ""
(** Looks up a concrete address for an id in the location table loc_tbl. *)
let map_id_to_location id loc_tbl =
match Hashtbl.find loc_tbl id with
| Some loc -> loc
| _ -> failwith "Expected location in hashtbl but failed"
(** Translates a list of incident ids to a list of concrete addresses. *)
let get_incident_locations_from_ids ids location_tbl =
let incident_locations = ref [] in
Sexplib__Sexp_with_layout.List.iter ids ~f:(fun id -> incident_locations := (map_id_to_location (Sexp.to_string id) location_tbl)::(!incident_locations)); !incident_locations
let build_description (incident_str : string) (end_point : string) (paths : string list) : string =
let pretty_paths = ref "" in
pretty_paths := !pretty_paths^end_point^"\n";
List.iter ~f:(fun path ->
let clean_path = String.drop_suffix path 3 in
pretty_paths := !pretty_paths^"\n "^clean_path
) paths;
sprintf "%s %s \n" incident_str !pretty_paths
let report_cwe _ =
Array.iter ~f:(fun (id, loc_hash) ->
let incident_str = (Hashtbl.find_exn known_incidents id) in
Hashtbl.iter_keys loc_hash ~f:(fun end_point ->
let paths = (Hashtbl.find_multi loc_hash end_point) in
let description = build_description incident_str end_point paths in
let other = List.map ~f:(fun path ->
let clean_path = String.drop_suffix path 3 in
["path"; clean_path]
) paths in
let cwe = sprintf "CWE%d" id in
collect_cwe_warning (cwe_warning_factory cwe version ~addresses:[end_point] ~other:other description)
)
) !cwe_incidents
let report_unknown_incidents _ =
Array.iter ~f:(fun (path, inc) ->
let description = inc ^ " " ^ path in
collect_cwe_warning (cwe_warning_factory inc version ~other:[["path"; path]] description)
) !unknown_cwe_incidents
let collect_known_incidents (cwe : int) (execution_path : string) =
Hashtbl.add_multi collected_locations ~key:cwe ~data:(String.rsplit2_exn execution_path ~on:' ')
let collect_unknown_incidents (path_inc : string * string) =
unknown_cwe_incidents := Array.append !unknown_cwe_incidents [|path_inc|]
let parse_reports _ =
Hashtbl.iter_keys collected_locations ~f:(fun id ->
let loc_hashtbl = Hashtbl.create (module String) ~size:3 in
List.iter ~f:(fun (path, end_point) ->
Hashtbl.add_multi loc_hashtbl ~key:end_point ~data:path
) (Hashtbl.find_multi collected_locations id);
cwe_incidents := Array.append !cwe_incidents [|(id, loc_hashtbl)|]
)
(** Reports an incident. *)
let report incident location_tbl =
match incident with
| name::ids ->
begin
let incident_locations = get_incident_locations_from_ids ids location_tbl in
let filtered_locs = Int.Set.to_list (Int.Set.of_list (List.concat incident_locations)) in
let incident_str = Sexp.to_string name in
let location_path = build_location_path filtered_locs in
if Set.mem !reported_events location_path
then
()
else
begin
reported_events := Set.add !reported_events location_path;
match incident_str with
| "memcheck-out-of-bound" -> collect_known_incidents 125 location_path
| "memcheck-double-release" -> collect_known_incidents 415 location_path
| "memcheck-use-after-release" -> collect_known_incidents 416 location_path
| _ -> collect_unknown_incidents (location_path, incident_str)
end
end
| __ -> failwith "Strange incident sexp encountered"
all:
bapbuild -pkgs yojson,unix,ppx_jane,cwe_checker_core cwe_checker_pointer_inference_debug.plugin
bapbundle install cwe_checker_pointer_inference_debug.plugin
clean:
bapbuild -clean
uninstall:
bapbundle remove cwe_checker_pointer_inference_debug.plugin
open Bap.Std
open Core_kernel
open Cwe_checker_core
include Self()
let main project =
let program = Project.program project in
let tid_map = Address_translation.generate_tid_map program in
Pointer_inference.run_and_print_debug project tid_map
module Cmdline = struct
open Config
let () = when_ready (fun ({get=(!!)}) -> Project.register_pass' main)
let () = manpage [`S "DESCRIPTION";
`P "This plugin prints verbose debug information from the pointer inference analysis of the cwe_checker to stdout."]
end
all:
bapbuild -pkgs yojson,unix,ppx_jane,cwe_checker_core cwe_checker_type_inference.plugin
bapbundle install cwe_checker_type_inference.plugin
clean:
bapbuild -clean
uninstall:
bapbundle remove cwe_checker_type_inference.plugin
open Bap.Std
open Core_kernel
open Cwe_checker_core
let () = Project.register_pass Type_inference.compute_pointer_register
all:
bapbuild -pkgs yojson,unix,ppx_jane,cwe_checker_core cwe_checker_type_inference_print.plugin
bapbundle install cwe_checker_type_inference_print.plugin
clean:
bapbuild -clean
uninstall:
bapbundle remove cwe_checker_type_inference_print.plugin
open Bap.Std
open Core_kernel
open Cwe_checker_core
include Self()
let main json_output file_output project =
let program = Project.program project in
let tid_map = Address_translation.generate_tid_map program in
Type_inference.print_type_info_tags project tid_map;
if json_output then
begin
match Project.get project filename with
| Some fname -> Log_utils.emit_json fname file_output
| None -> Log_utils.emit_json "" file_output
end
else
Log_utils.emit_native file_output
module Cmdline = struct
open Config
let json_output = flag "json" ~doc:"Outputs the result as JSON."
let file_output = param string "out" ~doc:"Path to output file."
let () = when_ready (fun ({get=(!!)}) -> Project.register_pass' ~deps:["cwe-checker-type-inference"] (main !!json_output !!file_output))
let () = manpage [`S "DESCRIPTION";
`P "This plugin prints the results of the type inference plugin."]
end
Runs cwe_checker's checks that utilize bap primus emulation.
\ No newline at end of file
;; taken from
;;https://github.com/BinaryAnalysisPlatform/bap-recipes/blob/master/primus-checks/limit-malloc.lisp
;; up to 4 Mb each chunk, up to 128 Mbytes total
(defmethod init ()
(set *malloc-max-chunk-size* (* 4 1024 1024))
(set *malloc-guard-edges* 0)
(set *malloc-max-arena-size* (* 32 *malloc-max-chunk-size*))
(set *malloc-arena-start* brk)
(set *malloc-zero-sentinel* 0))
(option primus-lisp-add $prefix)
(option primus-lisp-load
posix
memcheck-malloc
limit-malloc
taint-sources
sensitive-sinks)
(option primus-promiscuous-mode)
(option primus-greedy-scheduler)
(option primus-limit-max-length 4096)
(option pass cwe-checker-emulation)
Runs those checks of the cwe-checker plugin that rely on static analysis.
(option pass cwe-checker)
(option rooter internal)
(option cwe-checker-config $prefix/../../src/config.json)
Prints the results of the type inference pass of each block.
(option pass cwe-type-inference-print)
open Core_kernel
open Bap.Std
open Graphlib.Std
open Format
include Self()
module CG = Graphs.Callgraph
module CFG = Graphs.Tid
type proof =
| Calls of CG.edge path
| Sites of CFG.edge path
(** Taken from https://stackoverflow.com/questions/8373460/substring-check-in-ocaml *)
let contains_substring search target =
Option.is_some (String.substr_index ~pattern:search target)
let format_path get_source get_destination path tid_map =
let e_count = List.length (Seq.to_list (Path.edges path)) in
if e_count = 0 then "()" else
begin
let format_node n = sprintf "%s" (Address_translation.translate_tid_to_assembler_address_string n tid_map) in
let formated_start_node = format_node (get_source (Path.start path)) in
let formated_rest_nodes = List.map (Seq.to_list @@ Path.edges path) ~f:(fun e -> format_node (get_destination e)) in
let formated_full_path = "(" ^ formated_start_node ^ ", " ^ (String.concat ~sep:", " formated_rest_nodes) ^ ")" in
formated_full_path
end
let find_subfunction_name program name =
Term.enum sub_t program
|> Seq.find_map ~f:(fun s -> Option.some_if (contains_substring name (Sub.name s)) (Term.tid s))
let get_tids_from_cwe_hit (cwe_hit: Log_utils.CweWarning.t) =
cwe_hit.tids
let reaches cg callee target =
Graphlib.is_reachable (module CG) cg callee target
(* ignores indirect calls and jumps as well as return statements and interupts *)
let callsites cg target sub =
Term.enum blk_t sub |>
Seq.concat_map ~f:(fun blk ->
Term.enum jmp_t blk |> Seq.filter_map ~f:(fun j ->
match Jmp.kind j with
| Goto _ | Ret _ | Int (_,_) -> None
| Call destination -> begin match Call.target destination with
| Direct tid when reaches cg tid target -> Some (Term.tid blk)
| _ -> None
end))
let verify source destination program : proof option =
let cg = Program.to_graph program in
match Graphlib.shortest_path (module CG) cg source destination with
| Some path -> Some (Calls path)
| None ->
Term.enum sub_t program |> Seq.find_map ~f:(fun sub ->
let g = Sub.to_graph sub in
Seq.find_map (callsites cg source sub) ~f:(fun sc ->
Seq.find_map (callsites cg destination sub) ~f:(fun dc ->
if Tid.equal sc dc then None
else Graphlib.shortest_path (module CFG) g sc dc))) |>
Option.map ~f:(fun p -> Sites p)
let get_fst_tid_from_cwe_hit (cwe_hit: Log_utils.CweWarning.t) =
match cwe_hit.tids with
| [] -> None
| hd :: _ -> Some (Bap.Std.Tid.from_string_exn hd)
let cwe_hit_fst_addr cwe_hit =
match get_tids_from_cwe_hit cwe_hit with
| [] -> Bap.Std.Tid.from_string_exn "0x00"
| hd :: _ -> Bap.Std.Tid.from_string_exn hd
let block_has_callsite blk t =
Term.enum jmp_t blk |>
Seq.exists ~f:(fun j ->
match Jmp.kind j with
| Goto _ | Ret _ | Int (_,_) -> false
| Call destination -> begin match Call.target destination with
| Direct tid -> Tid.(=) tid t
| _ -> false
end)
let collect_callsites program t =
Term.enum sub_t program
|> Seq.filter_map ~f:(fun s -> if Term.enum blk_t s |>
Seq.exists ~f:(fun b -> block_has_callsite b t) then Some s else None)
|> Seq.map ~f:(fun s -> Term.tid s)
let sub_has_tid sub tid =
Term.enum blk_t sub
|> Seq.exists ~f:(fun blk -> Tid.(=) (Term.tid blk) tid || Blk.elts blk
|> Seq.exists ~f:(fun e -> match e with
| `Def d -> Tid.(=) (Term.tid d) tid
| `Jmp j -> Tid.(=) (Term.tid j) tid
| `Phi p -> Tid.(=) (Term.tid p) tid ))
let find_sub_tid_of_term_tid program tid =
match tid with
| Some t -> let s = Term.enum sub_t program
|> Seq.find ~f:(fun s -> sub_has_tid s t) in
begin
match s with
| Some f -> Some (Term.tid f)
| None -> None
end
| None -> None
let log_path p source source_tid destination tid_map =
let source_addr = Address_translation.translate_tid_to_assembler_address_string source_tid tid_map in
let destination_addr = Address_translation.translate_tid_to_assembler_address_string
(cwe_hit_fst_addr destination) tid_map in
begin match p with
| Calls p ->
let path_str = format_path CG.Edge.src CG.Edge.dst p tid_map in
let current_path = Log_utils.check_path_factory source source_addr destination_addr destination_addr ~path:[] ~path_str:path_str in
Log_utils.collect_check_path current_path
| Sites p -> let path_str = format_path CFG.Edge.src CFG.Edge.dst p tid_map in
let current_path = Log_utils.check_path_factory source source_addr destination_addr destination_addr ~path:[] ~path_str:path_str in
Log_utils.collect_check_path current_path
end
let verify_one program tid_map source destination source_tid destination_tid =
match verify source_tid destination_tid program with
| None -> ()
| Some p -> log_path p source source_tid destination tid_map
let find_source_sink_pathes source destination program tid_map =
match Option.both (find_subfunction_name program source) (find_sub_tid_of_term_tid program (get_fst_tid_from_cwe_hit destination)) with
| None -> () (*one or both functions are not utilized.*)
| Some (callsite_tid, destination_tid) ->
begin
collect_callsites program callsite_tid
|> Seq.iter ~f:(fun source_tid -> verify_one program tid_map source destination source_tid destination_tid )
end
let check_path prog tid_map input_functions cwe_hits =
List.iter input_functions ~f:(fun f ->
List.iter cwe_hits ~f:(fun h -> find_source_sink_pathes f h prog tid_map))
(**
This analyzer module checks if there exists a path from an input function (e.g. recv or scanf) to a location of a CWE hit. The existence of such a path is great news: we might trigger the CWE hit via user provided input. This helps analysts to further priotize the CWE hits.
The analysis prooves that there is a path that fulfills the following constraints:
- it starts at the location of an input function
- it ends at a CWE hit
This module is loosely based on the BAP tutorial (https://github.com/BinaryAnalysisPlatform/bap-tutorial/blob/master/src/path_check/path_check.ml).
*)
val name : string
val version : string
val check_path : Bap.Std.program Bap.Std.term -> Bap.Std.word Bap.Std.Tid.Map.t -> string list -> Log_utils.CweWarning.t list -> unit
open Bap.Std
open Core_kernel
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.(=) x y
type 'a mem_node = {
pos: Bitvector.t; (* address of the element *)
size: Bitvector.t; (* size (in bytes) of the element *)
data: ('a, unit) Result.t;
} [@@deriving bin_io, compare, sexp]
type 'a t = 'a mem_node list [@@deriving bin_io, compare, sexp]
let empty () : 'a t =
[]
(** Return an error mem_node at the given position with the given size. *)
let error_elem ~pos ~size =
{ pos = pos;
size = size;
data = Error ();}
let rec add mem_region elem ~pos ~size =
let () = if pos + size < pos then failwith "[CWE-checker] element out of bounds for mem_region" in
let new_node = {
pos=pos;
size=size;
data=Ok(elem);
} in
match mem_region with
| [] -> new_node :: []
| head :: tail ->
if head.pos + head.size <= pos then
head :: (add tail elem ~pos ~size)
else if pos + size <= head.pos then
new_node :: mem_region
else begin (* head and new node intersect => at the intersection, head gets overwritten and the rest of head gets marked as error. *)
let tail = if head.pos + head.size > pos + size then (* mark the right end of head as error *)
let err = error_elem ~pos:(pos + size) ~size:(head.pos + head.size - (pos + size)) in
err :: tail
else
tail in
let tail = add tail elem ~pos ~size in (* add the new element*)
let tail = if head.pos < pos then (* mark the left end of head as error *)
let err = error_elem ~pos:(head.pos) ~size:(pos - head.pos) in
err :: tail
else
tail in
tail
end
let rec get mem_region pos =
match mem_region with
| [] -> None
| head :: tail ->
if head.pos > pos then
None
else if head.pos = pos then
match head.data with
| Ok(x) -> Some(Ok(x, head.size))
| Error(_) -> Some(Error(()))
else if head.pos + head.size <= pos then
get tail pos
else
Some(Error(())) (* pos intersects some data, but does not equal its starting address*)
let rec remove 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
| [] -> []
| hd :: tl ->
if hd.pos + hd.size <= pos then
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 =
if hd.pos + hd.size > pos + size then
error_elem ~pos:(pos + size) ~size:(hd.pos + hd.size - (pos + size)) :: mem_region
else
mem_region in
let mem_region =
if hd.pos < pos then
error_elem ~pos:hd.pos ~size:(pos - hd.pos) :: mem_region
else
mem_region in
mem_region
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) :: []
| hd :: tl ->
if hd.pos + hd.size <= pos then
hd :: (mark_error tl ~pos ~size)
else if pos + size <= hd.pos then
(error_elem ~pos ~size) :: mem_region
else
let start_pos = Word.min pos hd.pos in
let end_pos_plus_one = Word.max (pos + size) (hd.pos + hd.size) in
mark_error tl ~pos:start_pos ~size:(end_pos_plus_one - start_pos)
(* TODO: This is probably a very inefficient implementation in some cases. Write a faster implementation if necessary. *)
let rec merge mem_region1 mem_region2 ~data_merge =
match (mem_region1, mem_region2) with
| (value, [])
| ([], value) -> value
| (hd1 :: tl1, hd2 :: tl2) ->
if hd1.pos + hd1.size <= hd2.pos then
hd1 :: merge tl1 mem_region2 ~data_merge
else if hd2.pos + hd2.size <= hd1.pos then
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
end
| _ -> { hd1 with data = Error(()) } :: merge tl1 tl2 ~data_merge
else
let start_pos = Word.min hd1.pos hd2.pos in
let end_pos_plus_one = Word.max (hd1.pos + hd1.size) (hd2.pos + hd2.size) in
let mem_region = merge tl1 tl2 ~data_merge in
mark_error mem_region ~pos:start_pos ~size:(end_pos_plus_one - start_pos)
let rec equal (mem_region1:'a t) (mem_region2:'a t) ~data_equal : bool =
match (mem_region1, mem_region2) with
| ([], []) -> true
| (hd1 :: tl1, hd2 :: tl2) ->
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
| _ -> false
else
false
| _ -> false
let map_data (mem_region: 'a t) ~(f: 'a -> 'b) : 'b t =
List.map mem_region ~f:(fun mem_node ->
{ pos = mem_node.pos;
size = mem_node.size;
data = Result.map mem_node.data ~f
}
)
let list_data (mem_region: 'a t) : 'a List.t =
List.filter_map mem_region ~f:(fun mem_node ->
match mem_node.data with
| Ok(value) -> Some(value)
| Error(_) -> None
)
let list_data_pos (mem_region: 'a t) : (Bitvector.t * 'a) List.t =
List.filter_map mem_region ~f:(fun mem_node ->
match mem_node.data with
| Ok(value) -> Some( mem_node.pos, value )
| Error(_) -> None
)
(** contains an abstract memory region data type where you can assign arbitrary data to locations
inside the memory regions. A memory region has no fixed size, so it can be used
for memory regions of variable size like arrays or stacks.
TODO: Right now this data structure is unsuited for elements that get only partially loaded. *)
open Bap.Std
open Core_kernel
type 'a t [@@deriving bin_io, compare, sexp]
(** Get an empty memory region- *)
val empty: unit -> 'a t
(** Add an element to the memory region. If the element intersects existing elements,
the non-overwritten part gets marked as Error *)
val add: 'a t -> 'a -> pos:Bitvector.t -> size:Bitvector.t -> 'a t
(** Mark the memory region between pos (included) and pos+size (excluded) as empty.
If elements get partially removed, mark the non-removed parts as Error *)
val remove: 'a t -> pos:Bitvector.t -> size:Bitvector.t -> 'a t
(** Returns the element and its size at position pos or None, when there is no element at that position.
If pos intersects an element but does not match its starting position, it returns Some(Error(())). *)
val get: 'a t -> Bitvector.t -> (('a * Bitvector.t), unit) Result.t Option.t
(** Merge two memory regions. Elements with the same position and size get merged using
data_merge, other intersecting elements get marked as Error. Note that data_merge
may return None (to remove the elements from the memory region) or Some(Error(_)) to
mark the merged element as error. *)
val merge: 'a t -> 'a t -> data_merge:('a -> 'a -> ('a, 'b) result option) -> 'a t
(** Check whether two memory regions are equal. *)
val equal: 'a t -> 'a t -> data_equal:('a -> 'a -> bool) -> bool
(** Mark an area in the mem_region as containing errors. *)
val mark_error: 'a t -> pos:Bitvector.t -> size:Bitvector.t -> 'a t
(** Map the contained data to new values. *)
val map_data: 'a t -> f:('a -> 'b) -> 'b t
(** List the contained data (ignoring error values). *)
val list_data: 'a t -> 'a List.t
(** List the contained data (ignoring error values) together with their positions. *)
val list_data_pos: 'a t -> (Bitvector.t * 'a) List.t
open Bap.Std
open Core_kernel
external rs_run_pointer_inference: Serde_json.t -> string = "rs_run_pointer_inference"
external rs_run_pointer_inference_and_print_debug: Serde_json.t -> unit = "rs_run_pointer_inference_and_print_debug"
type cwelist = Log_utils.CweWarning.t array [@@deriving yojson]
let run (project: Project.t) (tid_map: Bap.Std.word Bap.Std.Tid.Map.t) : unit =
let program = Project.program project in
let entry_points = Symbol_utils.get_program_entry_points program in
let entry_points = List.map entry_points ~f:(fun sub -> Term.tid sub) in
let extern_symbols = Symbol_utils.build_and_return_extern_symbols project program tid_map in
let project_serde = Serde_json.of_project project extern_symbols entry_points tid_map in
let cwe_warnings_json = Yojson.Safe.from_string @@ rs_run_pointer_inference project_serde in
match cwe_warnings_json with
| `List ((`List cwe_warnings) :: (`List log_messages) :: []) ->
List.iter cwe_warnings ~f:(fun warning -> Log_utils.collect_cwe_warning @@ Result.ok_or_failwith @@ Log_utils.CweWarning.of_yojson warning);
List.iter log_messages ~f:(fun message ->
match message with
| `String message_string ->
begin match String.lsplit2 message_string ~on:':' with
| Some("ERROR", msg) -> Log_utils.error @@ String.strip msg
| Some("DEBUG", msg) -> Log_utils.debug @@ String.strip msg
| Some("INFO", msg) -> Log_utils.info @@ String.strip msg
| _ -> failwith "Malformed log-message."
end
| _ -> failwith "Log-message is not a string."
)
| _ -> failwith "Log-message-json not as expected"
let run_and_print_debug (project: Project.t) (tid_map: Bap.Std.word Bap.Std.Tid.Map.t) : unit =
let program = Project.program project in
let entry_points = Symbol_utils.get_program_entry_points program in
let entry_points = List.map entry_points ~f:(fun sub -> Term.tid sub) in
let extern_symbols = Symbol_utils.build_and_return_extern_symbols project program tid_map in
let project_serde = Serde_json.of_project project extern_symbols entry_points tid_map in
rs_run_pointer_inference_and_print_debug project_serde
(** This module manages the communication with the actual pointer inference analysis
through the foreign function interface to Rust.
*)
open Bap.Std
(** Run the pointer inference analysis and log the returned CWE warnings and log messages. *)
val run: Project.t -> Bap.Std.word Bap.Std.Tid.Map.t -> unit
(** Run the pointer inference analysis and print the computed state of each basic block
(at the start and at the end of the basic block respectively)
as json to stdout.
Does not print log messages or CWE warnings.
The output is meant for debugging purposes.
*)
val run_and_print_debug: Project.t -> Bap.Std.word Bap.Std.Tid.Map.t -> unit
open Bap.Std
open Core_kernel
(** TODO:
interprocedural analysis
backward analysis to recognize which constants are pointers and which not.
maybe extend to track FunctionPointer,
TODO: There are no checks yet if a value from the stack of the calling function
is accessed. Maybe this should be part of another analysis.
TODO: the fixpoint analysis does not track whether a pointer could have an
unknown target as long as it has at least one known target. This should be
tracked in an extra analysis step after the fixpoint analysis finished.
*)
let name = "Type Inference"
let version = "0.2"
(* TODO: result_option and result_map should be abstracted away into its own data type and into its own file. *)
(* generic merge of two ('a, unit) Result.t Option.t *)
let merge_result_option val1 val2 =
match (val1, val2) with
| (Some(Ok(x)), Some(Ok(y))) when Poly.(=) x y -> Some(Ok(x))
| (Some(x), None)
| (None, Some(x)) -> Some(x)
| (None, None) -> None
| _ -> Some(Error(()))
(* generic binop of two ('a, unit) Result.t Option.t *)
let binop_result_option val1 val2 ~op =
match (val1, val2) with
| (Some(Ok(x)), Some(Ok(y))) -> Some(Ok(op x y))
| (Some(Ok(_)), None)
| (None, Some(Ok(_))) -> None
| (None, None) -> None
| _ -> Some(Error(()))
(* 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 ->
match values with
| `Left(x)
| `Right(x) -> Some(x)
| `Both(Ok(x1), Ok(x2)) -> Some(value_merge x1 x2)
| `Both(_, _) -> Some(Error(()))
)
(* generic equal of two ('a, unit) Result.t Option.t)*)
let equal_result_option val1 val2 ~value_equal =
match (val1, val2) with
| (Some(Ok(x)), Some(Ok(y))) -> value_equal x y
| (Some(Error(())), Some(Error(()))) -> true
| (None, None) -> true
| _ -> false
module PointerTargetInfo = struct
type t = {
offset: (Bitvector.t, unit) Result.t Option.t;
alignment: (int, unit) Result.t Option.t;
} [@@deriving bin_io, compare, sexp]
let merge info1 info2 =
{ offset = merge_result_option info1.offset info2.offset;
alignment = merge_result_option info1.alignment info2.alignment; }
let equal info1 info2 =
equal_result_option info1.offset info2.offset ~value_equal:Bitvector.(=) && equal_result_option info1.alignment info2.alignment ~value_equal: Int.(=)
end (* module *)
module Register = struct
type t =
| Pointer of PointerTargetInfo.t Tid.Map.t
| Data
[@@deriving bin_io, compare, sexp]
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 ->
match values with
| `Left(info)
| `Right(info) -> Some(info)
| `Both(info1, info2) -> Some(PointerTargetInfo.merge info1 info2)
)))
| (Data, Data) -> Ok(Data)
| _ -> Error(())
(* Checks whether two registers hold the same data *)
let equal reg1 reg2 =
match (reg1, reg2) with
| (Pointer(targets1), Pointer(targets2)) -> Map.equal PointerTargetInfo.equal targets1 targets2
| (Data, Data) -> true
| _ -> false
(** add to the offsets of all possible targets of the register. *)
let add_to_offsets register value_res_opt =
match register with
| Pointer(targets) ->
let new_targets = Map.map targets ~f:(fun target ->
{ target with offset = binop_result_option target.offset value_res_opt ~op:Bitvector.(+) }
) in
Pointer(new_targets)
| Data -> Data
(** subtract from the offsets of all possible targets of the register. *)
let sub_from_offsets register value_res_opt =
match register with
| Pointer(targets) ->
let new_targets = Map.map targets ~f:(fun target ->
{ target with offset = binop_result_option target.offset value_res_opt ~op:Bitvector.sub }
) in
Pointer(new_targets)
| Data -> Data
(** sets all target offsets and alignments to unknown. Right now used as long as alignment tracking is not implemented. *)
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
Pointer(new_targets)
| Data -> Data
end (* module *)
module TypeInfo = struct
type reg_state = (Register.t, unit) Result.t Var.Map.t [@@deriving bin_io, compare, sexp]
type t = {
stack: Register.t Mem_region.t;
reg: reg_state;
} [@@deriving bin_io, compare, sexp]
let merge state1 state2 =
let stack = Mem_region.merge state1.stack state2.stack ~data_merge:(fun x y -> Some(Register.merge x y )) in
let reg = merge_result_map state1.reg state2.reg ~value_merge:Register.merge in
{ stack = stack;
reg = reg;
}
let equal state1 state2 =
if Mem_region.equal state1.stack state2.stack ~data_equal:Register.equal then
Map.equal (fun reg1 reg2 -> match (reg1, reg2) with
| (Ok(register1), Ok(register2)) -> Register.equal register1 register2
| (Error(()), Error(())) -> true
| _ -> false
) state1.reg state2.reg
else
false
(** Get an empty state. *)
let empty () =
{ stack = Mem_region.empty ();
reg = Var.Map.empty;
}
(** add flag register as known data register *)
let add_flags state project =
let flags = Symbol_utils.flag_register_list project in
List.fold flags ~init:state ~f:(fun state register ->
{ state with reg = (Map.set state.reg ~key:register ~data:(Ok(Register.Data))) } )
(** set stack register as known stack pointer. Deletes other targets of the stack pointer. *)
let set_stack_register state ?offset ?alignment ~sub_tid ~project =
let stack_register = Symbol_utils.stack_register project in
let offset = match offset with
| Some(x) -> Some(Ok(x))
| None -> None in
let alignment = match alignment with
| Some(x) -> Some(Ok(x))
| 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 ~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. *)
let only_stack_pointer_and_flags sub_tid project =
let state = empty () in
let state = add_flags state project in
let state = set_stack_register state ?offset:None ?alignment:None ~sub_tid ~project in
state
(** create a new state with stack pointer as known pointer register and all flag
registers as known data registers. The stack itself is empty and the offset
is 0. (TODO for interprocedural analysis: Ensure that the return address is
marked as a pointer!) *)
let function_start_state sub_tid project =
let state = empty () in
let state = add_flags state project in
let zero_offset = Bitvector.of_int 0 ~width:(Symbol_utils.arch_pointer_size_in_bytes project * 8) in
let state = set_stack_register state ~offset:zero_offset ?alignment:None ~sub_tid ~project in
state
let remove_virtual_registers state =
{ state with reg = Map.filter_keys state.reg ~f:(fun var -> Var.is_physical var) }
let compute_stack_offset (state: t) (addr_exp: Exp.t) ~(sub_tid: Tid.t) ~(project: Project.t) : Bitvector.t Option.t =
let (register, offset) = match addr_exp with
| Bil.Var(var) -> (Some(var), Bitvector.of_int 0 ~width:(Symbol_utils.arch_pointer_size_in_bytes project * 8))
| Bil.BinOp(Bil.PLUS, Bil.Var(var), Bil.Int(num)) -> (Some(var), num)
| Bil.BinOp(Bil.MINUS, Bil.Var(var), Bil.Int(num)) -> (Some(var), Bitvector.neg (Bitvector.signed num))
| _ -> (None, Bitvector.of_int 0 ~width:(Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
match register with
| Some(var) ->
begin match Map.find state.reg var with
| Some(Ok(Pointer(targets))) ->
begin match Map.find targets sub_tid with
| Some(target_info) ->
begin match target_info.offset with
| Some(Ok(target_offset)) -> Some(Bitvector.(+) target_offset offset)
| _ -> None
end
| None -> None
end
| _ -> None
end
| _ -> None
(* Pretty printer that just prints the sexp. Needed for the creation of type_info_tag. *)
let pp ppf elem =
Format.fprintf ppf "%s" (Sexp.to_string (sexp_of_t elem))
end (* module *)
(* Create a tag for TypeInfo *)
let type_info_tag = Value.Tag.register (module TypeInfo)
~name:"type_info"
~uuid:"7a537f19-2dd1-49b6-b343-35b4b1d04c0b"
(** returns a list with all nested expressions. If expr1 is contained in expr2, then
expr1 will be included after expr2 in the list. *)
let nested_exp_list exp : Exp.t list =
let rec nested_exp_list exp acc = exp :: match exp with
| Bil.Load(exp1, exp2, _, _) -> nested_exp_list exp1 @@ nested_exp_list exp2 acc
| Bil.Store(exp1, exp2, exp3, _, _) -> nested_exp_list exp1 @@ nested_exp_list exp2 @@ nested_exp_list exp3 acc
| Bil.BinOp(_op, exp1, exp2) -> nested_exp_list exp1 @@ nested_exp_list exp2 acc
| Bil.UnOp(_op, exp1) -> nested_exp_list exp1 acc
| Bil.Var(_) -> acc
| Bil.Int(_) -> acc
| Bil.Cast(_, _, exp1) -> nested_exp_list exp1 acc
| Bil.Let(_, exp1, exp2) -> nested_exp_list exp1 @@ nested_exp_list exp2 acc
| Bil.Unknown(_) -> acc
| Bil.Ite(exp1, exp2, exp3) -> nested_exp_list exp1 @@ nested_exp_list exp2 @@ nested_exp_list exp3 acc
| Bil.Extract(_, _, exp1) -> nested_exp_list exp1 acc
| Bil.Concat(exp1, exp2) -> nested_exp_list exp1 @@ nested_exp_list exp2 acc in
nested_exp_list exp []
(** If exp is a load from the stack, return the corresponding element. If it may be
a load from the stack, but could also be a load from some other memory region,
we still assume that the type information on the stack is correct and return it.
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
| Some(offset) -> begin
match Mem_region.get state.TypeInfo.stack offset with
| Some(Ok(elem, elem_size)) ->
if Poly.(=) (Bitvector.to_int elem_size) (Ok(Size.in_bytes size)) then
Some(Ok(elem))
else
Some(Error())
| Some(Error()) -> Some(Error())
| None -> None
end
| None -> None
end
| _ -> None
(* compute the value of an expression. This is a stub and will be replaced when we
have a proper pass for value inference. *)
let value_of_exp exp =
match exp with
| Bil.Int(x) -> Some(Ok(x))
| _ -> None
let rec type_of_exp (exp: Exp.t) (state: TypeInfo.t) ~(sub_tid: Tid.t) ~(project: Project.t) : (Register.t, unit) Result.t Option.t =
let open Register in
match exp with
| Bil.Load(_) -> (* TODO: Right now only the stack is tracked for type infos. *)
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
(* 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(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(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(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(Data)), Some(Ok(Data))) -> Some(Ok(Data))
| (Bil.OR, _, _) -> None
| _ -> Some(Ok(Data)) (* every other operation should not yield valid pointers *)
end
| Bil.UnOp(_) -> Some(Ok(Data))
| Bil.Var(var) -> Map.find state.TypeInfo.reg var
| 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 *)
| 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
| (Some(Ok(value1)), Some(Ok(value2))) -> if Register.equal value1 value2 then Some(Ok(value1)) else None
| (Some(Error ()), Some(Error ())) -> Some(Error ())
| _ -> None
end
| Bil.Extract(_) -> Some(Ok(Data)) (* TODO: Similar to cast: Are there cases of 32bit-64bit-address-conversions here? *)
| Bil.Concat(_) -> Some(Ok(Data)) (* TODO: If alignment of the pointer is known, it could be used like AND and OR *)
let pointer_size_as_bitvector project =
let psize = Symbol_utils.arch_pointer_size_in_bytes project in
Bitvector.of_int psize ~width:(psize * 8)
(* If exp is a store to the stack, add the corresponding value to the stack if possible. If the
we cannot determine the value, delete the corresponding data on the stack.
Custom behaviour if we cannot determine the exact position of the store or if it
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) ->
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
let (targets_stack, target_is_unique) = match addr_type with
| Some(Ok(Pointer(targets))) -> (Option.is_some (Map.find targets sub_tid), Map.length targets = 1)
| _ -> (false, false) in
let pointer_size = Symbol_utils.arch_pointer_size_in_bytes project in
if targets_stack then
match stack_offset with
| Some(offset) ->
let new_stack =
if Size.in_bytes size = pointer_size then
match value with
| Some(Ok(inner_value)) -> Mem_region.add state.TypeInfo.stack inner_value ~pos:offset ~size:(pointer_size_as_bitvector project)
| Some(Error(_)) -> Mem_region.mark_error state.TypeInfo.stack ~pos:offset ~size:(Bitvector.of_int (Size.in_bytes size) ~width:pointer_size)
| None -> Mem_region.remove state.TypeInfo.stack ~pos:offset ~size:(Bitvector.of_int (Size.in_bytes size) ~width:pointer_size)
else (* store has to be data *)
Mem_region.add state.TypeInfo.stack Register.Data ~pos:offset ~size:(Bitvector.of_int (Size.in_bytes size) ~width:pointer_size) in
let new_state = { state with TypeInfo.stack = new_stack } in
if target_is_unique then (* previous value on the stack gets overwritten *)
new_state
else (* previous value on the stack may have been overwritten. We merge the two possible states to account for both cases *)
TypeInfo.merge state new_state
| None -> begin
if target_is_unique then (* There is a write on the stack, but we do not know where. To prevent our knowledge of the stack to get corrupted, we delete it. *)
{ state with TypeInfo.stack = Mem_region.empty ()}
else (* There may have been a write to the stack, but we do not know where. We optimistically assume that if it was a write, it did not change the TypeInfo there. *)
state
end
else (* store does not change the stack *)
state
| _ -> state
(* adds address registers of Loads and Stores to the list of known pointer register.
Note that this is a source of pointers, where we do not know where they point to.
This may confuse algorithms, if they assume that the pointer target list is exhaustive. *)
let add_mem_address_registers state exp ~sub_tid ~project =
let exp_list = nested_exp_list exp in
List.fold exp_list ~init:state ~f:(fun state exp ->
match exp with
| Bil.Load(_, addr_exp, _, _)
| Bil.Store(_, addr_exp, _, _, _) -> begin
match addr_exp with
| Bil.Var(addr)
| Bil.BinOp(Bil.PLUS, Bil.Var(addr), Bil.Int(_))
| Bil.BinOp(Bil.PLUS, Bil.Int(_), Bil.Var(addr))
| Bil.BinOp(Bil.MINUS, Bil.Var(addr), Bil.Int(_))
| Bil.BinOp(Bil.AND, Bil.Var(addr), Bil.Int(_))
| Bil.BinOp(Bil.AND, Bil.Int(_), Bil.Var(addr))
| Bil.BinOp(Bil.OR, Bil.Var(addr), Bil.Int(_))
| 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 ~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))
| Bil.BinOp(Bil.MINUS, Bil.Var(addr), exp2)
| Bil.BinOp(Bil.AND, Bil.Var(addr), exp2)
| 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 Poly.(=) (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 ~key:addr ~data:(Ok(Register.Pointer(Tid.Map.empty))) }
end
else
state
| _ -> state
end
| _ -> state
)
(* Remove any knowledge of the stack (except the stack_offset) and the registers (except stack and flag registers) from the state. *)
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 ~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
(* update the lhs of the definition with its new type *)
let state = match type_of_exp (Def.rhs def) state ~sub_tid ~project with
| Some(value) ->
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
{ state with TypeInfo.reg = reg } in
(* write something to the stack if the definition is a store to the stack *)
let state = set_stack_elem state (Def.rhs def) ~sub_tid ~project in
state
(** Add an integer to stack offset. *)
let add_to_stack_offset state num ~project =
match Map.find state.TypeInfo.reg (Symbol_utils.stack_register project) with
| Some(Ok(stack_register)) ->
let pointer_size = Symbol_utils.arch_pointer_size_in_bytes project in
let new_stack_value = Register.add_to_offsets stack_register (Some(Ok(Bitvector.of_int num ~width:(pointer_size * 8)))) in
{ state with TypeInfo.reg = Map.set state.TypeInfo.reg ~key:(Symbol_utils.stack_register project) ~data:(Ok(new_stack_value)) }
| _ -> state (* There is no known stack offset, so we return the old state. *)
(* TODO: Add entry to config for this? Since type inference is its own bap-pass, this may need a new config file...
Also important: update_state_jmp makes a lot of assumptions about the functions (like it does not interact with the stack).
If this list gets configurable, we probably need a concept how to annotate these types of assumptions in config files. *)
(** returns a list of known malloc-like functions. *)
let malloc_like_function_list () =
["malloc"; "calloc"; "realloc";]
(** updates the state on a call to a malloc-like function. Notable assumptions for
malloc-like functions:
- only one return register, which returns a unique pointer to a newly allocated
memory region. Note: Possible zero returns are handled by the CWE-476-check.
- the malloc-like-function does not touch the stack
- the standard calling convention of the target architecture is used. *)
let update_state_malloc_call state malloc_like_tid jmp_term ~project =
(* only keep callee-saved register information. Stack information is also kept. TODO: maybe add a "cut"-function to remove all stack info below the stack pointer? *)
let state = { state with TypeInfo.reg = Var.Map.filter_keys state.TypeInfo.reg ~f:(fun var -> Cconv.is_callee_saved var project) } in
(* add the return register with its new pointer target. The target is identified by the tid of the jmp instruction. *)
let malloc_like_fn = Term.find_exn sub_t (Project.program project) malloc_like_tid in
let arguments = Term.enum arg_t malloc_like_fn in
let return_arg_opt = Seq.find arguments ~f:(fun arg -> (* TODO: check whether there exists more than one return register! *)
match Bap.Std.Arg.intent arg with
| Some(Out) | Some(Both) -> true
| _ -> false
) in
let return_arg = match return_arg_opt with
| Some(x) -> x
| None -> failwith "[CWE-checker] malloc-like function has no return register" in
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 ~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))) }
(* TODO: Right now the conditional expression is not checked! Thus for conditional calls
(if bap generates conditional calls) the state would always be the state as if the call
branch has been taken even for the other branch. The way that the bap fixpoint function
works this could be quite complicated to implement. *)
let update_state_jmp state jmp ~sub_tid ~project =
match Jmp.kind jmp with
| Call(call) ->
let return_state = match Call.target call with
| Direct(tid) ->
let func_name = match String.lsplit2 (Tid.name tid) ~on:'@' with
| Some(_left, right) -> right
| None -> Tid.name tid in
if String.Set.mem (Symbol_utils.parse_dyn_syms project) func_name then
begin if List.exists (malloc_like_function_list ()) ~f:(fun elem -> String.(=) elem func_name) then
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. *)
(* 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
| 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? *)
| 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 ~key:var ~data:(Ok(Register.Pointer(Tid.Map.empty))) }
end
| Goto(_)
| Ret(_) -> state
(* This is public for unit test purposes. *)
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. *)
| `Jmp jmp -> update_state_jmp state jmp ~sub_tid ~project
(** updates a block analysis. *)
let update_block_analysis block register_state ~sub_tid ~project =
(* get all elements (Defs, Jumps, Phi-nodes) in the correct order *)
let elements = Blk.elts block in
let register_state = Seq.fold elements ~init:register_state ~f:(fun state element ->
update_type_info element state ~sub_tid ~project
) in
TypeInfo.remove_virtual_registers register_state (* virtual registers should not be accessed outside of the block where they are defined. *)
let intraprocedural_fixpoint func ~project =
let cfg = Sub.to_cfg func in
let sub_tid = Term.tid func in
(* default state for nodes *)
let only_sp = TypeInfo.only_stack_pointer_and_flags sub_tid project in
try
(* Create a starting solution where only the first block of a function knows the stack_offset. *)
let fn_start_state = TypeInfo.function_start_state sub_tid project in
let fn_start_block = Option.value_exn (Term.first blk_t func) in
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 -> Tid.(=) (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 ~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
let f = (fun node state ->
let block = Graphs.Ir.Node.label node in
update_block_analysis block state ~sub_tid ~project
) in
Graphlib.Std.Graphlib.fixpoint (module Graphs.Ir) cfg ~steps:100 ~rev:false ~init:init ~equal:equal ~merge:merge ~f:f
with
| _ -> (* An exception will be thrown if the function does not contain any blocks. In this case we can simply return an empty solution. *)
Graphlib.Std.Solution.create (Map.empty (module Graphs.Ir.Node)) only_sp
(** Extract the starting state of a node. *)
let extract_start_state node ~cfg ~solution ~sub_tid ~project =
let predecessors = Graphs.Ir.Node.preds node cfg in
if Seq.is_empty predecessors then
TypeInfo.function_start_state sub_tid project (* This should be the first block of a function. Maybe add a test for when there is more than one such block in a function? *)
else
let only_sp = TypeInfo.only_stack_pointer_and_flags sub_tid project in
Seq.fold predecessors ~init:only_sp ~f:(fun state node ->
TypeInfo.merge state (Graphlib.Std.Solution.get solution node)
)
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
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 tagged_block = Term.set_attr block type_info_tag start_state in
Term.update blk_t func tagged_block
)
) in
Project.with_program project program_with_tags
(** Prints type info to debug. *)
let print_type_info_to_debug state block_tid ~tid_map ~sub_tid ~project =
let register_list = Map.fold state.TypeInfo.reg ~init:[] ~f:(fun ~key:var ~data:reg str_list ->
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)) ^
")") :: str_list
| Ok(Register.Data) -> (Var.name var ^ ":Data, ") :: str_list
| Error(_) -> (Var.name var ^ ":Error, ") :: str_list ) in
let register_string = String.concat register_list in
let stack_offset_str =
match Map.find state.TypeInfo.reg (Symbol_utils.stack_register project) with
| Some(Ok(Pointer(targets))) ->
begin match Map.find targets sub_tid with
| Some(target) ->
begin match target.PointerTargetInfo.offset with
| Some(Ok(x)) ->
begin match Bitvector.to_int (Bitvector.signed x) with
| Ok(number) -> string_of_int number
| _ -> "NaN"
end
| Some(Error()) -> "Unknown (Error)"
| _ -> "Unknown"
end
| None -> "Unknown"
end
| _ -> "Unknown"
in
let debug_str = sprintf
"[%s] {%s} TypeInfo at %s:\nRegister: %s\nStackOffset: %s"
name
version
(Address_translation.translate_tid_to_assembler_address_string block_tid tid_map)
register_string
stack_offset_str in
Log_utils.debug debug_str
let print_type_info_tags ~project ~tid_map =
let program = Project.program project in
let functions = Term.enum sub_t program in
Seq.iter functions ~f:(fun func ->
let sub_tid = Term.tid func in
let blocks = Term.enum blk_t func in
Seq.iter blocks ~f:(fun block ->
match Term.get_attr block type_info_tag with
| Some(start_state) -> print_type_info_to_debug start_state (Term.tid block) ~tid_map ~sub_tid ~project
| None -> (* block has no type info tag, which should not happen *)
let error_str = sprintf
"[%s] {%s} Block has no TypeInfo at %s (block TID %s)"
name
version
(Address_translation.translate_tid_to_assembler_address_string (Term.tid block) tid_map)
(Tid.name (Term.tid block)) in
Log_utils.error error_str
)
)
let get_type_info_of_block ~(project: Project.t) (block: Blk.t) ~(sub_tid: Tid.t) : TypeInfo.t Tid.Map.t =
match Term.get_attr block type_info_tag with
| Some(start_state) ->
let elements = Blk.elts block in
let (type_info_map, _) = Seq.fold elements ~init:(Tid.Map.empty, start_state) ~f:(fun (type_info_map, state) element ->
match element with
| `Phi _ -> (type_info_map, state)
| `Def term ->
let new_type_info_map = Tid.Map.set type_info_map ~key:(Term.tid term) ~data:state in
let new_state = update_type_info element state ~sub_tid ~project in
(new_type_info_map, new_state)
| `Jmp term ->
let new_type_info_map = Tid.Map.set type_info_map ~key:(Term.tid term) ~data:state in
let new_state = update_type_info element state ~sub_tid ~project in
(new_type_info_map, new_state)
) in
type_info_map
| None -> failwith "[cwe_checker] Error: Tag not found"
(* Functions made available for unit tests *)
module Private = struct
let update_block_analysis = update_block_analysis
let function_start_state = TypeInfo.function_start_state
let compute_stack_offset = TypeInfo.compute_stack_offset
let only_stack_pointer_and_flags = TypeInfo.only_stack_pointer_and_flags
let merge_type_infos = TypeInfo.merge
let type_info_equal = TypeInfo.equal
end
(* 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 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.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
type t =
| Pointer of PointerTargetInfo.t Tid.Map.t
| Data
[@@deriving bin_io, compare, sexp]
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
type reg_state = (Register.t, unit) Result.t Var.Map.t [@@deriving bin_io, compare, sexp]
type t = {
stack: Register.t Mem_region.t;
reg: reg_state;
} [@@deriving bin_io, compare, sexp]
(* Pretty Printer. At the moment, the output is not pretty at all. *)
val pp: Format.formatter -> t -> unit
(** 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. *)
val compute_stack_offset: t -> Exp.t -> sub_tid:Tid.t -> project:Project.t -> Bitvector.t Option.t
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
(** Computes TypeInfo for the given project. Adds tags to each block containing the
TypeInfo at the start of the block. *)
val compute_pointer_register: Project.t -> Project.t
(** Print type info tags. TODO: If this should be used for more than debug purposes,
then the output format should be refactored accordingly. *)
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
is the type info before execution of the element, output is the type info
after execution of the element. sub_tid is the Tid of the current function
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
(** Get the type info for each def term and jump term in a block.
The returned type info for the tid of a (def/jmp) term is the tid before that term.
The sub_tid is the Tid of the current function which is internally used to mark
which pointers point to the current stack frame. *)
val get_type_info_of_block: project:Project.t -> Blk.t -> sub_tid:Tid.t -> TypeInfo.t Tid.Map.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
val type_info_equal: TypeInfo.t -> TypeInfo.t -> bool
end
...@@ -6,7 +6,7 @@ edition = "2018" ...@@ -6,7 +6,7 @@ edition = "2018"
[dependencies] [dependencies]
structopt = "0.3" structopt = "0.3"
cwe_checker_rs = { path = "../cwe_checker_rs" } cwe_checker_lib = { path = "../cwe_checker_lib" }
serde_json = "1.0" serde_json = "1.0"
directories = "3.0" directories = "3.0"
nix = "0.19.1" nix = "0.19.1"
\ No newline at end of file
use cwe_checker_rs::analysis::graph; use cwe_checker_lib::analysis::graph;
use cwe_checker_rs::utils::binary::RuntimeMemoryImage; use cwe_checker_lib::utils::binary::RuntimeMemoryImage;
use cwe_checker_rs::utils::log::print_all_messages; use cwe_checker_lib::utils::log::print_all_messages;
use cwe_checker_rs::utils::{get_ghidra_plugin_path, read_config_file}; use cwe_checker_lib::utils::{get_ghidra_plugin_path, read_config_file};
use cwe_checker_rs::AnalysisResults; use cwe_checker_lib::AnalysisResults;
use cwe_checker_rs::{intermediate_representation::Project, utils::log::LogMessage}; use cwe_checker_lib::{intermediate_representation::Project, utils::log::LogMessage};
use nix::{sys::stat, unistd}; use nix::{sys::stat, unistd};
use std::collections::HashSet; use std::collections::HashSet;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
...@@ -27,6 +27,8 @@ struct CmdlineArgs { ...@@ -27,6 +27,8 @@ struct CmdlineArgs {
out: Option<String>, out: Option<String>,
/// Specify a specific set of checks to be run as a comma separated list, e.g. 'CWE332,CWE476,CWE782'. /// Specify a specific set of checks to be run as a comma separated list, e.g. 'CWE332,CWE476,CWE782'.
///
/// Use the "--module-names" command line option to get a list of all valid check names.
#[structopt(long, short)] #[structopt(long, short)]
partial: Option<String>, partial: Option<String>,
...@@ -106,7 +108,7 @@ fn check_file_existence(file_path: String) -> Result<(), String> { ...@@ -106,7 +108,7 @@ fn check_file_existence(file_path: String) -> Result<(), String> {
/// Run the cwe_checker with Ghidra as its backend. /// Run the cwe_checker with Ghidra as its backend.
fn run_with_ghidra(args: CmdlineArgs) { fn run_with_ghidra(args: CmdlineArgs) {
let mut modules = cwe_checker_rs::get_modules(); let mut modules = cwe_checker_lib::get_modules();
if args.module_versions { if args.module_versions {
// Only print the module versions and then quit. // Only print the module versions and then quit.
println!("[cwe_checker] module_versions:"); println!("[cwe_checker] module_versions:");
...@@ -189,7 +191,7 @@ fn run_with_ghidra(args: CmdlineArgs) { ...@@ -189,7 +191,7 @@ fn run_with_ghidra(args: CmdlineArgs) {
// Right now there is only one debug printing function. // Right now there is only one debug printing function.
// When more debug printing modes exist, this behaviour will change! // When more debug printing modes exist, this behaviour will change!
if args.debug { if args.debug {
cwe_checker_rs::analysis::pointer_inference::run( cwe_checker_lib::analysis::pointer_inference::run(
&project, &project,
&runtime_memory_image, &runtime_memory_image,
&control_flow_graph, &control_flow_graph,
...@@ -217,7 +219,7 @@ fn run_with_ghidra(args: CmdlineArgs) { ...@@ -217,7 +219,7 @@ fn run_with_ghidra(args: CmdlineArgs) {
/// Only keep the modules specified by the `--partial` parameter in the `modules` list. /// Only keep the modules specified by the `--partial` parameter in the `modules` list.
/// The parameter is a comma-separated list of module names, e.g. 'CWE332,CWE476,CWE782'. /// The parameter is a comma-separated list of module names, e.g. 'CWE332,CWE476,CWE782'.
fn filter_modules_for_partial_run( fn filter_modules_for_partial_run(
modules: &mut Vec<&cwe_checker_rs::CweModule>, modules: &mut Vec<&cwe_checker_lib::CweModule>,
partial_param: &str, partial_param: &str,
) { ) {
let module_names: HashSet<&str> = partial_param.split(',').collect(); let module_names: HashSet<&str> = partial_param.split(',').collect();
...@@ -326,10 +328,10 @@ fn get_project_from_ghidra(file_path: &Path, binary: &[u8], quiet_flag: bool) -> ...@@ -326,10 +328,10 @@ fn get_project_from_ghidra(file_path: &Path, binary: &[u8], quiet_flag: bool) ->
// Open the FIFO // Open the FIFO
let file = std::fs::File::open(fifo_path.clone()).expect("Could not open FIFO."); let file = std::fs::File::open(fifo_path.clone()).expect("Could not open FIFO.");
let mut project_pcode: cwe_checker_rs::pcode::Project = let mut project_pcode: cwe_checker_lib::pcode::Project =
serde_json::from_reader(std::io::BufReader::new(file)).unwrap(); serde_json::from_reader(std::io::BufReader::new(file)).unwrap();
project_pcode.normalize(); project_pcode.normalize();
let project: Project = match cwe_checker_rs::utils::get_binary_base_address(binary) { let project: Project = match cwe_checker_lib::utils::get_binary_base_address(binary) {
Ok(binary_base_address) => project_pcode.into_ir_project(binary_base_address), Ok(binary_base_address) => project_pcode.into_ir_project(binary_base_address),
Err(_err) => { Err(_err) => {
if !quiet_flag { if !quiet_flag {
......
open Core_kernel
open Bap.Std
open Symbol_utils
open Log_utils
let name = "CWE190"
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
| Bil.TIMES | Bil.LSHIFT -> binops + 1
| _ -> binops
end)
let contains_multiplication d =
let rhs = Def.rhs d in
let binops = collect_muliplications rhs in
binops > 0
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
let address = (Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map) in
let description = sprintf "(Integer Overflow or Wraparound) Potential overflow due to multiplication at %s" address in
let tids = [Address_translation.tid_to_string @@ Term.tid blk] in
let symbols = [(Symbol_utils.get_symbol_name_from_jmp jmp symbols)] in
let cwe_warning = cwe_warning_factory name version description ~addresses:[address] ~tids ~symbols in
collect_cwe_warning cwe_warning)
let check_cwe prog proj tid_map symbol_names _ =
match symbol_names with
| hd::[] ->
let symbols = Symbol_utils.build_symbols hd prog in
let calls = get_calls prog in
let relevant_calls = filter_calls_to_symbols calls symbols in
check_calls relevant_calls prog proj tid_map symbols check_multiplication_before_symbol
| _ -> failwith "[CWE190] symbol_names not as expected"
(** This module implements a check for CWE-190: Integer overflow or wraparound.
An integer overflow can lead to undefined behaviour and is especially dangerous
in conjunction with memory management functions.
See {: https://cwe.mitre.org/data/definitions/190.html} for a detailed description.
{1 How the check works}
For each call to a function from the CWE190 symbol list we check whether the
basic block directly before the call contains a multiplication instruction.
If one is found, the call gets flagged as a CWE hit, as there is no overflow
check corresponding to the multiplication befor the call. The default CWE190
symbol list contains the memory allocation functions {i malloc}, {i xmalloc},
{i calloc} and {i realloc}. The list is configurable in config.json.
{1 False Positives}
- There is no check whether the result of the multiplication is actually used
as input to the function call. However, this does not seem to generate a lot
of false positives in practice.
- There is no value set analysis in place to determine whether an overflow is
possible or not at the specific instruction.
{1 False Negatives}
- All integer overflows not in a basic block right before a call to a function
from the CWE190 symbol list.
- All integer overflows caused by addition or subtraction.
*)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
open Core_kernel
open Bap.Std
open Log_utils
(* TODO: IVG via gitter:
I see, so you need the CU information, and yes BAP doesn't provide this.
The right way to do this thing (a little bit complicated, but it
will preserve the abstractions), would be the following:
- Define the abstract interface for the CU providers (you can use Source module
or just define it manually with the interface you like)
- Write plugins that will provide implementations
(i.e., using readelf, objdump, IDA, LLVM, or whatever). The implementation shall
subscribe to Project.Info.file information stream and generate CU information every time a new file is open.
Of course, for the prototype your approach will work,
but in general case it is better to use the approach described above. *)
let name = "CWE215"
let version = "0.1"
(* TODO: check if program contains strings like "DEBUG"*)
let check_cwe _ project _ _ _ =
match Project.get project filename with
| Some fname -> begin
let cmd = Format.sprintf "objdump --dwarf=decodedline %s | grep CU" fname in
try
let in_chan = Caml_unix.open_process_in cmd in
In_channel.input_lines in_chan |> List.iter ~f:(fun l ->
let description = sprintf "(Information Exposure Through Debug Information) %s" l in
let cwe_warning = cwe_warning_factory name version description ~symbols:[l] in
collect_cwe_warning cwe_warning)
with
Caml_unix.Unix_error (e,fm,argm) ->
Log_utils.error (sprintf "[%s] {%s} %s %s %s" name version (Caml_unix.error_message e) fm argm)
end
| _ -> failwith "[CWE215] symbol_names not as expected"
(** This module implements a check for CWE-215: Information Exposure Through Debug Information.
Sensitive debugging information can be leveraged to get a better understanding
of a binary in less time.
See {: https://cwe.mitre.org/data/definitions/215.html} for a detailed description.
{1 How the check works}
The binary is checked for debug strings using readelf.
{1 False Positives}
None known.
{1 False Negatives}
- There may be other debug information not found by readelf.
*)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
open Core_kernel
open Bap.Std
open Symbol_utils
open Log_utils
include Self()
let name = "CWE243"
let version = "0.1"
let get_call_dests_of_blk blk_tid sub =
match Term.find blk_t sub blk_tid with
| Some blk -> begin
Term.enum jmp_t blk
|> Seq.filter_map ~f:(fun jmp -> match Jmp.kind jmp with
| Goto _ | Ret _ | Int (_,_) -> None
| Call destination -> begin
match Call.target destination with
| Direct addr -> Some addr
| _ -> None
end)
end |> Seq.to_list
| _ -> []
let get_call_dests_of_sub sub =
let entry_blk =(Term.first blk_t sub) in
match entry_blk with
| Some blk -> begin
let blks = Graphlib.Std.Graphlib.postorder_traverse (module Graphs.Tid) (Sub.to_graph sub) ~start:(Term.tid blk) ~rev:true in
List.concat_map (Seq.to_list blks) ~f:(fun blk -> get_call_dests_of_blk blk sub)
end
| _ -> []
let rec check dests (symbols : symbol list) =
match dests with
| [] -> (List.length symbols) = 0
| hd :: tl ->
begin
match symbols with
| [] -> true
| first_symbol :: symbol_rest -> begin
match first_symbol.address with
| Some address -> if Tid.(=) address hd then check tl symbol_rest else check tl symbols
| _ -> false
end
end
let check_route sub symbols =
let call_dests = get_call_dests_of_sub sub in
let res = check call_dests symbols in
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 symbols = build_symbols path prog in
if List.length symbols = List.length path then
begin
if List.length symbols = List.length path then
check_route sub symbols
else
false
end
else
false
(** Checks a subfunction for CWE-243. Only functions that actually call "chroot" are considered.
It checks each of the configured VALID pathes found in config.json, e.g.
"chroot_pathes": [["chroot", "chdir"], ["chdir", "chroot", "setresuid"], ["chdir", "chroot", "seteuid"],
["chdir", "chroot", "setreuid"], ["chdir", "chroot", "setuid"]].
If all of them fail then we supose that the program handles chroot on
*)
let check_subfunction prog tid_map sub pathes =
if sub_calls_symbol prog sub "chroot" then
begin
let path_checks: Bool.t List.t = List.map pathes ~f:(fun path -> check_path prog tid_map sub path) in
if not (List.exists path_checks ~f:(fun x -> x)) then
let address = (Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map) in
let tid = Address_translation.tid_to_string @@ Term.tid sub in
let symbol = Term.name sub in
let description = sprintf
"(The program utilizes chroot without dropping privileges and/or changing the directory) at %s (%s)"
address
symbol in
let cwe_warning = cwe_warning_factory name version description ~addresses:[address] ~tids:[tid] ~symbols:[symbol] in
collect_cwe_warning cwe_warning
end
let check_cwe prog _proj tid_map pathes _ =
let chroot_symbol = find_symbol prog "chroot" in
match chroot_symbol with
| Some _ ->
Seq.iter (Term.enum sub_t prog) ~f:(fun sub -> check_subfunction prog tid_map sub pathes)
| _ -> ()
(** This module implements a check for CWE-243: Creation of chroot Jail Without Changing Working Directory.
Creating a chroot Jail without changing the working directory afterwards does
not prevent access to files outside of the jail.
See {: https://cwe.mitre.org/data/definitions/243.html} for detailed a description.
{1 How the check works}
According to {: http://www.unixwiz.net/techtips/chroot-practices.html}, there are
several ways to achieve the safe creation of a chroot jail, e.g. chdir -> chroot -> setuid.
They are configurable in config.json. We check whether each function that calls
chroot is using one of these safe call sequences to do so. If not, a warning is emitted.
{1 False Positives}
None known.
{1 False Negatives}
None known.
*)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
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) -> String.(=) 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: Sub.t) ~tid_map : Tid.t List.t =
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 -> Tid.(=) 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 _: Tid.t List.t = List.fold entry_points ~init:[] ~f:(fun already_checked_functions sub -> find_uncaught_exceptions ~tid_map:tid_map sub already_checked_functions program) in
()
(** This module implements a check for CWE-248: Uncaught Exception.
An uncaught exception may lead to a crash.
See {: https://cwe.mitre.org/data/definitions/248.html} for a detailed description.
{1 How the check works}
The tool searches for exception throws that are reachable in the callgraph without
touching a function that contains a catch block. We do not check whether a catch block
can actually catch the thrown exceptions, thus we generate some false negatives.
{1 False Positives}
- There is no check whether a specific exception throw can be triggered or not
{1 False Negatives}
- An exception that gets catched through one execution path but would not get
catched through a different execution path will not get flagged.
- It is not checked whether the catch block can actually catch a thrown exception
or not. A catch block may only be able to catch exceptions of a specific type.
*)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
open Core_kernel
open Symbol_utils
open Log_utils
let name = "CWE332"
let version = "0.1"
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 _ -> begin
let description = "(Insufficient Entropy in PRNG) program uses rand without calling srand before" in
let cwe_warning = cwe_warning_factory name version description in
collect_cwe_warning cwe_warning
end
end
| Some (_srand_tid, _rand_tid) -> ()
(** This module implements a check for CWE332: Insufficient Entropy in PRNG.
This can happen, for instance, if the PRNG is not seeded. A classical example
would be calling rand without srand. This could lead to predictable random
numbers and could, for example, weaken crypto functionality.
See {: https://cwe.mitre.org/data/definitions/332.html} for a detailed description.
{1 How the check works}
We check whether the program calls rand but not srand.
{1 False Positives}
None known
{1 False Negatives}
- It is not checked whether srand gets called before rand
*)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> 'a -> 'b -> string list list -> 'c -> unit
open Core_kernel
open Bap.Std
open Log_utils
let name = "CWE367"
let version = "0.1"
let get_calls_to_symbol symbol_name callsites program =
match Symbol_utils.find_symbol program symbol_name with
| Some symbol ->
begin
Seq.filter callsites ~f:(fun callsite -> match Jmp.kind callsite with
| Goto _ | Ret _ | Int (_,_) -> false
| Call destination -> match Call.target destination with
| Direct addr -> Tid.(=) addr symbol
| _ -> false)
end
| None -> Seq.empty
let get_blk_tid_of_tid sub tid =
let blk = Seq.find (Term.enum blk_t sub) ~f:(
fun b ->
match Term.last jmp_t b with
| Some last_term -> Tid.(=) tid (Term.tid last_term)
| None -> false) in
match blk with
| Some b -> Term.tid b
| _ -> assert(false)
let is_reachable sub source sink =
let cfg = Sub.to_graph sub in
let source_tid = Term.tid source in
let sink_tid = Term.tid sink in
let source_blk = get_blk_tid_of_tid sub source_tid in
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_pair =
match source_sink_pair with
| [source;sink;] -> begin
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
let source_calls = get_calls_to_symbol source calls program in
let sink_calls = get_calls_to_symbol sink calls program in
Seq.iter source_calls ~f:(fun source_call ->
Seq.iter sink_calls ~f:(fun sink_call ->
if is_reachable sub source_call sink_call then
let address = (Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map) in
let tid = Address_translation.tid_to_string @@ Term.tid sub in
let symbol = (Term.name sub) in
let other = [["source"; source]; ["sink"; sink]] in
let description = sprintf
"(Time-of-check Time-of-use Race Condition) %s is reachable from %s at %s (%s). This could lead to a TOCTOU."
sink
source
address
symbol in
let cwe_warning = cwe_warning_factory
name
version
description
~other:other
~addresses:[address]
~tids:[tid]
~symbols:[symbol] in
collect_cwe_warning cwe_warning
else
()))
end
else
()
end
| _ -> ()
let check_cwe program _proj tid_map symbol_pairs _ =
List.iter symbol_pairs ~f:(fun current_pair ->
let symbols = Symbol_utils.build_symbols current_pair in
Seq.iter (Term.enum sub_t program) ~f:(fun s -> handle_sub s program tid_map symbols current_pair))
(** This module implements a check for CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition.
Time-of-check Time-of-use race conditions happen when a property of a resource
(e.g. access rights of a file) get checked before the resource is accessed, leaving
a short time window for an attacker to change the entity and thus invalidating
the check before the access.
See {: https://cwe.mitre.org/data/definitions/367.html} for a detailed description.
{1 How the check works}
For pairs of (check-call, use-call), configurable in config.json, we check whether
a function may call the check-call before the use-call.
{1 False Positives}
- The check-call and the use-call may access different, unrelated resources
(e. g. different files).
{1 False Negatives}
- If the check-call and the use-call happen in different functions it will not
be found by the check.
*)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
open Core_kernel
open Bap.Std
open Log_utils
let name = "CWE426"
let version = "0.1"
let calls_privilege_changing_sub sub program symbols =
List.exists symbols ~f:(fun s -> Symbol_utils.sub_calls_symbol program sub s)
let handle_sub sub program tid_map symbols =
if calls_privilege_changing_sub sub program symbols then
begin
if Symbol_utils.sub_calls_symbol program sub "system" then
let symbol = Term.name sub in
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map in
let tid = Address_translation.tid_to_string @@ Term.tid sub in
let description = sprintf "(Untrusted Search Path) sub %s at %s may be vulnerable to PATH manipulation."
symbol
address in
let cwe_warning = cwe_warning_factory name version ~addresses:[address] ~tids:[tid] ~symbols:[symbol] description in
collect_cwe_warning cwe_warning
else
()
end
else ()
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)
| _ -> failwith "[CWE426] symbol_names not as expected"
(** This module implements a check for CWE-426: Untrusted Search Path.
Basically, the program searches for critical resources on an untrusted search
path that can be adjusted by an adversary. For example, see Nebula Level 1
({: https://exploit-exercises.com/nebula/level01/}).
According to the manual page of system() the following problems can arise:
"Do not use system() from a program with set-user-ID or set-group-ID privileges,
because strange values for some environment variables might be used to subvert
system integrity. Use the exec(3) family of functions instead, but not execlp(3)
or execvp(3). system() will not, in fact, work properly from programs with set-user-ID
or set-group-ID privileges on systems on which /bin/sh is bash version 2, since bash 2
drops privileges on startup. (Debian uses a modified bash which does not do this when invoked as sh.)"
See {: https://cwe.mitre.org/data/definitions/426.html} for a detailed description.
{1 How the check works}
We check whether a function that calls a privilege-changing function (configurable
in config.json) also calls system().
{1 False Positives}
- If the call to system() happens before the privilege-changing function, the call
may not be used for privilege escalation
{1 False Negatives}
- If the calls to the privilege-changing function and system() happen in different
functions, the calls will not be flagged as a CWE-hit.
- This check only finds potential privilege escalation bugs, but other types of
bugs can also be triggered by untrusted search paths.
*)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
open Core_kernel
open Bap.Std
open Log_utils
let name = "CWE457"
let version = "0.1"
let get_defs sub_ssa =
Term.enum blk_t sub_ssa
|> Seq.concat_map ~f:(fun blk -> Term.enum def_t blk)
let collect_stores_of_exp = Exp.fold ~init:0 (object
inherit [int] Exp.visitor
method! enter_store ~mem:_ ~addr:_ ~exp:_ _ _ stores =
stores + 1
end)
let exp_has_store e =
collect_stores_of_exp e > 0
let ints_of_exp = Exp.fold ~init:Word.Set.empty (object
inherit [Word.Set.t] Exp.visitor
method! enter_int i ints = Set.add ints i
end)
let vars_of_exp = Exp.fold ~init:Var.Set.empty (object
inherit [Var.Set.t] Exp.visitor
method! enter_var var vars = Set.add vars var
end)
let vars_contain_mem vars =
let mems = Set.filter vars ~f:(fun var -> match Var.to_string var with
| "mem" -> true
| _ -> false) in
Set.length mems > 0
(*FIXME: this is architecture dependent and ugly*)
let get_min_fp_offset arch =
match arch with
| `x86 | `x86_64 -> 0x10000
| _ -> 0x0
(*FIXME: this is architecture dependent and ugly*)
let get_fp_of_arch arch =
match arch with
| `x86 -> "EBP"
| `x86_64 -> "RBP"
| `armv4 | `armv5 | `armv6 | `armv7 | `armv4eb | `armv5eb | `armv6eb | `armv7eb -> "R11"
| `mips | `mips64 | `mips64el | `mipsel -> "FP"
| `ppc | `ppc64 | `ppc64le -> "R31"
| _ -> failwith "Unknown architecture."
let vars_contain_fp vars fp_pointer =
let regs = Set.filter vars ~f:(fun var -> String.(=) (Var.to_string var) fp_pointer) in
Set.length regs > 0
let is_interesting_load_store def fp_pointer =
let vars = vars_of_exp (Def.rhs def) in
let contains_fp = vars_contain_fp vars fp_pointer in
let contains_mem = vars_contain_mem vars in
contains_mem && contains_fp
(*TODO: implement real filtering*)
let filter_mem_address i min_fp_offset = Set.filter i ~f:(fun elem -> Word.(<) (Word.of_int ~width:32 min_fp_offset) elem)
let log_cwe_warning sub i d tid_map =
let word = Word.to_string i in
let other =[["word"; word]] in
let symbol = Sub.name sub in
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid d) tid_map in
let tid = Address_translation.tid_to_string @@ Term.tid d in
let description = sprintf
"(Use of Uninitialized Variable) Found potentially unitialized stack variable (FP + %s) in function %s at %s"
word
symbol
address in
let cwe_warning = cwe_warning_factory name version ~other:other ~addresses:[address] ~tids:[tid] ~symbols:[symbol] description in
collect_cwe_warning cwe_warning
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
let defs = get_defs sub in
Seq.iter defs ~f:(fun d ->
if is_interesting_load_store d fp_pointer then
let rhs = Def.rhs d in
let ints = ints_of_exp rhs in
begin
if exp_has_store rhs then
begin
let filter_mem_addresses = filter_mem_address ints min_fp_offset in
Set.iter filter_mem_addresses ~f:(fun addr -> stores := Array.append !stores [|addr|])
end
else
begin
let filter_mem_addresses = filter_mem_address ints min_fp_offset in
Set.iter filter_mem_addresses ~f:(fun i -> if not (Array.exists !stores ~f:(fun elem -> Word.(=) elem i)) then
log_cwe_warning sub i d tid_map)
end
end)
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)
(** This module implements a check for CWE-457: Use of Uninitialized Variable.
Accessing variables on the stack or heap before their initialization can lead
to unintended or undefined behaviour, which could be exploited by an attacker.
See {: https://cwe.mitre.org/data/definitions/457.html} for a detailed description.
{1 How the check works}
The check uses the frame pointer to look for loads to addresses which do not
have an associated store instruction.
{1 False Positives}
- The check is still very basic and can be easily get confused by loads/stores
through different registers than the frame pointer.
- Modern compilers often use only the stack pointer for stack access, freeing
up the frame pointer as a general purpose register. This is not recognized by
the check.
{1 False Negatives}
- Heap accesses are not examined by the check.
- Memory accesses through different registers than the frame pointer are not
examined by the check.
*)
val name: string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
open Core_kernel
open Bap.Std
open Symbol_utils
open Log_utils
let name = "CWE467"
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 =
Seq.iter (Term.enum def_t blk) ~f:(fun d -> match Exp.eval @@ Def.rhs d with
| Imm w ->
begin
try
if get_pointer_size (Project.arch proj) = (Word.to_int_exn w) then
begin
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map in
let tid = Address_translation.tid_to_string @@ Term.tid blk in
let symbol = Symbol_utils.get_symbol_name_from_jmp jmp symbols in
let description = sprintf
"(Use of sizeof on a Pointer Type) sizeof on pointer at %s (%s)."
address
symbol in
let cwe_warning = cwe_warning_factory name version ~addresses:[address] ~tids:[tid] ~symbols:[symbol] description in
collect_cwe_warning cwe_warning
end
with _ -> Log_utils.error "Caught exception in module [CWE467]."
end
| _ -> ())
let check_cwe prog proj tid_map symbol_names _ =
match symbol_names with
| hd::[] ->
let symbols = Symbol_utils.build_symbols hd prog in
let calls = get_calls prog in
let relevant_calls = filter_calls_to_symbols calls symbols in
check_calls relevant_calls prog proj tid_map symbols check_input_is_pointer_size
| _ -> failwith "[CWE467] symbol_names not as expected"
(** This module implements a check for CWE-467: Use of sizeof() on a Pointer Type.
Functions like malloc and memmove take a size parameter of some data size as
input. If accidentially the size of a pointer to the data instead of the size of
the data itself gets passed to the function, this can have severe consequences.
See {: https://cwe.mitre.org/data/definitions/467.html} for a detailed description.
{1 How the check works}
The check is quite basic: We check whether in the basic block before a call
to a function listed in the symbols for CWE467 (configurable in in config.json)
an immediate value that equals the size of a pointer (e.g. 4 bytes on x86) is
referenced.
{1 False Positives}
- It is not checked whether the immediate value is actually an input to the call
or not. However, this does not seem to produce false positives in practice.
- The size value might be correct and not a bug.
{1 False Negatives}
- If the incorrect size value is generated before the basic block that contains
the call, the check will not be able to find it.
*)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
open Core_kernel
open Bap.Std
open Log_utils
let name = "CWE476"
let version = "0.3"
(* TODO: This check is based on Mem_region, which does not support partial access yet.
Thus partially written tainted values may be marked as error and thus the taint is falsely forgotten. *)
(** Each taint is denoted by the Tid of the basic block where it originated from.
Each value can be tainted by different sources at the same time. *)
module Taint = Tid.Set
(** The state contains taint information for all registers and stack variables. *)
module State = struct
type t = {
register: Taint.t Var.Map.t;
stack: Taint.t Mem_region.t;
} [@@deriving bin_io, compare, sexp]
(** Get an empty state without tainted values. *)
let empty : t =
{ register = Var.Map.empty;
stack = Mem_region.empty () }
(** equality function for states *)
let equal (state1: t) (state2: t) : Bool.t =
let reg_equal = Var.Map.equal Taint.equal state1.register state2.register in
let stack_equal = Mem_region.equal state1.stack state2.stack ~data_equal:Taint.equal in
reg_equal && stack_equal
(** set the taint of a register *)
let set_register (state: t) (register: Var.t) (taint: Taint.t) : t =
{ state with register = Var.Map.set state.register ~key:register ~data: taint}
(** return the taint of a register *)
let find_register (state: t) (register: Var.t) : Taint.t Option.t =
Var.Map.find state.register register
(** only remove the register var from the list of tainted registers *)
let remove_register (state: t) (register: Var.t) : t =
{ state with register = Var.Map.remove state.register register }
(** set the taint of a stack element *)
let set_stack (state: t) ~(pos: Bitvector.t) ~(size: Bitvector.t) (taint: Taint.t) : t =
{ state with stack = Mem_region.add state.stack taint ~pos ~size }
(** get the taint from the stack
TODO: Mem_region is currently unsound for only partially loaded values, which might lead to errors here. *)
let find_stack (state: t) ~(pos: Bitvector.t) : Taint.t Option.t =
match Mem_region.get state.stack pos with
| Some(Ok(taint, _size)) -> Some(taint)
| _ -> None
(** remove a stack element *)
let remove_stack (state: t) ~(pos: Bitvector.t) ~(size: Bitvector.t) : t =
{ state with stack = Mem_region.remove state.stack ~pos ~size}
(** remove all Tids contained in the taint from all taints in the state *)
let remove_taint (state: t) (taint_to_remove: Taint.t) : t =
let register_list = Var.Map.to_alist state.register in
let cleaned_register = List.fold register_list ~init:Var.Map.empty ~f:(fun cleaned_register (register, taint) ->
let cleaned_taint = Tid.Set.diff taint taint_to_remove in
if Tid.Set.is_empty cleaned_taint then
cleaned_register
else
Var.Map.set cleaned_register ~key:register ~data:cleaned_taint
) in
let cleaned_stack = Mem_region.map_data state.stack ~f:(fun taint ->
Tid.Set.diff taint taint_to_remove
) in
{ register = cleaned_register;
stack = cleaned_stack; }
(** The union of two states is the union of all taints *)
let union (state1: t) (state2: t) : t =
let register = Var.Map.merge state1.register state2.register ~f:(fun ~key:_ values->
match values with
| `Both (taint1, taint2) -> Some (Taint.union taint1 taint2)
| `Left taint | `Right taint -> Some taint
) in
let stack = Mem_region.merge state1.stack state2.stack ~data_merge:(fun taint1 taint2 ->
Some( Ok(Taint.union taint1 taint2) )
) in
{ register = register;
stack = stack; }
(** remove virtual register from the state (useful at the end of a block) *)
let remove_virtual_register (state: t) : t =
{ state with register = Var.Map.filter_keys state.register ~f:(fun var -> Var.is_physical var) }
end
(** The stack info contains all necessary information to access stack variables. *)
module StackInfo = struct
type t = {
type_info: Type_inference.TypeInfo.t;
sub_tid: Tid.t;
project: Project.t;
strict_mem_policy: Bool.t;
}
(** If the expression denotes an address on the stack, return the address. *)
let get_address (stack_info: t) (expression: Exp.t) : Bitvector.t Option.t =
Type_inference.TypeInfo.compute_stack_offset stack_info.type_info expression ~sub_tid:stack_info.sub_tid ~project:stack_info.project
(** Assemble a StackInfo.t object. *)
let assemble (pointer_info_map: Type_inference.TypeInfo.t Tid.Map.t) (term_tid: Tid.t) ~(sub_tid: Tid.t) ~(project: Project.t) ~(strict_mem_policy: Bool.t) : t =
{ type_info = Tid.Map.find_exn pointer_info_map term_tid;
sub_tid = sub_tid;
project = project;
strict_mem_policy = strict_mem_policy; }
(**/**)
(* assemble a mock StackInfo for unit tests *)
let assemble_mock_info (mock_tid: Tid.t) (project: Project.t) : t =
{ type_info = { Type_inference.TypeInfo.stack = Mem_region.empty (); Type_inference.TypeInfo.reg = Var.Map.empty};
sub_tid = mock_tid;
project = project;
strict_mem_policy = false; }
(**/**)
end
(** append taint to the list of already found cwe_hits *)
let append_to_hits (cwe_hits:Taint.t ref) (taint: Taint.t) : unit =
cwe_hits := Taint.union !cwe_hits taint
(** Check whether an expression contains a tainted value.
Memory accesses through tainted values are added to cwe_hits, but the Tids are not removed from the state. *)
let rec contains_taint (exp: Exp.t) (state: State.t) ~(cwe_hits: Taint.t ref) ~(stack: StackInfo.t) : Taint.t =
match exp with
| Bil.Load(_mem, addr, _endian, _size)->
begin
let access_taint = contains_taint addr state ~cwe_hits ~stack in
let () = if Bool.(=) (Taint.is_empty access_taint) false then append_to_hits cwe_hits access_taint in
match StackInfo.get_address stack addr with
| Some(stack_offset) -> Option.value (State.find_stack state ~pos:stack_offset) ~default:Taint.empty
| None -> Taint.empty
end
| Bil.Store(_mem, addr, val_expression, _,_) ->
begin
let access_taint = contains_taint addr state ~cwe_hits ~stack in
let value_taint = contains_taint val_expression state ~cwe_hits ~stack in
let () = if Bool.(=) (Taint.is_empty access_taint) false then append_to_hits cwe_hits access_taint in
match StackInfo.get_address stack addr with
| Some(_) -> Taint.empty
| None ->
let () = if stack.strict_mem_policy && (Bool.(=) (Taint.is_empty value_taint) false) then append_to_hits cwe_hits value_taint in
Taint.empty
end
| Bil.BinOp(Bil.XOR, Bil.Var(var1), Bil.Var(var2)) when Var.(=) var1 var2 -> Taint.empty (* standard assembly shortcut for setting a register to NULL *)
| Bil.BinOp(_, exp1, exp2) -> Taint.union (contains_taint exp1 state ~cwe_hits ~stack) (contains_taint exp2 state ~cwe_hits ~stack)
| Bil.UnOp(_, exp) -> contains_taint exp state ~cwe_hits ~stack
| Bil.Var(var) -> Option.value (State.find_register state var) ~default:Taint.empty
| Bil.Int(_) -> Taint.empty
| Bil.Cast(_, _, exp) -> contains_taint exp state ~cwe_hits ~stack
| Bil.Let(var, exp1, exp2) ->
Taint.union_list (
(contains_taint exp1 state ~cwe_hits ~stack)
:: (contains_taint exp2 state ~cwe_hits ~stack)
:: (contains_taint (Bil.var var) state ~cwe_hits ~stack) :: [])
| Bil.Unknown(_) -> Taint.empty
| Bil.Ite(if_, then_, else_) ->
Taint.union_list (
(contains_taint if_ state ~cwe_hits ~stack)
:: (contains_taint then_ state ~cwe_hits ~stack)
:: (contains_taint else_ state ~cwe_hits ~stack) :: [])
| Bil.Extract(_,_, exp) -> contains_taint exp state ~cwe_hits ~stack
| Bil.Concat(exp1, exp2) -> Taint.union (contains_taint exp1 state ~cwe_hits ~stack) (contains_taint exp2 state ~cwe_hits ~stack)
(** Parse an expression for memory accesses through tainted values and taint contained in the value itself.
All memory accesses except for loading/storing values from/to the stack get flagged as cwe_hits.
Returns the taint of the expression and the new state, with the Tids of new cwe_hits removed from both. *)
let parse_taint_of_exp (exp: Exp.t) (state: State.t) ~(cwe_hits: Taint.t ref) ~(stack: StackInfo.t) : Taint.t * State.t =
let hits_to_clean : Taint.t ref = ref Taint.empty in
let unchecked_taint = contains_taint exp state ~cwe_hits:hits_to_clean ~stack in
let () = append_to_hits cwe_hits !hits_to_clean in
let state = State.remove_taint state !hits_to_clean in
let unchecked_taint = Taint.diff unchecked_taint !hits_to_clean in
(unchecked_taint, state)
(** If an formerly unchecked return value was checked then remove all registers pointing
to the source of this return value from state. *)
let checks_value (exp: Exp.t) (state: State.t) ~(cwe_hits: Taint.t ref) ~(stack: StackInfo.t) : State.t =
match exp with
| Bil.Ite(if_, _then_, _else_) -> begin
let (taint_to_remove, state) = parse_taint_of_exp if_ state ~cwe_hits ~stack in
if Bool.(=) (Taint.is_empty taint_to_remove) false then
State.remove_taint state taint_to_remove
else
state
end
| _ -> state
(** flags any access (not just memory access) from an unchecked source as a cwe_hit. *)
let flag_any_access (exp: Exp.t) (state: State.t) ~(cwe_hits: Taint.t ref) ~(stack: StackInfo.t) : State.t=
let (taint_to_flag, state) = parse_taint_of_exp exp state ~cwe_hits ~stack in
let () = append_to_hits cwe_hits taint_to_flag in
State.remove_taint state taint_to_flag
(** flag all unchecked registers and stack variables that may be used as return values.
That means stack variables above the return pointer get flagged,
but variables below the return pointer are treated as local variables and do not get flagged.
Return empty state *)
let flag_unchecked_return_values (state: State.t) ~(cwe_hits: Taint.t ref) ~(project: Project.t) : State.t =
let taint_to_flag = Var.Map.fold state.register ~init:Taint.empty ~f:(fun ~key ~data taint_accum ->
if Cconv.is_return_register key project then
Taint.union taint_accum data
else
taint_accum
) in
let taint_to_flag = List.fold (Mem_region.list_data_pos state.stack) ~init:taint_to_flag ~f:(fun taint_accum (position_unsigned, taint_value) ->
let position = Bitvector.to_int_exn (Bitvector.signed position_unsigned) in
if position >= 0 then
Taint.union taint_accum taint_value
else
taint_accum
) in
let () = append_to_hits cwe_hits taint_to_flag in
State.empty
(** flag all register taints as cwe_hits, but not taints that are only contained in stack variables *)
let flag_register_taints (state: State.t) ~(cwe_hits: Taint.t ref) : State.t =
let taint_to_flag = List.fold (Var.Map.data state.register) ~init: Taint.empty ~f:(fun taint_accum register_taint ->
Taint.union taint_accum register_taint
) in
let () = append_to_hits cwe_hits taint_to_flag in
State.remove_taint state taint_to_flag
(** Flag all possible parameter register as cwe_hits. These registers may be input values to an extern function call.
This can lead to false positives if a function does not use all of these registers for argument passing. *)
let flag_parameter_register (state: State.t) ~(cwe_hits: Taint.t ref) ~(project: Project.t) : State.t =
let taint_to_flag = Var.Map.fold state.register ~init:Taint.empty ~f:(fun ~key ~data taint_accum ->
if Cconv.is_parameter_register key project then
Taint.union taint_accum data
else
taint_accum
) in
let () = append_to_hits cwe_hits taint_to_flag in
State.remove_taint state taint_to_flag
(** Remove the taint of non-callee-saved register (without flagging them).
For taints in parameter register we assume that they are checked by the callee, thus we also remove the corresponding Tids from the state. *)
let untaint_non_callee_saved_register (state: State.t) ~(project: Project.t) : State.t =
let taint_to_remove = Var.Map.fold state.register ~init:Taint.empty ~f:(fun ~key ~data taint_accum ->
if Cconv.is_callee_saved key project then
taint_accum
else
Taint.union taint_accum data
) in
let state = State.remove_taint state taint_to_remove in
Var.Map.fold state.register ~init:state ~f:(fun ~key ~data:_ state ->
if Cconv.is_callee_saved key project then
state
else
State.remove_register state key
)
(** If the expression is a store onto a stack variable, write the corresponding taint to the stack. *)
let update_stack_on_stores (exp: Exp.t) (state: State.t) ~(stack: StackInfo.t) : State.t =
let pointer_size = Symbol_utils.arch_pointer_size_in_bytes stack.project in
match exp with
| Bil.Store(_mem, address_exp, value, _endian, size) -> begin
let value_taint = contains_taint value state ~cwe_hits:(ref Taint.empty) ~stack in
match StackInfo.get_address stack address_exp with
| Some(address) ->
if Taint.is_empty value_taint then
State.remove_stack state ~pos:address ~size:(Bitvector.of_int (Size.in_bytes size) ~width:pointer_size)
else
State.set_stack state value_taint ~pos:address ~size:(Bitvector.of_int (Size.in_bytes size) ~width:pointer_size)
| None -> state
end
| _ -> state
(** Updates the state depending on the def. If memory is accessed using an unchecked return value,
then the access is added to the list of cwe_hits. *)
let update_state_def (def: Def.t) (state: State.t) ~(cwe_hits: Taint.t ref) ~(stack: StackInfo.t) : State.t =
let (lhs, rhs) = (Def.lhs def, Def.rhs def) in
let state = checks_value rhs state ~cwe_hits ~stack in
let (rhs_taint, state) = parse_taint_of_exp rhs state ~cwe_hits ~stack in
let state =
if Taint.is_empty rhs_taint then
State.remove_register state lhs
else
State.set_register state lhs rhs_taint in
update_stack_on_stores rhs state ~stack
(** Taint the return registers of a function as unchecked return values. *)
let taint_return_registers (func_tid: Tid.t) (state: State.t) ~(project: Project.t) ~(block: Blk.t) : State.t =
let func = Term.find_exn sub_t (Project.program project) func_tid in
let arguments = Term.enum arg_t func in
(* Every return register is tainted as unchecked return value. *)
Seq.fold arguments ~init:state ~f:(fun state arg ->
match Bap.Std.Arg.intent arg with
| None | Some(In) -> state
| Some(Out) | Some(Both) ->
let variable = match Bap.Std.Arg.rhs arg with
| Bil.Var(var) -> var
| _ -> failwith "[CWE476] Return register wasn't a register." in
State.set_register state variable (Taint.add Taint.empty (Term.tid block))
)
(** Updates the state depending on the jump. On a jump to a function from the function list
taint all return registers as unchecked return values. *)
let update_state_jmp
(jmp: Jmp.t)
(state: State.t)
~(cwe_hits: Taint.t ref)
~(malloc_like_functions: String.t List.t)
~(extern_functions: String.Set.t)
~(stack: StackInfo.t)
~(block: Blk.t)
~(strict_call_policy: Bool.t) : State.t =
(* first check the guard condition for unchecked access. Any normal access clears the access from being unchecked *)
let condition_exp = Jmp.cond jmp in
let state = begin
let (condition_taint, state) = parse_taint_of_exp condition_exp state ~cwe_hits ~stack in
if Taint.is_empty condition_taint then
state
else
State.remove_taint state condition_taint
end in
match Jmp.kind jmp with
| Goto(Indirect(exp)) -> flag_any_access exp state ~cwe_hits ~stack
| Goto(Direct(_)) -> state
| Ret(_) -> if strict_call_policy then
flag_unchecked_return_values state ~cwe_hits ~project:stack.project
else
state
| Int(_, _) -> flag_register_taints state ~cwe_hits
| Call(call) ->
(* flag tainted values in the call and return expressions of indirect calls *)
let state = match Call.return call with
| Some(Indirect(exp)) -> flag_any_access exp state ~cwe_hits ~stack
| _ -> state in
let state = begin match Call.target call with
| Indirect(exp) -> flag_any_access exp state ~cwe_hits ~stack
| _ -> state end in
(* flag tainted values in the parameter registers (if strict_call_policy is set to true)*)
let state = match (Call.target call, strict_call_policy) with
| (Indirect(_), false)
| (Direct(_), false) -> state
| (Indirect(_), true) -> flag_parameter_register state ~cwe_hits ~project:stack.project (* TODO: indirect calls are handled as extern calls right now. Change that *)
| (Direct(tid), true) ->
let sub = Term.find_exn sub_t (Project.program stack.project) tid in
if Set.mem extern_functions (Sub.name sub) then
flag_parameter_register state ~cwe_hits ~project:stack.project
else (* flag all registers for intern calls, as these do not necessarily adhere to any calling convention *)
flag_register_taints state ~cwe_hits
in
(* remove the taint of non-callee-saved registers *)
let state = match Call.target call with
| Direct(tid) ->
let sub = Term.find_exn sub_t (Project.program stack.project) tid in
if Set.mem extern_functions (Sub.name sub) then
untaint_non_callee_saved_register state ~project:stack.project
else (* we untaint all registers for internal function calls, as these do not necessarily adhere to any calling convention *)
{ state with register = Var.Map.empty }
| Indirect(_) -> (* we treat all indirect calls as extern function calls, since we cannot handle indirect calls properly yet *)
untaint_non_callee_saved_register state ~project:stack.project
in
(* introduce new taint for the return values of malloc_like_functions *)
match Call.target call with
| Indirect(_) -> state
| Direct(tid) ->
if List.exists malloc_like_functions ~f:(fun elem -> String.(=) elem (Tid.name tid)) then
taint_return_registers tid state ~project:stack.project ~block
else
state
(** updates a block analysis.
The strict call policy decides the behaviour on call and return instructions:
strict: unchecked values in registers get flagged as cwe_hits
non-strict: unchecked values in registers get marked as checked. It is assumed that the callee checks these values. *)
let update_block_analysis
(block: Blk.t)
(state: State.t)
~(cwe_hits: Taint.t ref)
~(malloc_like_functions: String.t List.t)
~(extern_functions: String.Set.t)
~(sub_tid: Tid.t)
~(project: Project.t)
~(strict_call_policy: Bool.t)
~(strict_mem_policy: Bool.t) : State.t =
let elements = Blk.elts block in
let type_info_map = Type_inference.get_type_info_of_block ~project block ~sub_tid in
let state = Seq.fold elements ~init:state ~f:(fun state element ->
match element with
| `Def def ->
let stack = StackInfo.assemble type_info_map (Term.tid def) ~sub_tid ~project ~strict_mem_policy in
update_state_def def state ~cwe_hits ~stack
| `Phi _phi -> state (* We ignore phi terms for this analysis. *)
| `Jmp jmp ->
let stack = StackInfo.assemble type_info_map (Term.tid jmp) ~sub_tid ~project ~strict_mem_policy in
update_state_jmp jmp state ~cwe_hits ~malloc_like_functions ~extern_functions ~stack ~block ~strict_call_policy
) in
State.remove_virtual_register state (* virtual registers should not be accessed outside of the block where they are defined. *)
(** print a cwe_hit to the log *)
let print_hit (tid: Tid.t) ~(sub: Sub.t) ~(malloc_like_functions: String.t List.t) ~(tid_map: Word.t Tid.Map.t) : unit =
let block = Option.value_exn (Term.find blk_t sub tid) in
let jmps = Term.enum jmp_t block in
let _: Jmp.t = Seq.find_exn jmps ~f:(fun jmp ->
match Jmp.kind jmp with
| Call(call) -> begin
match Call.target call with
| Direct(call_tid) -> Option.is_some (List.find malloc_like_functions ~f:(fun fn_name ->
if String.(=) fn_name (Tid.name call_tid) then
begin
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid jmp) tid_map in
let tids = [Address_translation.tid_to_string (Term.tid jmp)] in
let description = sprintf
"(NULL Pointer Dereference) There is no check if the return value is NULL at %s (%s)."
address
fn_name in
let cwe_warning = cwe_warning_factory
name
version
~addresses:[address]
~tids:tids
~symbols:[fn_name]
description in
collect_cwe_warning cwe_warning;
true
end else
false
))
| _ -> false
end
| _ -> false
) in ()
let check_cwe (_prog: Program.t) (project: Project.t) (tid_map: Word.t Tid.Map.t) (symbol_names: String.t List.t List.t) (parameters: String.t List.t) =
let symbols = match symbol_names with
| hd :: _ -> hd
| _ -> failwith "[CWE476] symbol_names not as expected" in
let (strict_call_policy_string, strict_mem_policy_string, max_steps_string) = match parameters with
| par1 :: par2 :: par3 :: _ -> (par1, par2, par3)
| _ -> failwith "[CWE476] parameters not as expected" in
let strict_call_policy = match String.split strict_call_policy_string ~on:'=' with
| "strict_call_policy" :: policy :: [] -> bool_of_string policy
| _ -> failwith "[CWE476] parameters not as expected" in
let strict_mem_policy = match String.split strict_mem_policy_string ~on:'=' with
| "strict_memory_policy" :: policy :: [] -> bool_of_string policy
| _ -> failwith "[CWE476] parameters not as expected" in
let max_steps = match String.split max_steps_string ~on:'=' with
| "max_steps" :: num :: [] -> int_of_string num
| _ -> failwith "[CWE476] parameters not as expected" in
let malloc_like_functions = List.map symbols ~f:(fun symb -> "@" ^ symb) in
let extern_functions = Symbol_utils.parse_dyn_syms project in
(* run the pointer inference analysis. TODO: This should be done somewhere else as this analysis will be needed in more than one check! *)
let project = Type_inference.compute_pointer_register project in
let subfunctions = Term.enum sub_t (Project.program project) in
Seq.iter subfunctions ~f:(fun subfn ->
let cfg = Sub.to_cfg subfn in
let cwe_hits = ref Taint.empty in
let empty = Map.empty (module Graphs.Ir.Node) in
let init = Graphlib.Std.Solution.create empty State.empty in
let equal = State.equal in
let merge = State.union in
let f = (fun node state ->
let block = Graphs.Ir.Node.label node in
update_block_analysis block state ~cwe_hits ~malloc_like_functions ~extern_functions ~sub_tid:(Term.tid subfn) ~project ~strict_call_policy ~strict_mem_policy
) in
let _: ('n, 'd) Graphlib.Std.Solution.t = Graphlib.Std.Graphlib.fixpoint (module Graphs.Ir) cfg ~steps:max_steps ~rev:false ~init:init ~equal:equal ~merge:merge ~f:f in
Tid.Set.iter (!cwe_hits) ~f:(fun hit -> print_hit hit ~sub:subfn ~malloc_like_functions ~tid_map)
)
(**/**)
(* Functions made public for unit tests *)
module Private = struct
module StackInfo = StackInfo
module Taint = Taint
module State = State
let flag_unchecked_return_values = flag_unchecked_return_values
let flag_register_taints = flag_register_taints
let flag_parameter_register = flag_parameter_register
let untaint_non_callee_saved_register = untaint_non_callee_saved_register
end
(** This module implements a check for CWE-476: NULL Pointer Dereference.
Functions like malloc() may return NULL values instead of pointers to indicate
failed calls. If one tries to access memory through this return value without
checking it for being NULL first, this can crash the program.
See {: https://cwe.mitre.org/data/definitions/476.html} for a detailed description.
{1 How the check works}
Using dataflow analysis we search for an execution path where a memory access using the return value of
a symbol happens before the return value is checked through a conditional jump instruction.
Note that the check relies on Bap-generated stubs to identify return registers of the
checked functions. Therefore it only works for functions for which Bap generates
these stubs.
{2 Parameters configurable in config.json}
- strict_call_policy=\{true, false\}: Determines behaviour on call and return instructions.
If false, we assume that the callee, resp. the caller on a return instruction,
checks all unchecked values still contained in parameter registers. If true, every
unchecked value on a call or return instruction gets reported.
- strict_mem_policy=\{true, false\}:
Determines behaviour on writing an unchecked return value to a memory region other than the stack.
If true, these instances get reported.
Depending on the coding style, this can lead to a lot false positives if return values are
only checked after writing them to their target destination.
If false, these instances do not get reported, which in turn can lead to false negatives.
- max_steps=<num>: Max number of steps for the dataflow fixpoint algorithm.
{2 Symbols configurable in config.json}
The symbols are the functions whose return values are assumed to be potential
NULL pointers.
{1 False Positives}
- If strict_mem_policy is set to true, writing a return value to memory other than the stack
gets reported even if a NULL pointer check happens right afterwards.
- The check has no knowledge about the actual number of parameters that an extern function call takes.
This can lead to false positives if strict_call_policy is set to true.
{1 False Negatives}
- We do not check whether an access to a potential NULL pointer happens regardless
of a prior check.
- We do not check whether the conditional jump instruction checks specifically
for the return value being NULL or something else
- For functions with more than one return value we do not distinguish between
the return values.
- If strict_mem_policy is set to false, unchecked return values that are
saved somewhere other than the stack may be missed.
- The check has no knowledge about the actual number of parameters that an extern function call takes.
This can lead to false negatives, especially if function parameters are passed on the stack.
*)
open Bap.Std
open Core_kernel
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
(**/**)
(* Functions made public for unit tests *)
module Private : sig
module Taint : module type of Tid.Set
module State : sig
type t
val empty: t
val set_register: t -> Var.t -> Taint.t -> t
val find_register: t -> Var.t -> Taint.t Option.t
val union: t -> t -> t
end
module StackInfo : sig
type t
val assemble_mock_info: Tid.t -> Project.t -> t
end
val flag_unchecked_return_values: State.t -> cwe_hits: Taint.t ref -> project: Project.t -> State.t
val flag_register_taints: State.t -> cwe_hits: Taint.t ref -> State.t
val flag_parameter_register: State.t -> cwe_hits: Taint.t ref -> project: Project.t -> State.t
val untaint_non_callee_saved_register: State.t -> project: Project.t -> State.t
end
open Core_kernel
open Bap.Std
open Log_utils
let name = "CWE560"
let version = "0.1"
let upper_bound_of_correct_umask_arg_value = 100
let upper_bound_of_correct_chmod_arg_value = 1000
let collect_int_values = Exp.fold ~init:[] (object
inherit [word list] Exp.visitor
method! enter_int x addrs = x :: addrs
end)
let is_chmod_style_arg umask_arg =
umask_arg > upper_bound_of_correct_umask_arg_value && umask_arg < upper_bound_of_correct_chmod_arg_value
let check_umask_arg tid_map blk w =
try
let umask_arg = Word.to_int_exn w in
if is_chmod_style_arg umask_arg then
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid blk) tid_map in
let tid = Address_translation.tid_to_string @@ Term.tid blk in
let umask_arg_str = sprintf "%d" umask_arg in
let description = sprintf
"(Use of umask() with chmod-style Argument) Function %s calls umask with argument %s"
address
umask_arg_str in
let other = [["umask_arg"; umask_arg_str]] in
let cwe_warning = cwe_warning_factory name version ~addresses:[address] ~tids:[tid] ~other:other description in
collect_cwe_warning cwe_warning
with _ -> Log_utils.error "Caught exception in module [CWE560]."
let check_umask_callsite tid_map blk =
Seq.iter (Term.enum def_t blk) ~f:(fun d ->
let rhs = Def.rhs d in
let int_values = collect_int_values rhs in
List.iter int_values ~f:(fun x -> check_umask_arg tid_map blk x)
)
let blk_calls_umask sym_umask blk =
Term.enum jmp_t blk
|> Seq.exists ~f:(fun callsite -> Symbol_utils.calls_callsite_symbol callsite sym_umask)
let check_subfunction program tid_map sym_umask sub =
if Symbol_utils.sub_calls_symbol program sub "umask" then
Term.enum blk_t sub
|> Seq.filter ~f:(fun blk -> blk_calls_umask sym_umask blk)
|> Seq.iter ~f:(fun blk -> check_umask_callsite tid_map blk)
else
()
let check_subfunctions program tid_map sym_umask =
Seq.iter (Term.enum sub_t program) ~f:(fun sub -> check_subfunction program tid_map sym_umask sub)
let check_cwe program _ tid_map _ _ =
let sym = Symbol_utils.get_symbol_of_string program "umask" in
match sym with
| None -> ()
| Some sym_umask -> check_subfunctions program tid_map sym_umask
(* Functions made available for unit tests *)
module Private = struct
let is_chmod_style_arg = is_chmod_style_arg
end
(** This module implements a check for CWE-560: Use of umask() with chmod-style Argument.
The program uses the system call umask(2) with arguements for chmod(2). For instance,
instead of a reasonable value like 0022 a value like 0666 is passed. This may result wrong
read and/or write access to files and directories, which could be utilized to bypass
protection mechanisms.
See {: https://cwe.mitre.org/data/definitions/560.html} for a detailed description.
{1 How the check works}
This check looks for umask calls and checks if they have a reasonable value, i.e. smaller than
a certain value, currently set to 1000 and greater than a reasonable value for umask, currently set to 100.
{1 False Positives}
- The current version considers all immediate values of an umask callsite's basic
block. It does not check whether the value is an input to the call or not.
{1 False Negatives}
- If the input to umask is not defined in the basic block before the call, the
check will not see it.
- Calls where the input is not an immediate value but a variable are not examined.
*)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
(* functions made available for unit tests: *)
module Private : sig
val is_chmod_style_arg : int -> bool
end
open Core_kernel
open Bap.Std
open Log_utils
let name = "CWE676"
let version = "0.1"
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 ->
match Jmp.kind j with
| Goto _ | Ret _ | Int (_,_) -> None
| Call dst -> match Call.target dst with
| Direct tid when Tid.(=) tid (Term.tid target) ->
Some (Term.name callee, Term.tid j, Term.name target)
| _ -> None))
let get_calls_to_symbols cg subfunctions symbols =
(Seq.concat_map subfunctions ~f:(fun subfunction ->
Seq.concat_map symbols ~f:(fun symbol -> get_call_to_target cg subfunction symbol)))
(* FIXME: refactor variable names *)
let print_calls calls ~tid_map =
Seq.iter calls ~f:(fun call -> match call with
| (a, b, c) ->
begin
let address = Address_translation.translate_tid_to_assembler_address_string b tid_map in
let tid = Address_translation.tid_to_string b in
let other = [["dangerous_function"; c]] in
let description = sprintf
"(Use of Potentially Dangerous Function) %s (%s) -> %s."
a
address
c in
let cwe_warning = cwe_warning_factory
name
version
~other:other
~addresses:[address]
~tids:[tid]
~symbols:[a]
description in
collect_cwe_warning cwe_warning
end
)
let resolve_symbols prog symbols =
Term.enum sub_t prog |>
Seq.filter ~f:(fun s -> List.exists ~f:(fun x -> String.(=) x (Sub.name s)) symbols)
let check_cwe prog _proj tid_map symbol_names _ =
match symbol_names with
| hd::[] ->
let subfunctions = Term.enum sub_t prog in
let cg = Program.to_graph prog in
get_calls_to_symbols cg subfunctions (resolve_symbols prog hd)
|> print_calls ~tid_map:tid_map
| _ -> failwith "[CWE676] symbol_names not as expected"
(** This module implements a check for CWE-676: Use of Potentially Dangerous Function.
Potentially dangerous functions like memcpy can lead to security issues like buffer
overflows.
See {: https://cwe.mitre.org/data/definitions/676.html} for a detailed description.
{1 How the check works}
Calls to dangerous functions are flagged. The list of functions that are considered
dangerous can be configured in config.json. The default list is taken from
{: https://github.com/01org/safestringlib/wiki/SDL-List-of-Banned-Functions}.
{1 False Positives}
None known
{1 False Negatives}
None known
*)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
open Core_kernel
open Bap.Std
open Log_utils
let name = "CWE782"
let version = "0.1"
(*TODO: check if binary is setuid*)
let handle_sub sub program tid_map _symbols =
if Symbol_utils.sub_calls_symbol program sub "ioctl" then
begin
let address = Address_translation.translate_tid_to_assembler_address_string (Term.tid sub) tid_map in
let tid = Address_translation.tid_to_string @@ Term.tid sub in
let symbol = Term.name sub in
let description = sprintf
"(Exposed IOCTL with Insufficient Access Control) Program uses ioctl at %s (%s). Be sure to double check the program and the corresponding driver."
symbol
address in
let cwe_warning = cwe_warning_factory name version ~addresses:[address] ~tids:[tid] ~symbols:[symbol] description in
collect_cwe_warning cwe_warning
end
else
()
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)
(** This module implements a check for CWE-782: Exposed IOCTL with Insufficient Access Control.
See {: https://cwe.mitre.org/data/definitions/782.html} for a detailed description.
{1 How the check works}
Calls to ioctl() get flagged as CWE hits.
{1 False Positives}
- We cannot check whether the call contains sufficient access control.
{1 False Negatives}
- There are other ways to expose I/O control without access control.
*)
val name : string
val version : string
val check_cwe : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
open Bap.Std
let name = "Memory"
let version = "0.1"
let check_cwe (_program: Program.t) (project: Project.t) (tid_map: word Tid.Map.t) (_: string list list) (_: string list) =
Pointer_inference.run project tid_map
(** This module implements memory-related CWE checks.
Right now the check detects cases of
- {{: https://cwe.mitre.org/data/definitions/415.html} CWE 415: Double Free}
- {{: https://cwe.mitre.org/data/definitions/416.html} CWE 416: Use After Free}
{1 How the check works}
Via Dataflow Analysis, the check tries to keep track of all memory objects and pointers
known at specific points in the program.
It also keeps track of the status of memory object, i.e. if they have been already freed.
Access to an already freed object generates a CWE warning.
In cases where the analysis cannot reliably determine whether accessed memory has been freed or not,
a CWE warning may (or may not) be issued to the user based on the likelihood of it being a false positive.
Note that the check is still experimental.
Bugs may occur and the rate of false positive and false negative warnings is not yet known.
*)
val name: string
val version: string
val check_cwe: Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit
opam-version: "2.0"
name: "cwe_checker_core"
version: "0.4"
synopsis: "Core library for the cwe_checker package"
description: """
Core library for the cwe_checker suite of tools.
"""
maintainer: "CWE_checker Team <nils-edvin.enkelmann@fkie.fraunhofer.de>"
authors: [ "Thomas Barabosch <thomas.barabosch@fkie.fraunhofer.de>" "Nils-Edvin Enkelmann <nils-edvin.enkelmann@fkie.fraunhofer.de>" ]
license: "LGPL-3.0"
homepage: "https://github.com/fkie-cad/cwe_checker"
bug-reports: "https://github.com/fkie-cad/cwe_checker/issues"
dev-repo: "git+https://github.com/fkie-cad/cwe_checker"
depends: [
"ocaml" {>= "4.08.0"}
"dune" {>= "2.0"}
"yojson" {>= "1.6.0"}
"bap" {>= "2.2.0"}
"alcotest" {>= "0.8.3"}
"core_kernel" {>= "v0.14"}
"ppx_jane" {>= "v0.14"}
"ppx_deriving_yojson" {>= "3.5.1"}
"odoc" {>= "1.4"}
]
depexts: [
"binutils"
]
conflicts: [
"fkie-cad-cwe-checker" {!= "0.2"}
]
build: [
[ "dune" "build" "--profile" "release" ]
]
install: [
[ "dune" "install" ]
]
[package] [package]
name = "cwe_checker_rs" name = "cwe_checker_lib"
version = "0.5.0-dev" version = "0.5.0-dev"
authors = ["Nils-Edvin Enkelmann <nils-edvin.enkelmann@fkie.fraunhofer.de>"] authors = ["Nils-Edvin Enkelmann <nils-edvin.enkelmann@fkie.fraunhofer.de>"]
edition = "2018" edition = "2018"
...@@ -19,5 +19,4 @@ directories = "3.0" ...@@ -19,5 +19,4 @@ directories = "3.0"
goblin = "0.2" goblin = "0.2"
[lib] [lib]
name = "cwe_checker_rs" name = "cwe_checker_lib"
crate-type = ["staticlib", "cdylib", "lib"]
(library
(name cwe_checker_core)
(public_name cwe_checker_core)
(libraries
yojson
bap
bap-api
bap-abi
core_kernel
ppx_deriving_yojson.runtime)
(preprocess (pps ppx_jane ppx_deriving_yojson))
(foreign_archives cwe_checker_rs)
(c_library_flags (-lpthread -lc -lm)) ; needed for linking the Rust runtime
)
(include_subdirs unqualified) ; Include all subdirs when looking for source files
open Core_kernel
open Bap.Std
open Format
let version = "0.3-dev"
type cwe_module = {
cwe_func : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit;
name : string;
version : string;
requires_pairs : bool;
has_parameters : bool;
}
let known_modules = [{cwe_func = Cwe_190.check_cwe; name = Cwe_190.name; version = Cwe_190.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_215.check_cwe; name = Cwe_215.name; version = Cwe_215.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_243.check_cwe; name = Cwe_243.name; version = Cwe_243.version; requires_pairs = true; has_parameters = false};
{cwe_func = Cwe_248.check_cwe; name = Cwe_248.name; version = Cwe_248.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_332.check_cwe; name = Cwe_332.name; version = Cwe_332.version; requires_pairs = true; has_parameters = false};
{cwe_func = Cwe_367.check_cwe; name = Cwe_367.name; version = Cwe_367.version; requires_pairs = true; has_parameters = false};
{cwe_func = Cwe_426.check_cwe; name = Cwe_426.name; version = Cwe_426.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_457.check_cwe; name = Cwe_457.name; version = Cwe_457.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_467.check_cwe; name = Cwe_467.name; version = Cwe_467.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_476.check_cwe; name = Cwe_476.name; version = Cwe_476.version; requires_pairs = false; has_parameters = true};
{cwe_func = Cwe_560.check_cwe; name = Cwe_560.name; version = Cwe_560.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_676.check_cwe; name = Cwe_676.name; version = Cwe_676.version; requires_pairs = false; has_parameters = false};
{cwe_func = Cwe_782.check_cwe; name = Cwe_782.name; version = Cwe_782.version; requires_pairs = false; has_parameters = false};
{cwe_func = Memory_cwes.check_cwe; name = Memory_cwes.name; version = Memory_cwes.version; requires_pairs = false; has_parameters = false}]
let cmdline_flags = [
("version", "Print the version number of the cwe_checker and quit");
("module-versions", "Prints out the version numbers of all known modules.");
("json", "Outputs the result as JSON.");
("no-logging", "Outputs no logging (info, error, warning). This does not pollute STDOUT when output json to it.");
("check-path", "Checks if there is a path from an input function to a CWE hit.");
]
let cmdline_params = [
("config", "Path to configuration file.");
("out", "Path to output file.");
("partial", "Comma separated list of modules to apply on binary, e.g. 'CWE332,CWE476,CWE782'");
("api", "C header file for additional subroutine information.")
]
let build_version_sexp () =
List.map known_modules ~f:(fun cwe -> Format.sprintf "\"%s\": \"%s\"" cwe.name cwe.version)
|> String.concat ~sep:", "
let print_module_versions () =
Log_utils.info (sprintf "[cwe_checker] module_versions: {%s}" (build_version_sexp ()))
let print_version () =
print_endline version
let print_help_message ((): unit) : unit =
let flags = cmdline_flags in
let params = cmdline_params in
Printf.printf("Help:\n\nThe CWE checker is called using the following command structure:\n\n
cwe_checker path/to/binary -[FLAG] -[PARAM=VALUE] ...\n\nThe following flags and parameters are available:\n\nFLAGS:\n\n");
List.iter ~f:(fun x -> Printf.printf " -%s: %s\n" (fst x) (snd x)) flags;
Printf.printf("\nPARAMETERS:\n\n");
List.iter ~f:(fun x -> Printf.printf " -%s: %s\n" (fst x) (snd x)) params
let execute_cwe_module (cwe : cwe_module) (json : Yojson.Basic.t) (project : Project.t) (program : program term) (tid_address_map : word Tid.Map.t) : unit =
let parameters = match cwe.has_parameters with
| false -> []
| true -> Json_utils.get_parameter_list_from_json json cwe.name in
if cwe.requires_pairs then
let symbol_pairs = Json_utils.get_symbol_lists_from_json json cwe.name in
cwe.cwe_func program project tid_address_map symbol_pairs parameters
else
let symbols = Json_utils.get_symbols_from_json json cwe.name in
cwe.cwe_func program project tid_address_map [symbols] parameters
let check_valid_module_list (modules : string list) : unit =
let known_module_names = List.map ~f:(fun x -> x.name) known_modules in
match List.find modules ~f:(fun module_name -> not (Stdlib.List.mem module_name known_module_names) ) with
| Some module_name ->
failwith ("[cwe_checker] Unknown CWE module " ^ module_name ^ ". Known modules: " ^ String.concat (List.map ~f:(fun x -> x ^ " ") known_module_names));
| None -> ()
let partial_run (json : Yojson.Basic.t) (project : Project.t) (program : program term) (tid_address_map : word Tid.Map.t) (modules : string list) : unit =
let () = check_valid_module_list modules in
Log_utils.info (sprintf "[cwe_checker] Just running the following analyses: %s." (String.concat (List.map ~f:(fun x -> x ^ " ") modules)));
List.iter modules ~f:(fun cwe ->
let cwe_mod = match List.find known_modules ~f:(fun x -> String.(=) x.name cwe) with
| Some(module_) -> module_
| None -> failwith "[cwe_checker] Unknown CWE module" in
execute_cwe_module cwe_mod json project program tid_address_map
)
let full_run (json : Yojson.Basic.t) (project : Project.t) (program : program term) (tid_address_map : word Tid.Map.t) : unit =
List.iter known_modules ~f:(fun cwe ->
if String.(<>) cwe.name "Memory" then (* TODO: Remove this when the memory check is more stable *)
execute_cwe_module cwe json project program tid_address_map)
let build_output_path (path : string) : string =
try
match Sys.is_directory path with
| false -> path
| true ->
let path = match String.is_suffix path ~suffix:"/" with
| true -> path
| false -> path ^ "/" in
let path = path ^ "out-" ^ string_of_float (Caml_unix.time ()) in
Log_utils.info (sprintf "Created: %s" path);
path
with
| _ -> path (* file does not exist. We generate a new file with this name. *)
let main (flags : Bool.t String.Map.t) (params : String.t String.Map.t) (project : Project.t) =
let config = String.Map.find_exn params "config" in
let module_versions = String.Map.find_exn flags "module-versions" in
let partial_update = String.Map.find_exn params "partial" in
let check_path = String.Map.find_exn flags "check-path" in
let json_output = String.Map.find_exn flags "json" in
let file_output = String.Map.find_exn params "out" in
let no_logging = String.Map.find_exn flags "no-logging" in
let print_version_flag = String.Map.find_exn flags "version" in
if print_version_flag then
print_version ()
else
if module_versions then
print_module_versions ()
else
begin
if no_logging then Log_utils.turn_off_logging ();
let config =
if String.(=) config "" then
(* try the standard installation path for the config file instead *)
match Sys.getenv_opt "OPAM_SWITCH_PREFIX" with
| Some(prefix) -> prefix ^ "/etc/cwe_checker/config.json"
| None -> ""
else
config in
if String.(=) config "" then
Log_utils.error "[cwe_checker] No configuration file provided! Aborting..."
else if Bool.(=) (Sys.file_exists config) false then
Log_utils.error "[cwe_checker] Configuration file not found. Aborting..."
else
begin
let prog = Project.program project in
let tid_address_map = Address_translation.generate_tid_map prog in
let json = Yojson.Basic.from_file config in
let () = match Symbol_utils.check_if_symbols_resolved project prog tid_address_map with
| false -> Log_utils.error "BAP is not able to resolve external symbols."
| true -> () in
if String.(=) partial_update "" then
full_run json project prog tid_address_map
else
partial_run json project prog tid_address_map (String.split partial_update ~on: ',');
if check_path then
begin
let check_path_sources = Json_utils.get_symbols_from_json json "check_path" in
let check_path_sinks = Log_utils.get_cwe_warnings () in
Check_path.check_path prog tid_address_map check_path_sources check_path_sinks
end;
let file_output =
if String.(<>) file_output "" then
build_output_path file_output
else
file_output in
if json_output then
begin
match Project.get project filename with
| Some fname -> Log_utils.emit_json fname file_output
| None -> Log_utils.emit_json "" file_output
end
else
Log_utils.emit_native file_output
end
end
(** This module defines the main driving function for the cwe_checker plugin in BAP.
*)
open Core_kernel
open Bap.Std
type cwe_module = {
cwe_func : Bap.Std.program Bap.Std.term -> Bap.Std.project -> Bap.Std.word Bap.Std.Tid.Map.t -> string list list -> string list -> unit;
name : string;
version : string;
requires_pairs : bool;
has_parameters : bool;
}
val version: String.t
(** prints the version number *)
val print_version: unit -> unit
val known_modules: cwe_module List.t
val cmdline_flags: (String.t * String.t) List.t
val cmdline_params: (String.t * String.t) List.t
val print_module_versions: unit -> unit
val check_valid_module_list: string list -> unit
(** prints the help message *)
val print_help_message: unit -> unit
(** Executes one CWE module *)
val execute_cwe_module: cwe_module -> Yojson.Basic.t -> Bap.Std.project -> Bap.Std.program Bap.Std.term -> Bap.Std.word Bap.Std.Tid.Map.t -> unit
(** Only runs checks on CWE module specified by user. *)
val partial_run: Yojson.Basic.t -> Bap.Std.project -> Bap.Std.program Bap.Std.term -> Bap.Std.word Bap.Std.Tid.Map.t -> string list -> unit
(** Runs checks on all supported CWE modules. *)
val full_run: Yojson.Basic.t -> Bap.Std.project -> Bap.Std.program Bap.Std.term -> Bap.Std.word Bap.Std.Tid.Map.t -> unit
val build_output_path: string -> string
(** The main function drives the execution of the cwe_checker plugin in BAP.
The command line arguments are passed as maps from their name to to their values
(Bool.t for flags, String.t for other arguments) to this function.
*)
val main: Bool.t String.Map.t -> String.t String.Map.t -> Project.t -> unit
open Core_kernel
open Bap.Std
let translate_tid_to_assembler_address_string (tid : tid) (tid_map : word Tid.Map.t) : string =
match Tid.Map.find tid_map tid with
| Some asm_addr -> Word.to_string asm_addr
| _ -> "UNKNOWN"
let generate_tid_map (prog : program term) : word Tid.Map.t =
let last_addr = ref None in
(object
inherit [addr Tid.Map.t] Term.visitor
method! enter_term _ t addrs = match Term.get_attr t address with
| None -> begin
match !last_addr with
| Some addr -> Map.add_exn addrs ~key:(Term.tid t) ~data:addr
| None -> addrs
end
| Some addr -> begin
last_addr := Some addr;
Map.add_exn addrs ~key:(Term.tid t) ~data:addr
end
end)#run prog Tid.Map.empty
let tid_to_string tid = Bap.Std.Tid.name tid
(** This module helps to translate between IR addresses and addresses found in the actual assembler code.
At first, a mapping between the two addressing schemes has to be computed with the function generate_tid_map.
Call this function once at start up.Then, we can translate IR addresses (Bap.Std.tid) to addresses
in assembler code (represented as string). *)
(** Generates a map that maps from TIDs to real addresses of the assembly code. *)
val generate_tid_map :
Bap.Std.program Bap.Std.term -> Bap.Std.word Bap.Std.Tid.Map.t
(** Translates a TID to a real address of the assembly code.
It requires a TID -> address mapping that can be generated with generate_tid_map. *)
val translate_tid_to_assembler_address_string :
Bap.Std.tid -> Bap.Std.word Bap.Std.Tid.Map.t -> string
val tid_to_string :
Bap.Std.tid -> string
open Bap.Std
open Core_kernel
let callee_saved_registers = ref None
let bin_format = ref ""
let json = ref None
let get_json (() : unit) : Yojson.Basic.t =
match !json with
| Some(json) -> !json
| None -> begin
let path = match Sys.getenv_opt "OPAM_SWITCH_PREFIX" with
| Some(prefix) -> prefix ^ "/etc/cwe_checker/registers.json"
| None -> failwith "[cwe_checker] registers.json not found." in
Yojson.Basic.from_file path
end
let supported_architectures = ref []
let get_supported_architectures (() : unit) : string list =
match !supported_architectures with
| [] -> begin
supported_architectures := List.append !supported_architectures (List.map (Json_utils.get_arch_list_from_json (get_json ()) "elf") ~f:(fun kv -> match kv with (k, _) -> k));
!supported_architectures
end
| _ -> !supported_architectures
let infer_bin_format_from_symbols (project : Project.t) : string =
match Option.is_some (Symtab.find_by_name (Project.symbols project) "__GetPEImageBase") with
| true -> "pe"
| false -> "elf"
let extract_bin_format (project : Project.t) : string =
match !bin_format with
| "" -> begin
let header = Support_functions.call_objdump project ~flag:"-f" ~err:"[cwe_checker] Parsing of file header failed:" in
let arch = Project.arch project in
match header with
| _::line::_ -> begin
let chop_idx = match arch with
| `x86_64 -> 2
| _ -> 1 in
match List.hd_exn (List.drop (List.rev (String.split_on_chars line ~on:[' '; '-'])) chop_idx) with
| "elf32" | "elf64" -> bin_format := "elf"; !bin_format
| "pei" -> bin_format := "pe"; !bin_format
| _ -> infer_bin_format_from_symbols project
end
| _ -> infer_bin_format_from_symbols project
end
| _ -> !bin_format
let get_register_list (project : Project.t) (context : string) : string list =
let bap_arch = Arch.to_string (Project.arch project) in
let arch = match bap_arch with
| "i386" | "i686" -> "x86"
| "powerpc" -> "ppc"
| "powerpc64" -> "ppc64"
| "powerpc64le" -> "ppc64le"
| _ -> bap_arch in
match Stdlib.List.mem arch (get_supported_architectures ()) with
| true -> begin
let json_bin = Json_utils.get_bin_format_from_json (get_json ()) (extract_bin_format project) in
match arch with
| "x86" -> begin
let conv = match Project.get project Bap_abi.name with
| Some(c) -> c
| _ -> Log_utils.info "[cwe_checker] Could not infer calling convention. Assuming cdecl as standard"; "cdecl" in
let json_arch = Json_utils.get_arch_from_json json_bin ~conv:conv arch in
Json_utils.get_registers_from_json json_arch context
end
| _ -> begin
let json_arch = Json_utils.get_arch_from_json json_bin arch in
Json_utils.get_registers_from_json json_arch context
end
end
| false -> failwith "No calling convention implemented for the given architecture"
let is_callee_saved var project =
match !callee_saved_registers with
| Some(register_set) -> String.Set.mem register_set (Var.name var)
| None ->
callee_saved_registers := Some(String.Set.of_list (get_register_list project "callee_saved"));
String.Set.mem (Option.value_exn !callee_saved_registers) (Var.name var)
let is_parameter_register (var: Var.t) (project: Project.t) : Bool.t =
let param_register = get_register_list project "params" in
Option.is_some (List.find param_register ~f:(String.equal (Var.name var)))
let is_return_register (var: Var.t) (project: Project.t) : Bool.t =
let ret_register = get_register_list project "return" in
Option.is_some (List.find ret_register ~f:(String.equal (Var.name var)))
open Bap.Std
open Core_kernel
(** Returns a json object for registers.json. If not yet in memory, the json file is read. *)
val get_json: unit -> Yojson.Basic.t
(** Returns whether a variable is callee saved according to the calling convention
of the target architecture. Should only used for calls to functions outside
of the program, not for calls between functions inside the program. *)
val is_callee_saved: Var.t -> Project.t -> Bool.t
(** Returns whether a variable may be used to pass parameters to a function.
This depends on the calling convention of the target architecture and should only be used for extern function calls. *)
val is_parameter_register: Var.t -> Project.t -> Bool.t
(** Returns whether a variable may be used for return values of function calls.
This depends on the calling convention of the target architecture and should only be used for extern function calls. *)
val is_return_register: Var.t -> Project.t -> Bool.t
(** Returns a string list of supported architectures from the registers.json. *)
val get_supported_architectures : unit -> string list
(** Infers the binary format using the file's symbol table. *)
val infer_bin_format_from_symbols : Project.t -> string
(** Returns the binary format by either an objdump call or via the file's symbol table. *)
val extract_bin_format : Project.t -> string
(** Returns a list of registers based on the file's binary format, architecture,
calling_convention and context (e.g. callee saved, parameter etc.) *)
val get_register_list : Project.t -> string -> string list
open Core_kernel
open Yojson.Basic.Util
(** Extracts the symbols to check for from json document.
An example looks like this:
"CWE467": {
"symbols": ["strncmp", "malloc",
"alloca", "_alloca", "strncat", "wcsncat",
"strncpy", "wcsncpy", "stpncpy", "wcpncpy",
"memcpy", "wmemcpy", "memmove", "wmemmove", "memcmp", "wmemcmp"],
"_comment": "any function that takes something of type size_t could be a possible candidate."
}, *)
let get_symbols_from_json (json : Yojson.Basic.t) cwe =
[json]
|> filter_member cwe
|> filter_member "symbols"
|> flatten
|> List.map ~f:to_string
let get_symbol_lists_from_json (json : Yojson.Basic.t) cwe =
[json]
|> filter_member cwe
|> filter_member "pairs"
|> flatten
|> List.map ~f:(fun l -> List.map (to_list l) ~f:to_string)
let get_parameter_list_from_json (json : Yojson.Basic.t) cwe =
[json]
|> filter_member cwe
|> filter_member "parameters"
|> flatten
|> List.map ~f:to_string
let get_arch_from_json (bin_format : Yojson.Basic.t) ?(conv : string = "") (arch : string) : Yojson.Basic.t =
match arch with
| "x86" -> bin_format |> member arch |> member conv
| _ -> bin_format |> member arch
let get_bin_format_from_json (json : Yojson.Basic.t) (bin_format : string) : Yojson.Basic.t =
json |> member bin_format
let get_registers_from_json (arch : Yojson.Basic.t) (context : string) : string list =
arch |> member context |> to_list |> filter_string
let get_arch_list_from_json (json : Yojson.Basic.t) (bin_format : string) : (string * Yojson.Basic.t) list =
json |> member bin_format |> to_assoc
(** This module implements functionality related to parsing the JSON configuration file.
*)
(** Returns pairs of symbols for a given CWE check. *)
val get_symbol_lists_from_json : Yojson.Basic.t -> string -> string list list
(** Returns symbols for a given CWE check. *)
val get_symbols_from_json : Yojson.Basic.t -> string -> string list
(** Returns parameters for a given CWE check. *)
val get_parameter_list_from_json : Yojson.Basic.t -> string -> string list
(** Returns an architecture's registers based on calling convention. *)
val get_arch_from_json : Yojson.Basic.t -> ?conv:string -> string -> Yojson.Basic.t
(** Returns json object containing either PE or ELF calling conventions for all architectures. *)
val get_bin_format_from_json : Yojson.Basic.t -> string -> Yojson.Basic.t
(** Returns registers for a given architecture and calling_convention specified by context. (e.g. callee saved, parameter etc.) *)
val get_registers_from_json : Yojson.Basic.t -> string -> string list
(** Returns a list of all architectures supported for a given binary format. (e.g. ELF) *)
val get_arch_list_from_json : Yojson.Basic.t -> string -> (string * Yojson.Basic.t) list
open Core_kernel
module CweWarning = struct
type t = {
name : string;
version : string;
addresses: string list;
tids: string list;
symbols: string list;
other : string list list;
description : string;
} [@@deriving yojson]
end
module CheckPath = struct
type t = {
source : string;
destination : string;
source_addr : string;
destination_addr : string;
path : string list;
path_str : string;
} [@@deriving yojson]
end
module CweCheckerResult = struct
type t = {
binary : string;
time : float;
warnings : CweWarning.t list;
check_path : CheckPath.t list;
} [@@deriving yojson]
end
let cwe_warning_store = ref []
let check_path_store = ref []
let no_logging = ref false
let turn_off_logging () = no_logging := true
let cwe_warning_factory name version ?(other = []) ?(addresses = []) ?(tids = []) ?(symbols = []) description =
{
CweWarning.name = name;
CweWarning.version = version;
CweWarning.description = description;
CweWarning.other = other;
CweWarning.addresses = addresses;
CweWarning.tids = tids;
CweWarning.symbols = symbols;
}
let check_path_factory ?(path = []) ?(path_str = "") source source_addr destination destination_addr =
{
CheckPath.source = source;
CheckPath.source_addr = source_addr;
CheckPath.destination = destination;
CheckPath.destination_addr = destination_addr;
CheckPath.path = path;
CheckPath.path_str = path_str;
}
let collect_cwe_warning warning = cwe_warning_store := !cwe_warning_store @ [warning]
let collect_check_path path = check_path_store := !check_path_store @ [path]
let get_cwe_warnings () = !cwe_warning_store
let emit_json target_path out_path =
let cwe_warning_result = {
CweCheckerResult.binary = target_path;
CweCheckerResult.time = Caml_unix.time ();
CweCheckerResult.warnings = !cwe_warning_store;
CweCheckerResult.check_path = !check_path_store
} in
let output = Yojson.Safe.pretty_to_string (CweCheckerResult.to_yojson cwe_warning_result) in
if String.(=) out_path "" then
print_endline output
else
Out_channel.write_all out_path ~data:output
let emit_native out_path =
let output_check_path = List.map !check_path_store ~f:(fun (check_path:CheckPath.t) ->
sprintf "[CheckPath] %s(%s) -> %s via %s" check_path.source check_path.source_addr check_path.destination_addr check_path.path_str) in
let output_warnings = List.map !cwe_warning_store ~f:(fun (cwe_warning:CweWarning.t) ->
sprintf "[%s] (%s) \n %s" cwe_warning.name cwe_warning.version cwe_warning.description) in
let output_lines = output_warnings @ output_check_path in
if String.(=) out_path "" then
List.iter output_lines ~f:print_endline
else
Out_channel.write_lines out_path output_lines
let debug message = if !no_logging then () else print_endline ("DEBUG: " ^ message)
let info message = if !no_logging then () else print_endline ("INFO: " ^ message)
let error message = if !no_logging then () else print_endline ("ERROR: " ^ message)
(** This module implements the logging logic or cwe_checker.
Each check may produce a CweWarning, which holds information regarding the current CWE hit.
These CweWarnings are stored globally so that we can output at the very end. This may be
necessary, for instance, when the output should be a JSON document.
CWE checks can utilize the function cwe_warning_factory to create CweWarning objects and
the function collect_cwe_warning to store them globally.
At the moment, cwe_checker supports plain text and JSON output. The corresponding functions
are emit_native and emit_json.
In addition, there are several functions (debug, error, info) to notify the user of certain
events. Note that these functions may pollute the output.
*)
module CweWarning : sig
type t = {
name : string;
version : string;
addresses: string list;
tids: string list;
symbols: string list;
other : string list list;
description : string;
} [@@deriving yojson]
end
module CheckPath : sig
type t = {
source : string;
destination : string;
source_addr : string;
destination_addr : string;
path : string list;
path_str : string;
}
end
(**
This function turns on or off the logging of debug, error, and info messages to STDOUT / STDERR.
Use if you do not want to pollute your JSON output when outputting to STDOUT.
*)
val turn_off_logging : unit -> unit
(**
Factory function to easily build an element of type CweWarning.t.
It takes the following input parameters:
- name: name of the CWE
- version: version of the cwe_check
- other: list of abritrary string elements (use as needed)
- addresses: list of relevant assembly addresses as strings
- tids: list of relevant TIDs as strings
- symbols: list of associated symbols as strings
- description: string description of the CWE
*)
val cwe_warning_factory : string -> string -> ?other:string list list -> ?addresses:string list -> ?tids:string list -> ?symbols:string list -> string -> CweWarning.t
(**
Factory function to easily build an element of type CheckPath.t.
It takes the following input parameters:
- path: a list of strings of node on the path
- path_str: a string of the path between source and destination
- source: symbol of source
- source_addr: assembly address of source
- destination: symbol / address of destination
- destination_addr: assembly address of destination
*)
val check_path_factory : ?path:string list -> ?path_str:string -> string -> string -> string -> string -> CheckPath.t
(**
Add one CweWarning.t element to an internal store. All elements are emited by calling one of the emit_* functions.
*)
val collect_cwe_warning : CweWarning.t -> unit
(**
Add one CheckPath.t element to an internal store. All elements are emited by calling one of the emit_* functions.
*)
val collect_check_path : CheckPath.t -> unit
(**
Returns the internal store of CweWarning.t elements as a list.
*)
val get_cwe_warnings : unit -> CweWarning.t list
(**
Emits stored CweWarning.t and CheckPath.t elements as json.
target_path is the path of the current BAP project and out_path is the path a json output file.
*)
val emit_json : string -> string -> unit
(**
Emits stored CweWarning.t and CheckPath.t elements.
target_path is the path of the current BAP project and out_path is the path an output file.
*)
val emit_native : string -> unit
val debug : string -> unit
val error : string -> unit
val info : string -> unit
{
"pe" : {
"x86" : {
"cdecl" : {
"callee_saved" : ["EBX", "ESI", "EDI", "EBP"],
"float_callee_saved" : [],
"params" : [],
"float_params" : [],
"return" : ["EDX", "EAX"],
"float_return" : ["ST0"]
},
"ms" : {
"callee_saved" : ["EBX", "ESI", "EDI", "EBP"],
"float_callee_saved" : [],
"params" : [],
"float_params" : [],
"return" : ["EDX", "EAX"],
"float_return" : ["ST0"]
},
"fastcall" : {
"callee_saved" : ["EBX", "ESI", "EDI", "EBP"],
"float_callee_saved" : [],
"params" : ["ECX", "EDX"],
"float_params" : [],
"return" : ["EDX", "EAX"],
"float_return" : ["ST0"]
}
},
"x86_64" : {
"callee_saved" : ["RBX", "RBP", "RSP", "RDI", "RSI", "R12", "R13", "R14", "R15"],
"float_callee_saved" : ["XMM6", "XMM7", "XMM8", "XMM9", "XMM10", "XMM11", "XMM12", "XXM13", "XMM14", "XMM15"],
"params" : ["RCX", "RDX", "R8", "R9"],
"float_params" : ["XMM0", "XMM1", "XMM2", "XMM3", "YMM0", "YMM1", "YMM2", "YMM3", "ZMM0", "ZMM1", "ZMM2", "ZMM3"],
"return" : ["RAX"],
"float_return" : ["XMM0", "YMM0", "ZMM0"]
},
"armv4" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"armv5" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"armv6" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"armv7" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"armv4eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"armv5eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"armv6eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"armv7eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"thumbv4" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"thumbv5" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"thumbv6" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"thumbv7" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"thumbv4eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"thumbv5eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"thumbv6eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"thumbv7eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "D0", "D1", "Q0"]
},
"aarch64" : {
"callee_saved" : ["X18", "X19", "X20", "X21", "X22", "X23", "X24", "X25", "X26", "X27", "X28", "X29", "SP", "X30", "LR",
"W19", "W20", "W21", "W22", "W23", "W24", "W25", "W26", "W27", "W28", "W29", "W30"],
"float_callee_saved" : ["V8", "V9", "V10", "V11", "V12", "V13", "V14", "V15"],
"params" : ["X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "W0", "W1", "W2", "W3", "W4", "W5", "W6", "W7"],
"float_params" : ["V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7"],
"return" : ["X0", "W0"],
"float_return" : ["V0"]
},
"aarch64_be" : {
"callee_saved" : ["X18", "X19", "X20", "X21", "X22", "X23", "X24", "X25", "X26", "X27", "X28", "X29", "SP", "X30", "LR",
"W19", "W20", "W21", "W22", "W23", "W24", "W25", "W26", "W27", "W28", "W29", "W30"],
"float_callee_saved" : ["V8", "V9", "V10", "V11", "V12", "V13", "V14", "V15"],
"params" : ["X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "W0", "W1", "W2", "W3", "W4", "W5", "W6", "W7"],
"float_params" : ["V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7"],
"return" : ["X0", "W0"],
"float_return" : ["V0"]
},
"ppc" : {
"callee_saved" : [],
"float_callee_saved" : [],
"params" : [],
"float_params" : [],
"return" : [],
"float_return" : []
},
"ppc64" : {
"callee_saved" : [],
"float_callee_saved" : [],
"params" : [],
"float_params" : [],
"return" : [],
"float_return" : []
},
"ppc64le" : {
"callee_saved" : [],
"float_callee_saved" : [],
"params" : [],
"float_params" : [],
"return" : [],
"float_return" : []
},
"mips" : {
"callee_saved" : [],
"float_callee_saved" : [],
"params" : [],
"float_params" : [],
"return" : [],
"float_return" : []
},
"mipsel" : {
"callee_saved" : [],
"float_callee_saved" : [],
"params" : [],
"float_params" : [],
"return" : [],
"float_return" : []
},
"mips64" : {
"callee_saved" : [],
"float_callee_saved" : [],
"params" : [],
"float_params" : [],
"return" : [],
"float_return" : []
},
"mips64el" : {
"callee_saved" : [],
"float_callee_saved" : [],
"params" : [],
"float_params" : [],
"return" : [],
"float_return" : []
}
},
"elf" : {
"x86" : {
"cdecl" : {
"callee_saved" : ["EBX", "ESI", "EDI", "EBP"],
"float_callee_saved" : [],
"params" : [],
"float_params" : [],
"return" : ["EDX", "EAX"],
"float_return" : ["ST0"]
},
"stdcall" : {
"callee_saved" : ["EBX", "ESI", "EDI", "EBP"],
"float_callee_saved" : [],
"params" : [],
"float_params" : [],
"return" : ["EDX", "EAX"],
"float_return" : ["ST0"]
},
"fastcall" : {
"callee_saved" : ["EBX", "ESI", "EDI", "EBP"],
"float_callee_saved" : [],
"params" : ["ECX", "EDX"],
"float_params" : [],
"return" : ["EDX", "EAX"],
"float_return" : ["ST0"]
}
},
"x86_64" : {
"callee_saved" : ["RBP", "RSP", "RBX", "R12", "R13", "R14", "R15"],
"float_callee_saved" : [],
"params" : ["RDI", "RSI", "RDX", "RCX", "R8", "R9"],
"float_params" : ["XMM0", "XMM1", "XMM2", "XMM3", "XMM4", "XMM5", "XMM6", "XMM7",
"YMM0", "YMM1", "YMM2", "YMM3", "YMM4", "YMM5", "YMM6", "YMM7",
"ZMM0", "ZMM1", "ZMM2", "ZMM3", "ZMM4", "ZMM5", "ZMM6", "ZMM7"],
"return" : ["RAX", "RDX"],
"float_return" : ["XMM0", "XMM1", "YMM0", "ZMM0", "ST0", "ST1"]
},
"armv4" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"armv5" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"armv6" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"armv7" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"armv4eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"armv5eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"armv6eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"armv7eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"thumbv4" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"thumbv5" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"thumbv6" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"thumbv7" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"thumbv4eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"thumbv5eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"thumbv6eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"thumbv7eb" : {
"callee_saved" : ["R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R13", "SP", "R14", "LR"],
"float_callee_saved" : ["S16", "S17", "S18", "S19", "S20", "S21", "S22", "S23",
"S24", "S25", "S26", "S27", "S28", "S29", "S30", "S31",
"D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", "Q4", "Q5", "Q6", "Q7"],
"params" : ["R0", "R1", "R2", "R3"],
"float_params" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"],
"return" : ["R0", "R1", "R2", "R3"],
"float_return" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9",
"S10", "S11", "S12", "S13", "S14", "S15", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
"Q0", "Q1", "Q2", "Q3"]
},
"aarch64" : {
"callee_saved" : ["X18", "X19", "X20", "X21", "X22", "X23", "X24", "X25", "X26", "X27", "X28", "X29", "SP", "X30", "LR",
"W19", "W20", "W21", "W22", "W23", "W24", "W25", "W26", "W27", "W28", "W29", "W30"],
"float_callee_saved" : ["V8", "V9", "V10", "V11", "V12", "V13", "V14", "V15"],
"params" : ["X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "W0", "W1", "W2", "W3", "W4", "W5", "W6", "W7"],
"float_params" : ["V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7"],
"return" : ["X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "W0", "W1", "W2", "W3", "W4", "W5", "W6", "W7"],
"float_return" : ["V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7"]
},
"aarch64_be" : {
"callee_saved" : ["X18", "X19", "X20", "X21", "X22", "X23", "X24", "X25", "X26", "X27", "X28", "X29", "SP", "X30", "LR",
"W19", "W20", "W21", "W22", "W23", "W24", "W25", "W26", "W27", "W28", "W29", "W30"],
"float_callee_saved" : ["V8", "V9", "V10", "V11", "V12", "V13", "V14", "V15"],
"params" : ["X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "W0", "W1", "W2", "W3", "W4", "W5", "W6", "W7"],
"float_params" : ["V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7"],
"return" : ["X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "W0", "W1", "W2", "W3", "W4", "W5", "W6", "W7"],
"float_return" : ["V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7"]
},
"ppc" : {
"callee_saved" : ["R14", "R15", "R16", "R17", "R18", "R19", "R20", "R21",
"R22", "R23", "R24", "R25", "R26", "R27", "R28", "R29", "R30", "R31"],
"float_callee_saved" : ["F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21",
"F22", "F23", "F24", "F25", "F26", "F27", "F28", "F29", "F30", "F31"],
"params" : ["R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10"],
"float_params" : ["F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9",
"F10", "F11", "F12", "F13"],
"return" : ["R3", "R4"],
"float_return" : ["F1", "F2", "F3", "F4"]
},
"ppc64" : {
"callee_saved" : ["R1", "R2", "R14", "R15", "R16", "R17", "R18", "R19", "R20", "R21",
"R22", "R23", "R24", "R25", "R26", "R27", "R28", "R29", "R30", "R31"],
"float_callee_saved" : ["F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21",
"F22", "F23", "F24", "F25", "F26", "F27", "F28", "F29", "F30", "F31"],
"params" : ["R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10"],
"float_params" : ["F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9",
"F10", "F11", "F12", "F13"],
"return" : ["R3", "R4"],
"float_return" : ["F1", "F2", "F3", "F4"]
},
"ppc64le" : {
"callee_saved" : ["R1", "R2", "R14", "R15", "R16", "R17", "R18", "R19", "R20", "R21",
"R22", "R23", "R24", "R25", "R26", "R27", "R28", "R29", "R30", "R31"],
"float_callee_saved" : ["F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21",
"F22", "F23", "F24", "F25", "F26", "F27", "F28", "F29", "F30", "F31"],
"params" : ["R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10"],
"float_params" : ["F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9",
"F10", "F11", "F12", "F13"],
"return" : ["R3", "R4"],
"float_return" : ["F1", "F2", "F3", "F4"]
},
"mips" : {
"callee_saved" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "GP", "SP", "FP"],
"float_callee_saved" : ["F20", "F21", "F22", "F23", "F24", "F25", "F26",
"F27", "F28", "F29", "F30"],
"params" : ["A0", "A1", "A2", "A3"],
"float_params" : ["F12", "F13", "F14"],
"return" : ["V0", "V1"],
"float_return" : ["F0", "F1", "F2", "F3"]
},
"mipsel" : {
"callee_saved" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "GP", "SP", "FP"],
"float_callee_saved" : ["F20", "F21", "F22", "F23", "F24", "F25", "F26",
"F27", "F28", "F29", "F30"],
"params" : ["A0", "A1", "A2", "A3"],
"float_params" : ["F12", "F13", "F14"],
"return" : ["V0", "V1"],
"float_return" : ["F0", "F1", "F2", "F3"]
},
"mips64" : {
"callee_saved" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "GP", "SP", "FP"],
"float_callee_saved" : ["F20", "F21", "F22", "F23", "F24", "F25", "F26",
"F27", "F28", "F29", "F30"],
"params" : ["A0", "A1", "A2", "A3"],
"float_params" : ["F12", "F13", "F14"],
"return" : ["V0", "V1"],
"float_return" : ["F0", "F1", "F2", "F3"]
},
"mips64el" : {
"callee_saved" : ["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "GP", "SP", "FP"],
"float_callee_saved" : ["F20", "F21", "F22", "F23", "F24", "F25", "F26",
"F27", "F28", "F29", "F30"],
"params" : ["A0", "A1", "A2", "A3"],
"float_params" : ["F12", "F13", "F14"],
"return" : ["V0", "V1"],
"float_return" : ["F0", "F1", "F2", "F3"]
}
}
}
(** This module contains FFI-functionality for generating serde_json objects on the Rust side
which is used for converting complex Ocaml data structures to Rust data structures.
*)
open Core_kernel
open Bap.Std
open Symbol_utils
type t = nativeint
external rs_finalize_json_builder: t -> unit = "rs_finalize_json_builder"
external rs_build_serde_null: unit -> t = "rs_build_serde_null"
external rs_build_serde_bool: bool -> t = "rs_build_serde_bool"
external rs_build_serde_number: int -> t = "rs_build_serde_number"
external rs_build_serde_string: string -> t = "rs_build_serde_string"
external rs_build_serde_array_from_list: t list -> t = "rs_build_serde_array_from_list"
external rs_build_serde_object: (string * t) list -> t = "rs_build_serde_object"
external rs_build_bitvector: string -> t = "rs_build_serde_bitvector"
external rs_convert_json_to_string: t -> string = "rs_convert_json_to_string"
let add_finalizer value =
(Gc.Expert.add_finalizer_exn value rs_finalize_json_builder) (* TODO: if test throws Invalid_argument exceptions, the values to finalize must be wrapped in ref to ensure heap allocation! *)
let build_null (): t =
let value = rs_build_serde_null () in
let () = add_finalizer value in
value
let build_number (num: int) : t =
let value = rs_build_serde_number num in
let () = add_finalizer value in
value
let build_bool (boolean: bool) : t =
let value = rs_build_serde_bool boolean in
let () = add_finalizer value in
value
let build_string (string_val: string) : t =
let value = rs_build_serde_string string_val in
let () = add_finalizer value in
value
let build_array (obj_list: t list) : t =
let value = rs_build_serde_array_from_list obj_list in
let () = add_finalizer value in
value
let build_object (entries: (string * t) list) : t =
let value = rs_build_serde_object entries in
let () = add_finalizer value in
value
let to_string (serde_json: t) : String.t =
rs_convert_json_to_string serde_json
let of_var_type (var_type: Bil.Types.typ) : t =
match var_type with
| Imm bitsize ->
build_object (
("Immediate", build_number bitsize) :: []
)
| Mem (addr_size, size) ->
build_object (
("Memory", build_object (
("addr_size", build_number (Size.in_bits addr_size)) ::
("elem_size", build_number (Size.in_bits size)) :: []
)) :: [])
| Unk -> build_string "Unknown"
let of_var (var: Var.t) : t =
build_object [
("name", build_string (Var.name var));
("type_", of_var_type (Var.typ var));
("is_temp", build_bool (Var.is_virtual var));
]
let of_cast_type (cast_type: Bil.Types.cast) : t =
build_string (Sexp.to_string (Bil.Types.sexp_of_cast cast_type))
let of_binop_type (binop: Bil.Types.binop) : t =
build_string (Sexp.to_string (Bil.Types.sexp_of_binop binop))
let of_unop_type (unop: Bil.Types.unop) : t =
build_string (Sexp.to_string (Bil.Types.sexp_of_unop unop))
let of_endianness (endianness: Bitvector.endian) : t =
build_string (Sexp.to_string (Bitvector.sexp_of_endian endianness))
let of_bitvector (bitv: Bitvector.t) : t =
let value = rs_build_bitvector (Bitvector.to_string bitv) in
let () = add_finalizer value in
value
let rec of_exp (exp: Exp.t) : t =
begin match exp with
| Var(var) ->
build_object (("Var", of_var var) :: [])
| Int(bitvector) ->
build_object (("Const", of_bitvector bitvector) :: [])
| Load(mem, addr, endian, size) ->
build_object [ ("Load", build_object [
("memory", of_exp mem);
("address", of_exp addr);
("endian", of_endianness endian);
("size", build_number (Size.in_bits size));
]);]
| Store(mem, addr, value, endian, size) ->
build_object [ ("Store", build_object [
("memory", of_exp mem);
("address", of_exp addr);
("value", of_exp value);
("endian", of_endianness endian);
("size", build_number (Size.in_bits size));
]);]
| BinOp(type_, lhs, rhs) ->
build_object [ ("BinOp", build_object [
("op", of_binop_type type_);
("lhs", of_exp lhs);
("rhs", of_exp rhs);
]);]
| UnOp(type_, exp) ->
build_object [ ("UnOp", build_object [
("op", of_unop_type type_);
("arg", of_exp exp);
]);]
| Cast(cast, width, exp) ->
build_object [ ("Cast", build_object [
("kind", of_cast_type cast);
("width", build_number width);
("arg", of_exp exp);
]);]
| Let(var, bound_exp, body_exp) ->
build_object [ ("Let", build_object [
("var", of_var var);
("bound_exp", of_exp bound_exp);
("body_exp", of_exp body_exp)
]);]
| Unknown(text, typ) ->
build_object [ ("Unknown", build_object [
("description", build_string text);
("type_", of_var_type typ);
]);]
| Ite(if_, then_, else_) ->
build_object [ ("IfThenElse", build_object [
("condition", of_exp if_);
("true_exp", of_exp then_);
("false_exp", of_exp else_);
]);]
| Extract(high, low, exp) ->
build_object [ ("Extract", build_object [
("low_bit", build_number low);
("high_bit", build_number high);
("arg", of_exp exp)
]);]
| Concat(left, right) ->
build_object [ ("Concat", build_object [
("left", of_exp left);
("right", of_exp right)
]);]
end
let of_tid (tid: Tid.t) (tid_map: word Tid.Map.t) : t =
build_object [
("id", build_string @@ Tid.name tid);
("address", build_string @@ Address_translation.translate_tid_to_assembler_address_string tid tid_map);
]
let of_def (def: Def.t) (tid_map: word Tid.Map.t) : t =
build_object [
("tid", of_tid (Term.tid def) tid_map);
("term", build_object [
("lhs", of_var (Def.lhs def));
("rhs", of_exp (Def.rhs def));
]);
]
let of_jmp_label (jmp_label: label) (tid_map: word Tid.Map.t) : t =
match jmp_label with
| Direct(tid) ->
build_object [
("Direct", of_tid tid tid_map);
]
| Indirect(exp) ->
build_object [
("Indirect", of_exp exp);
]
let of_call (call: Call.t) (tid_map: word Tid.Map.t) : t =
build_object [
("target", of_jmp_label (Call.target call) tid_map);
("return_", match Call.return call with
| Some(target) -> of_jmp_label target tid_map
| None -> build_null ()
);
]
let of_jmp_kind (kind: jmp_kind) (tid_map: word Tid.Map.t) : t =
match kind with
| Call(call) ->
build_object [
("Call", of_call call tid_map);
]
| Goto(label) ->
build_object [
("Goto", of_jmp_label label tid_map);
]
| Ret(label) ->
build_object [
("Return", of_jmp_label label tid_map);
]
| Int(interrupt_num, tid) ->
build_object [
("Interrupt", build_object [
("value", build_number interrupt_num );
("return_addr", of_tid tid tid_map)
]);
]
let of_jmp (jmp: Jmp.t) (tid_map: word Tid.Map.t) : t =
(* Since BAP 2.0 doesn't emit return statements anymore,
we have check the is_return hint to correct the jump kind for return statements. *)
let is_return = match Term.get_attr jmp Disasm.insn with
| None -> false
| Some(insn) -> Insn.(is return) insn in
let jmp_kind = if is_return then
match Jmp.kind jmp with
| Call(call) -> begin match Call.target call with
| Indirect(exp) -> Ret(Indirect(exp))
| _ -> Jmp.kind jmp
end
| _ -> Jmp.kind jmp
else
Jmp.kind jmp in
build_object [
("tid", of_tid (Term.tid jmp) tid_map);
("term", build_object [
("condition", if Option.is_some (Jmp.guard jmp) then of_exp (Jmp.cond jmp) else build_null ());
("kind", of_jmp_kind jmp_kind tid_map);
]);
]
let of_blk (blk: Blk.t) (tid_map: word Tid.Map.t) : t =
let defs = Seq.to_list (Term.enum def_t blk) in
let defs = List.map defs ~f:(fun def -> of_def def tid_map) in
let jmps = Seq.to_list (Term.enum jmp_t blk) in
let jmps = List.map jmps ~f:(fun jmp -> of_jmp jmp tid_map) in
build_object [
("tid", of_tid (Term.tid blk) tid_map);
("term", build_object [
("defs", build_array defs);
("jmps", build_array jmps);
]);
]
let of_sub (sub: Sub.t) (tid_map: word Tid.Map.t) : t =
let blocks = Seq.to_list (Term.enum blk_t sub) in
let blocks = List.map blocks ~f:(fun block -> of_blk block tid_map) in
build_object [
("tid", of_tid (Term.tid sub) tid_map);
("term", build_object [
("name", build_string (Sub.name sub));
("blocks", build_array blocks);
]);
]
let of_extern_symbol (symbol: extern_symbol) (tid_map: word Tid.Map.t) : t =
build_object [
("tid", of_tid symbol.tid tid_map);
("address", build_string symbol.address);
("name", build_string symbol.name);
("calling_convention", match symbol.cconv with
| Some(cconv) -> build_string cconv
| None -> build_null ()
);
("arguments", build_array (List.map symbol.args ~f:(fun (var, expr, intent) ->
build_object [
("var", of_var var);
("location", of_exp expr);
("intent", match intent with
| Some(In) -> build_string "Input"
| Some(Out) -> build_string "Output"
| Some(Both) -> build_string "Both"
| None -> build_string "Unknown"
)
]
)))
]
let of_program (program: Program.t) (extern_symbols: extern_symbol List.t) (entry_points: Tid.t List.t) (tid_map: word Tid.Map.t) : t =
let subs = Seq.to_list (Term.enum sub_t program) in
let subs = List.map subs ~f:(fun sub -> of_sub sub tid_map) in
build_object [
("tid", of_tid (Term.tid program) tid_map);
("term", build_object [
("subs", build_array subs);
("extern_symbols", build_array (List.map extern_symbols ~f:(fun sym -> of_extern_symbol sym tid_map)));
("entry_points", build_array (List.map entry_points ~f:(fun tid -> of_tid tid tid_map)));
]);
]
let of_project (project: Project.t) (extern_symbols: extern_symbol List.t) (entry_points: Tid.t List.t) (tid_map: word Tid.Map.t) : t =
build_object [
("program", of_program (Project.program project) extern_symbols entry_points tid_map);
("cpu_architecture", build_string (Arch.to_string (Project.arch project)));
("stack_pointer_register", of_var (Symbol_utils.stack_register project));
("callee_saved_registers", build_array (List.map (Cconv.get_register_list project "callee_saved") ~f:(fun reg_name -> build_string reg_name) ));
("parameter_registers", build_array (List.map (Cconv.get_register_list project "params") ~f:(fun reg_name -> build_string reg_name) ));
("return_registers", build_array (List.map (Cconv.get_register_list project "return") ~f:(fun reg_name -> build_string reg_name) ))
]
(** This module allows the creation of SerdeJson objects that can be deserialized
to the corresponding data type in Rust.
Note that this is not optimized for speed, extensive usage could lead to measureable slowdown.
*)
open Core_kernel
open Bap.Std
type t
(** Build a Json Null object *)
val build_null: unit -> t
(** Build a Json boolean object *)
val build_bool: Bool.t -> t
(** Build a Json number object *)
val build_number: int -> t
(** Build a Json string object *)
val build_string: String.t -> t
(** Build a Json array object from a list of Json objects *)
val build_array: t List.t -> t
(** Build a Json object from a list of key-value-pairs *)
val build_object: (String.t * t) List.t -> t
(** Get the Json string corresponding to a Json object *)
val to_string: t -> String.t
val of_var_type: Bil.Types.typ -> t
val of_var: Var.t -> t
val of_cast_type: Bil.Types.cast -> t
val of_binop_type: Bil.Types.binop -> t
val of_unop_type: Bil.Types.unop -> t
val of_endianness: Bitvector.endian -> t
val of_bitvector: Bitvector.t -> t
val of_exp: Exp.t -> t
val of_tid: Tid.t -> word Tid.Map.t -> t
val of_def: Def.t -> word Tid.Map.t -> t
val of_jmp_label: Label.t -> word Tid.Map.t -> t
val of_call: Call.t -> word Tid.Map.t -> t
val of_jmp_kind: jmp_kind -> word Tid.Map.t -> t
val of_jmp: Jmp.t -> word Tid.Map.t -> t
val of_blk: Blk.t -> word Tid.Map.t -> t
val of_sub: Sub.t -> word Tid.Map.t -> t
val of_extern_symbol: Symbol_utils.extern_symbol -> word Tid.Map.t -> t
val of_program: Program.t -> Symbol_utils.extern_symbol List.t -> Tid.t List.t -> word Tid.Map.t -> t
val of_project: Project.t -> Symbol_utils.extern_symbol List.t -> Tid.t List.t -> word Tid.Map.t -> t
open Core_kernel
open Bap.Std
let call_objdump (proj : Project.t) ~flag:(flag : string) ~err:(err : string) : string list =
match Project.get proj filename with
| None -> failwith "[cwe_checker] Project has no file name."
| Some(fname) -> begin
try
let cmd = Format.sprintf ("objdump %s %s") flag fname in
let in_chan = Caml_unix.open_process_in cmd in
let lines = In_channel.input_lines in_chan in
let () = In_channel.close in_chan in
lines
with
Caml_unix.Unix_error (e,fm,argm) ->
failwith (Format.sprintf "%s %s %s %s" err (Caml_unix.error_message e) fm argm)
end
(** Calls objdump with customisable flag and error message. Returns output lines as string list. *)
val call_objdump : Bap.Std.Project.t -> flag:string -> err:string -> string list
open Core_kernel
open Bap.Std
type symbol =
{
address : tid option;
name : string;
}
type extern_symbol =
{
tid : tid;
address : string;
name : string;
cconv : string option;
args : (Var.t * Exp.t * intent option) list;
}
let extern_symbol_blacklist = [
"__cxa_atexit";
"__libc_start_main";
"__cxa_finalize"
]
let extern_symbols = ref []
let dyn_syms = ref None
let found_calls = ref []
let call_finder_run = ref false
(** Parse a line from the dyn-syms output table of objdump. Return the name of a symbol if the symbol is an extern function name. *)
let parse_dyn_sym_line (line : string) : string option =
let columns = String.split_on_chars ~on:[ ' ' ; '\t' ; '\n' ; '\r' ] line
|> List.filter ~f:(fun x -> String.(<>) x "") in
(* Check whether the symbol is a function --> DF and if it is referenced in the file, but defined outside it --> *UND* *)
match ((Stdlib.List.mem "DF" columns) && (Stdlib.List.mem "*UND*" columns)) with
| true -> List.last columns
| false -> None
let parse_dyn_syms (project : Project.t) : String.Set.t =
match !dyn_syms with
| Some(symbol_set) -> symbol_set
| None -> begin
let lines = Support_functions.call_objdump project ~flag:"--dynamic-syms" ~err:"[cwe_checker] Parsing of dynamic symbols failed:" in
match lines with
| _ :: _ :: _ :: _ :: tail -> (* The first four lines are not part of the table *)
let symbol_set = String.Set.of_list (List.filter_map tail ~f:parse_dyn_sym_line) in
dyn_syms := Some(symbol_set);
symbol_set
| _ ->
dyn_syms := Some(String.Set.empty);
String.Set.empty
end
let get_project_calling_convention (project : Project.t) : string option =
Project.get project Bap_abi.name
let build_extern_symbols (project : Project.t) (program : program term) (parsed_symbols : string list) (tid_map : word Tid.Map.t) : unit =
let calling_convention = get_project_calling_convention project in
extern_symbols := List.append !extern_symbols (Seq.to_list (Seq.filter_map (Term.enum sub_t program) ~f:(fun s ->
let sub_name = Sub.name s in
let sub_tid = Term.tid s in
match (Stdlib.List.mem sub_name parsed_symbols) with
| true -> begin
let addr = Address_translation.translate_tid_to_assembler_address_string sub_tid tid_map in
let args = Seq.to_list (Seq.map (Term.enum arg_t s) ~f:(fun a -> (Arg.lhs a, Arg.rhs a, Arg.intent a))) in
Some({tid=sub_tid; address=addr; name=sub_name; cconv=calling_convention; args=args;})
end
| false -> None)))
let build_and_return_extern_symbols (project : Project.t) (program : program term) (tid_map : word Tid.Map.t) : extern_symbol list =
let parsed_symbols = parse_dyn_syms project in
if String.Set.is_empty parsed_symbols then []
else begin
match !extern_symbols with
| [] -> build_extern_symbols project program (String.Set.to_list parsed_symbols) tid_map; !extern_symbols
| _ -> !extern_symbols
end
let add_as_extern_symbol (project : Project.t) (program : program term) (symbol : string) (tid_map : word Tid.Map.t) : unit =
Seq.iter (Term.enum sub_t program) ~f:(fun s ->
match String.equal (Sub.name s) symbol with
| true -> begin
let sub_tid = Term.tid s in
let args = Seq.to_list (Seq.map (Term.enum arg_t s) ~f:(fun a -> (Arg.lhs a, Arg.rhs a, Arg.intent a))) in
let addr = Address_translation.translate_tid_to_assembler_address_string sub_tid tid_map in
extern_symbols := List.append !extern_symbols [{tid=sub_tid; address=addr; name=(Sub.name s); cconv=(get_project_calling_convention project); args=args}]
end
| false -> ()
)
let find_symbol (program : program term) (name : string) : tid option =
Term.enum sub_t program |>
Seq.find_map ~f:(fun s -> Option.some_if (String.(=) (Sub.name s) name) (Term.tid s))
let build_symbols (symbol_names : string list) (prog : program term) : symbol list =
List.map symbol_names ~f:(fun symbol -> let symbol_address = find_symbol prog symbol in
{address = symbol_address; name = symbol;})
|> List.filter ~f:(fun symbol -> match symbol.address with
| Some _ -> true
| _ -> false)
let get_symbol_of_string (prog : program term) (name : string) : symbol option =
let symbol_address = find_symbol prog name in
match symbol_address with
| Some _ -> Some ({
address = symbol_address
; name = name
})
| None -> None
let get_symbol (tid : tid) (symbols : symbol list) : symbol option =
List.find symbols ~f:(
fun symbol -> match symbol.address with
| Some address -> Tid.(=) tid address
| None -> false)
let get_symbol_name_from_jmp (jmp : Jmp.t) (symbols : symbol list) : string =
match Jmp.kind jmp with
| Goto _ | Ret _ | Int (_,_) -> assert(false)
| Call destination -> begin
match Call.target destination with
| Direct addr ->
begin
let symbol = List.find symbols ~f:(fun symbol -> match symbol.address with
| Some address -> Tid.(=) addr address
| _ -> assert(false)) in match symbol with
| Some s -> s.name
| _ -> assert(false)
end
| _ -> assert(false)
end
let get_direct_callsites_of_sub (sub : sub term) : jmp term Sequence.t =
Term.enum blk_t sub |>
Seq.concat_map ~f:(fun blk ->
Term.enum jmp_t blk |> Seq.filter_map ~f:(fun j ->
match Jmp.kind j with
| Goto _ | Ret _ | Int (_,_) -> None
| Call destination -> begin match Call.target destination with
| Direct _tid -> Some j
| _ -> None
end))
let sub_calls_symbol (prog : program term) (sub : sub term) (symbol_name : string) : bool =
let symbol_struct = find_symbol prog symbol_name in
match symbol_struct with
| Some s -> begin
let callsites = get_direct_callsites_of_sub sub in
Seq.exists callsites ~f:(fun callsite -> match Jmp.kind callsite with
| Goto _ | Ret _ | Int (_,_) -> false
| Call destination -> match Call.target destination with
| Direct addr -> Tid.(=) addr s
| _ -> false)
end
| _ -> false
let calls_callsite_symbol (jmp : Jmp.t) (symbol : symbol) : bool =
match Jmp.kind jmp with
| Goto _ | Ret _ | Int (_,_) -> false
| Call dst -> begin
match Call.target dst with
| Direct tid -> begin
match symbol.address with
| Some symbol_tid -> Tid.(=) tid symbol_tid
| None -> false
end
| _ -> false
end
type concrete_call =
{
call_site : tid;
symbol_address : tid;
name : string;
}
let call_finder : (tid * tid) list Term.visitor = object
inherit [(tid * tid) list] Term.visitor
method! enter_jmp jmp tid_list = match Jmp.kind jmp with
| Goto _ | Ret _ | Int (_,_) -> tid_list
| Call destination -> begin
match Call.target destination with
| Direct addr -> (Term.tid jmp, addr) :: tid_list
| _ -> tid_list
end
end
let get_calls (program : program term) : (tid * tid) list =
match !call_finder_run with
| true -> !found_calls
| false -> begin
call_finder_run := true;
found_calls := call_finder#run program [];
!found_calls
end
let check_if_symbols_resolved (project : Project.t) (program : program term) (tid_map : word Tid.Map.t) : bool =
let extern = build_and_return_extern_symbols project program tid_map in
let extern = List.filter extern ~f:(fun ext_sym -> not (Stdlib.List.mem ext_sym.name extern_symbol_blacklist)) in
match List.is_empty extern with
| true -> false
| false -> begin
let calls = List.map (get_calls program) ~f:(fun call -> match call with (_, dst) -> dst) in
let not_resolved = List.filter extern ~f:(fun ext_sym -> not (Stdlib.List.mem ext_sym.tid calls)) in
List.length extern <> List.length not_resolved
end
let transform_call_to_concrete_call ((src_tid, dst_tid) : tid * tid) (symbols : symbol list) : concrete_call =
match (get_symbol dst_tid symbols) with
| Some symbol -> {call_site = src_tid; symbol_address = dst_tid; name = symbol.name}
| None -> assert(false)
let filter_calls_to_symbols (calls : (tid * tid) list) (symbols : symbol list) : concrete_call list =
List.filter calls ~f:(
fun (_, dst) -> List.exists symbols ~f:(
fun symbol -> match symbol.address with
| Some address -> Tid.(=) address dst
| None -> false))
|> List.map ~f:(fun call -> transform_call_to_concrete_call call symbols)
let is_interesting_callsite (jmp : Jmp.t) (relevant_calls : concrete_call list): bool =
match Jmp.kind jmp with
| Goto _ | Ret _ | Int (_,_) -> false
| Call dst -> match Call.target dst with
| Direct tid -> List.exists relevant_calls ~f:(fun c -> Tid.(=) c.symbol_address tid)
| _ -> false
let check_calls (relevant_calls : concrete_call list) (prog : program term) (proj : 'a) (tid_map : 'b) (symbols : 'c) (check_func) : unit =
Seq.iter (Term.enum sub_t prog)
~f:(fun sub ->
begin
Seq.iter (Term.enum blk_t sub)
~f:(fun blk -> Seq.iter (Term.enum jmp_t blk)
~f:(fun jmp -> if is_interesting_callsite jmp relevant_calls then
check_func proj prog sub blk jmp tid_map symbols))
end)
let get_symbol_call_count_of_sub (symbol_name : string) (sub : Sub.t) (prog : Program.t) : int =
match find_symbol prog symbol_name with
| Some s -> begin
Seq.to_list (get_direct_callsites_of_sub sub)
|> List.filter ~f:(fun callsite ->
match Jmp.kind callsite with
| Goto _ | Ret _ | Int (_,_) -> false
| Call destination -> match Call.target destination with
| Direct addr -> Tid.(=) addr s
| _ -> false)
|> List.length
end
| _ -> 0
let extract_direct_call_tid_from_block (block : blk term) : tid option =
let jmp_instructions = Term.enum jmp_t block in
Seq.fold jmp_instructions ~init:None ~f:(fun already_found instr ->
match already_found with
| Some(symb) -> Some(symb)
| None ->
match Jmp.kind instr with
| Goto _ | Ret _ | Int (_,_) -> None
| Call dst -> match Call.target dst with
| Direct tid ->
Some(tid)
| _ -> None)
let get_program_entry_points (program : program term) : sub term List.t =
let subfunctions = Term.enum sub_t program in
let entry_points = Seq.filter subfunctions ~f:(fun subfn -> Term.has_attr subfn Sub.entry_point) in
match Seq.find subfunctions ~f:(fun subfn -> String.(=) "main" (Sub.name subfn)) with
| Some(main_fn) ->
if Seq.exists entry_points ~f:(fun elem -> Sub.(=) elem main_fn) then
Seq.to_list entry_points
else
main_fn :: (Seq.to_list entry_points)
| None -> Seq.to_list entry_points
let stack_register (project : Project.t) : Var.t =
let arch = Project.arch project in
let module Target = (val target_of_arch arch) in
Target.CPU.sp
let flag_register_list (project : Project.t) : Var.t list =
let arch = Project.arch project in
let module Target = (val target_of_arch arch) in
Target.CPU.zf :: Target.CPU.cf :: Target.CPU.vf :: Target.CPU.nf :: []
let arch_pointer_size_in_bytes (project : Project.t) : int =
let arch = Project.arch project in
Size.in_bytes (Arch.addr_size arch)
(** This module implements functionality to work with symbols (e.g. malloc).*)
open Core_kernel
type concrete_call = {
call_site : Bap.Std.tid
; symbol_address : Bap.Std.tid
; name : string;
}
(** This type represents a symbol like malloc or memcpy. *)
type symbol = {
address : Bap.Std.tid option
; name : string;
}
(** This type represents an external symbol. *)
type extern_symbol = {
tid : Bap.Std.tid
; address : string
; name : string
; cconv : string option
; args : (Bap.Std.Var.t * Bap.Std.Exp.t * Bap.Std.intent option) list;
}
(** Returns a list of those function names that are extern symbols.
TODO: Since we do not do name demangling here, check whether bap name demangling
yields different function names for the symbols. *)
val parse_dyn_syms: Bap.Std.Project.t -> String.Set.t
(** Parses each line returned from dynamic symbol call. *)
val parse_dyn_sym_line : string -> string option
(** Returns the calling convention for the whole project inferred by Bap. *)
val get_project_calling_convention : Bap.Std.Project.t -> string option
(** Checks whether the external symbols have already been built. If not, it calls the symbol builder. *)
val build_and_return_extern_symbols : Bap.Std.Project.t -> Bap.Std.program Bap.Std.term -> Bap.Std.word Bap.Std.Tid.Map.t -> extern_symbol list
(** Builds a list of function symbols type from external function names given by objdump. *)
val build_extern_symbols : Bap.Std.Project.t -> Bap.Std.program Bap.Std.term -> string list -> Bap.Std.word Bap.Std.Tid.Map.t -> unit
(** Adds an analysed internal symbol to the list of external symbols. *)
val add_as_extern_symbol : Bap.Std.Project.t -> Bap.Std.program Bap.Std.term -> string -> Bap.Std.word Bap.Std.Tid.Map.t -> unit
(** Finds a symbol string in a program and returns its IR address (tid). *)
val find_symbol : Bap.Std.program Bap.Std.term -> string -> Bap.Std.tid option
(** builds a list of symbols from a list of strings for a given program
TODO: maybe another data structure like a hashmap would be better. *)
val build_symbols : string list -> Bap.Std.program Bap.Std.term -> symbol list
(** Gets a symbol from a symbol list *)
val get_symbol : Bap.Std.tid -> symbol list -> symbol option
(** Gets a symbol from a string *)
val get_symbol_of_string : Bap.Std.program Bap.Std.term -> string -> symbol option
(** Given a JMP and symbol list, it returns its name as string.
Use only if you are sure that the JMP calls a symbol. *)
val get_symbol_name_from_jmp : Bap.Std.Jmp.t -> symbol list -> string
(** Checks if a subfunction calls a symbol. *)
val sub_calls_symbol: Bap.Std.program Bap.Std.term -> Bap.Std.sub Bap.Std.term -> string -> bool
(** Checks if a callsite calls a symbol *)
val calls_callsite_symbol : Bap.Std.Jmp.t -> symbol -> bool
(** This function finds all (direct) calls in a program. It returns a list of tuples of (callsite, address).*)
val call_finder : (Bap.Std.tid * Bap.Std.tid) list Bap.Std.Term.visitor
(** Checks whether the call_finder has already extracted the calls from the program, and if so, returns a global variable.
Otherwise the call_finder is called *)
val get_calls : Bap.Std.program Bap.Std.term -> (Bap.Std.tid * Bap.Std.tid) list
(** Checks whether extern symbols have been resolved by Bap. If not a single symbol has been resolved, an error message is returned. *)
val check_if_symbols_resolved : Bap.Std.Project.t -> Bap.Std.program Bap.Std.term -> Bap.Std.word Bap.Std.Tid.Map.t -> bool
(** Transform a call (e.g. found with call_finder) to concrete_call with the symbol resolved.*)
val transform_call_to_concrete_call :
Bap.Std.tid * Bap.Std.tid -> symbol list -> concrete_call
(** Filters out all calls (callsite, target) that do not call to a known symbol.*)
val filter_calls_to_symbols :
(Bap.Std.tid * Bap.Std.tid) list ->
symbol list -> concrete_call list
(** Checks if a callsite is in a list of (interesting) concrete_calls *)
val is_interesting_callsite : Bap.Std.Jmp.t -> concrete_call list -> bool
(** TODO *)
val check_calls :
concrete_call list ->
Bap.Std.program Bap.Std.term ->
'a ->
'b ->
'c ->
('a ->
Bap.Std.program Bap.Std.term ->
Bap.Std.sub Bap.Std.term ->
Bap.Std.blk Bap.Std.term -> Bap.Std.jmp Bap.Std.term -> 'b -> 'c -> unit) ->
unit
(** Returns a sequence of all (direct) callsites in a subfunction *)
val get_direct_callsites_of_sub :
Bap.Std.sub Bap.Std.term -> Bap.Std.jmp Bap.Std.term Core_kernel.Sequence.t
(** Returns call count of symbol in function *)
val get_symbol_call_count_of_sub : string -> Bap.Std.Sub.t -> Bap.Std.Program.t -> int
(** Returns Some(target tid) if the block contains a direct call or None if it does not. *)
val extract_direct_call_tid_from_block : Bap.Std.blk Bap.Std.term -> Bap.Std.tid option
(** Returns a sequence of all entry points of the program.
TODO: The _start entry point usually calls a libc-function which then calls the main function. Since right now only direct
calls are tracked, our graph traversal may never find the main function. For now, we add it by hand to the entry points. *)
val get_program_entry_points : Bap.Std.program Bap.Std.term -> Bap.Std.sub Bap.Std.term List.t
(** Returns the stack register on the architecture of the given project. *)
val stack_register: Bap.Std.Project.t -> Bap.Std.Var.t
(** Returns a list of the known flag registers on the architecture of the given project.
TODO: Right now it only returns flag registers that exist on all architectures.
We should add known architecture dependend flag registers, too. *)
val flag_register_list: Bap.Std.Project.t -> Bap.Std.Var.t list
(** Returns the pointer size in bytes on the architecture of the given project. *)
val arch_pointer_size_in_bytes: Bap.Std.Project.t -> int
import os
import subprocess
def build_bap_cmd(filename, target, arch, compiler, check_name = None):
if check_name is None:
check_name = 'CWE%s' % target
if 'travis' in os.environ['USER']:
abs_path = os.path.abspath('test/artificial_samples/build/cwe_%s_%s_%s.out' % (filename, arch, compiler))
cmd = 'docker run --rm -v %s:/tmp/input cwe-checker:latest cwe_checker /tmp/input -partial=%s' % (abs_path, check_name)
else:
cmd = 'cwe_checker test/artificial_samples/build/cwe_%s_%s_%s.out -partial=%s' % (filename, arch, compiler, check_name)
return cmd.split()
def build_bap_emulation_cmd(filename, target, arch, compiler):
if 'travis' in os.environ['USER']:
abs_path = os.path.abspath('test/artificial_samples/build/cwe_%s_%s_%s.out' % (filename, arch, compiler))
cmd = 'docker run --rm -v %s:/tmp/input cwe-checker:latest bap /tmp/input --recipe=recipes/emulation' % abs_path
else:
cmd = 'bap test/artificial_samples/build/cwe_%s_%s_%s.out --recipe=recipes/emulation' % (filename, arch, compiler)
return cmd.split()
def execute_and_check_occurence(filename, target, arch, compiler, string, check_name = None):
occurence = 0
bap_cmd = build_bap_cmd(filename, target, arch, compiler, check_name)
output = subprocess.check_output(bap_cmd)
for l in output.splitlines():
if string in l:
occurence += 1
return occurence
def execute_emulation_and_check_occurence(filename, target, arch, compiler, string):
occurence = 0
bap_cmd = build_bap_emulation_cmd(filename, target, arch, compiler)
output = subprocess.check_output(bap_cmd)
for l in output.splitlines():
if string in l:
occurence += 1
return occurence
import json
import os
import subprocess
import unittest
class TestCheckPath(unittest.TestCase):
def setUp(self):
if 'travis' in os.environ['USER']:
abs_path = os.path.abspath('test/artificial_samples/build/check_path_x64_gcc.out')
self.cmd = 'docker run --rm -v %s:/tmp/input cwe-checker:latest cwe_checker /tmp/input -json -check-path -no-logging' % abs_path
else:
self.cmd = 'cwe_checker test/artificial_samples/build/check_path_x64_gcc.out -config=src/config.json -json -check-path -no-logging'
def test_check_path_01_x64_gcc(self):
output = subprocess.check_output(self.cmd.split())
j = json.loads(output)
self.assertTrue('check_path' in j)
self.assertEqual(len(j['check_path']), 5)
import unittest
import cwe_checker_testlib
class TestCwe190(unittest.TestCase):
def setUp(self):
self.target = '190'
self.string = b'Integer Overflow or Wraparound'
@unittest.skip("FIXME")
def test_cwe190_01_arm_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe190_01_aarch64_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe190_01_x86_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe190_01_x64_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_mips_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_mipsel_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_mips64_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_mips64el_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_ppc_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe190_01_ppc64_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_ppc64le_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_arm_clang(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe190_01_aarch64_clang(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe190_01_x86_clang(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe190_01_x64_clang(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Depends on proper MIPS support in BAP")
def test_cwe190_01_mips_clang(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_mipsel_clang(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_mips64_clang(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_mips64el_clang(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe190_01_ppc64_clang(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_ppc64le_clang(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_x86_mingw_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe190_01_x64_mingw_gcc(self):
expect_res = 3
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe215(unittest.TestCase):
def setUp(self):
self.target = '215'
self.filename = '476'
self.string = b'Information Exposure Through Debug Information'
def test_cwe215_01_x64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_x64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_x86_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'x86', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_x86_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'x86', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_arm_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'arm', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_arm_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'arm', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe215_01_aarch64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'aarch64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe215_01_aarch64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'aarch64', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_mips_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'mips', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_mips_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'mips', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_mipsel_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'mipsel', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_mipsel_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'mipsel', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_mips64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'mips64', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_mips64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'mips64', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_mips64el_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'mips64el', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_mips64el_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'mips64el', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_ppc_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'ppc', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe215_01_ppc64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'ppc64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe215_01_ppc64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'ppc64', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_ppc64le_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'ppc64le', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe215_01_ppc64le_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'ppc64le', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe215_01_x86_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'x86', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe215_01_x64_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.filename, self.target, 'x64', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe243(unittest.TestCase):
def setUp(self):
self.target = '243'
self.string = b'The program utilizes chroot without dropping privileges and/or changing the directory'
def test_cwe243_01_x64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe243_01_x64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe243_01_x86_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe243_01_x86_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_01_arm_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_01_arm_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe243_01_aarch64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe243_01_aarch64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_01_mips_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Depends on proper MIPS support in BAP")
def test_cwe243_01_mips_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_01_mipsel_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_01_mipsel_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_01_mips64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_01_mips64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_01_mips64el_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_01_mips64el_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_01_ppc_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe243_01_ppc64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe243_01_ppc64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_01_ppc64le_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_01_ppc64le_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_x64_gcc(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_x64_clang(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Investigate and fix this issue")
def test_cwe243_02_x86_gcc(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'x86', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_x86_clang(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'x86', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_arm_gcc(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'arm', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_arm_clang(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'arm', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe243_02_aarch64_gcc(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'aarch64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe243_02_aarch64_clang(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'aarch64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Depends on proper MIPS support in BAP")
def test_cwe243_02_mips_gcc(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'mips', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_mips_clang(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'mips', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_mipsel_gcc(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'mipsel', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_mipsel_clang(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'mipsel', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_mips64_gcc(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'mips64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_mips64_clang(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'mips64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_mips64el_gcc(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'mips64el', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_mips64el_clang(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'mips64el', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_ppc_gcc(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'ppc', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe243_02_ppc64_gcc(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'ppc64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe243_02_ppc64_clang(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'ppc64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_ppc64le_gcc(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'ppc64le', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe243_02_ppc64le_clang(self):
expect_res = 0
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target + '_clean', 'ppc64le', 'clang', self.string)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe248(unittest.TestCase):
def setUp(self):
self.target = '248'
self.string = b'Possibly Uncaught Exception'
def test_cwe248_01_x64_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'g++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe248_01_x64_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Fix CPP compilation issue for x86")
def test_cwe248_01_x86_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'g++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe248_01_x86_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe248_01_arm_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'g++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe248_01_aarch64_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'g++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Depends on proper MIPS support in BAP")
def test_cwe248_01_mips_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'g++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe248_01_mipsel_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'g++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe248_01_mips64_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'g++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe248_01_mips64el_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'g++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe248_01_ppc_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'g++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe248_01_ppc64_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'g++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe248_01_ppc64le_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'g++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe248_01_x86_mingw_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'mingw32-g++', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe248_01_x64_mingw_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'mingw32-g++', self.string)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe332(unittest.TestCase):
def setUp(self):
self.target = '332'
self.string = b'Insufficient Entropy in PRNG'
def test_cwe332_01_x64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe332_01_x64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe332_01_x86_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe332_01_x86_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_arm_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_arm_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe332_01_aarch64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe332_01_aarch64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_mips_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_mips_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_mipsel_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_mipsel_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Depends on proper MIPS support in BAP')
def test_cwe332_01_mips64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_mips64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_mips64el_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_mips64el_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_ppc_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe332_01_ppc64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe332_01_ppc64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_ppc64le_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_ppc64le_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_x86_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe332_01_x64_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe367(unittest.TestCase):
def setUp(self):
self.target = '367'
self.string = b'Time-of-check Time-of-use Race Condition'
def test_cwe367_01_x64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe367_01_x64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe367_01_x86_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe367_01_x86_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe367_01_arm_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe367_01_arm_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe367_01_aarch64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe367_01_aarch64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Depends on proper MIPS support in BAP')
def test_cwe367_01_mips_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe367_01_mips_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe367_01_mipsel_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe367_01_mipsel_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe367_01_mips64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe367_01_mips64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe367_01_mips64el_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe367_01_mips64el_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe367_01_ppc_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe367_01_ppc64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe367_01_ppc64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe367_01_ppc64le_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe367_01_ppc64le_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe367_01_x86_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe367_01_x64_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe415(unittest.TestCase):
def setUp(self):
self.target = '415'
self.string = b'Double Free'
self.check_name = 'Memory'
def test_cwe415_01_x64_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
def test_cwe415_01_x64_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME: Needs pointer alignment tracking, or else SP = SP & Const loses the stack offset')
def test_cwe415_01_x86_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
def test_cwe415_01_x86_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
def test_cwe415_01_arm_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
def test_cwe415_01_arm_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe415_01_aarch64_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe415_01_aarch64_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME: Check again when BAP handles the ZERO register of MIPS.")
def test_cwe415_01_mips_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME: Check again when BAP handles the ZERO register of MIPS.")
def test_cwe415_01_mips_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME: Check again when BAP handles the ZERO register of MIPS.")
def test_cwe415_01_mipsel_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME: Check again when BAP handles the ZERO register of MIPS.")
def test_cwe415_01_mipsel_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("BAP does not recognize extern calls")
def test_cwe415_01_mips64_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("BAP does not recognize extern calls")
def test_cwe415_01_mips64_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("BAP does not recognize extern calls")
def test_cwe415_01_mips64el_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("BAP does not recognize extern calls")
def test_cwe415_01_mips64el_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
def test_cwe415_01_ppc_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('Dynamic Symbol calls are mangled by BAP')
def test_cwe415_01_ppc64_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('Dynamic Symbol calls are mangled by BAP')
def test_cwe415_01_ppc64_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('Dynamic Symbol calls are mangled by BAP')
def test_cwe415_01_ppc64le_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('Dynamic Symbol calls are mangled by BAP')
def test_cwe415_01_ppc64le_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME')
def test_cwe415_01_x86_mingw_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'mingw32-gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME')
def test_cwe415_01_x64_mingw_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'mingw32-gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe416(unittest.TestCase):
def setUp(self):
self.target = '416'
self.string = b'Use After Free'
self.check_name = 'Memory'
def test_cwe416_01_x64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
def test_cwe416_01_x64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME: Needs pointer alignment tracking, or else SP = SP & Const loses the stack offset')
def test_cwe416_01_x86_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
def test_cwe416_01_x86_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
def test_cwe416_01_arm_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
def test_cwe416_01_arm_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe416_01_aarch64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe416_01_aarch64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME: Check again when BAP handles the ZERO register of MIPS.")
def test_cwe416_01_mips_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME: Check again when BAP handles the ZERO register of MIPS.")
def test_cwe416_01_mips_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME: Check again when BAP handles the ZERO register of MIPS.")
def test_cwe416_01_mipsel_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME: Check again when BAP handles the ZERO register of MIPS.")
def test_cwe416_01_mipsel_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("BAP does not recognize extern calls")
def test_cwe416_01_mips64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("BAP does not recognize extern calls")
def test_cwe416_01_mips64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("BAP does not recognize extern calls")
def test_cwe416_01_mips64el_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("BAP does not recognize extern calls")
def test_cwe416_01_mips64el_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
#@unittest.skip('FIXME: Check again when moved to BAP 2.1')
def test_cwe416_01_ppc_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('Dynamic Symbol calls are mangled by BAP')
def test_cwe416_01_ppc64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('Dynamic Symbol calls are mangled by BAP')
def test_cwe416_01_ppc64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('Dynamic Symbol calls are mangled by BAP')
def test_cwe416_01_ppc64le_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('Dynamic Symbol calls are mangled by BAP')
def test_cwe416_01_ppc64le_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'clang', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe416_01_x86_mingw_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'mingw32-gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME')
def test_cwe416_01_x64_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'mingw32-gcc', self.string, self.check_name)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe426(unittest.TestCase):
def setUp(self):
self.target = '426'
self.string = b'Untrusted Search Path'
def test_cwe426_01_x64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe426_01_x64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_x86_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe426_01_x86_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_arm_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_arm_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe426_01_aarch64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe426_01_aarch64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Depends on proper MIPS support in BAP")
def test_cwe426_01_mips_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_mips_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_mipsel_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_mipsel_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_mips64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_mips64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_mips64el_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_mips64el_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_ppc_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe426_01_ppc64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe426_01_ppc64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_ppc64le_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe426_01_ppc64le_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'clang', self.string)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe457(unittest.TestCase):
def setUp(self):
self.target = '457'
self.string = b'Use of Uninitialized Variable'
@unittest.skip("FIXME")
def test_cwe457_01_x64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe457_01_x64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe457_01_x86_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe457_01_x86_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe457_01_arm_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe457_01_arm_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe457_01_aarch64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe457_01_aarch64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Depends on proper MIPS support in BAP")
def test_cwe457_01_mips_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe457_01_mips_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe457_01_mipsel_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe457_01_mipsel_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe457_01_mips64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe457_01_mips64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe457_01_mips64el_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe457_01_mips64el_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe457_01_ppc_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe457_01_ppc64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe457_01_ppc64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe457_01_ppc64le_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe457_01_ppc64le_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe457_01_x86_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe457_01_x64_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe467(unittest.TestCase):
def setUp(self):
self.target = '467'
self.string = b'Use of sizeof on a Pointer Type'
def test_cwe467_01_x64_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe467_01_x64_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_x86_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe467_01_x86_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_arm_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_arm_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe467_01_aarch64_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe467_01_aarch64_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Depends on proper MIPS support in BAP")
def test_cwe467_01_mips_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_mips_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_mipsel_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_mipsel_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_mips64_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_mips64_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_mips64el_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_mips64el_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_ppc_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe467_01_ppc64_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe467_01_ppc64_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_ppc64le_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_ppc64le_clang(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_x86_mingw_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe467_01_x64_mingw_gcc(self):
expect_res = 2
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe476(unittest.TestCase):
def setUp(self):
self.target = '476'
self.string = b'NULL Pointer Dereference'
def test_cwe476_01_x64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe476_01_x64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe476_01_x86_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe476_01_x86_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe476_01_arm_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe476_01_arm_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe476_01_aarch64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe476_01_aarch64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Depends on proper MIPS support in BAP")
def test_cwe476_01_mips_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe476_01_mips_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe476_01_mipsel_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe476_01_mipsel_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe476_01_mips64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe476_01_mips64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe476_01_mips64el_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe476_01_mips64el_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe476_01_ppc_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Fix issue in CWE476 implementation to support PPC")
def test_cwe476_01_ppc64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Fix issue in CWE476 implementation to support PPC")
def test_cwe476_01_ppc64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Fix issue in CWE476 implementation to support PPC")
def test_cwe476_01_ppc64le_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Fix issue in CWE476 implementation to support PPC")
def test_cwe476_01_ppc64le_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe476_01_x86_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe476_01_x64_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe560(unittest.TestCase):
def setUp(self):
self.target = '560'
self.string = b'Use of umask() with chmod-style Argument'
def test_cwe560_01_x64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe560_01_x64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe560_01_x86_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe560_01_x86_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Args of umask to not seem to be found by BAP. Investigate in the future")
def test_cwe560_01_arm_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe560_01_arm_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe560_01_aarch64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe560_01_aarch64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Depends on proper MIPS support in BAP")
def test_cwe560_01_mips_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe560_01_mips_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe560_01_mipsel_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe560_01_mipsel_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe560_01_mips64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe560_01_mips64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe560_01_mips64el_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe560_01_mips64el_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe560_01_ppc_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe560_01_ppc64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe560_01_ppc64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe560_01_ppc64le_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe560_01_ppc64le_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe560_01_x86_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe560_01_x64_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe676(unittest.TestCase):
def setUp(self):
self.target = '676'
self.string = b'Use of Potentially Dangerous Function'
def test_cwe676_01_x64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe676_01_x64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
def test_cwe676_01_x86_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe676_01_x86_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe676_01_arm_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe676_01_arm_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'arm', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe676_01_aarch64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe676_01_aarch64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'aarch64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("Depends on proper MIPS support in BAP")
def test_cwe676_01_mips_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe676_01_mips_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe676_01_mipsel_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe676_01_mipsel_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mipsel', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe676_01_mips64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe676_01_mips64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe676_01_mips64el_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe676_01_mips64el_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'mips64el', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe676_01_ppc_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe676_01_ppc64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('Not supported by BAP. (no recognizable code backtrace)')
def test_cwe676_01_ppc64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe676_01_ppc64le_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip('FIXME!')
def test_cwe676_01_ppc64le_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'ppc64le', 'clang', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe676_01_x86_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x86', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
@unittest.skip("FIXME")
def test_cwe676_01_x64_mingw_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'mingw32-gcc', self.string)
self.assertEqual(res, expect_res)
import unittest
import cwe_checker_testlib
class TestCwe782(unittest.TestCase):
def setUp(self):
self.target = '782'
self.string = b'Exposed IOCTL with Insufficient Access Control'
def test_cwe782_01_x64_gcc(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'gcc', self.string)
self.assertEqual(res, expect_res)
def test_cwe782_01_x64_clang(self):
expect_res = 1
res = cwe_checker_testlib.execute_and_check_occurence(
self.target, self.target, 'x64', 'clang', self.string)
self.assertEqual(res, expect_res)
import os
import subprocess
import json
import unittest
class TestFileOutput(unittest.TestCase):
def setUp(self):
self.res_file = '/tmp/res.json'
self.cmd = 'bap test/artificial_samples/build/cwe_190_x64_gcc.out --pass=cwe-checker --cwe-checker-config=src/config.json --cwe-checker-json --cwe-checker-out=%s' % self.res_file
def test_can_output_file(self):
if 'travis' in os.environ['USER']:
self.skipTest('Travis detected: can not create files on Travis!')
subprocess.check_output(self.cmd.split())
with open(self.res_file) as f:
j = json.load(f)
os.remove(self.res_file)
self.assertTrue('warnings' in j)
import os
import subprocess
import json
import unittest
class TestJson(unittest.TestCase):
def setUp(self):
if 'travis' in os.environ['USER']:
abs_path = os.path.abspath('test/artificial_samples/build/cwe_190_x64_gcc.out')
self.cmd = 'docker run --rm -v %s:/tmp/input cwe-checker:latest cwe_checker /tmp/input -json -no-logging' % abs_path
else:
self.cmd = 'cwe_checker test/artificial_samples/build/cwe_190_x64_gcc.out -config=src/config.json -json -no-logging'
def test_can_output_json(self):
output = subprocess.check_output(self.cmd.split())
j = json.loads(output)
self.assertTrue('warnings' in j)
all:
bapbundle remove unit_tests_cwe_checker.plugin
bapbuild -r -Is analysis,checkers,utils unit_tests_cwe_checker.plugin -pkgs alcotest,yojson,unix,ppx_jane,cwe_checker_core
bapbundle install unit_tests_cwe_checker.plugin
bap tmp/no_symbols_gcc.out --pass=unit-tests-cwe-checker --unit-tests-cwe-checker-tests=Symbols
bap tmp/arrays_gcc.out --pass=unit-tests-cwe-checker --unit-tests-cwe-checker-tests=DynSyms
bap tmp/arrays_x86_64-w64-mingw32_gcc.out --pass=unit-tests-cwe-checker --unit-tests-cwe-checker-tests=Cconv
bap tmp/arrays_i686-w64-mingw32_gcc.out --pass=unit-tests-cwe-checker --unit-tests-cwe-checker-tests=Cconv
bap tmp/arrays_x86_clang.out --pass=unit-tests-cwe-checker --unit-tests-cwe-checker-tests=Cconv
bap tmp/arrays_arm_gcc.out --pass=unit-tests-cwe-checker --unit-tests-cwe-checker-tests=Cconv
bap tmp/arrays_mips64_gcc.out --pass=unit-tests-cwe-checker --unit-tests-cwe-checker-tests=Cconv
bap tmp/arrays_powerpc_gcc.out --pass=unit-tests-cwe-checker --unit-tests-cwe-checker-tests=Cconv
bap tmp/arrays_gcc.out --pass=unit-tests-cwe-checker --unit-tests-cwe-checker-tests=MemRegion,TypeInference,CWE476,SerdeJson
bap tmp/c_constructs_gcc.out --pass=unit-tests-cwe-checker --unit-tests-cwe-checker-tests=CWE560,AddrTrans
bapbundle remove unit_tests_cwe_checker.plugin
clean:
bapbuild -clean
open Bap.Std
(* open Core_kernel *)
open Cwe_checker_core
let check msg x = Alcotest.(check bool) msg true x
let test_add () : unit =
let bv num = Bitvector.of_int num ~width:32 in
let x = Mem_region.empty () in
let x = Mem_region.add x "Five" ~pos:(bv 3) ~size:(bv 5) in
let x = Mem_region.add x "Seven" ~pos:(bv 9) ~size:(bv 7) in
let x = Mem_region.add x "Three" ~pos:(bv 0) ~size:(bv 3) in
check "add_ok" (Some(Ok("Five", bv 5)) = (Mem_region.get x (bv 3)));
check "add_err" (Some(Error(())) = (Mem_region.get x (bv 1)));
check "add_none" (None = (Mem_region.get x (bv 8)))
let test_minus () =
let bv num = Bitvector.of_int num ~width:32 in
let x = Mem_region.empty () in
let x = Mem_region.add x "One" ~pos:(bv (-8)) ~size:(bv 8) in
check "negative_index" (Some(Ok("One", bv 8)) = Mem_region.get x (Bitvector.unsigned (bv (-8))))
let test_remove () =
let bv num = Bitvector.of_int num ~width:32 in
let x = Mem_region.empty () in
let x = Mem_region.add x "One" ~pos:(bv 0) ~size:(bv 10) in
let x = Mem_region.add x "Two" ~pos:(bv 15) ~size:(bv 11) in
let x = Mem_region.remove x ~pos:(bv 5) ~size:(bv 20) in
check "remove_error_before" (Some(Error()) = Mem_region.get x (bv 4));
check "remove_none1" (None = Mem_region.get x (bv 5));
check "remove_none2" (None = Mem_region.get x (bv 24));
check "remove_error_after1" (Some(Error()) = Mem_region.get x (bv 25));
check "remove_error_after2" (None = Mem_region.get x (bv 26))
let test_mark_error () =
let bv num = Bitvector.of_int num ~width:32 in
let x = Mem_region.empty () in
let x = Mem_region.add x "One" ~pos:(bv 0) ~size:(bv 10) in
let x = Mem_region.mark_error x ~pos:(bv 5) ~size:(bv 10) in
check "mark_error1" (Some(Error()) = Mem_region.get x (bv 0));
check "mark_error2" (Some(Error()) = Mem_region.get x (bv 14));
check "mark_error3" (None = Mem_region.get x (bv 15))
let test_merge () =
let bv num = Bitvector.of_int num ~width:32 in
let x = Mem_region.empty () in
let x = Mem_region.add x "One" ~pos:(bv 0) ~size:(bv 10) in
let x = Mem_region.add x "Two" ~pos:(bv 15) ~size:(bv 5) in
let x = Mem_region.add x "Three" ~pos:(bv 25) ~size:(bv 5) in
let y = Mem_region.empty () in
let y = Mem_region.add y "One" ~pos:(bv 1) ~size:(bv 10) in
let y = Mem_region.add y "Two" ~pos:(bv 15) ~size:(bv 5) in
let y = Mem_region.add y "Four" ~pos:(bv 25) ~size:(bv 5) in
let merge_fn a b = if a = b then Some(Ok(a)) else Some(Error()) in
let z = Mem_region.merge x y ~data_merge:merge_fn in
check "merge_intersect" (Some(Error()) = Mem_region.get z (bv 0));
check "merge_match_ok" (Some(Ok("Two", bv 5)) = Mem_region.get z (bv 15));
check "merge_match_error" (Some(Error()) = Mem_region.get z (bv 25))
let test_equal () =
let bv num = Bitvector.of_int num ~width:32 in
let x = Mem_region.empty () in
let x = Mem_region.add x "One" ~pos:(bv 0) ~size:(bv 10) in
let x = Mem_region.add x "Two" ~pos:(bv 15) ~size:(bv 5) in
let y = Mem_region.empty () in
let y = Mem_region.add y "Two" ~pos:(bv 15) ~size:(bv 5) in
check "equal_no" (false = (Mem_region.equal x y ~data_equal:(fun x y -> x = y)));
let y = Mem_region.add y "One" ~pos:(bv 0) ~size:(bv 10) in
check "equal_yes" (Mem_region.equal x y ~data_equal:(fun x y -> x = y))
let test_around_zero () =
let bv num = Bitvector.of_int num ~width:32 in
let x = Mem_region.empty () in
let x = Mem_region.add x "One" ~pos:(bv (-5)) ~size:(bv 10) in
let x = Mem_region.add x "Two" ~pos:(bv 0) ~size:(bv 10) in
check "around_zero1" (Some(Error()) = Mem_region.get x (bv (-5)));
let x = Mem_region.empty () in
let x = Mem_region.add x "One" ~pos:(bv 0) ~size:(bv 10) in
let x = Mem_region.add x "Two" ~pos:(bv (-5)) ~size:(bv 10) in
check "around_zero2" (Some(Error()) = Mem_region.get x (bv 0));
let x = Mem_region.empty () in
let x = Mem_region.add x "One" ~pos:(bv (-5)) ~size:(bv 20) in
let x = Mem_region.add x "Two" ~pos:(bv 0) ~size:(bv 10) in
check "around_zero3" (Some(Error()) = Mem_region.get x (bv (-5)));
let x = Mem_region.empty () in
let x = Mem_region.add x "One" ~pos:(bv 0) ~size:(bv 10) in
let x = Mem_region.add x "Two" ~pos:(bv (-5)) ~size:(bv 20) in
check "around_zero2" (Some(Error()) = Mem_region.get x (bv 0))
let test_list_data () =
let bv num = Bitvector.of_int num ~width:32 in
let x = Mem_region.empty () in
let x = Mem_region.add x "One" ~pos:(bv (15)) ~size:(bv 10) in
let x = Mem_region.add x "Two" ~pos:(bv 0) ~size:(bv 10) in
let x = Mem_region.add x "Three" ~pos:(bv (-5)) ~size:(bv 10) in
let x = Mem_region.add x "Four" ~pos:(bv (-15)) ~size:(bv 10) in
let data_list = "Four" :: "Three" :: "One" :: [] in
check "list_data" (Mem_region.list_data x = data_list);
let data_pos_list = (bv (-15), "Four") :: (bv (-5), "Three") :: (bv 15, "One") ::[] in
check "list_data_pos" (Mem_region.list_data_pos x = data_pos_list);
let pos_minus = Bitvector.to_int_exn (Bitvector.signed (bv (-5))) in
let pos_plus = Bitvector.to_int_exn (Bitvector.signed (bv 5)) in
check "expected_sign_minus" (pos_minus < 0);
check "expected_sign_plus" (pos_plus > 0)
let tests = [
"Add", `Quick, test_add;
"Negative Indices", `Quick, test_minus;
"Remove", `Quick, test_remove;
"Mark_error", `Quick, test_mark_error;
"Merge", `Quick, test_merge;
"Equal", `Quick, test_equal;
"Around Zero", `Quick, test_around_zero;
"List data", `Quick, test_list_data;
]
val tests: unit Alcotest.test_case list
open Bap.Std
open Core_kernel
open Cwe_checker_core
open Type_inference
open Type_inference.Private
let check msg x = Alcotest.(check bool) msg true x
let example_project = ref None
(** create a bitvector with value x and width the width of pointers in the example project. *)
let bv x =
Bitvector.of_int x ~width:(Symbol_utils.arch_pointer_size_in_bytes (Option.value_exn !example_project) * 8)
(* TODO: As soon as more pointers than stack pointer are tracked, add more tests! *)
let create_block_from_defs def_list =
let block = Blk.Builder.create () in
let () = List.iter def_list ~f:(fun def -> Blk.Builder.add_def block def) in
Blk.Builder.result block
let test_preamble () =
let project = Option.value_exn !example_project in
let stack_register = Symbol_utils.stack_register project in
let sub = Sub.create ~name:"TestSub" () in
let sub_tid = Term.tid sub in
let fn_start_state = function_start_state sub_tid project in
(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 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" (Poly.(=) (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 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
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 register1), Bitvector.LittleEndian, `r64)) in
let call_term = Jmp.create (Call (Call.create ~target:(Label.direct sub_tid) () )) in
let block = Blk.Builder.create () in
let () = Blk.Builder.add_def block def1 in
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 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" (Poly.(=) (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" (Option.is_none (Mem_region.get state.TypeInfo.stack (bv (-8)))) 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 -> String.(=) (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_def block def1 in
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" (Poly.(=) (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" (Poly.(=) (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 -> String.(=) (Sub.name sub) "free") in
let call_term = Jmp.create (Call (Call.create ~target:(Label.direct (Term.tid extern_sub)) () )) in
let block = Blk.Builder.create () in
let () = Blk.Builder.add_def block def1 in
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" (Poly.(=) (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" (Poly.(<>) (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 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 () = check "update_pointer_register" (
match Var.Map.find state.TypeInfo.reg register1 with
| Some(Ok(Pointer(_))) -> true
|_ -> false
) in
let () = check "update_data_register" (Poly.(=) (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 () = check "add_mem_address_registers" (
match Var.Map.find state.TypeInfo.reg register2 with
| Some(Ok(Pointer(_))) -> true
| _ -> false
) in
()
let test_update_stack () =
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
let def1 = Def.create register1 (Bil.binop Bil.AND (Bil.var stack_register) (Bil.int (bv 8))) in
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 () = check "write_to_stack" (
match Mem_region.get state.TypeInfo.stack (bv (-8)) with
| Some(Ok(Pointer(_targets), size )) when Poly.(=) size (bv (Symbol_utils.arch_pointer_size_in_bytes project)) -> true
| _ -> false
) in
let () = check "load_from_stack" (
match Var.Map.find state.TypeInfo.reg register2 with
| Some(Ok(Pointer(_))) -> true
| _ -> false
) in
()
let test_address_registers_on_load_and_store () =
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
let def1 = Def.create register1 (Bil.binop Bil.XOR (Bil.var stack_register) (Bil.int (bv 8))) in
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 () = 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" (Poly.(=) (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 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 merged_state = merge_type_infos state1 state1 in
let () = check "merge_same_stack_offset" (Poly.(=) (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" (Option.is_none (compute_stack_offset merged_state (Bil.var stack_register) ~sub_tid ~project)) in
let merged_state = merge_type_infos generic_empty_state state1 in
let () = check "merge_with_unknown_stack_offset" (Poly.(=) (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" (Option.is_none (compute_stack_offset merged_state (Bil.var stack_register) ~sub_tid ~project)) in
()
let test_type_info_equal () =
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" (Bool.(=) 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
(* 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 -> String.(=) (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
(* 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 (Symbol_utils.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) -> String.(=) (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
| (target_tid, _) :: [] -> Tid.(=) target_tid (Term.tid call_term)
| _ -> false
end
| _ -> false
end
| None -> false
) in
()
let tests = [
"Update Stack Offset", `Quick, test_update_stack_offset;
"Update Register", `Quick, test_update_reg;
"Update Stack", `Quick, test_update_stack;
"Preserve Stack data on calls", `Quick, test_preserve_stack_offset_on_stubs;
"Merge TypeInfos", `Quick, test_merge_type_infos;
"Equality check for TypeInfos", `Quick, test_type_info_equal;
"Address register handling on load/store instructions", `Quick, test_address_registers_on_load_and_store;
"Malloc calls mark return register", `Quick, test_malloc_call_return_reg;
]
open Bap.Std
open Core_kernel
val example_project: Project.t option ref
val tests: unit Alcotest.test_case list
open Bap.Std
open Core_kernel
open Cwe_checker_core
open Cwe_checker_core.Cwe_476.Private
let check msg x = Alcotest.(check bool) msg true x
let example_project : Project.t Option.t ref = ref None
let call_handling_test () =
let project = Option.value_exn !example_project in
let state = State.empty in
let mock_tid = Tid.create () in
let mock_taint = Taint.add Taint.empty mock_tid in
let mock_hits = ref Taint.empty in
let rax_register = Var.create "RAX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let rbx_register = Var.create "RBX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let rdx_register = Var.create "RDX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let state = State.set_register state rax_register mock_taint in
let _state = flag_unchecked_return_values state ~cwe_hits:mock_hits ~project in
check "flag_RAX_return" (Bool.(=) false (Taint.is_empty !mock_hits));
let state = State.empty in
let state = State.set_register state rbx_register mock_taint in
mock_hits := Taint.empty;
let _state = flag_unchecked_return_values state ~cwe_hits:mock_hits ~project in
check "dont_flag_RBX_return" (Taint.is_empty !mock_hits);
let state = State.empty in
mock_hits := Taint.empty;
let state = State.set_register state rbx_register mock_taint in
let _state = flag_register_taints state ~cwe_hits:mock_hits in
check "flag_all_registers" (Bool.(=) false (Taint.is_empty !mock_hits));
let state = State.empty in
mock_hits := Taint.empty;
let other_mock_taint = Taint.add Taint.empty (Tid.create ()) in
let state = State.set_register state rdx_register mock_taint in
let state = State.set_register state rbx_register other_mock_taint in
let state = flag_parameter_register state ~cwe_hits:mock_hits ~project in
check "flag_RDX_parameter" (Bool.(=) false (Taint.is_empty !mock_hits) && Option.is_none (State.find_register state rdx_register));
check "dont_flag_RBX_parameter" (Option.is_some (State.find_register state rbx_register));
let state = State.empty in
mock_hits := Taint.empty;
let state = State.set_register state rax_register mock_taint in
let state = State.set_register state rbx_register other_mock_taint in
let state = untaint_non_callee_saved_register state ~project in
check "RAX_non_callee_saved" (Option.is_none (State.find_register state rax_register));
check "RBX_callee_saved" (Option.is_some (State.find_register state rbx_register));
()
let state_test () =
let project = Option.value_exn !example_project in
let state = State.empty in
let mock_tid = Tid.create () in
let mock_taint = Taint.add Taint.empty mock_tid in
let _mock_hits = ref Taint.empty in
let rax_register = Var.create "RAX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let rbx_register = Var.create "RBX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let rdx_register = Var.create "RDX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let state1 = State.set_register state rax_register mock_taint in
let state2 = State.set_register state rbx_register mock_taint in
let union_state = State.union state1 state2 in
check "state_union_RAX" (Option.is_some (State.find_register union_state rax_register));
check "state_union_RBX" (Option.is_some (State.find_register union_state rbx_register));
check "state_union_not_RDX" (Option.is_none (State.find_register union_state rdx_register));
()
(* TODO: write checks for expression handling!! *)
let tests = [
"Call Handling", `Quick, call_handling_test;
"State Operations", `Quick, state_test;
]
open Bap.Std
open Core_kernel
val example_project: Project.t option ref
val tests: unit Alcotest.test_case list
open Core_kernel
open Cwe_checker_core
let check msg x = Alcotest.(check bool) msg true x
let test_is_chmod_style_arg_with_umask_arg () : unit =
let res = Cwe_560.Private.is_chmod_style_arg 022 in
check "empty" (Bool.(=) res false)
let test_is_chmod_style_arg_with_chmod_arg () : unit =
let res = Cwe_560.Private.is_chmod_style_arg 666 in
check "empty" (Bool.(=) res true)
let tests = [
"Is chmod style argument with umask argument?", `Quick, test_is_chmod_style_arg_with_umask_arg;
"Is chmod style argument with chmod argument?", `Quick, test_is_chmod_style_arg_with_chmod_arg;
]
val tests: unit Alcotest.test_case list
#!/bin/bash
tmp="tmp/"
# create a tmp directory if not yet created
if [ ! -d $tmp ]; then
mkdir -p $tmp;
fi
c_compiler=(gcc
x86_64-w64-mingw32-gcc
i686-w64-mingw32-gcc
arm-linux-gnueabi-gcc
aarch64-linux-gnu-gcc
mips-linux-gnu-gcc
mipsel-linux-gnu-gcc
mips64-linux-gnuabi64-gcc
mips64el-linux-gnuabi64-gcc
powerpc-linux-gnu-gcc
powerpc64-linux-gnu-gcc
powerpc64le-linux-gnu-gcc
clang)
cpp_compiler=(g++
x86_64-w64-mingw32-g++
i686-w64-mingw32-g++
arm-linux-gnueabi-g++
aarch64-linux-gnu-g++
mips-linux-gnu-g++
mipsel-linux-gnu-g++
mips64-linux-gnuabi64-g++
mips64el-linux-gnuabi64-g++
powerpc-linux-gnu-g++
powerpc64-linux-gnu-g++
powerpc64le-linux-gnu-g++)
# In clang cross compilation there is no target for ppc 32 bit
targets=(x86_64-linux-gnuabi64
arm-linux-gnueabi
aarch64-linux-gnu
mips-linux-gnu
mipsel-linux-gnu
mips64-linux-gnuabi64
mips64el-linux-gnuabi64
powerpc64-linux-gnu
powerpc64le-linux-gnu)
x86_flag="-m32"
c_flag="-std=c11"
flags="-g -fno-stack-protector"
target_flag="-target"
function compile_clang () {
for target in ${targets[@]}
do
build_name="$tmp$2"
build_name+="_$(cut -d'-' -f1 <<<$target)_clang.out"
$1 $flags $c_flag $target_flag $target $3 -o $build_name
done
}
function compile_x86 () {
build_name="$tmp$2"
build_name+="_x86_gcc.out"
gcc $x86_flag $flags $c_flag $1 -o $build_name
build_name="$tmp$2"
build_name+="_x86_clang.out"
clang $x86_flag $flags $c_flag $1 -o $build_name
}
function compile_x86_for_cpp () {
build_name="$tmp${2}_x86_g++.out"
g++ $x86_flag $flags $1 -o $build_name
}
function compile_gcc () {
build_name="$tmp$2"
if [[ $1 == gcc ]]; then
build_name+="_$1.out"
else
if [[ $1 == *w64* ]]; then
build_name+="_${1%-*}_gcc.out"
else
build_name+="_${1%%-*}_gcc.out"
fi
fi
$1 $flags $c_flag $3 -o $build_name
}
function compile_c () {
file_name=$2
c_file=$1
for compiler in ${c_compiler[@]}
do
if [[ $compiler == clang ]]; then
compile_clang "$compiler" "$file_name" "$c_file"
else
compile_gcc "$compiler" "$file_name" "$c_file"
fi
done
compile_x86 "$c_file" "$file_name"
}
function compile_cpp () {
for compiler in ${cpp_compiler[@]}
do
build_name="$tmp$2"
if [[ $compiler == g++ ]]; then
build_name+="_$compiler.out"
else
if [[ $compiler == *w64* ]]; then
build_name+="_${compiler%-*}_g++.out"
else
build_name+="_${compiler%%-*}_g++.out"
fi
fi
$compiler $flags $1 -o $build_name
done
compile_x86_for_cpp "$1" "$2"
}
function main () {
test_file="$1"
echo "Compiling test file $test_file"
file_name="${test_file%.*}"
file_name="${file_name##*/}"
if [[ $test_file == *.c ]]; then
compile_c "$test_file" "$file_name"
elif [[ $test_file == *.cpp ]]; then
compile_cpp "$test_file" "$file_name"
fi
}
main "$@"
(executable
(name unit_tests_cwe_checker)
(libraries
alcotest
yojson
bap
cwe_checker_core
core_kernel)
(preprocess (pps ppx_jane))
)
(include_subdirs unqualified) ; Include all subdirs when looking for source files
(rule
(alias runtest)
(deps unit_tests_cwe_checker.exe)
(action (run %{deps} --color=always))
)
#!/usr/bin/env bash
./compile_testfile.sh testfiles/arrays.c
./compile_testfile.sh testfiles/c_constructs.c
./compile_testfile.sh testfiles/no_symbols.c
#include <stdio.h>
#include <stdlib.h>
void heap_based_array(){
char* a = malloc(20);
for(int i=0; i<20;i++){
*(a + i) = 'A';
}
free(a);
}
void stack_based_array(){
char a[20];
for(int i=0; i<20;i++){
a[i] = 'A';
}
}
int main(int argc, char *argv[argc])
{
stack_based_array();
heap_based_array();
return 0;
}
#include <stdlib.h>
void if_statement(){
void* bla = malloc(89);
int a = 2;
if (a < 4){
a = 33;
}
free(bla);
}
void for_loop(){
void* bla = malloc(89);
int a = 34;
for(int i = 0;i < 100; i++){
a += i;
}
free(bla);
}
void nested_for_loop(){
void* bla = malloc(89);
int a = 34;
for(int i = 0; i < 100; i++){
for(int j = 0; j < 200; j++){
a += i + j;
}
}
free(bla);
}
int main(){
if_statement();
for_loop();
nested_for_loop();
}
(**
This module contains the unit test infrastructure to coordiate
each unit test in the cwe_checker/unit/ folder.
To add an unit test and the corresponding test file,
a few steps have to be performed before execution:
- add the test list from your unit test to unit_test_list in this module
- if your unit test utilises a example project, it has to be added to the set_example_project
in this module.
- add your corresponding test file to the testfiles folder in cwe_checker/unit/
- call the unit test as bap plugin in the Makefile contained in the unit folder.
The compiled test file will lie in the tmp folder.
- lastly, run compile_testfile.sh in the specify_test_files_for_compilation contained in the unit folder
*)
open Bap.Std
open Core_kernel
include Self()
let cmdline_params = [
("tests", "Comma separated list defining which tests should be executed with the current test file. e.g. MemRegion,TypeInference,CWE476,...")
]
let unit_test_list = [
"MemRegion", Mem_region_test.tests;
"TypeInference", Type_inference_test.tests;
"Cconv", Cconv_test.tests;
"CWE476", Cwe_476_test.tests;
"CWE560", Cwe_560_test.tests;
"AddrTrans", Address_translation_test.tests;
"Symbols", Symbol_utils_test.tests;
"DynSyms", Parse_dyn_syms_test.tests;
"SerdeJson", Serde_json_test.tests;
]
let check_for_cconv (project : Project.t) (arch : string) =
match arch with
| "i386" | "i686" -> Cconv_test.example_cconv := Project.get project Bap_abi.name
| _ -> ()
let get_test_bin_format (project : Project.t) =
let filename = match (Project.get project filename) with
| Some(f) -> f
| _ -> failwith "Test file has no file name" in
match String.is_substring filename ~substring:"mingw32" with
| true -> "pe"
| false -> "elf"
let set_example_project (project : Project.t) (tests : string list) =
let arch = Arch.to_string (Project.arch project) in
List.iter tests ~f:(fun test ->
match test with
| "TypeInference" -> Type_inference_test.example_project := Some(project)
| "Cconv" -> begin
Cconv_test.example_project := Some(project);
Cconv_test.example_arch := Some(arch);
check_for_cconv project arch;
Cconv_test.example_bin_format := Some(get_test_bin_format project)
end
| "CWE476" -> Cwe_476_test.example_project := Some(project)
| "Symbols" -> Symbol_utils_test.example_project := Some(project)
| "DynSyms" -> Parse_dyn_syms_test.example_project := Some(project)
| "SerdeJson" -> Serde_json_test.example_project := Some(project)
| _ -> ()
)
let check_user_input (tests : string list) =
let test_list = List.map unit_test_list ~f:(fun test -> match test with (name, _) -> name) in
List.iter tests ~f:(fun test ->
match Stdlib.List.mem test test_list with
| true -> ()
| false -> failwith (Printf.sprintf "Test %s is not a valid test." test)
)
let filter_tests (tests : string list) : (string * unit Alcotest.test_case list) list =
List.filter unit_test_list ~f:(fun (name, _) ->
match Stdlib.List.mem name tests with
| true -> true
| false -> false
)
let run_tests (params : String.t String.Map.t) (project : Project.t) =
let test_param = match String.Map.find params "tests" with
| Some(param) -> param
| None -> failwith "No tests were provided to the unittest plugin." in
let tests = (String.split test_param ~on: ',') in
check_user_input tests;
set_example_project project tests;
Alcotest.run "Unit tests" ~argv:[|"DoNotComplainWhenRunAsABapPlugin";"--color=always";|] (filter_tests tests)
let generate_bap_params params =
List.map params ~f:(fun (name, docstring) -> (name, Config.param Config.string name ~doc:docstring))
let () =
(* Check whether this file is run as an executable (via dune runtest) or
as a bap plugin *)
if String.(=) Sys.argv.(0) "bap" then
let cmdline_params = generate_bap_params cmdline_params in
let () = Config.when_ready (fun ({get=(!!)}) ->
let params: String.t String.Map.t = List.fold cmdline_params ~init:String.Map.empty ~f:(fun param_map (name, bap_param) ->
String.Map.set param_map ~key:name ~data:(!!bap_param)) in
Project.register_pass' (run_tests params)
) in
()
else
(* The file was run as a standalone executable. Use make to build and run the unit test plugin *)
let () = try
Sys.chdir (Sys.getenv "PWD" ^ "/test/unit")
with _ -> (* In the docker image the environment variable PWD is not set *)
Sys.chdir "/home/cwe/cwe_checker/test/unit"
in
exit (Sys.command "make all")
open Bap.Std
open Core_kernel
open Cwe_checker_core
open Address_translation
let check msg x = Alcotest.(check bool) msg true x
let test_translate_tid_to_assembler_address_string () =
let tid_map = Tid.Map.empty in
let tid_1 = Tid.create () in
let tid_2 = Tid.create () in
let tid_map = Map.add_exn tid_map ~key:tid_1 ~data:(Addr.of_bool true) in
let () = check "TID not correctly mapped to address" (String.(=) (translate_tid_to_assembler_address_string tid_1 tid_map) "1:1u") in
let () = check "TID not correctly mapped to address" (String.(=) (translate_tid_to_assembler_address_string tid_2 tid_map) "UNKNOWN") in
()
let test_generate_tid_map () =
let program = Program.create () in
let program = Term.set_attr program address (Addr.of_bool false) in
let s = Sub.create () in
let b = Blk.create () in
let x = Var.create "x" (Bil.Imm 8) in
let y = Var.create "y" (Bil.Imm 8) in
let z = Var.create "z" (Bil.Imm 8) in
let d_1 = Def.create x Bil.(var y + var z) in
let b = Term.append def_t b d_1 in
let b = Term.set_attr b address (Addr.of_bool true) in
let s = Term.append blk_t s b in
let program = Term.append sub_t program s in
let tid_map = generate_tid_map program in
let () = check "address not in vicinity" (String.(=) (translate_tid_to_assembler_address_string (Term.tid s) tid_map) "UNKNOWN") in
let () = check "address not in vicinity" (String.(=) (translate_tid_to_assembler_address_string (Term.tid d_1) tid_map) "1:1u") in
()
let tests = [
"Generate TID map", `Quick, test_generate_tid_map;
"Translate TID to assembler address string", `Quick, test_translate_tid_to_assembler_address_string
]
val tests: unit Alcotest.test_case list
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 example_cconv = ref None
let example_arch = ref None
let example_bin_format = 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 arch = Option.value_exn !example_arch in
let cconv = Option.value !example_cconv ~default: "" in
let bin_format = Option.value_exn !example_bin_format in
match cconv with
| "cdecl" | "fastcall" | "stdcall" | "ms" -> begin
let register = Var.create "EBX" (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 "EAX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "caller_saved_register" (Bool.(=) (is_callee_saved register project) false) in
()
end
| "" -> begin
match arch with
| "x86_64" -> begin
match bin_format with
| "pe" -> begin
let register = Var.create "RDI" (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 "R8" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "caller_saved_register" (Bool.(=) (is_callee_saved register project) false) in
()
end
| "elf" -> begin
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 "RDI" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "caller_saved_register" (Bool.(=) (is_callee_saved register project) false) in
()
end
| _ -> failwith "Not a valid binary format"
end
| "mips" | "mipsel" | "mips64" | "mips64el" -> begin
let register = Var.create "S0" (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 "A0" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "callee_saved_register" (Bool.(=) (is_callee_saved register project) false) in
()
end
| "armv4" | "armv5" | "armv6" | "armv7" | "armv4eb" | "armv5eb"
| "armv6eb" | "armv7eb" | "thumbv4" | "thumbv5" | "thumbv6" | "thumbv7"
| "thumbv4eb" | "thumbv5eb" | "thumbv6eb" | "thumbv7eb" -> begin
let register = Var.create "R4" (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 "R0" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "callee_saved_register" (Bool.(=) (is_callee_saved register project) false) in
()
end
| "aarch64" -> begin
let register = Var.create "X18" (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 "X0" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "callee_saved_register" (Bool.(=) (is_callee_saved register project) false) in
()
end
| "powerpc" -> begin
let register = Var.create "R14" (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 "R4" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "callee_saved_register" (Bool.(=) (is_callee_saved register project) false) in
()
end
| "powerpc64" | "powerpc64le" -> begin
let register = Var.create "R14" (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 "R10" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "callee_saved_register" (Bool.(=) (is_callee_saved register project) false) in
()
end
| _ -> failwith "Not a supported architecture"
end
| _ -> failwith "Not a supported calling convention"
let test_parameter_register () =
(* this test assumes, that the example project is a x64 binary *)
let project = Option.value_exn !example_project in
let arch = Option.value_exn !example_arch in
let cconv = Option.value !example_cconv ~default: "" in
let bin_format = Option.value_exn !example_bin_format in
match cconv with
| "cdecl" | "stdcall" | "ms" -> begin
let register = Var.create "EBX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (Bool.(=) (is_parameter_register register project) false) in
let register = Var.create "EAX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (Bool.(=) (is_parameter_register register project) false) in
()
end
| "fastcall" -> begin
let register = Var.create "EDX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (is_parameter_register register project) in
let register = Var.create "ECX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (is_parameter_register register project) in
()
end
| "" -> begin
match arch with
| "x86_64" -> begin
match bin_format with
| "pe" -> begin
let register = Var.create "R8" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (is_parameter_register register project) in
let register = Var.create "RDI" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (Bool.(=) (is_parameter_register register project) false) in
()
end
| "elf" -> begin
let register = Var.create "RDI" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (is_parameter_register register project) in
let register = Var.create "RBP" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (Bool.(=) (is_parameter_register register project) false) in
()
end
| _ -> failwith "Not a valid binary format"
end
| "mips" | "mipsel" | "mips64" | "mips64el" -> begin
let register = Var.create "A3" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (is_parameter_register register project) in
let register = Var.create "V0" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (Bool.(=) (is_parameter_register register project) false) in
()
end
| "aarch64" -> begin
let register = Var.create "X2" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (is_parameter_register register project) in
let register = Var.create "X23" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (Bool.(=) (is_parameter_register register project) false) in
()
end
| "armv4" | "armv5" | "armv6" | "armv7" | "armv4eb" | "armv5eb"
| "armv6eb" | "armv7eb" | "thumbv4" | "thumbv5" | "thumbv6" | "thumbv7"
| "thumbv4eb" | "thumbv5eb" | "thumbv6eb" | "thumbv7eb" -> begin
let register = Var.create "R3" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (is_parameter_register register project) in
let register = Var.create "LR" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (Bool.(=) (is_parameter_register register project) false) in
()
end
| "powerpc" -> begin
let register = Var.create "R3" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (is_parameter_register register project) in
let register = Var.create "F1" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (Bool.(=) (is_parameter_register register project) false) in
()
end
| "powerpc64" | "powerpc64le" -> begin
let register = Var.create "R3" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (is_parameter_register register project) in
let register = Var.create "R31" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "parameter_register" (Bool.(=) (is_parameter_register register project) false) in
()
end
| _ -> failwith "Not a supported architecture"
end
| _ -> failwith "Not a supported calling convention"
let test_return_register () =
(* this test assumes, that the example project is a x64 binary *)
let project = Option.value_exn !example_project in
let arch = Option.value_exn !example_arch in
let cconv = Option.value !example_cconv ~default: "" in
let bin_format = Option.value_exn !example_bin_format in
match cconv with
| "cdecl" | "fastcall" | "stdcall" | "ms" -> begin
let register = Var.create "EDX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "return_register" (is_return_register register project) in
let register = Var.create "EBP" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "no_return_register" (Bool.(=) (is_return_register register project) false) in
()
end
| "" -> begin
match arch with
| "x86_64" -> begin
match bin_format with
| "pe" -> begin
let register = Var.create "RAX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "return_register" (is_return_register register project) in
let register = Var.create "RDX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "no_return_register" (Bool.(=) (is_return_register register project) false) in
()
end
| "elf" -> begin
let register = Var.create "RDX" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "return_register" (is_return_register register project) in
let register = Var.create "R12" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "no_return_register" (Bool.(=) (is_return_register register project) false) in
()
end
| _ -> failwith "Not a valid binary format"
end
| "mips" | "mipsel" | "mips64" | "mips64el" -> begin
let register = Var.create "V0" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "return_register" (is_return_register register project) in
let register = Var.create "A0" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "no_return_register" (Bool.(=) (is_return_register register project) false) in
()
end
| "aarch64" -> begin
let register = Var.create "X1" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "return_register" (is_return_register register project) in
let register = Var.create "X30" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "no_return_register" (Bool.(=) (is_return_register register project) false) in
()
end
| "armv4" | "armv5" | "armv6" | "armv7" | "armv4eb" | "armv5eb"
| "armv6eb" | "armv7eb" | "thumbv4" | "thumbv5" | "thumbv6" | "thumbv7"
| "thumbv4eb" | "thumbv5eb" | "thumbv6eb" | "thumbv7eb" -> begin
let register = Var.create "R3" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "return_register" (is_return_register register project) in
let register = Var.create "R4" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "no_return_register" (Bool.(=) (is_return_register register project) false) in
()
end
| "powerpc" -> begin
let register = Var.create "R3" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "return_register" (is_return_register register project) in
let register = Var.create "R10" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "no_return_register" (Bool.(=) (is_return_register register project) false) in
()
end
| "powerpc64" | "powerpc64le" -> begin
let register = Var.create "R3" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "return_register" (is_return_register register project) in
let register = Var.create "R25" (Bil.Imm (Symbol_utils.arch_pointer_size_in_bytes project * 8)) in
let () = check "no_return_register" (Bool.(=) (is_return_register register project) false) in
()
end
| _ -> failwith "Not a supported architecture"
end
| _ -> failwith "Not a supported calling convention"
let test_extract_bin_format () =
let project = Option.value_exn !example_project in
let () = check "bin_format" (Poly.(=) (extract_bin_format project) (Option.value_exn !example_bin_format)) in
()
let tests = [
"Callee saved register", `Quick, test_callee_saved;
"Parameter register", `Quick, test_parameter_register;
"Return register", `Quick, test_return_register;
"Extract bin format", `Quick, test_extract_bin_format;
]
open Bap.Std
open Core_kernel
val example_project: Project.t option ref
val example_cconv: string option ref
val example_arch: string option ref
val example_bin_format: string option ref
val tests: unit Alcotest.test_case list
open Core_kernel
open Cwe_checker_core
open Symbol_utils
let check msg x = Alcotest.(check bool) msg true x
let example_project = ref None
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 "realloc_not_a_dyn_sym" (Bool.(=) false (String.Set.mem (parse_dyn_syms project) "realloc")) in
()
let tests = [
"Parse Dynamic Symbols", `Quick, test_parse_dyn_syms;
]
open Bap.Std
val example_project: Project.t option ref
val tests: unit Alcotest.test_case list
open Core_kernel
open Cwe_checker_core
open Bap.Std
let example_project = ref None
let check msg x = Alcotest.(check bool) msg true x
let test_serde () =
let open Serde_json in
let serde = build_null () in
let json = to_string serde in
print_endline json;
check "serde_null" (String.(=) json "null");
let serde = build_bool true in
let json = to_string serde in
print_endline json;
check "serde_bool" (String.(=) json "true");
let serde = build_number 45 in
let json = to_string serde in
print_endline json;
check "serde_number" (String.(=) json "45");
let serde = build_string "hello" in
let json = to_string serde in
print_endline json;
check "serde_string" (String.(=) json "\"hello\"");
let serde = build_array [build_number 23; build_bool false] in
let json = to_string serde in
print_endline json;
check "serde_array" (String.(=) json "[23,false]");
let serde = build_object [("hello", build_number 23); ("world", build_bool false)] in
let json = to_string serde in
print_endline json;
check "serde_object" (String.(=) json "{\"hello\":23,\"world\":false}")
let test_type_conversions () =
let var_type = Bil.Types.Mem (`r64, `r8) in
let serde = Serde_json.of_var_type var_type in
let json = Serde_json.to_string serde in
print_endline json;
check "Var_Type" (String.(=) json "{\"Memory\":{\"addr_size\":64,\"elem_size\":8}}");
let var = Var.create "RAX" var_type in
let serde = Serde_json.of_var var in
let json = Serde_json.to_string serde in
print_endline json;
check "Var" (String.(=) json "{\"is_temp\":false,\"name\":\"RAX\",\"type_\":{\"Memory\":{\"addr_size\":64,\"elem_size\":8}}}");
let cast_type = Bil.Types.UNSIGNED in
let serde = Serde_json.of_cast_type cast_type in
let json = Serde_json.to_string serde in
print_endline json;
check "Cast_Type" (String.(=) json "\"UNSIGNED\"");
let unop = Bil.Types.NEG in
let serde = Serde_json.of_unop_type unop in
let json = Serde_json.to_string serde in
print_endline json;
check "Unop_Type" (String.(=) json "\"NEG\"");
let bitv = Bitvector.of_int ~width:8 234 in
let serde = Serde_json.of_bitvector bitv in
let json = Serde_json.to_string serde in
print_endline json;
check "Bitvector" (String.(=) json "{\"digits\":[234],\"width\":[8]}");
let exp = Bil.binop Bil.PLUS (Bil.int bitv) (Bil.int bitv) in
let serde = Serde_json.of_exp exp in
let json = Serde_json.to_string serde in
print_endline json;
check "Expression" (String.(=) json "{\"BinOp\":{\"lhs\":{\"Const\":{\"digits\":[234],\"width\":[8]}},\"op\":\"PLUS\",\"rhs\":{\"Const\":{\"digits\":[234],\"width\":[8]}}}}");
let tid = Tid.for_name "block" in
let term = Blk.create ~tid () in
let tid_map = Tid.Map.empty in
let serde = Serde_json.of_blk term tid_map in
let json = Serde_json.to_string serde in
print_endline json;
check "Block_term" (String.(=) json "{\"term\":{\"defs\":[],\"jmps\":[]},\"tid\":{\"address\":\"UNKNOWN\",\"id\":\"@block\"}}";)
let test_project_conversion () =
let project = Option.value_exn !example_project in
let program = Project.program project in
let tid_map = Address_translation.generate_tid_map program in
let extern_symbols = Symbol_utils.build_and_return_extern_symbols project program tid_map in
let entry_points = [] in
let serde = Serde_json.of_program program extern_symbols entry_points tid_map in
let _json = Serde_json.to_string serde in
(* TODO: The unit test for pointer inference should be moved to another file *)
Pointer_inference.run project tid_map;
Log_utils.emit_json "bin" "";
Log_utils.emit_native "";
check "Project" true
let tests = [
"Serde Json Conversions", `Quick, test_serde;
"Type Conversions", `Quick, test_type_conversions;
"Project conversion", `Quick, test_project_conversion;
]
open Bap.Std
open Core_kernel
val example_project: Project.t option ref
val tests: unit Alcotest.test_case list
open Core_kernel
open Cwe_checker_core
open Bap.Std
open Symbol_utils
let check msg x = Alcotest.(check bool) msg true x
let example_project = ref None
let test_check_if_symbols_resolved () =
let project = Option.value_exn !example_project in
let program = Project.program project in
let tid_address_map = Address_translation.generate_tid_map program in
let () = check "no_symbols" (Bool.(=) (check_if_symbols_resolved project program tid_address_map) false) in
()
let tests = [
"Check if Symbols Resolved", `Quick, test_check_if_symbols_resolved;
]
open Bap.Std
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