|
|
|
@ -24,6 +24,9 @@ type call_state =
|
|
|
|
|
{ astate: AbductiveDomain.t (** caller's abstract state computed so far *)
|
|
|
|
|
; subst: (AbstractValue.t * ValueHistory.t) AddressMap.t
|
|
|
|
|
(** translation from callee addresses to caller addresses and their caller histories *)
|
|
|
|
|
; invalid_subst: (AbstractValue.t * ValueHistory.t) AddressMap.t
|
|
|
|
|
(** this is a subset of subst that contains all invalid matches between callee-pre and
|
|
|
|
|
caller *)
|
|
|
|
|
; rev_subst: AbstractValue.t AddressMap.t
|
|
|
|
|
(** the inverse translation from [subst] from caller addresses to callee addresses *)
|
|
|
|
|
; visited: AddressSet.t
|
|
|
|
@ -33,15 +36,18 @@ type call_state =
|
|
|
|
|
visit each subgraph from each formal independently so we reset [visited] between the
|
|
|
|
|
visit of each formal *) }
|
|
|
|
|
|
|
|
|
|
let pp_call_state fmt {astate; subst; rev_subst; visited} =
|
|
|
|
|
let pp_call_state fmt {astate; subst; invalid_subst; rev_subst; visited} =
|
|
|
|
|
F.fprintf fmt
|
|
|
|
|
"@[<v>{ astate=@[<hv2>%a@];@,\
|
|
|
|
|
\ subst=@[<hv2>%a@];@,\
|
|
|
|
|
\ invalid_subst=@[<hv2>%a@];@,\
|
|
|
|
|
\ rev_subst=@[<hv2>%a@];@,\
|
|
|
|
|
\ visited=@[<hv2>%a@]@,\
|
|
|
|
|
\ }@]" AbductiveDomain.pp astate
|
|
|
|
|
(AddressMap.pp ~pp_value:(fun fmt (addr, _) -> AbstractValue.pp fmt addr))
|
|
|
|
|
subst
|
|
|
|
|
(AddressMap.pp ~pp_value:(fun fmt (addr, _) -> AbstractValue.pp fmt addr))
|
|
|
|
|
invalid_subst
|
|
|
|
|
(AddressMap.pp ~pp_value:AbstractValue.pp)
|
|
|
|
|
rev_subst AddressSet.pp visited
|
|
|
|
|
|
|
|
|
@ -58,6 +64,7 @@ type contradiction =
|
|
|
|
|
[foo(z,z)] where the spec for [foo(x,y)] says that [x] and [y] are disjoint. *)
|
|
|
|
|
| FormalActualLength of
|
|
|
|
|
{formals: Var.t list; actuals: ((AbstractValue.t * ValueHistory.t) * Typ.t) list}
|
|
|
|
|
| ISLPreconditionMismatch
|
|
|
|
|
| PathCondition
|
|
|
|
|
|
|
|
|
|
let pp_contradiction fmt = function
|
|
|
|
@ -71,6 +78,8 @@ let pp_contradiction fmt = function
|
|
|
|
|
(List.length actuals)
|
|
|
|
|
| PathCondition ->
|
|
|
|
|
F.pp_print_string fmt "path condition evaluates to false"
|
|
|
|
|
| ISLPreconditionMismatch ->
|
|
|
|
|
F.pp_print_string fmt "pre does not imply call state"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exception Contradiction of contradiction
|
|
|
|
@ -82,7 +91,9 @@ let fold_globals_of_stack call_loc stack call_state ~f =
|
|
|
|
|
| Var.ProgramVar pvar when Pvar.is_global pvar ->
|
|
|
|
|
let call_state, addr_hist_caller =
|
|
|
|
|
let astate, var_value =
|
|
|
|
|
Stack.eval [ValueHistory.VariableAccessed (pvar, call_loc)] var call_state.astate
|
|
|
|
|
Stack.eval call_loc
|
|
|
|
|
[ValueHistory.VariableAccessed (pvar, call_loc)]
|
|
|
|
|
var call_state.astate
|
|
|
|
|
in
|
|
|
|
|
if phys_equal astate call_state.astate then (call_state, var_value)
|
|
|
|
|
else ({call_state with astate}, var_value)
|
|
|
|
@ -278,17 +289,44 @@ let apply_arithmetic_constraints callee_proc_name call_location pre_post call_st
|
|
|
|
|
in
|
|
|
|
|
let call_state = conjoin_callee_arith pre_post call_state in
|
|
|
|
|
(* check all callee addresses that make sense for the caller, i.e. the domain of [call_state.subst] *)
|
|
|
|
|
AddressMap.fold
|
|
|
|
|
(fun addr_callee addr_hist_caller call_state ->
|
|
|
|
|
match
|
|
|
|
|
BaseAddressAttributes.find_opt addr_callee
|
|
|
|
|
(pre_post.AbductiveDomain.pre :> BaseDomain.t).attrs
|
|
|
|
|
with
|
|
|
|
|
| None ->
|
|
|
|
|
call_state
|
|
|
|
|
| Some callee_attrs ->
|
|
|
|
|
one_address_sat callee_attrs addr_hist_caller call_state )
|
|
|
|
|
call_state.subst call_state
|
|
|
|
|
if Config.pulse_isl then
|
|
|
|
|
AddressMap.fold
|
|
|
|
|
(fun addr_callee addr_hist_caller call_state ->
|
|
|
|
|
let callee_attr =
|
|
|
|
|
BaseAddressAttributes.find_opt addr_callee
|
|
|
|
|
(pre_post.AbductiveDomain.pre :> BaseDomain.t).attrs
|
|
|
|
|
in
|
|
|
|
|
let caller_attr =
|
|
|
|
|
BaseAddressAttributes.find_opt (fst addr_hist_caller)
|
|
|
|
|
(call_state.astate.AbductiveDomain.post :> BaseDomain.t).attrs
|
|
|
|
|
in
|
|
|
|
|
match (callee_attr, caller_attr) with
|
|
|
|
|
| Some callee_attrs, None ->
|
|
|
|
|
one_address_sat callee_attrs addr_hist_caller call_state
|
|
|
|
|
| Some callee_attrs, Some caller_attrs ->
|
|
|
|
|
if (* check implication *)
|
|
|
|
|
Attributes.isl_subset callee_attrs caller_attrs then
|
|
|
|
|
{ call_state with
|
|
|
|
|
invalid_subst= AddressMap.add addr_callee addr_hist_caller call_state.invalid_subst
|
|
|
|
|
}
|
|
|
|
|
else if Attributes.is_uninitialized caller_attrs then
|
|
|
|
|
one_address_sat callee_attrs addr_hist_caller call_state
|
|
|
|
|
else raise (Contradiction ISLPreconditionMismatch)
|
|
|
|
|
| _ ->
|
|
|
|
|
call_state )
|
|
|
|
|
call_state.subst call_state
|
|
|
|
|
else
|
|
|
|
|
AddressMap.fold
|
|
|
|
|
(fun addr_callee addr_hist_caller call_state ->
|
|
|
|
|
match
|
|
|
|
|
BaseAddressAttributes.find_opt addr_callee
|
|
|
|
|
(pre_post.AbductiveDomain.pre :> BaseDomain.t).attrs
|
|
|
|
|
with
|
|
|
|
|
| None ->
|
|
|
|
|
call_state
|
|
|
|
|
| Some callee_attrs ->
|
|
|
|
|
one_address_sat callee_attrs addr_hist_caller call_state )
|
|
|
|
|
call_state.subst call_state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let materialize_pre callee_proc_name call_location pre_post ~captured_vars_with_actuals ~formals
|
|
|
|
@ -351,7 +389,11 @@ let record_post_cell callee_proc_name call_loc ~edges_pre_opt
|
|
|
|
|
let attrs_post_caller =
|
|
|
|
|
add_call_to_attributes callee_proc_name call_loc hist_caller attrs_callee_post
|
|
|
|
|
in
|
|
|
|
|
let astate = AddressAttributes.abduce_and_add addr_caller attrs_post_caller call_state.astate in
|
|
|
|
|
let astate =
|
|
|
|
|
if Config.pulse_isl then
|
|
|
|
|
AddressAttributes.add_attrs addr_caller attrs_post_caller call_state.astate
|
|
|
|
|
else AddressAttributes.abduce_and_add addr_caller attrs_post_caller call_state.astate
|
|
|
|
|
in
|
|
|
|
|
{call_state with astate}
|
|
|
|
|
in
|
|
|
|
|
let subst, translated_post_edges =
|
|
|
|
@ -392,13 +434,25 @@ let rec record_post_for_address callee_proc_name call_loc ({AbductiveDomain.pre;
|
|
|
|
|
match AbductiveDomain.find_post_cell_opt addr_callee pre_post with
|
|
|
|
|
| None ->
|
|
|
|
|
call_state
|
|
|
|
|
| Some ((edges_post, _attrs_post) as cell_callee_post) ->
|
|
|
|
|
| Some ((edges_post, attrs_post) as cell_callee_post) ->
|
|
|
|
|
let edges_pre_opt = BaseMemory.find_opt addr_callee (pre :> BaseDomain.t).BaseDomain.heap in
|
|
|
|
|
let call_state_after_post =
|
|
|
|
|
if is_cell_read_only ~edges_pre_opt ~cell_post:cell_callee_post then call_state
|
|
|
|
|
else
|
|
|
|
|
let attrs_post =
|
|
|
|
|
if Config.pulse_isl then
|
|
|
|
|
match
|
|
|
|
|
AbductiveDomain.find_post_cell_opt (fst addr_hist_caller) call_state.astate
|
|
|
|
|
with
|
|
|
|
|
| None ->
|
|
|
|
|
attrs_post
|
|
|
|
|
| Some (_, attrs_caller) ->
|
|
|
|
|
(* if post attr is abduced, use the one in caller which is up-to-date *)
|
|
|
|
|
Attributes.replace_isl_abduced attrs_post attrs_caller
|
|
|
|
|
else attrs_post
|
|
|
|
|
in
|
|
|
|
|
record_post_cell callee_proc_name call_loc ~edges_pre_opt addr_hist_caller
|
|
|
|
|
~cell_callee_post call_state
|
|
|
|
|
~cell_callee_post:(edges_post, attrs_post) call_state
|
|
|
|
|
in
|
|
|
|
|
Memory.Edges.fold ~init:call_state_after_post edges_post
|
|
|
|
|
~f:(fun call_state (_access, (addr_callee_dest, _)) ->
|
|
|
|
@ -580,6 +634,45 @@ let check_all_valid callee_proc_name call_location {AbductiveDomain.pre; _} call
|
|
|
|
|
call_state.subst (Ok call_state.astate)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let isl_check_all_invalid invalid_addr_callers callee_proc_name call_location
|
|
|
|
|
{AbductiveDomain.pre; _} pre_astate astate =
|
|
|
|
|
match astate.AbductiveDomain.isl_status with
|
|
|
|
|
| AbductiveDomain.PostStatus.ISLOk ->
|
|
|
|
|
Ok astate
|
|
|
|
|
| AbductiveDomain.PostStatus.ISLError ->
|
|
|
|
|
AbstractValue.Map.fold
|
|
|
|
|
(fun addr_pre (addr_caller, hist_caller) astate_result ->
|
|
|
|
|
let mk_access_trace callee_access_trace =
|
|
|
|
|
Trace.ViaCall
|
|
|
|
|
{ in_call= callee_access_trace
|
|
|
|
|
; f= Call callee_proc_name
|
|
|
|
|
; location= call_location
|
|
|
|
|
; history= hist_caller }
|
|
|
|
|
in
|
|
|
|
|
match astate_result with
|
|
|
|
|
| Error _ ->
|
|
|
|
|
astate_result
|
|
|
|
|
| Ok astate -> (
|
|
|
|
|
match
|
|
|
|
|
BaseAddressAttributes.get_invalid addr_caller
|
|
|
|
|
(pre_astate.AbductiveDomain.post :> BaseDomain.t).attrs
|
|
|
|
|
with
|
|
|
|
|
| None ->
|
|
|
|
|
astate_result
|
|
|
|
|
| Some (invalidation, invalidation_trace) -> (
|
|
|
|
|
match BaseAddressAttributes.get_invalid addr_pre (pre :> BaseDomain.t).attrs with
|
|
|
|
|
| None ->
|
|
|
|
|
astate_result
|
|
|
|
|
| Some (_, callee_access_trace) ->
|
|
|
|
|
let access_trace = mk_access_trace callee_access_trace in
|
|
|
|
|
L.d_printfln "ERROR: caller's %a invalid!" AbstractValue.pp addr_caller ;
|
|
|
|
|
Error
|
|
|
|
|
( Diagnostic.AccessToInvalidAddress
|
|
|
|
|
{calling_context= []; invalidation; invalidation_trace; access_trace}
|
|
|
|
|
, AbductiveDomain.set_isl_status ISLError astate ) ) ) )
|
|
|
|
|
invalid_addr_callers (Ok astate)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(* - read all the pre, assert validity of addresses and materializes *everything* (to throw stuff
|
|
|
|
|
in the *current* pre as appropriate so that callers of the current procedure will also know
|
|
|
|
|
about the deeper reads)
|
|
|
|
@ -599,7 +692,11 @@ let apply_prepost callee_proc_name call_location ~callee_prepost:pre_post
|
|
|
|
|
L.d_printfln "Applying pre/post for %a(%a):@\n%a" Procname.pp callee_proc_name
|
|
|
|
|
(Pp.seq ~sep:"," Var.pp) formals AbductiveDomain.pp pre_post ;
|
|
|
|
|
let empty_call_state =
|
|
|
|
|
{astate; subst= AddressMap.empty; rev_subst= AddressMap.empty; visited= AddressSet.empty}
|
|
|
|
|
{ astate
|
|
|
|
|
; subst= AddressMap.empty
|
|
|
|
|
; invalid_subst= AddressMap.empty
|
|
|
|
|
; rev_subst= AddressMap.empty
|
|
|
|
|
; visited= AddressSet.empty }
|
|
|
|
|
in
|
|
|
|
|
(* read the precondition *)
|
|
|
|
|
match
|
|
|
|
@ -618,8 +715,15 @@ let apply_prepost callee_proc_name call_location ~callee_prepost:pre_post
|
|
|
|
|
L.d_printfln "Pre applied successfully. call_state=%a" pp_call_state call_state ;
|
|
|
|
|
match
|
|
|
|
|
let open IResult.Let_syntax in
|
|
|
|
|
let+ astate = check_all_valid callee_proc_name call_location pre_post call_state in
|
|
|
|
|
let* astate =
|
|
|
|
|
if Config.pulse_isl then
|
|
|
|
|
Ok
|
|
|
|
|
(AbductiveDomain.set_isl_status pre_post.AbductiveDomain.isl_status call_state.astate)
|
|
|
|
|
else check_all_valid callee_proc_name call_location pre_post call_state
|
|
|
|
|
in
|
|
|
|
|
(* reset [visited] *)
|
|
|
|
|
let invalid_subst = call_state.invalid_subst in
|
|
|
|
|
let pre_astate = astate in
|
|
|
|
|
let call_state = {call_state with astate; visited= AddressSet.empty} in
|
|
|
|
|
(* apply the postcondition *)
|
|
|
|
|
let call_state, return_caller =
|
|
|
|
@ -633,6 +737,12 @@ let apply_prepost callee_proc_name call_location ~callee_prepost:pre_post
|
|
|
|
|
~callee_prepost:pre_post.AbductiveDomain.topl call_state.astate
|
|
|
|
|
else call_state.astate
|
|
|
|
|
in
|
|
|
|
|
let+ astate =
|
|
|
|
|
if Config.pulse_isl then
|
|
|
|
|
isl_check_all_invalid invalid_subst callee_proc_name call_location pre_post pre_astate
|
|
|
|
|
astate
|
|
|
|
|
else Ok astate
|
|
|
|
|
in
|
|
|
|
|
(astate, return_caller)
|
|
|
|
|
with
|
|
|
|
|
| post ->
|
|
|
|
|