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 -> tval are_same_values_as_pre_formals :
IR.Procdesc.t ->
Pulselib.PulseBasicInterface.AbstractValue.t list ->
t ->
boolval mk_join_state :
pre:(PulseBaseStack.t * PulseBaseMemory.t * PulseBaseAddressAttributes.t) ->
post:(PulseBaseStack.t * PulseBaseMemory.t * PulseBaseAddressAttributes.t) ->
Pulselib.PulseBasicInterface.Formula.t ->
Decompiler.t ->
need_dynamic_type_specialization:
Pulselib.PulseBasicInterface.AbstractValue.Set.t ->
PulseTopl.state ->
Pulselib.PulseBasicInterface.TransitiveInfo.t ->
PulseMutualRecursion.Set.t ->
Pulselib.PulseBasicInterface.SkippedCalls.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 has_reachable_in_inner_pre_heap :
Pulselib.PulseBasicInterface.AbstractValue.t list ->
t ->
booltrue if there is a value in the provided list that is reachable from the pre-condition after some non-trivial steps in the pre heap, i.e. the value is gotten from at least one dereference from the parameters of the current procedure. Used to detect likely-harmless recursive calls since heap progress has been made.
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 ->
Pulselib.PulseBasicInterface.AbstractValue.t list ->
t ->
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 fold_pointer_targets :
IR.Tenv.t ->
PathContext.t ->
[ `LocalDecl of
IR.Pvar.t
* (Pulselib.PulseBasicInterface.AbstractValue.t
* Pulselib.PulseBasicInterface.ValueHistory.t)
option
| `Malloc of
Pulselib.PulseBasicInterface.AbstractValue.t
* Pulselib.PulseBasicInterface.ValueHistory.t ] ->
IR.Typ.t ->
IBase.Location.t ->
f:
((Pulselib.PulseBasicInterface.AbstractValue.t
* Pulselib.PulseBasicInterface.ValueHistory.t) ->
t ->
t) ->
t ->
tCall the provided f on each address in memory corresponding to a field in the struct (or primitive value) starting at the provided address. Also deals with nested struct values. Each address is generated fresh and placed in the heap (or stack if the address comes from a `LocalDecl) before calling f.
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 add_event_to_value_origin :
PathContext.t ->
IBase.Location.t ->
Pulselib.PulseBasicInterface.ValueHistory.event ->
Pulselib.PulseBasicInterface.ValueOrigin.t ->
t ->
tval transfer_transitive_info_to_caller :
IR.Procname.t ->
IBase.Location.t ->
Summary.t ->
t ->
tmodule Topl : sig ... endmodule CanonValue : sig ... endsee PulseCanonValue