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.
397 lines
12 KiB
397 lines
12 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 attributes_dir_name = "attributes"
|
|
let captured_dir_name = "captured"
|
|
let sources_dir_name = "sources"
|
|
let specs_dir_name = "specs"
|
|
|
|
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"
|
|
|
|
(** 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
|
|
|
|
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 ^ CRC.crc16 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 = ref 2
|
|
|
|
(** 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 to activate ondemand mode. *)
|
|
let ondemand_enabled = 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
|
|
|
|
(** Flag to activate intraprocedural-only analysis *)
|
|
let intraprocedural = ref false
|
|
|
|
(** 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
|
|
|
|
(** max number of procedures in each cluster *)
|
|
let max_cluster_size = ref 2000
|
|
|
|
(** 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
|
|
|
|
(** Flag for the on-the-fly predicate discovery *)
|
|
let on_the_fly = ref true
|
|
|
|
(** if true, skip the re-execution phase *)
|
|
let only_footprint = ref false
|
|
|
|
(** flag: only analyze procedures which were analyzed before but have no specs *)
|
|
let only_nospecs = ref false
|
|
|
|
(** flag: only analyze procedures dependent on previous skips which now have a .specs file *)
|
|
let only_skips = 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
|
|
|
|
(** If not "", only consider functions recursively called by function [!slice_fun] *)
|
|
let slice_fun = ref ""
|
|
|
|
(** 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 false
|
|
|
|
(** set to true to printing tracing information for the analysis *)
|
|
let trace_anal = ref false
|
|
|
|
(** 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_assertion_failure = from_env_variable "INFER_REPORT_ASSERTION_FAILURE"
|
|
let default_failure_name = "ASSERTION_FAILURE"
|
|
|
|
let analyze_models = from_env_variable "INFER_ANALYZE_MODELS"
|
|
|
|
module Experiment = struct
|
|
|
|
(** if true, activate the subtyping routines in C++ as well, not just in Java *)
|
|
let activate_subtyping_in_cpp = ref false
|
|
|
|
(** 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\">"
|
|
|
|
(** 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"
|