Pulselib.PulseAbductiveDomainmodule BaseDomain = PulseBaseDomainmodule Decompiler = PulseDecompilermodule DecompilerExpr = PulseDecompilerExprmodule PathContext = PulsePathContextBaseDomainThe operations in this module take care of two things that are important for the safety of domain operations:
1. Propagate operations on the current state to the pre-condition when necessary. This is the "abductive" part of the domain and follows the principles of biabduction as laid out in *Compositional Shape Analysis by Means of Bi-Abduction*, JACM, 2011, https://doi.org/10.1145/2049697.2049700. Simply put, a read from a memory location that is reachable from the precondition for the first time is taken to mean that this memory location had better been allocated since the beginning of the function for the operation to be safe, hence we "abduce" that this was the case and add it to the pre-condition (unless we already know that address to be invalid of course).
2. Normalize values (AbstractValue.t) in and out of the state according to their canonical representatives as per the current path condition. The idea is that one can ask about any abstract value and functions in this API will normalize it as needed first before querying the abstract state, and normalize any value found in the abstract state as needed too. For example, values in the raw map corresponding to the memory are not necessarily normalized at all times but the API allows one to pretend they are by normalizing them on the fly. On the other hand, *keys* in the memory map are always normalized so values must be normalized before being looked up in the map and this module takes care of that transparently too. See also PulseCanonValue.
module type BaseDomainSig = sig ... endsignature common to the "normal" Domain, representing the post at the current program point, and the inverted PreDomain, representing the inferred pre-condition
module PostDomain : BaseDomainSigThe post abstract state at each program point, or current state.
module PreDomain : BaseDomainSigThe inferred pre-condition at each program point, biabduction style.
type t = private {post : PostDomain.t;state at the current program point
*)pre : PreDomain.t;inferred procedure pre-condition leading to the current program point
*)path_condition : Pulselib.PulseBasicInterface.Formula.t;arithmetic facts true along the path (holding for both pre and post since abstract values are immutable)
decompiler : Decompiler.t;topl : PulseTopl.state;state at of the Topl monitor at the current program point, when Topl is enabled
*)need_dynamic_type_specialization : Pulselib.PulseBasicInterface.AbstractValue.Set.t;a set of abstract values that are used as receiver of method calls in the instructions reached so far
*)transitive_info : Pulselib.PulseBasicInterface.TransitiveInfo.t;record transitive information inter-procedurally
*)recursive_calls : PulseMutualRecursion.Set.t;skipped_calls : Pulselib.PulseBasicInterface.SkippedCalls.t;metadata: procedure calls for which no summary was found
*)}pre/post on a single program path
include Ppx_compare_lib.Equal.S with type t := tval equal : t Base__Ppx_compare_lib.equalval pp : Stdlib.Format.formatter -> t -> unitval mk_initial : IR.Tenv.t -> IR.ProcAttributes.t -> tmodule Stack : sig ... endSafe version of PulseBaseStack
module Memory : sig ... endSafe version of PulseBaseMemory
module AddressAttributes : sig ... endSafe version of PulseBaseAddressAttributes
val apply_unknown_effect :
?havoc_filter:
(Pulselib.PulseBasicInterface.AbstractValue.t ->
PulseAccess.t ->
(Pulselib.PulseBasicInterface.AbstractValue.t
* Pulselib.PulseBasicInterface.ValueHistory.t) ->
bool) ->
Pulselib.PulseBasicInterface.ValueHistory.t ->
Pulselib.PulseBasicInterface.AbstractValue.t ->
t ->
tdo as much as possible to assume "the best" happened at that location: deallocate and initialize everything reachable from the address, then havoc all the edges starting from the address passing havoc_filter (by default everything passes the filter)
val find_post_cell_opt :
Pulselib.PulseBasicInterface.AbstractValue.t ->
t ->
BaseDomain.cell optionval fold_all :
?var_filter:(IR.Var.t -> bool) ->
init:'accum ->
finish:('accum -> 'final) ->
f:
(IR.Var.t ->
'accum ->
Pulselib.PulseBasicInterface.AbstractValue.t ->
Pulselib.PulseBasicInterface.Access.t list ->
('accum, 'final) IStdlib.IStd.Continue_or_stop.t) ->
?f_revisit:
(IR.Var.t ->
'accum ->
Pulselib.PulseBasicInterface.AbstractValue.t ->
Pulselib.PulseBasicInterface.Access.t list ->
'accum) ->
t ->
[ `Post | `Pre ] ->
'finalval reachable_addresses_from :
?edge_filter:(Pulselib.PulseBasicInterface.Access.t -> bool) ->
Pulselib.PulseBasicInterface.AbstractValue.t Stdlib.Seq.t ->
t ->
[ `Pre | `Post ] ->
Pulselib.PulseBasicInterface.AbstractValue.Set.tCompute the set of abstract addresses that are reachable from given abstract addresses.
val get_unreachable_attributes :
t ->
Pulselib.PulseBasicInterface.AbstractValue.t listcollect the addresses that have attributes but are unreachable in the current post-condition
val mark_potential_leaks :
IBase.Location.t ->
dead_roots:IR.Var.t list ->
t ->
tval add_recursive_call :
IBase.Location.t ->
IR.Procname.t ->
t ->
t * PulseMutualRecursion.tval add_recursive_calls : PulseMutualRecursion.Set.t -> t -> tval add_skipped_call :
IR.Procname.t ->
Pulselib.PulseBasicInterface.Trace.t ->
t ->
tval add_skipped_calls : Pulselib.PulseBasicInterface.SkippedCalls.t -> t -> tval add_missed_captures : IR.Typ.Name.Set.t -> t -> tval set_path_condition : Pulselib.PulseBasicInterface.Formula.t -> t -> tval record_transitive_access : IBase.Location.t -> t -> tval record_call_resolution :
caller:IR.Procdesc.t ->
IBase.Location.t ->
Pulselib.PulseBasicInterface.TransitiveInfo.Callees.call_kind ->
Pulselib.PulseBasicInterface.TransitiveInfo.Callees.resolution ->
t ->
tval add_need_dynamic_type_specialization :
Pulselib.PulseBasicInterface.AbstractValue.t ->
t ->
tval map_decompiler : t -> f:(Decompiler.t -> Decompiler.t) -> tval add_block_source :
Pulselib.PulseBasicInterface.AbstractValue.t ->
string ->
t ->
tval set_post_edges :
Pulselib.PulseBasicInterface.AbstractValue.t ->
PulseBaseMemory.Edges.t ->
t ->
tdirectly set the edges for the given address, bypassing abduction altogether
val set_post_cell :
PathContext.t ->
(Pulselib.PulseBasicInterface.AbstractValue.t
* Pulselib.PulseBasicInterface.ValueHistory.t) ->
BaseDomain.cell ->
IBase.Location.t ->
t ->
tdirectly set the edges and attributes for the given address, bypassing abduction altogether
val incorporate_new_eqs :
Pulselib.PulseBasicInterface.Formula.new_eqs ->
t ->
(t,
[> `PotentialInvalidAccess of
t
* Pulselib.PulseBasicInterface.AbstractValue.t
* (Pulselib.PulseBasicInterface.Trace.t
* Pulselib.PulseBasicInterface.Invalidation.must_be_valid_reason
option) ])
IStdlib.IStd.result
Pulselib.PulseBasicInterface.SatUnsat.tCheck that the new equalities discovered are compatible with the current pre and post heaps, e.g. x = 0 is not compatible with x being allocated, and x = y is not compatible with x and y being allocated separately. In those cases, the result is Unsat.
val incorporate_new_eqs_on_val :
Pulselib.PulseBasicInterface.Formula.new_eqs ->
Pulselib.PulseBasicInterface.AbstractValue.t ->
Pulselib.PulseBasicInterface.AbstractValue.tSimilar to incorporate_new_eqs, but apply to an abstract value.
module Summary : sig ... endval transfer_transitive_info_to_caller :
IR.Procname.t ->
IBase.Location.t ->
Summary.t ->
t ->
tmodule Topl : sig ... endmodule CanonValue : sig ... endsee PulseCanonValue