explicit inter-checker dependencies

Summary:
Before: `RegisterCheckers` activates each checker based on a boolean
condition about which other checkers can enable it, eg for pulse:
```
(* registerCheckers.ml *)
active= Config.(is_checker_enabled Pulse || is_checker_enabled Impurity)
```

After: `Checker` declares for each checker the list of its dependencies,
eg for impurity:
```
(* Checker.ml *)
  name= "impurity";
  activates= [Pulse]
```

Now `Config` computes for each checker whether it was transitively
activated by other checkers or not. It saves us from having to encode
the logic from before everywhere we want to know "is checker X
running?"; this was prone to errors.

It will also allow us to display which checkers actually run to the user
more easily.

Reviewed By: ezgicicek

Differential Revision: D21622198

fbshipit-source-id: 004931192
master
Jules Villard 5 years ago committed by Facebook GitHub Bot
parent 2bbd25087c
commit 248eaf87c7

@ -71,30 +71,23 @@ let intraprocedural_with_field_dependency payload_field checker =
Procedure (CallbackOfChecker.intraprocedural_with_field_dependency payload_field checker)
type callback = callback_fun * Language.t
type checker = {name: string; active: bool; callbacks: callback list}
type checker = {checker: Checker.t; callbacks: (callback_fun * Language.t) list}
let all_checkers =
(* The order of the list is important for those checkers that depend on other checkers having run
before them. *)
[ { name= "Self captured in block checker"
; active= Config.is_checker_enabled SelfInBlock
; callbacks= [(intraprocedural SelfInBlock.checker, Clang)] }
; { name= "Class loading analysis"
; active= Config.is_checker_enabled ClassLoads
[ {checker= SelfInBlock; callbacks= [(intraprocedural SelfInBlock.checker, Clang)]}
; { checker= ClassLoads
; callbacks= [(interprocedural Payloads.Fields.class_loads ClassLoads.analyze_procedure, Java)]
}
; { name= "purity"
; active= Config.(is_checker_enabled Purity || is_checker_enabled LoopHoisting)
; { checker= Purity
; callbacks=
(let purity =
interprocedural2 Payloads.Fields.purity Payloads.Fields.buffer_overrun_analysis
Purity.checker
in
[(purity, Java); (purity, Clang)] ) }
; { name= "Starvation analysis"
; active= Config.is_checker_enabled Starvation
; { checker= Starvation
; callbacks=
(let starvation = interprocedural Payloads.Fields.starvation Starvation.analyze_procedure in
let starvation_file_reporting =
@ -104,8 +97,7 @@ let all_checkers =
; (starvation_file_reporting, Java)
; (starvation, Clang)
; (starvation_file_reporting, Clang) ] ) }
; { name= "loop hoisting"
; active= Config.is_checker_enabled LoopHoisting
; { checker= LoopHoisting
; callbacks=
(let hoisting =
interprocedural3
@ -115,102 +107,74 @@ let all_checkers =
Hoisting.checker
in
[(hoisting, Clang); (hoisting, Java)] ) }
; { name= "cost analysis"
; active=
Config.(
is_checker_enabled Cost
|| (is_checker_enabled LoopHoisting && hoisting_report_only_expensive))
; { checker= Cost
; callbacks=
(let checker =
interprocedural3 ~set_payload:(Field.fset Payloads.Fields.cost) Payloads.Fields.cost
Payloads.Fields.buffer_overrun_analysis Payloads.Fields.purity Cost.checker
in
[(checker, Clang); (checker, Java)] ) }
; { name= "uninitialized variables"
; active= Config.is_checker_enabled Uninit
; callbacks= [(interprocedural Payloads.Fields.uninit Uninit.checker, Clang)] }
; { name= "SIOF"
; active= Config.is_checker_enabled SIOF
; callbacks= [(interprocedural Payloads.Fields.siof Siof.checker, Clang)] }
; { name= "litho-required-props"
; active= Config.is_checker_enabled LithoRequiredProps
; {checker= Uninit; callbacks= [(interprocedural Payloads.Fields.uninit Uninit.checker, Clang)]}
; {checker= SIOF; callbacks= [(interprocedural Payloads.Fields.siof Siof.checker, Clang)]}
; { checker= LithoRequiredProps
; callbacks= [(interprocedural Payloads.Fields.litho_required_props RequiredProps.checker, Java)]
}
; (* toy resource analysis to use in the infer lab, see the lab/ directory *)
{ name= "resource leak"
; active= Config.is_checker_enabled ResourceLeakLabExercise
{ checker= ResourceLeakLabExercise
; callbacks=
[ ( (* the checked-in version is intraprocedural, but the lab asks to make it
interprocedural later on *)
interprocedural Payloads.Fields.lab_resource_leaks ResourceLeaks.checker
, Java ) ] }
; { name= "RacerD"
; active= Config.is_checker_enabled RacerD
; { checker= RacerD
; callbacks=
(let racerd_proc = interprocedural Payloads.Fields.racerd RacerD.analyze_procedure in
let racerd_file = file RacerDIssues Payloads.Fields.racerd RacerD.file_analysis in
[(racerd_proc, Clang); (racerd_proc, Java); (racerd_file, Clang); (racerd_file, Java)] ) }
; { name= "quandary"
; active= Config.(is_checker_enabled Quandary)
; { checker= Quandary
; callbacks=
[ (interprocedural Payloads.Fields.quandary JavaTaintAnalysis.checker, Java)
; (interprocedural Payloads.Fields.quandary ClangTaintAnalysis.checker, Clang) ] }
; { name= "pulse"
; active= Config.(is_checker_enabled Pulse || is_checker_enabled Impurity)
; { checker= Pulse
; callbacks=
(let pulse = interprocedural Payloads.Fields.pulse Pulse.checker in
[(pulse, Clang); (pulse, Java)] ) }
; { name= "impurity"
; active= Config.is_checker_enabled Impurity
; { checker= Impurity
; callbacks=
(let impurity =
intraprocedural_with_field_dependency Payloads.Fields.pulse Impurity.checker
in
[(impurity, Java); (impurity, Clang)] ) }
; { name= "printf args"
; active= Config.is_checker_enabled PrintfArgs
; callbacks= [(intraprocedural PrintfArgs.checker, Java)] }
; { name= "liveness"
; active= Config.is_checker_enabled Liveness
; callbacks= [(intraprocedural Liveness.checker, Clang)] }
; { name= "inefficient keyset iterator"
; active= Config.is_checker_enabled InefficientKeysetIterator
; {checker= PrintfArgs; callbacks= [(intraprocedural PrintfArgs.checker, Java)]}
; {checker= Liveness; callbacks= [(intraprocedural Liveness.checker, Clang)]}
; { checker= InefficientKeysetIterator
; callbacks= [(intraprocedural InefficientKeysetIterator.checker, Java)] }
; { name= "immutable cast"
; active= Config.is_checker_enabled ImmutableCast
; { checker= ImmutableCast
; callbacks=
[(intraprocedural_with_payload Payloads.Fields.nullsafe ImmutableChecker.analyze, Java)] }
; { name= "fragment retains view"
; active= Config.is_checker_enabled FragmentRetainsView
; { checker= FragmentRetainsView
; callbacks= [(intraprocedural FragmentRetainsViewChecker.callback_fragment_retains_view, Java)]
}
; { name= "eradicate"
; active= Config.is_checker_enabled Eradicate
; { checker= Eradicate
; callbacks=
[ (intraprocedural_with_payload Payloads.Fields.nullsafe Eradicate.analyze_procedure, Java)
; (file NullsafeFileIssues Payloads.Fields.nullsafe FileLevelAnalysis.analyze_file, Java) ]
}
; { name= "buffer overrun checker"
; active= Config.(is_checker_enabled BufferOverrun)
; { checker= BufferOverrunChecker
; callbacks=
(let bo_checker =
interprocedural2 Payloads.Fields.buffer_overrun_checker
Payloads.Fields.buffer_overrun_analysis BufferOverrunChecker.checker
in
[(bo_checker, Clang); (bo_checker, Java)] ) }
; { name= "buffer overrun analysis"
; active=
Config.(
is_checker_enabled BufferOverrun || is_checker_enabled Cost
|| is_checker_enabled LoopHoisting || is_checker_enabled Purity)
; { checker= BufferOverrunAnalysis
; callbacks=
(let bo_analysis =
interprocedural Payloads.Fields.buffer_overrun_analysis
BufferOverrunAnalysis.analyze_procedure
in
[(bo_analysis, Clang); (bo_analysis, Java)] ) }
; { name= "biabduction"
; active= Config.(is_checker_enabled Biabduction || is_checker_enabled TOPL)
; { checker= Biabduction
; callbacks=
(let biabduction =
dynamic_dispatch Payloads.Fields.biabduction
@ -219,8 +183,7 @@ let all_checkers =
else Interproc.analyze_procedure )
in
[(biabduction, Clang); (biabduction, Java)] ) }
; { name= "annotation reachability"
; active= Config.is_checker_enabled AnnotationReachability
; { checker= AnnotationReachability
; callbacks=
(let annot_reach =
interprocedural Payloads.Fields.annot_map AnnotationReachability.checker
@ -229,12 +192,13 @@ let all_checkers =
let get_active_checkers () =
let filter_checker {active} = active in
let filter_checker {checker} = Config.is_checker_enabled checker in
List.filter ~f:filter_checker all_checkers
let register checkers =
let register_one {name; callbacks} =
let register_one {checker; callbacks} =
let name = (Checker.config checker).name in
let register_callback (callback, language) =
match callback with
| Procedure procedure_cb ->
@ -252,12 +216,12 @@ let register checkers =
module LanguageSet = Caml.Set.Make (Language)
let pp_checker fmt {name; callbacks} =
let pp_checker fmt {checker; callbacks} =
let langs_of_callbacks =
List.fold_left callbacks ~init:LanguageSet.empty ~f:(fun langs (_, lang) ->
LanguageSet.add lang langs )
|> LanguageSet.elements
in
F.fprintf fmt "%s (%a)" name
F.fprintf fmt "%s (%a)" (Checker.config checker).name
(Pp.seq ~sep:", " (Pp.of_string ~f:Language.to_string))
langs_of_callbacks

@ -10,7 +10,8 @@ open! IStd
type t =
| AnnotationReachability
| Biabduction
| BufferOverrun
| BufferOverrunAnalysis
| BufferOverrunChecker
| ClassLoads
| Cost
| Eradicate
@ -38,13 +39,15 @@ type t =
type support = NoSupport | Support | ExperimentalSupport | ToySupport
type cli_flags = {long: string; deprecated: string list; show_in_help: bool}
type config =
{ support: Language.t -> support
{ name: string
; support: Language.t -> support
; short_documentation: string
; cli_flag: string
; show_in_help: bool
; cli_flags: cli_flags option
; enabled_by_default: bool
; cli_deprecated_flags: string list }
; activates: t list }
(* support for languages should be consistent with the corresponding
callbacks registered. Or maybe with the issues reported in link
@ -63,197 +66,210 @@ let config checker =
in
match checker with
| AnnotationReachability ->
{ support= supports_clang_and_java
{ name= "annotation reachability"
; support= supports_clang_and_java
; short_documentation=
"the annotation reachability checker. Given a pair of source and sink annotation, e.g. \
@PerformanceCritical and @Expensive, this checker will warn whenever some method \
annotated with @PerformanceCritical calls, directly or indirectly, another method \
annotated with @Expensive"
; show_in_help= true
; cli_flag= "annotation-reachability"
; cli_flags= Some {long= "annotation-reachability"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [] }
| Biabduction ->
{ support= supports_clang_and_java
{ name= "biabduction"
; support= supports_clang_and_java
; short_documentation=
"the separation logic based bi-abduction analysis using the checkers framework"
; show_in_help= true
; cli_flag= "biabduction"
; cli_flags= Some {long= "biabduction"; deprecated= []; show_in_help= true}
; enabled_by_default= true
; cli_deprecated_flags= [] }
| BufferOverrun ->
{ support= supports_clang_and_java
; activates= [] }
| BufferOverrunAnalysis ->
{ name= "buffer overrun analysis"
; support= supports_clang_and_java
; short_documentation=
"internal part of the buffer overrun analysis that computes values at each program \
point, automatically triggered when analyses that depend on these are run"
; cli_flags= None
; enabled_by_default= false
; activates= [] }
| BufferOverrunChecker ->
{ name= "buffer overrun checker"
; support= supports_clang_and_java
; short_documentation= "the buffer overrun analysis"
; show_in_help= true
; cli_flag= "bufferoverrun"
; cli_flags= Some {long= "bufferoverrun"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [BufferOverrunAnalysis] }
| ClassLoads ->
{ support= supports_java
{ name= "Class loading analysis"
; support= supports_java
; short_documentation= "Java class loading analysis"
; show_in_help= true
; cli_flag= "class-loads"
; cli_flags= Some {long= "class-loads"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [] }
| Cost ->
{ support= supports_clang_and_java
{ name= "cost analysis"
; support= supports_clang_and_java
; short_documentation= "checker for performance cost analysis"
; show_in_help= true
; cli_flag= "cost"
; cli_flags= Some {long= "cost"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [BufferOverrunAnalysis] }
| Eradicate ->
{ support= supports_java
{ name= "eradicate"
; support= supports_java
; short_documentation= "the eradicate @Nullable checker for Java annotations"
; show_in_help= true
; cli_flag= "eradicate"
; cli_flags= Some {long= "eradicate"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [] }
| FragmentRetainsView ->
{ support= supports_java
{ name= "fragment retains view"
; support= supports_java
; short_documentation=
"detects when Android fragments are not explicitly nullified before becoming unreabable"
; show_in_help= true
; cli_flag= "fragment-retains-view"
; cli_flags= Some {long= "fragment-retains-view"; deprecated= []; show_in_help= true}
; enabled_by_default= true
; cli_deprecated_flags= [] }
; activates= [] }
| ImmutableCast ->
{ support= supports_java
{ name= "immutable cast"
; support= supports_java
; short_documentation=
"the detection of object cast from immutable type to mutable type. For instance, it will \
detect cast from ImmutableList to List, ImmutableMap to Map, and ImmutableSet to Set."
; show_in_help= true
; cli_flag= "immutable-cast"
; cli_flags= Some {long= "immutable-cast"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [] }
| Impurity ->
{ support= supports_clang_and_java_experimental
{ name= "impurity"
; support= supports_clang_and_java_experimental
; short_documentation= "[EXPERIMENTAL] Impurity analysis"
; show_in_help= true
; cli_flag= "impurity"
; cli_flags= Some {long= "impurity"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [Pulse] }
| InefficientKeysetIterator ->
{ support= supports_java
{ name= "inefficient keyset iterator"
; support= supports_java
; short_documentation=
"Check for inefficient uses of keySet iterator that access both the key and the value."
; show_in_help= true
; cli_flag= "inefficient-keyset-iterator"
; cli_flags= Some {long= "inefficient-keyset-iterator"; deprecated= []; show_in_help= true}
; enabled_by_default= true
; cli_deprecated_flags= [] }
; activates= [] }
| Linters ->
{ support= supports_clang
{ name= "AST Language (AL) linters"
; support= supports_clang
; short_documentation= "syntactic linters"
; show_in_help= true
; cli_flag= "linters"
; cli_flags= Some {long= "linters"; deprecated= []; show_in_help= true}
; enabled_by_default= true
; cli_deprecated_flags= [] }
; activates= [] }
| LithoRequiredProps ->
{ support= supports_java_experimental
{ name= "litho-required-props"
; support= supports_java_experimental
; short_documentation= "[EXPERIMENTAL] Required Prop check for Litho"
; show_in_help= true
; cli_flag= "litho-required-props"
; cli_flags= Some {long= "litho-required-props"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [] }
| Liveness ->
{ support= supports_clang
{ name= "liveness"
; support= supports_clang
; short_documentation= "the detection of dead stores and unused variables"
; show_in_help= true
; cli_flag= "liveness"
; cli_flags= Some {long= "liveness"; deprecated= []; show_in_help= true}
; enabled_by_default= true
; cli_deprecated_flags= [] }
; activates= [] }
| LoopHoisting ->
{ support= supports_clang_and_java
{ name= "loop hoisting"
; support= supports_clang_and_java
; short_documentation= "checker for loop-hoisting"
; show_in_help= true
; cli_flag= "loop-hoisting"
; cli_flags= Some {long= "loop-hoisting"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [BufferOverrunAnalysis; Purity] }
| NullsafeDeprecated ->
{ support= (fun _ -> NoSupport)
{ name= "nullsafe"
; support= (fun _ -> NoSupport)
; short_documentation= "[RESERVED] Reserved for nullsafe typechecker, use --eradicate for now"
; show_in_help= false
; cli_flag= "nullsafe"
; cli_flags=
Some
{ long= "nullsafe"
; deprecated= ["-check-nullable"; "-suggest-nullable"]
; show_in_help= false }
; enabled_by_default= false
; cli_deprecated_flags= ["-check-nullable"; "-suggest-nullable"] }
; activates= [] }
| PrintfArgs ->
{ support= supports_java
{ name= "printf args"
; support= supports_java
; short_documentation=
"the detection of mismatch between the Java printf format strings and the argument types \
For, example, this checker will warn about the type error in `printf(\"Hello %d\", \
\"world\")`"
; show_in_help= true
; cli_flag= "printf-args"
; cli_flags= Some {long= "printf-args"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [] }
| Pulse ->
{ support= supports_clang_and_java_experimental
{ name= "pulse"
; support= supports_clang_and_java_experimental
; short_documentation= "[EXPERIMENTAL] memory and lifetime analysis"
; show_in_help= true
; cli_flag= "pulse"
; cli_flags= Some {long= "pulse"; deprecated= ["-ownership"]; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= ["-ownership"] }
; activates= [] }
| Purity ->
{ support= supports_clang_and_java_experimental
{ name= "purity"
; support= supports_clang_and_java_experimental
; short_documentation= "[EXPERIMENTAL] Purity analysis"
; show_in_help= true
; cli_flag= "purity"
; cli_flags= Some {long= "purity"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [BufferOverrunAnalysis] }
| Quandary ->
{ support= supports_clang_and_java
{ name= "quandary"
; support= supports_clang_and_java
; short_documentation= "the quandary taint analysis"
; show_in_help= true
; cli_flag= "quandary"
; cli_flags= Some {long= "quandary"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [] }
| RacerD ->
{ support= supports_clang_and_java
{ name= "RacerD"
; support= supports_clang_and_java
; short_documentation= "the RacerD thread safety analysis"
; show_in_help= true
; cli_flag= "racerd"
; cli_flags= Some {long= "racerd"; deprecated= ["-threadsafety"]; show_in_help= true}
; enabled_by_default= true
; cli_deprecated_flags= ["-threadsafety"] }
; activates= [] }
| ResourceLeakLabExercise ->
{ support= (fun _ -> ToySupport)
{ name= "resource leak lab exercise"
; support= (fun _ -> ToySupport)
; short_documentation= ""
; show_in_help= false
; cli_flag= "resource-leak"
; cli_flags= Some {long= "resource-leak"; deprecated= []; show_in_help= false}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [] }
| SIOF ->
{ support= supports_clang
{ name= "SIOF"
; support= supports_clang
; short_documentation= "the Static Initialization Order Fiasco analysis (C++ only)"
; show_in_help= true
; cli_flag= "siof"
; cli_flags= Some {long= "siof"; deprecated= []; show_in_help= true}
; enabled_by_default= true
; cli_deprecated_flags= [] }
; activates= [] }
| SelfInBlock ->
{ support= supports_clang
{ name= "Self captured in block checker"
; support= supports_clang
; short_documentation=
"checker to flag incorrect uses of when Objective-C blocks capture self"
; show_in_help= true
; cli_flag= "self_in_block"
; cli_flags= Some {long= "self_in_block"; deprecated= []; show_in_help= true}
; enabled_by_default= true
; cli_deprecated_flags= [] }
; activates= [] }
| Starvation ->
{ support= supports_clang_and_java
{ name= "Starvation analysis"
; support= supports_clang_and_java
; short_documentation= "starvation analysis"
; show_in_help= true
; cli_flag= "starvation"
; cli_flags= Some {long= "starvation"; deprecated= []; show_in_help= true}
; enabled_by_default= true
; cli_deprecated_flags= [] }
; activates= [] }
| TOPL ->
{ support= supports_clang_and_java_experimental
{ name= "TOPL"
; support= supports_clang_and_java_experimental
; short_documentation= "TOPL"
; show_in_help= true
; cli_flag= "topl"
; cli_flags= Some {long= "topl"; deprecated= []; show_in_help= true}
; enabled_by_default= false
; cli_deprecated_flags= [] }
; activates= [Biabduction] }
| Uninit ->
{ support= supports_clang
{ name= "uninitialized variables"
; support= supports_clang
; short_documentation= "checker for use of uninitialized values"
; show_in_help= true
; cli_flag= "uninit"
; cli_flags= Some {long= "uninit"; deprecated= []; show_in_help= true}
; enabled_by_default= true
; cli_deprecated_flags= [] }
; activates= [] }

@ -10,7 +10,8 @@ open! IStd
type t =
| AnnotationReachability
| Biabduction
| BufferOverrun
| BufferOverrunAnalysis
| BufferOverrunChecker
| ClassLoads
| Cost
| Eradicate
@ -45,15 +46,21 @@ type support =
(** the checker is for teaching purposes only (like experimental but with no plans to improve
it) *)
type cli_flags =
{ long: string
(** The flag to enable this option on the command line, without the leading "--" (like the
[~long] argument of {!CommandLineOption} functions). *)
; deprecated: string list
(** More command-line flags, similar to [~deprecated] arguments in {!CommandLineOption}. *)
; show_in_help: bool }
type config =
{ support: Language.t -> support
{ name: string
; support: Language.t -> support
; short_documentation: string
; cli_flag: string
(** the flag to enable this option on the command line, without the leading "--" (like the
[~long] argument of [CommandLineOption] functions) *)
; show_in_help: bool
; cli_flags: cli_flags option
(** If [None] then the checker cannot be enabled/disabled from the command line. *)
; enabled_by_default: bool
; cli_deprecated_flags: string list
(** more command-line flags, similar to [~deprecated] arguments *) }
; activates: t list (** TODO doc *) }
val config : t -> config

@ -531,34 +531,41 @@ and analyzer =
instance, to enable only the biabduction analysis, run with $(b,--biabduction-only)."
and _checkers =
(* checkers *)
and () =
let open Checker in
let in_analyze_help = InferCommand.[(Analyze, manual_generic)] in
let mk_checker ?f checker =
let config = Checker.config checker in
let in_help = if config.show_in_help then in_analyze_help else [] in
let var =
CLOpt.mk_bool ?f ~long:config.cli_flag ~in_help ~default:config.enabled_by_default
~deprecated:config.cli_deprecated_flags config.short_documentation
match config.cli_flags with
| None ->
(* HACK: return a constant ref if the checker cannot be enabled/disabled from the command line *)
ref config.enabled_by_default
| Some {long; deprecated; show_in_help} ->
let in_help = if show_in_help then in_analyze_help else [] in
CLOpt.mk_bool ?f ~long ~in_help ~default:config.enabled_by_default ~deprecated
config.short_documentation
in
all_checkers := (checker, config, var) :: !all_checkers
in
List.iter Checker.all ~f:mk_checker ;
let mk_only (_checker, config, var) =
Option.iter config.cli_flags ~f:(fun {long; show_in_help} ->
let (_ : bool ref) =
CLOpt.mk_bool_group ~long:(config.cli_flag ^ "-only")
CLOpt.mk_bool_group ~long:(long ^ "-only")
~in_help:InferCommand.[(Analyze, manual_generic)]
~f:(fun b ->
disable_all_checkers () ;
var := b ;
b )
( if config.show_in_help then
Printf.sprintf "Enable $(b,--%s) and disable all other checkers" config.cli_flag
( if show_in_help then
Printf.sprintf "Enable $(b,--%s) and disable all other checkers" long
else "" )
[] (* do all the work in ~f *) []
(* do all the work in ~f *)
in
()
() )
in
List.iter ~f:mk_only !all_checkers ;
let _default_checkers : bool ref =
@ -568,8 +575,11 @@ and _checkers =
( "Default checkers: "
^ ( List.rev_filter_map
~f:(fun (_, config, _) ->
if config.enabled_by_default then Some (Printf.sprintf "$(b,--%s)" config.cli_flag)
else None )
match config.cli_flags with
| Some {long} when config.enabled_by_default ->
Some (Printf.sprintf "$(b,--%s)" long)
| _ ->
None )
!all_checkers
|> String.concat ~sep:", " ) )
~f:(fun b ->
@ -3101,10 +3111,47 @@ and xcpretty = !xcpretty
(** Configuration values derived from command-line options *)
let is_checker_enabled c =
List.find_map_exn checkers ~f:(fun (checker, enabled) ->
if Checker.equal checker c then Some enabled else None )
let mem_checkers enabled_checkers (c : Checker.t) = List.mem ~equal:Checker.equal enabled_checkers c
let enabled_checkers =
(* invariant: [newly_enabled_checkers] is *included* in [enabled_checkers] and [enabled_checkers]
has at most one occurrence of each checker.
NOTE: the complexity of this is quadratic in the number of checkers but this is fine as we
don't have hundreds of checkers (yet). Also we have few dependencies and shallow dependency
chains so we don't even get close to the worst case. *)
let rec fixpoint newly_enabled_checkers enabled_checkers =
let newly_enabled_checkers, enabled_checkers' =
List.fold newly_enabled_checkers ~init:([], enabled_checkers)
~f:(fun (newly_enabled_checkers, enabled_checkers) checker ->
let to_enable =
(Checker.config checker).activates
|> List.filter ~f:(fun checker_dep -> not (mem_checkers enabled_checkers checker_dep))
in
match to_enable with
| [] ->
(newly_enabled_checkers, enabled_checkers)
| _ :: _ ->
(to_enable @ newly_enabled_checkers, to_enable @ enabled_checkers) )
in
if List.is_empty newly_enabled_checkers then enabled_checkers
else fixpoint newly_enabled_checkers enabled_checkers'
in
let enabled_checkers =
let enabled0 =
List.filter_map checkers ~f:(fun (checker, active) -> if active then Some checker else None)
in
fixpoint enabled0 enabled0
in
if
hoisting_report_only_expensive
&& mem_checkers enabled_checkers LoopHoisting
&& not (mem_checkers enabled_checkers Cost)
then fixpoint [Checker.Cost] (Checker.Cost :: enabled_checkers)
else enabled_checkers
let is_checker_enabled c = mem_checkers enabled_checkers c
let clang_frontend_action_string =
let text = if capture then ["translating"] else [] in
@ -3123,7 +3170,7 @@ let clang_frontend_action_string =
a call to unknown code and true triggers lazy dynamic dispatch. The latter mode follows the
JVM semantics and creates procedure descriptions during symbolic execution using the type
information found in the abstract state *)
let dynamic_dispatch = is_checker_enabled Biabduction || is_checker_enabled TOPL
let dynamic_dispatch = is_checker_enabled Biabduction
(** Check if a Java package is external to the repository *)
let java_package_is_external package =

@ -137,23 +137,29 @@ let assert_failure = register_from_string ~id:"Assert_failure" [Biabduction]
let bad_footprint = register_from_string ~id:"Bad_footprint" [Biabduction]
let buffer_overrun_l1 = register_from_string ~id:"BUFFER_OVERRUN_L1" [BufferOverrun]
let buffer_overrun_l1 = register_from_string ~id:"BUFFER_OVERRUN_L1" [BufferOverrunChecker]
let buffer_overrun_l2 = register_from_string ~id:"BUFFER_OVERRUN_L2" [BufferOverrun]
let buffer_overrun_l2 = register_from_string ~id:"BUFFER_OVERRUN_L2" [BufferOverrunChecker]
let buffer_overrun_l3 = register_from_string ~id:"BUFFER_OVERRUN_L3" [BufferOverrun]
let buffer_overrun_l3 = register_from_string ~id:"BUFFER_OVERRUN_L3" [BufferOverrunChecker]
let buffer_overrun_l4 = register_from_string ~enabled:false ~id:"BUFFER_OVERRUN_L4" [BufferOverrun]
let buffer_overrun_l4 =
register_from_string ~enabled:false ~id:"BUFFER_OVERRUN_L4" [BufferOverrunChecker]
let buffer_overrun_l5 = register_from_string ~enabled:false ~id:"BUFFER_OVERRUN_L5" [BufferOverrun]
let buffer_overrun_r2 = register_from_string ~id:"BUFFER_OVERRUN_R2" [BufferOverrun]
let buffer_overrun_l5 =
register_from_string ~enabled:false ~id:"BUFFER_OVERRUN_L5" [BufferOverrunChecker]
let buffer_overrun_s2 = register_from_string ~id:"BUFFER_OVERRUN_S2" [BufferOverrun]
let buffer_overrun_t1 = register_from_string ~id:"BUFFER_OVERRUN_T1" [BufferOverrun]
let buffer_overrun_r2 = register_from_string ~id:"BUFFER_OVERRUN_R2" [BufferOverrunChecker]
let buffer_overrun_s2 = register_from_string ~id:"BUFFER_OVERRUN_S2" [BufferOverrunChecker]
let buffer_overrun_t1 = register_from_string ~id:"BUFFER_OVERRUN_T1" [BufferOverrunChecker]
let buffer_overrun_u5 =
register_from_string ~enabled:false ~id:"BUFFER_OVERRUN_U5" [BufferOverrunChecker]
let buffer_overrun_u5 = register_from_string ~enabled:false ~id:"BUFFER_OVERRUN_U5" [BufferOverrun]
let cannot_star = register_from_string ~id:"Cannot_star" [Biabduction]
@ -219,11 +225,12 @@ let component_with_unconventional_superclass =
let condition_always_false =
register_from_string ~enabled:false ~id:"CONDITION_ALWAYS_FALSE" [Biabduction; BufferOverrun]
register_from_string ~enabled:false ~id:"CONDITION_ALWAYS_FALSE"
[Biabduction; BufferOverrunChecker]
let condition_always_true =
register_from_string ~enabled:false ~id:"CONDITION_ALWAYS_TRUE" [Biabduction; BufferOverrun]
register_from_string ~enabled:false ~id:"CONDITION_ALWAYS_TRUE" [Biabduction; BufferOverrunChecker]
let constant_address_dereference =
@ -379,20 +386,24 @@ let inefficient_keyset_iterator =
register_from_string ~id:"INEFFICIENT_KEYSET_ITERATOR" [InefficientKeysetIterator]
let inferbo_alloc_is_big = register_from_string ~id:"INFERBO_ALLOC_IS_BIG" [BufferOverrun]
let inferbo_alloc_is_big = register_from_string ~id:"INFERBO_ALLOC_IS_BIG" [BufferOverrunChecker]
let inferbo_alloc_is_negative =
register_from_string ~id:"INFERBO_ALLOC_IS_NEGATIVE" [BufferOverrunChecker]
let inferbo_alloc_is_negative = register_from_string ~id:"INFERBO_ALLOC_IS_NEGATIVE" [BufferOverrun]
let inferbo_alloc_is_zero = register_from_string ~id:"INFERBO_ALLOC_IS_ZERO" [BufferOverrunChecker]
let inferbo_alloc_is_zero = register_from_string ~id:"INFERBO_ALLOC_IS_ZERO" [BufferOverrun]
let inferbo_alloc_may_be_big =
register_from_string ~id:"INFERBO_ALLOC_MAY_BE_BIG" [BufferOverrunChecker]
let inferbo_alloc_may_be_big = register_from_string ~id:"INFERBO_ALLOC_MAY_BE_BIG" [BufferOverrun]
let inferbo_alloc_may_be_negative =
register_from_string ~id:"INFERBO_ALLOC_MAY_BE_NEGATIVE" [BufferOverrun]
register_from_string ~id:"INFERBO_ALLOC_MAY_BE_NEGATIVE" [BufferOverrunChecker]
let inferbo_alloc_may_be_tainted =
register_from_string ~id:"INFERBO_ALLOC_MAY_BE_TAINTED" [BufferOverrun]
register_from_string ~id:"INFERBO_ALLOC_MAY_BE_TAINTED" [BufferOverrunChecker]
let infinite_cost_call ~kind = register_from_cost_string ~enabled:false "INFINITE_%s" ~kind
@ -403,18 +414,18 @@ let inherently_dangerous_function =
let insecure_intent_handling = register_from_string ~id:"INSECURE_INTENT_HANDLING" [Quandary]
let integer_overflow_l1 = register_from_string ~id:"INTEGER_OVERFLOW_L1" [BufferOverrun]
let integer_overflow_l1 = register_from_string ~id:"INTEGER_OVERFLOW_L1" [BufferOverrunChecker]
let integer_overflow_l2 = register_from_string ~id:"INTEGER_OVERFLOW_L2" [BufferOverrun]
let integer_overflow_l2 = register_from_string ~id:"INTEGER_OVERFLOW_L2" [BufferOverrunChecker]
let integer_overflow_l5 =
register_from_string ~enabled:false ~id:"INTEGER_OVERFLOW_L5" [BufferOverrun]
register_from_string ~enabled:false ~id:"INTEGER_OVERFLOW_L5" [BufferOverrunChecker]
let integer_overflow_r2 = register_from_string ~id:"INTEGER_OVERFLOW_R2" [BufferOverrun]
let integer_overflow_r2 = register_from_string ~id:"INTEGER_OVERFLOW_R2" [BufferOverrunChecker]
let integer_overflow_u5 =
register_from_string ~enabled:false ~id:"INTEGER_OVERFLOW_U5" [BufferOverrun]
register_from_string ~enabled:false ~id:"INTEGER_OVERFLOW_U5" [BufferOverrunChecker]
let interface_not_thread_safe = register_from_string ~id:"INTERFACE_NOT_THREAD_SAFE" [RacerD]
@ -544,7 +555,7 @@ let unary_minus_applied_to_unsigned_expression =
let uninitialized_value = register_from_string ~id:"UNINITIALIZED_VALUE" [Uninit]
let unreachable_code_after = register_from_string ~id:"UNREACHABLE_CODE" [BufferOverrun]
let unreachable_code_after = register_from_string ~id:"UNREACHABLE_CODE" [BufferOverrunChecker]
let use_after_delete = register_from_string ~id:"USE_AFTER_DELETE" [Pulse]

@ -135,7 +135,7 @@ let rec find_normal_variable_load_ tenv (seen : Exp.Set.t) node id : DExp.t opti
in
Some (DExp.Dretcall (fun_dexp, args_dexp, loc, call_flags))
| Sil.Store {e1= Exp.Lvar pvar; e2= Exp.Var id0}
when Config.(is_checker_enabled Biabduction || is_checker_enabled TOPL)
when Config.is_checker_enabled Biabduction
&& Ident.equal id id0
&& not (Pvar.is_frontend_tmp pvar) ->
(* this case is a hack to make bucketing continue to work in the presence of copy

Loading…
Cancel
Save