123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program is free software: you can redistribute it and/or modify *)(* it under the terms of the GNU Lesser General Public License as published *)(* by the Free Software Foundation, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* This program is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(** [Nonrel ∈ 𝒱 → 𝒟] lifts a non-relational value abstraction into an
abstract domain of partial environments from variables to values.
*)openCore.AllopenSig.Abstraction.ValueopenMopsa_utils(** {2 Identifier for the non-relation domain} *)(** ****************************************** *)type_id+=D_nonrel:(moduleVALUEwithtypet='v)->(var,'v)Lattices.Partial_map.mapidlet()=letopenEqinregister_id{eq=(letf:typeab.witness->aid->bid->(a,b)eqoption=funnextid1id2->matchid1,id2with|D_nonrelv1,D_nonrelv2->beginletmoduleV1=(valv1)inletmoduleV2=(valv2)inmatchequal_idV1.idV2.idwith|SomeEq->SomeEq|None->Noneend|_->next.eqid1id2inf);}(** {2 Variable's context} *)(** ********************** *)(** The context of a variable keeps (flow-insensitive) information about the
variable that can pushed by external domains and consumed by the value
abstraction.
This is useful to implement a widening with thresholds: external
heuristics discover the theresholds and put them in the context of the
variable. When [widen] is called on a the value of a variable, it is enriched
with its context. *)moduleK=GenContextKey(structtype'at='actxVarMap.tletprintppfmtm=Format.fprintffmt"variables contexts:@, @[<v>%a@]"(Format.pp_print_list~pp_sep:(funfmt()->Format.fprintffmt"@,")(funfmt(v,c)->Format.fprintffmt"%a:@, @[<v>%a@]"pp_varv(pp_ctxpp)c))(VarMap.bindingsm)end)letvar_ctx_key=K.keyletadd_var_ctxvarkvctx=letmap=tryfind_ctxvar_ctx_keyctxwithNot_found->VarMap.emptyinletvctx=tryVarMap.findvarmapwithNot_found->empty_ctxinadd_ctxvar_ctx_key(VarMap.addvar(add_ctxkvvctx)map)ctxletfind_var_ctx_optvarkctx=matchfind_ctx_optvar_ctx_keyctxwith|None->None|Somemap->matchVarMap.find_optvarmapwith|None->None|Somevctx->find_ctx_optkvctxletfind_var_ctxvarkctx=matchfind_var_ctx_optvarkctxwith|None->raiseNot_found|Somev->vletremove_var_ctxvarkctx=tryletmap=find_ctxvar_ctx_keyctxinletvctx=VarMap.findvarmapinadd_ctxvar_ctx_key(VarMap.addvar(remove_ctxkvctx)map)ctxwithNot_found->ctx(** {2 Variable bounds} *)(** ******************* *)(** The bounds of a variable is an invariant about its value that is always valid.
It is put in the context of the variable and is used to refine its value whenever it
changes. *)moduleVarBoundsKey=GenContextKey(structtype'at=constantletprintppfmtc=Format.fprintffmt"@[<h>bounds: %a@]"pp_constantcend)(** Context for saving the bounds of a variable *)letvar_bounds_ctx=VarBoundsKey.key(** Add the bounds of a variable to context *)letadd_var_bounds_ctxvbctx=add_var_ctxvvar_bounds_ctxbctx(** Add the bounds of a variable to flow *)letadd_var_bounds_flowvbflow=letctx=add_var_bounds_ctxvb(Flow.get_ctxflow)inFlow.set_ctxctxflow(** Remove the bounds of a variable from context *)letremove_var_bounds_ctxvctx=remove_var_ctxvvar_bounds_ctxctx(** Remove the bounds of a variable from flow *)letremove_var_bounds_flowvflow=letctx=remove_var_bounds_ctxv(Flow.get_ctxflow)inFlow.set_ctxctxflow(** Find the bounds of a variable in context *)letfind_var_bounds_ctx_optvctx=find_var_ctx_optvvar_bounds_ctxctx(** {2 Non-relational domain} *)(** ************************* *)moduleMake(Value:VALUE):Sig.Abstraction.Simplified.SIMPLIFIEDwithtypet=(var,Value.t)Lattices.Partial_map.map=struct(** {2 Domain header} *)(** ***************** *)(** Map with variables as keys. *)moduleVarMap=Lattices.Partial_map.Make(Var)(Value)includeVarMapletid=D_nonrel(moduleValue)letname="framework.abstraction.combiners.value.nonrel"letdebugfmt=Debug.debug~channel:namefmtletmergepre(a1,e1)(a2,e2)=leta1',a2'=generic_merge~add~remove~find(a1,e1)(a2,e2)intryVarMap.map2zo(fun_v1->v1)(fun_v2->v2)(fun_v1v2->letv=Value.meetv1v2inifValue.is_bottomvthenraiseBot.Found_BOTelsev)a1'a2'withBot.Found_BOT->VarMap.bottom(* This value manager isn't able to perform sub-evaluations.
It is used only to compute values of constants used as bounds of variables. *)letimprecise_value_man={bottom=Value.bottom;top=Value.top;is_bottom=Value.is_bottom;subset=Value.subset;join=Value.join;meet=Value.meet;print=Value.print;get=(funv->v);set=(funv_->v);eval=(fune->Value.top);avalue=(funavkv->matchValue.avalueavkvwith|Somer->r|None->top_avalueavk);ask=(funq->Exceptions.panic"Queries not accessible");}letbound_range=Location.mk_fresh_range()(* Constrain the value of a variable with its bounds *)letmeet_with_bound_constraintsctxvarv=matchfind_var_bounds_ctx_optvarctxwith|None->v|Somebounds->letvv=Value.evalimprecise_value_man(mk_constantboundsbound_range~etyp:var.vtyp)inValue.meetvvvletwidenctxa1a2=letopenBot_topinifa1==a2thena1elsematcha1,a2with|BOT,x|x,BOT->x|TOP,x|x,TOP->TOP|Nbtm1,Nbtm2->Nbt(MapExtPoly.map2zo(fun_v1->v1)(fun_v2->v2)(funvarv1v2->letvctx=matchfind_ctx_optvar_ctx_keyctxwith|None->empty_ctx|Somemap->matchCore.Ast.Var.VarMap.find_optvarmapwith|None->empty_ctx|Somec->cinletw=Value.widenvctxv1v2in(* Apply the bounds constraints*)meet_with_bound_constraintsctxvarw)m1m2)lettop_of_typtyprange=Value.evalimprecise_value_man(mk_toptyprange)letaddctxvarva=letvv=meet_with_bound_constraintsctxvarvinVarMap.addvarvva(** {2 Evaluation of expressions} *)(** ***************************** *)(** Value manager *)letrecvalue_man(cache:Value.tvexpr)(map:t):(Value.t,Value.t)value_man={imprecise_value_manwitheval=(fune->matchfind_vexpr_optecachewith|Some(v,_)->v|None->matchevalemapwith|Some(v,_)->v|None->Value.top);ask=(funquery->matchValue.ask(value_mancachemap)querywith|Somer->r|None->raiseNot_found);}(** Forward evaluation returns the abstract value of the expression,
but also a tree annotated by the intermediate abstract
values for each sub-expression *)andeval(e:expr)(a:t):(Value.t*Value.tvexpr)option=ifnot(Value.accept_typee.etyp)thenNoneelsematchekindewith|E_var(var,mode)->(* Get the value of the variable from the map *)letv=findvarain(v,empty_vexpr)|>OptionExt.return|_whenfor_all_expr(funee->Value.accept_typeee.etyp)(funs->false)e->(* Before asking the domain to evaluate the expression, evaluate each sub-expression
their evaluations in the manager. This will speedup the evaluation when the domain
[Value.eval] will request these values. *)letparts,build=structure_of_expreinletreciter=function|[]->Someempty_vexpr|ee::tl->evaleea|>OptionExt.bind@@fun(vv,vee)->itertl|>OptionExt.lift@@funtl->add_vexpreevvveetliniterparts.exprs|>OptionExt.lift@@funve->letv=Value.eval(value_manvea)ein(v,ve)|_->None(** Backward refinement of expressions; given an annotated tree, and
a target value, refine the environment using the variables in the
expression *)letrecrefinectx(e:expr)(ve:Value.tvexpr)(r:Value.t)(a:t):t=ifValue.is_bottomrthenbottomelsematche.ekindwith|E_var(var,mode)->(* Refine the value of the variable in the map *)ifvar_modevarmode=WEAKthenaelseaddctxvar(Value.meet(findvara)r)a|_->(* Refine the sub-expressions by calling [Value.backward].
* Note that we need to apply this function to the root sub-expressions only *)letveroot=root_vexprveinletveroot'=Value.backward(value_manvea)everootrin(* Go back to the whole value expression by merging [veroot'] with [ve].
Missing sub-expressions in [veroot'] will be copied from [ve]. *)letve'=merge_vexprValue.meetveveroot'infold_root_vexpr(funacceevveev->refinectxeeeevvvacc)ave'(* utility function to reduce the complexity of testing boolean expressions;
it handles the boolean operators &&, ||, ! internally, by induction
on the syntax
if r=true, keep the states that may satisfy the expression;
if r=false, keep the states that may falsify the expression
*)letrecfilterctx(e:expr)(b:bool)(a:t):toption=matchekindewith|E_unop(O_log_not,e)->filterctxe(notb)a|E_binop(O_log_and,e1,e2)->filterctxe1ba|>OptionExt.bind@@funa1->filterctxe2ba|>OptionExt.bind@@funa2->(ifbthenmeetelsejoin)a1a2|>OptionExt.return|E_binop(O_log_or,e1,e2)->filterctxe1ba|>OptionExt.bind@@funa1->filterctxe2ba|>OptionExt.bind@@funa2->(ifbthenjoinelsemeet)a1a2|>OptionExt.return|E_binop(O_log_xor,e1,e2)->filterctxe1ba|>OptionExt.bind@@funa1->filterctxe2ba|>OptionExt.bind@@funa2->filterctxe1(notb)a|>OptionExt.bind@@funna1->filterctxe2(notb)a|>OptionExt.bind@@funna2->(ifbthenjoin(meeta1na2)(meetna1a2)elsejoin(meeta1a2)(meetna1na2))|>OptionExt.return(* arithmetic comparison part, handled by Value *)|E_binop(op,e1,e2)whenis_comparison_opop->(* evaluate forward each argument expression *)evale1a|>OptionExt.bind@@fun(v1,ve1)->evale2a|>OptionExt.bind@@fun(v2,ve2)->(* apply comparison *)letr1,r2=Value.compare(value_manempty_vexpra)opbe1v1e2v2in(* propagate backward on both argument expressions *)refinectxe1ve1(Value.meetv1r1)a|>refinectxe2ve2(Value.meetv2r2)|>OptionExt.return|_->(* Filter on arbitrary expressions (variables, predicates, etc.).
First, evaluate the expression *)evalea|>OptionExt.lift@@fun(v,ve)->(* Then filter the obtained value to match the truth value [b] *)letw=Value.filterbe.etypvin(* Now refine the sub-expresions w.r.t. the filtered value *)refinectxeve(Value.meetvw)a(** {2 Transfer functions} *)(** ********************** *)letinitprog=emptyletexecstmtmanctx(map:t):toption=matchskindstmtwith|S_remove{ekind=E_var(v,_)}whenValue.accept_typev.vtyp->VarMap.removevmap|>OptionExt.return|S_add{ekind=E_var(v,_)}whenValue.accept_typev.vtyp->(* Check if the variable is already present *)ifVarMap.memvmapthenOptionExt.returnmapelseaddctxv(top_of_typv.vtypstmt.srange)map|>OptionExt.return|S_projectvarswhenList.for_all(function{ekind=E_var_}->true|_->false)vars->letvars=List.map(function|{ekind=E_var(v,_)}->v|_->assertfalse)varsinList.fold_left(funaccv->addctxv(findvmap)acc)emptyvars|>OptionExt.return|S_rename({ekind=E_var(var1,_)},{ekind=E_var(var2,_)})whenValue.accept_typevar1.vtyp->letv=findvar1mapinremovevar1map|>addctxvar2v|>OptionExt.return|S_forget{ekind=E_var(var,_)}whenValue.accept_typevar.vtyp->addctxvar(top_of_typvar.vtypstmt.srange)map|>OptionExt.return|S_assign({ekind=E_var(var,mode)},e)whenValue.accept_typevar.vtyp->evalemap|>OptionExt.lift@@fun(v,_)->letmap'=addctxvarvmapinbeginmatchvar_modevarmodewith|STRONG->map'|WEAK->joinmapmap'end|S_expand({ekind=E_var(v,_)},vl)whenValue.accept_typev.vtyp&&List.for_all(function{ekind=E_var_}->true|_->false)vl->letvl=List.map(function|{ekind=E_var(v,_)}->v|_->assertfalse)vlinletvalue=findvmapinList.fold_left(funaccv'->addctxv'valueacc)mapvl|>OptionExt.return|S_fold({ekind=E_var(v,mode)},vl)whenValue.accept_typev.vtyp&&List.for_all(function{ekind=E_var_}->true|_->false)vl->(* Collect values of variables vl before removing them from the map *)letvalue,map'=List.fold_left(fun(accv,accm)->function|{ekind=E_var(vv,_)}->letaccv'=findvvmap|>Value.joinaccvinletaccm'=removevvaccminaccv',accm'|_->assertfalse)(Value.bottom,map)vlinletvalue'=ifmemvmapthenValue.joinvalue(findvmap)elsevalueinaddctxvvalue'map'|>OptionExt.return|S_assumee->filterctxetruemap|_->Noneletask:typer.('a,r)query->('a,t)Sig.Abstraction.Simplified.simplified_man->'actx->t->roption=funquerymanctxmap->matchquerywith|Q_avalue(e,av)whenValue.accept_typee.etyp->evalemap|>OptionExt.bind@@fun(v,ve)->Value.avalueavv|_->Value.ask(value_manempty_vexprmap)queryletprint_stateprintera=Print.pprintprinter~path:[KeyValue.display](pboxVarMap.printa)letprint_exprmanctxaprinterexp=matchevalexpawith|None->()|Some(v,_)->Print.pprintprinter~path:[KeyValue.display;matchekindexpwith|E_var(v,_)->Obj(Varv)|_->fkey"%a"pp_exprexp](pboxValue.printv)end