Reviewed By: cristianoc Differential Revision: D3734044 fbshipit-source-id: 014b0d9master
parent
89fa74bc9b
commit
36b0a957bd
@ -0,0 +1,108 @@
|
||||
(*
|
||||
* Copyright (c) 2016 - 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.
|
||||
*)
|
||||
|
||||
open! Utils
|
||||
|
||||
module F = Format
|
||||
module L = Logging
|
||||
|
||||
(** functor that makes it easy to write a basic abstract interpretation checker by lifting
|
||||
a type, comparison function, reporting function, and exec function into an analyzer *)
|
||||
|
||||
module type Spec = sig
|
||||
type astate (** what state do you want to propagate? *)
|
||||
|
||||
(** implement the state the analysis should start from here *)
|
||||
val initial : astate
|
||||
|
||||
(** implement how an instruction changes your state here.
|
||||
input is the previous state, current instruction, current node kind, current procedure and
|
||||
type environment.
|
||||
*)
|
||||
val exec_instr : astate -> Sil.instr -> Cfg.Node.nodekind -> Procname.t -> Tenv.t -> astate
|
||||
|
||||
(** log errors here.
|
||||
input is a state, location where the state occurs in the source, and the current procedure.
|
||||
*)
|
||||
val report : astate -> Location.t -> Procname.t -> unit
|
||||
|
||||
val compare : astate -> astate -> int
|
||||
end
|
||||
|
||||
module type S = sig
|
||||
(** add YourChecker.checker to registerCallbacks.ml to run your checker *)
|
||||
val checker : Callbacks.proc_callback_args -> unit
|
||||
end
|
||||
|
||||
module Make (Spec : Spec) : S = struct
|
||||
|
||||
(* powerset domain over Spec.astate *)
|
||||
module Domain = struct
|
||||
include
|
||||
AbstractDomain.FiniteSet
|
||||
(PrettyPrintable.MakePPSet(
|
||||
struct
|
||||
type t = Spec.astate
|
||||
let compare = Spec.compare
|
||||
let pp_element _ _ = ()
|
||||
end)
|
||||
)
|
||||
|
||||
let initial = singleton Spec.initial
|
||||
|
||||
let widen ~prev ~next ~num_iters =
|
||||
let iters_befor_timeout = 1000 in
|
||||
(* failsafe for accidental non-finite height domains *)
|
||||
if num_iters >= iters_befor_timeout
|
||||
then
|
||||
failwith
|
||||
("Stopping analysis after 1000 iterations without convergence." ^
|
||||
"Make sure your domain is finite height.")
|
||||
else widen ~prev ~next ~num_iters
|
||||
end
|
||||
|
||||
module TransferFunctions (CFG : ProcCfg.S) = struct
|
||||
module CFG = CFG
|
||||
module Domain = Domain
|
||||
type extras = ProcData.no_extras
|
||||
|
||||
let exec_instr astate_set proc_data node instr =
|
||||
let node_kind = CFG.kind node in
|
||||
let pname = Cfg.Procdesc.get_proc_name proc_data.ProcData.pdesc in
|
||||
Domain.fold
|
||||
(fun astate acc ->
|
||||
Domain.add (Spec.exec_instr astate instr node_kind pname proc_data.ProcData.tenv) acc)
|
||||
astate_set
|
||||
Domain.empty
|
||||
end
|
||||
|
||||
module Analyzer =
|
||||
AbstractInterpreter.Make
|
||||
(ProcCfg.Exceptional)
|
||||
(Scheduler.ReversePostorder)
|
||||
(TransferFunctions)
|
||||
|
||||
let checker { Callbacks.proc_desc; proc_name; tenv; } =
|
||||
let nodes = Cfg.Procdesc.get_nodes proc_desc in
|
||||
let do_reporting node_id state =
|
||||
let astate_set = state.AbstractInterpreter.post in
|
||||
if not (Domain.is_empty astate_set)
|
||||
then
|
||||
(* should never fail since keys in the invariant map should always be real node id's *)
|
||||
let node =
|
||||
IList.find
|
||||
(fun node -> Cfg.Node.id_compare node_id (Cfg.Node.get_id node) = 0)
|
||||
nodes in
|
||||
Domain.iter
|
||||
(fun astate ->
|
||||
Spec.report astate (ProcCfg.Exceptional.loc node) proc_name)
|
||||
astate_set in
|
||||
let inv_map = Analyzer.exec_pdesc (ProcData.make_default proc_desc tenv) in
|
||||
Analyzer.M.iter do_reporting inv_map
|
||||
end
|
Loading…
Reference in new issue