You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
444 lines
14 KiB
444 lines
14 KiB
(*
|
|
* Copyright (c) 2009 - 2013 Monoidics ltd.
|
|
* Copyright (c) 2013 - present Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*)
|
|
|
|
module F = Format;;
|
|
|
|
(* Initialization *)
|
|
|
|
F.set_margin 100
|
|
|
|
let set_minor_heap_size nMb = (* increase the minor heap size to speed up gc *)
|
|
let ctrl = Gc.get () in
|
|
let oneMb = 1048576 in
|
|
let new_size = max ctrl.Gc.minor_heap_size (nMb * oneMb)
|
|
in Gc.set { ctrl with Gc.minor_heap_size = new_size };;
|
|
|
|
set_minor_heap_size 1
|
|
|
|
let from_env_variable var_name =
|
|
try
|
|
let _ = Sys.getenv var_name in true
|
|
with Not_found -> false
|
|
|
|
let get_env_variable var_name =
|
|
try
|
|
let v = Sys.getenv var_name in
|
|
if v = "" then None else Some v
|
|
with Not_found -> None
|
|
|
|
let attributes_dir_name = "attributes"
|
|
let captured_dir_name = "captured"
|
|
let sources_dir_name = "sources"
|
|
let specs_dir_name = "specs"
|
|
let perf_stats_prefix = "perf_stats"
|
|
let backend_stats_dir_name = "backend_stats"
|
|
let frontend_stats_dir_name = "frontend_stats"
|
|
let reporting_stats_dir_name = "reporting_stats"
|
|
|
|
let default_in_zip_results_dir = "infer"
|
|
|
|
let default_buck_out = "buck-out"
|
|
|
|
let proc_stats_filename = "proc_stats.json"
|
|
|
|
let global_tenv_filename = "global.tenv"
|
|
|
|
(** Name of the infer configuration file *)
|
|
let inferconfig_file = ".inferconfig"
|
|
|
|
let inferconfig_home : string option ref = ref None
|
|
|
|
let suppress_warnings_annotations : string option ref = ref None
|
|
|
|
(** List of paths to the directories containing specs for library functions. *)
|
|
let specs_library = ref []
|
|
|
|
(** path to lib/specs to retrieve the default models *)
|
|
let models_dir =
|
|
let bin_dir = Filename.dirname Sys.executable_name in
|
|
let lib_dir = Filename.concat (Filename.concat bin_dir Filename.parent_dir_name) "lib" in
|
|
let lib_specs_dir = Filename.concat lib_dir specs_dir_name in
|
|
lib_specs_dir
|
|
|
|
let string_crc_hex32 s =
|
|
Digest.to_hex (Digest.string s)
|
|
|
|
module JarCache =
|
|
struct
|
|
let infer_cache : string option ref = ref None
|
|
|
|
let mkdir s =
|
|
try
|
|
Unix.mkdir s 0o700;
|
|
true
|
|
with Unix.Unix_error _ -> false
|
|
|
|
let extract_specs dest_dir jarfile =
|
|
let zip_channel = Zip.open_in jarfile in
|
|
let entries = Zip.entries zip_channel in
|
|
let extract_entry entry =
|
|
let dest_file = Filename.concat dest_dir (Filename.basename entry.Zip.filename) in
|
|
if Filename.check_suffix entry.Zip.filename ".specs"
|
|
then Zip.copy_entry_to_file zip_channel entry dest_file in
|
|
List.iter extract_entry entries;
|
|
Zip.close_in zip_channel
|
|
|
|
let handle_jar jarfile =
|
|
match !infer_cache with
|
|
| Some cache_dir ->
|
|
let basename = Filename.basename jarfile in
|
|
let key = basename ^ string_crc_hex32 jarfile in
|
|
let key_dir = Filename.concat cache_dir key in
|
|
|
|
if (mkdir key_dir)
|
|
then extract_specs key_dir jarfile;
|
|
|
|
specs_library := !specs_library @ [key_dir]
|
|
| None -> ()
|
|
end
|
|
|
|
type zip_library = {
|
|
zip_filename: string;
|
|
zip_channel: Zip.in_file;
|
|
models: bool;
|
|
}
|
|
|
|
let zip_filename zip_library =
|
|
zip_library.zip_filename
|
|
|
|
let zip_channel zip_library =
|
|
zip_library.zip_channel
|
|
|
|
(** list of the zip files to search for specs files *)
|
|
let zip_libraries : zip_library list ref = ref []
|
|
|
|
let add_zip_library zip_filename =
|
|
if !JarCache.infer_cache != None
|
|
then
|
|
JarCache.handle_jar zip_filename
|
|
else
|
|
(* The order matters, the jar files should be added following the order *)
|
|
(* specs files should be searched in them *)
|
|
zip_libraries := !zip_libraries @ [{ zip_filename = zip_filename; zip_channel = Zip.open_in zip_filename; models = false }]
|
|
|
|
let add_models zip_filename =
|
|
zip_libraries := !zip_libraries @ [{ zip_filename = zip_filename; zip_channel = Zip.open_in zip_filename; models = true }]
|
|
|
|
let project_root : string option ref = ref None
|
|
|
|
(** FLAGS AND GLOBAL VARIABLES *)
|
|
|
|
(** Flag for abstracting fields of structs
|
|
0 = no
|
|
1 = forget some fields during matching (and so lseg abstraction) *)
|
|
let abs_struct = ref 1
|
|
|
|
(** Flag for abstracting numerical values
|
|
0 = no abstraction.
|
|
1 = evaluate all expressions abstractly.
|
|
2 = 1 + abstract constant integer values during join.
|
|
*)
|
|
let abs_val_default = 2
|
|
let abs_val =
|
|
ref abs_val_default
|
|
|
|
let reset_abs_val () =
|
|
abs_val := abs_val_default
|
|
|
|
(** if true, completely ignore the possibility that errors can be caused by unknown procedures
|
|
* during the symbolic execution phase *)
|
|
let angelic_execution = ref true
|
|
|
|
(** Flag for forgetting memory leak
|
|
false = no
|
|
true = forget leaked memory cells during abstraction
|
|
*)
|
|
let allowleak = ref false
|
|
|
|
(** Flag for ignoring arrays and pointer arithmetic.
|
|
0 = treats both features soundly.
|
|
1 = assumes that the size of every array is infinite.
|
|
2 = assumes that all heap dereferences via array indexing and pointer arithmetic are correct.
|
|
*)
|
|
let array_level = ref 0
|
|
|
|
(** Check whether to report Analysis_stops message in user mode *)
|
|
let analysis_stops = ref false
|
|
|
|
type os_type = Unix | Win32 | Cygwin
|
|
|
|
let os_type = match Sys.os_type with
|
|
| "Win32" -> Win32
|
|
| "Cygwin" -> Cygwin
|
|
| _ -> Unix
|
|
|
|
(** default path of the project results directory *)
|
|
let default_results_dir =
|
|
Filename.concat (Sys.getcwd ()) "infer-out"
|
|
|
|
(** If true shows internal exceptions*)
|
|
let developer_mode = ref false
|
|
|
|
(** flag: dotty output filename **)
|
|
let dotty_output = ref "icfg.dot"
|
|
|
|
(** command line option to activate the eradicate checker. *)
|
|
let eradicate = ref false
|
|
|
|
(** should the checkers be run? *)
|
|
let checkers_enabled () = not !eradicate
|
|
|
|
(** flag for reactive mode:
|
|
the analysis starts from the files captured since the "infer" command started *)
|
|
let reactive_mode = ref false
|
|
|
|
(** Continue the capture for reactive mode:
|
|
If a procedure was changed beforehand, keep the changed marking. *)
|
|
let continue_capture = ref false
|
|
|
|
(** Merge the captured results directories specified in the dependency file *)
|
|
let merge = ref false
|
|
|
|
(** Flag for footprint discovery mode *)
|
|
let footprint = ref true
|
|
|
|
(** Set in the middle of forcing delayed prints *)
|
|
let forcing_delayed_prints = ref false
|
|
|
|
(** If true, treat calls to no-arg getters as idempotent w.r.t non-nullness *)
|
|
let idempotent_getters = ref true
|
|
|
|
(** if true, changes to code are checked at the procedure level; if false, at the file level *)
|
|
let incremental_procs = ref true
|
|
|
|
(** if active, join x+j and x+k for constants j and k *)
|
|
let join_plus = ref true
|
|
|
|
(** Flag to tune the final information-loss check used by the join
|
|
0 = use the most aggressive join for preconditions
|
|
1 = use the least aggressive join for preconditions
|
|
*)
|
|
let join_cond = ref 1
|
|
|
|
(** Flag for turning on the transformation that
|
|
null is assigned to a program variable when it becomes dead.
|
|
**)
|
|
let liveness = ref true
|
|
|
|
(** if true, give static procs a long name filename::procname *)
|
|
let long_static_proc_names = ref false
|
|
|
|
(** Number of lines of code in original file *)
|
|
let nLOC = ref 0
|
|
|
|
(** Maximum level of recursion during the analysis, after which a timeout is generated *)
|
|
let max_recursion = ref 5
|
|
|
|
(** Flag to tune the level of applying the meet operator for
|
|
preconditions during the footprint analysis.
|
|
0 = do not use the meet.
|
|
1 = use the meet to generate new preconditions.
|
|
*)
|
|
let meet_level = ref 1
|
|
|
|
(** Monitor the size of the props, and print every time the current max is exceeded *)
|
|
let monitor_prop_size = ref false
|
|
|
|
(** Flag for using the nonempty lseg only **)
|
|
let nelseg = ref false
|
|
|
|
(** Flag to activate nonstop mode: the analysis continues after in encounters errors *)
|
|
let nonstop = ref false
|
|
|
|
(** if true, skip the re-execution phase *)
|
|
let only_footprint = ref false
|
|
|
|
(** if true, user simple pretty printing *)
|
|
let pp_simple = ref true
|
|
|
|
(** flag: if true print full type info *)
|
|
let print_types = ref false
|
|
|
|
(** if true, acrtivate color printing by diff'ing w.r.t. previous prop *)
|
|
let print_using_diff = ref true
|
|
|
|
(** path of the project results directory *)
|
|
let results_dir = ref default_results_dir
|
|
|
|
(** Flag to tune the level of abstracting the postconditions of specs discovered
|
|
by the footprint analysis.
|
|
0 = nothing special.
|
|
1 = filter out redundant posts implied by other posts. *)
|
|
let spec_abs_level = ref 1
|
|
|
|
(** Flag for test mode *)
|
|
let test = ref true
|
|
|
|
(** Flag set to enable detailed tracing informatin during error explanation *)
|
|
let trace_error = ref false
|
|
|
|
(** Flag set to enable detailed tracing information during re-arrangement *)
|
|
let trace_rearrange = ref false
|
|
|
|
(** Flag set to enable detailed tracing information during join *)
|
|
let trace_join = ref false
|
|
|
|
(** Flag set to enable detailed tracing information during array abstraction *)
|
|
let trace_absarray = ref false
|
|
|
|
(** Consider the size of types during analysis, e.g. cannot use an int pointer to write to a char *)
|
|
let type_size = ref false
|
|
|
|
(** if true, compact summaries before saving *)
|
|
let save_compact_summaries = ref true
|
|
|
|
(** If true, save the execution time in summaries.
|
|
This makes the analysis nondeterministic. *)
|
|
let save_time_in_summaries = ref false
|
|
|
|
(** flag: if true enables printing proposition compatible for the SMT project *)
|
|
let smt_output = ref false
|
|
|
|
(** flag: if true performs taint analysis *)
|
|
let taint_analysis = ref true
|
|
|
|
(** Flag for turning on the optimization based on locality
|
|
0 = no
|
|
1 = based on reachability
|
|
*)
|
|
let undo_join = ref true
|
|
|
|
(** visit mode for the worklist:
|
|
0 depth - fist visit
|
|
1 bias towards exit node
|
|
2 least visited first *)
|
|
let worklist_mode = ref 0
|
|
|
|
(** flag: if true write dot files in db dir*)
|
|
let write_dotty = ref false
|
|
|
|
(** flag: if true write html files in db dir *)
|
|
let write_html = ref false
|
|
|
|
let subtype_multirange = ref true
|
|
|
|
let optimistic_cast = ref false
|
|
|
|
(** if true, filter out errors in low likelyhood buckets, and only show then in developer mode *)
|
|
let filter_buckets = ref false
|
|
|
|
(** if true, show buckets in textual description of errors *)
|
|
let show_buckets = ref false
|
|
|
|
(** if true, show memory leak buckets in textual description of errors *)
|
|
let show_ml_buckets = ref false
|
|
|
|
(** if true, print cfg nodes in the dot file that are not defined in that file *)
|
|
let dotty_cfg_libs = ref true
|
|
|
|
(** if true, it deals with messages (method calls) in objective-c using the objective-c
|
|
typical semantics. That is: if the receiver is nil then the method is nop and it returns 0.
|
|
When the flag is false we deal with messages as standard method / function calls *)
|
|
let objc_method_call_semantics = ref true
|
|
|
|
(** if true, generate preconditions for runtime exceptions in Java and report errors for the public
|
|
methods having preconditions to throw runtime exceptions *)
|
|
let report_runtime_exceptions = ref false
|
|
|
|
(** if true, sanity-check inferred preconditions against Nullable annotations and report
|
|
inconsistencies *)
|
|
let report_nullable_inconsistency = ref true
|
|
|
|
(** true if the current objective-c source file is compiled with automatic reference counting (ARC) *)
|
|
let arc_mode = ref false
|
|
|
|
let objc_memory_model_on = ref false
|
|
|
|
let report_custom_error = from_env_variable "INFER_REPORT_CUSTOM_ERROR"
|
|
let default_failure_name = "ASSERTION_FAILURE"
|
|
|
|
let analyze_models = from_env_variable "INFER_ANALYZE_MODELS"
|
|
|
|
(** initial time of the analysis, i.e. when this module is loaded, gotten from Unix.time *)
|
|
let initial_analysis_time = Unix.time ()
|
|
|
|
let symops_timeout, seconds_timeout =
|
|
let default_symops_timeout = 333 in
|
|
let default_seconds_timeout = 10.0 in
|
|
let long_symops_timeout = 1000 in
|
|
let long_seconds_timeout = 30.0 in
|
|
if analyze_models then
|
|
(* use longer timeouts when analyzing models *)
|
|
long_symops_timeout, long_seconds_timeout
|
|
else
|
|
default_symops_timeout, default_seconds_timeout
|
|
|
|
(** number of symops to multiply by the number of iterations, after which there is a timeout *)
|
|
let symops_per_iteration = ref symops_timeout
|
|
|
|
(** number of seconds to multiply by the number of iterations, after which there is a timeout *)
|
|
let seconds_per_iteration = ref seconds_timeout
|
|
|
|
(** Set the timeout values in seconds and symops, computed as a multiple of the integer parameter *)
|
|
let iterations = ref 1
|
|
|
|
(** experimental: dynamic dispatch for interface calls only in Java. off by default because of the
|
|
cost *)
|
|
let sound_dynamic_dispatch = from_env_variable "INFER_SOUND_DYNAMIC_DISPATCH"
|
|
|
|
(** experimental: handle dynamic dispatch by following the JVM semantics and creating
|
|
during the symbolic excution procedure descriptions using the types information
|
|
found in the abstract state *)
|
|
let lazy_dynamic_dispatch = from_env_variable "INFER_LAZY_DYNAMIC_DISPATCH"
|
|
|
|
module Experiment = struct
|
|
|
|
(** if true, a precondition with e.g. index 3 in an array does not require the caller to have index 3 too
|
|
this mimics what happens with direct access to the array without a procedure call, where the index is simply materialized if not there *)
|
|
let allow_missing_index_in_proc_call = ref true
|
|
|
|
(** if true, a procedure call succeeds even when there is a bound error
|
|
this mimics what happens with a direct array access where an error is produced and the analysis continues *)
|
|
let bound_error_allowed_in_procedure_call = ref true
|
|
|
|
end
|
|
|
|
let source_file_extentions = [".java"; ".m"; ".mm"; ".c"; ".cc"; ".cpp"; ".h"]
|
|
|
|
let anonymous_block_prefix = "__objc_anonymous_block_"
|
|
|
|
let anonymous_block_num_sep = "______"
|
|
|
|
let property_attributes = "property_attributes"
|
|
|
|
let ivar_attributes = "ivar_attributes"
|
|
|
|
let unsafe_unret = "<\"Unsafe_unretained\">"
|
|
|
|
let weak = "<\"Weak\">"
|
|
|
|
let assign = "<\"Assign\">"
|
|
|
|
let start_filename = ".start"
|
|
|
|
(** Programming language. *)
|
|
type language = C_CPP | Java
|
|
|
|
(** current language *)
|
|
let curr_language = ref C_CPP
|
|
|
|
let string_of_language = function
|
|
| Java -> "Java"
|
|
| C_CPP -> "C_CPP"
|
|
|
|
let show_progress_bar = ref true
|
|
|
|
let nsnotification_center_checker_backend = ref false
|