(* * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. *) open! IStd module F = Format open PulseBasicInterface module BaseAddressAttributes = PulseBaseAddressAttributes module BaseDomain = PulseBaseDomain module BaseMemory = PulseBaseMemory module BaseStack = PulseBaseStack (** Layer on top of {!BaseDomain} to propagate operations on the current state to the pre-condition when necessary The abstract type [t] is a pre/post pair in the style of biabduction. *) (** signature common to the "normal" [Domain], representing the post at the current program point, and the inverted [PreDomain], representing the inferred pre-condition*) module type BaseDomainSig = sig (* private because the lattice is not the same for preconditions and postconditions so we don't want to confuse them *) type t = private BaseDomain.t val empty : t val update : ?stack:BaseStack.t -> ?heap:BaseMemory.t -> ?attrs:BaseAddressAttributes.t -> t -> t val filter_addr : f:(AbstractValue.t -> bool) -> t -> t (**filter both heap and attrs *) val partition_addr : f:(AbstractValue.t -> bool) -> t -> (BaseMemory.t * BaseAddressAttributes.t) * (BaseMemory.t * BaseAddressAttributes.t) (**partition both heap and attrs *) val pp : F.formatter -> t -> unit end module Domain : BaseDomainSig (** The post abstract state at each program point, or current state. *) module PreDomain : BaseDomainSig (** The inferred pre-condition at each program point, biabduction style. NOTE: [PreDomain] and [Domain] theoretically differ in that [PreDomain] should be the inverted lattice of [Domain], but since we never actually join states or check implication the two collapse into one. * *) (** biabduction-style pre/post state + skipped calls *) type t = private { post: Domain.t (** state at the current program point*) ; pre: PreDomain.t (** inferred pre at the current program point *) ; skipped_calls: SkippedCalls.t (** set of skipped calls *) } val leq : lhs:t -> rhs:t -> bool val pp : Format.formatter -> t -> unit val mk_initial : Procdesc.t -> t val get_pre : t -> BaseDomain.t val get_post : t -> BaseDomain.t val get_skipped_calls : t -> SkippedCalls.t (** stack operations like {!BaseStack} but that also take care of propagating facts to the precondition *) module Stack : sig val add : Var.t -> BaseStack.value -> t -> t val remove_vars : Var.t list -> t -> t val fold : (Var.t -> BaseStack.value -> 'a -> 'a) -> t -> 'a -> 'a val find_opt : Var.t -> t -> BaseStack.value option val eval : ValueHistory.t -> Var.t -> t -> t * (AbstractValue.t * ValueHistory.t) (** return the value of the variable in the stack or create a fresh one if needed *) val mem : Var.t -> t -> bool val exists : (Var.t -> BaseStack.value -> bool) -> t -> bool end (** memory operations like {!BaseMemory} but that also take care of propagating facts to the precondition *) module Memory : sig module Access = BaseMemory.Access module Edges = BaseMemory.Edges val add_edge : AbstractValue.t * ValueHistory.t -> Access.t -> AbstractValue.t * ValueHistory.t -> Location.t -> t -> t val eval_edge : AbstractValue.t * ValueHistory.t -> Access.t -> t -> t * (AbstractValue.t * ValueHistory.t) (** [eval_edge (addr,hist) access astate] follows the edge [addr --access--> .] in memory and returns what it points to or creates a fresh value if that edge didn't exist. *) val find_opt : AbstractValue.t -> t -> BaseMemory.Edges.t option val find_edge_opt : AbstractValue.t -> Access.t -> t -> (AbstractValue.t * ValueHistory.t) option end (** attribute operations like {!BaseAddressAttributes} but that also take care of propagating facts to the precondition *) module AddressAttributes : sig val abduce_attribute : AbstractValue.t -> Attribute.t -> t -> t (** add the attribute to the pre, if meaningful (does not modify the post) *) val abduce_and_add : AbstractValue.t -> Attributes.t -> t -> t (** add the attributes to both the current state and, if meaningful, the pre *) val add_one : AbstractValue.t -> Attribute.t -> t -> t (** add the attribute only to the post *) val check_valid : Trace.t -> AbstractValue.t -> t -> (t, Invalidation.t * Trace.t) result val invalidate : AbstractValue.t * ValueHistory.t -> Invalidation.t -> Location.t -> t -> t val allocate : Procname.t -> AbstractValue.t * ValueHistory.t -> Location.t -> t -> t val get_closure_proc_name : AbstractValue.t -> t -> Procname.t option val is_std_vector_reserved : AbstractValue.t -> t -> bool val std_vector_reserve : AbstractValue.t -> t -> t val get_citv : AbstractValue.t -> t -> (CItv.t * Trace.t) option val get_bo_itv : AbstractValue.t -> t -> Itv.ItvPure.t val find_opt : AbstractValue.t -> t -> Attributes.t option end val is_local : Var.t -> t -> bool val find_post_cell_opt : AbstractValue.t -> t -> BaseDomain.cell option val discard_unreachable : t -> t * BaseAddressAttributes.t (** [discard_unreachable astate] garbage collects unreachable addresses in the state to make it smaller, and retuns the new state and the attributes of discarded addresses *) val add_skipped_call : Procname.t -> PulseTrace.t -> t -> t val add_skipped_calls : SkippedCalls.t -> t -> t val of_post : Procdesc.t -> t -> t val set_post_edges : AbstractValue.t -> BaseMemory.Edges.t -> t -> t (** directly set the edges for the given address, bypassing abduction altogether *) val set_post_cell : AbstractValue.t * ValueHistory.t -> BaseDomain.cell -> Location.t -> t -> t (** directly set the edges and attributes for the given address, bypassing abduction altogether *)