123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(** Relational numeric abstract domain, based on APRON. *)openMopsaopenSig.Abstraction.SimplifiedopenCommonopenAstopenApron_manageropenApron_transformerletopt_show_relational_domain=reffalselet()=register_domain_option"universal.numeric.relational"{key="-show-relational-def-domain";category="Numeric";doc=" display the domain on which the relational abstract state is defined";spec=Setopt_show_relational_domain;default="false";}letopt_enforce_sign_constraints=reffalselet()=register_shared_option"universal.numeric.relational"{key="-enforce-sign-constraints";category="Numeric";doc=" enforce sign constraints of variables in the relational domain";spec=Setopt_enforce_sign_constraints;default="false";}(** Query to retrieve relational variables *)type('a,_)query+=|Q_related_vars:var->('a,varlist)query|Q_constant_vars:('a,varlist)querylet()=register_query{join=(letf:typear.query_pool->(a,r)query->r->r->r=funnextqueryab->matchquerywith|Q_related_vars_->a@b|Q_constant_vars->a@b|_->next.pool_joinqueryabinf);meet=(letf:typear.query_pool->(a,r)query->r->r->r=funnextqueryab->matchquerywith|Q_related_vars_->a@b|Q_constant_vars->a@b|_->next.pool_meetqueryabinf);}(** Factory functor *)moduleMake(ApronManager:APRONMANAGER)=structincludeApronTransformer(ApronManager)typet=ApronManager.tApron.Abstract1.t(** Abstract element *)*Binding.t(** Bindings between Mopsa and Apron variables *)includeGenDomainId(structtypenonrect=tletname=ApronManager.nameend)letnumeric_name=ApronManager.numeric_name(** {2 Command-line options} *)(** ************************ *)let()=import_shared_optionrounding_option_namename(** {2 Environment utility functions} *)(** ********************************* *)(* boolean of return triple is true iff unify had no effect *)letunifyabs1abs2=letenv1=Apron.Abstract1.envabs1andenv2=Apron.Abstract1.envabs2inletenv,o1,o2=Apron.Environment.lce_changeenv1env2in(Apron.Abstract1.change_environmentApronManager.manabs1envfalse),(Apron.Abstract1.change_environmentApronManager.manabs2envfalse),o1=None&&o2=Noneletadd_missing_vars(a,bnd)lv=letenv=Apron.Abstract1.envainletlv=List.sort_uniqcomparelvinletlv=List.filter(funv->not(Apron.Environment.mem_varenv(Binding.mopsa_to_apron_varvbnd|>fst)))lvinletint_vars,bnd=letlv'=List.filter(funv->vtypv=T_int||vtypv=T_bool)lvinBinding.mopsa_to_apron_varslv'bndinletfloat_vars,bnd=letlv'=List.filter(function{vtyp=T_float_}->true|_->false)lvinBinding.mopsa_to_apron_varslv'bndinletenv'=Apron.Environment.addenv(Array.of_listint_vars)(Array.of_listfloat_vars)inApron.Abstract1.change_environmentApronManager.manaenv'false,bndletprint_stateprintera=if!opt_show_relational_domainthenletdom=Binding.Equiv.fold(fun(a,_)acc->a::acc)(snda)[]inpp_obj_mapprinter[(String"domain",List(List.map(funv->String(Format.asprintf"%a"pp_varv))dom,{sopen="{";ssep=",";sclose="}";sbind=""}));(String"relations",pbox(Apron_pp.pp_envApronManager.man)a);]~path:[Key"numeric-relations"]elsepprintprinter(pbox(Apron_pp.pp_envApronManager.man)a)~path:[Key"numeric-relations"](** {2 Lattice operators} *)(** ********************* *)lettop=Apron.Abstract1.topApronManager.manempty_env,Binding.emptyletbottom=Apron.Abstract1.bottomApronManager.manempty_env,Binding.emptyletis_bottom(abs,_)=Apron.Abstract1.is_bottomApronManager.manabsletsubset(abs1,_)(abs2,_)=letabs1',abs2',_=unifyabs1abs2inApron.Abstract1.is_leqApronManager.manabs1'abs2'letjoin(abs1,bnd1)(abs2,bnd2)=letabs1',abs2',unchanged=unifyabs1abs2inifnotunchangedthenDebug.debug~channel:(name^".join")"Heterogenous case detected@\na1 = %a@\n~> a1' (after unification) = %a@\na1 = %a@\n~> a1' (after unification) = %a@\n"(formatprint_state)(abs1,bnd1)(formatprint_state)(abs1',bnd1)(formatprint_state)(abs2,bnd2)(formatprint_state)(abs2,bnd2);Apron.Abstract1.joinApronManager.manabs1'abs2',Binding.concatbnd1bnd2letmeet(abs1,bnd1)(abs2,bnd2)=letabs1',abs2',_=unifyabs1abs2inApron.Abstract1.meetApronManager.manabs1'abs2',Binding.concatbnd1bnd2letwidenbnd(abs1,bnd1)(abs2,bnd2)=letabs1',abs2',_=unifyabs1abs2inApron.Abstract1.wideningApronManager.manabs1'abs2',Binding.concatbnd1bnd2(** {2 Transfer functions} *)(** ********************** *)letinitprog=topletremove_var(v:var)(a,bnd)=letenv=Apron.Abstract1.envainifis_env_varv(a,bnd)thenletvv,bnd=Binding.mopsa_to_apron_varvbndinletenv=Apron.Environment.removeenv[|vv|]inletbnd=Binding.remove_apron_varvvbndin(Apron.Abstract1.change_environmentApronManager.manaenvtrue,bnd)else(a,bnd)letforget_varv(a,bnd)=letv,bnd=Binding.mopsa_to_apron_varvbndinApron.Abstract1.forget_arrayApronManager.mana[|v|]false,bndletbound_varv(abs,bnd)=ifis_env_varv(abs,bnd)thenletvv,_=Binding.mopsa_to_apron_varvbndinApron.Abstract1.bound_variableApronManager.manabsvv|>Values.Intervals.Integer.Value.of_apronelseValues.Intervals.Integer.Value.topletis_var_numeric_typev=is_numeric_type(vtypv)letassumestmtask(a,bnd)=matchskindstmtwith|S_assumee->beginleta,bnd=add_missing_vars(a,bnd)(Visitor.expr_varse)intryletdnf,a,bnd,l=bexp_to_apron(funexp->ask(Common.mk_float_maybenan_queryexp))e(a,bnd)[]inletenv=Apron.Abstract1.envainleta'=Dnf.reduce_conjunction(funconj->lettcons_list=List.map(fun(op,e1,typ1,e2,typ2)->lettyp=ifis_float_typetyp1||is_float_typetyp2thenApron.Texpr1.Realelseifis_int_typetyp1&&is_int_typetyp2thenApron.Texpr1.IntelseExceptions.panic_at(srangestmt)"Unsupported case (%a, %a) in stmt @[%a@]"pp_typtyp1pp_typtyp2pp_stmtstmtinletdiff=Apron.Texpr1.Binop(Apron.Texpr1.Sub,e1,e2,typ,!opt_float_rounding)inletdiff_texpr=Apron.Texpr1.of_exprenvdiffinApron.Tcons1.makediff_texprop)conjintcons_array_of_tcons_listenvtcons_list|>Apron.Abstract1.meet_tcons_arrayApronManager.mana)~join:(Apron.Abstract1.joinApronManager.man)dnf|>remove_tmplinSome(a',bnd)withImpreciseExpression->Some(a,bnd)|UnsupportedExpression->Noneend|_->assertfalseletmerge(pre,bnd)((a1,bnd1),e1)((a2,bnd2),e2)=letbnd=Binding.concatbnd1bnd2inletx1,x2=generic_merge~add:(funvitvx->ifis_numerical_varvthenletrange=tag_range(Location.R_fresh(-1))"relational merge"inadd_missing_varsx[v]|>forget_varv|>assume(mk_assume(constraints_of_itv(mk_varvrange)itvrange)range)(fun_->assertfalse)|>OptionExt.none_to_exnelsex)~find:(funvx->bound_varvx)~remove:(funvx->ifis_numerical_varvthenremove_varvxelsex)((a1,bnd),e1)((a2,bnd),e2)inlet()=Debug.debug~channel:(name^".merge")"after generic merge@\na1 = %a@\na2 = %a@\nnow meeting results"(formatprint_state)x1(formatprint_state)x2inmeetx1x2(** Add the sign contraint (if existing) of a given variable into the relationnal domain *)letenforce_sign_constraintvaraskctxrange=ifnot!opt_enforce_sign_constraintsthenFun.idelseletfake_range=mk_tagged_range(String_tag"var sign constraint")rangeinmatchFramework.Combiners.Value.Nonrel.find_var_bounds_ctx_optvarctxwith|Some(C_int_interval(Finitel,Finiteu))whenZ.equallu->letstmt=mk_assume(mk_binop~etyp:T_int(mk_varvarfake_range)O_eq(mk_constant~etyp:T_int(C_intl)fake_range)fake_range)fake_rangeinOptionExt.bind(assumestmtask)|Some(C_int_interval(Finitel,_))whenZ.leqZ.zerol->letstmt=mk_assume(mk_binop~etyp:T_int(mk_varvarfake_range)O_ge(mk_constant~etyp:T_int(C_intl)fake_range)fake_range)fake_rangeinOptionExt.bind(assumestmtask)|Some(C_int_interval(_,Finiteu))whenZ.geqZ.zerou->letstmt=mk_assume(mk_binop~etyp:T_int(mk_varvarfake_range)O_le(mk_constant~etyp:T_int(C_intu)fake_range)fake_range)fake_rangeinOptionExt.bind(assumestmtask)|_->Fun.idletrecexecstmtmanctx(a,bnd)=matchskindstmtwith|S_add{ekind=E_var(var,_)}whenis_var_numeric_typevar->add_missing_vars(a,bnd)[var]|>OptionExt.return|>enforce_sign_constraintvarman.askctxstmt.srange|S_remove{ekind=E_var(var,_)}whenis_var_numeric_typevar->remove_varvar(a,bnd)|>OptionExt.return|S_forget{ekind=E_var(var,_)}whenis_var_numeric_typevar->forget_varvar(a,bnd)|>OptionExt.return|S_rename({ekind=E_var(var1,_)},{ekind=E_var(var2,_)})whenis_var_numeric_typevar1&&is_var_numeric_typevar2->leta,bnd'=add_missing_vars(a,bnd)[var1]inleta,bnd'=remove_varvar2(a,bnd')inletv1,_=Binding.mopsa_to_apron_varvar1bndinletbnd'=Binding.remove_apron_varv1bndinletv2,bnd'=Binding.mopsa_to_apron_varvar2bnd'in(Apron.Abstract1.rename_arrayApronManager.mana[|v1|][|v2|],bnd')|>OptionExt.return|S_projectvarswhenList.for_all(function{ekind=E_var(v,_)}->is_var_numeric_typev|_->false)vars->letvars=List.map(function|{ekind=E_var(v,_)}->v|_->assertfalse)varsinletenv=Apron.Abstract1.envainletvars,bnd=Binding.mopsa_to_apron_varsvarsbndinletold_vars1,old_vars2=Apron.Environment.varsenvinletold_vars=Array.to_listold_vars1@Array.to_listold_vars2inletto_remove=List.filter(funv->not(List.memvvars))old_varsinletbnd=Binding.remove_apron_varsto_removebndinletnew_env=Apron.Environment.removeenv(Array.of_listto_remove)inSome(Apron.Abstract1.change_environmentApronManager.mananew_envtrue,bnd)|S_assign({ekind=E_var(var,mode)},e)whenvar_modevarmode=STRONG&&is_var_numeric_typevar->leta,bnd=add_missing_vars(a,bnd)(var::(Visitor.expr_varse))inletv,bnd=Binding.mopsa_to_apron_varvarbndinbegintrylete,a,bnd,l=exp_to_apron(funexp->man.ask(Common.mk_float_maybenan_queryexp))e(a,bnd)[]inletaenv=Apron.Abstract1.envainlettexp=Apron.Texpr1.of_expraenveinleta'=Apron.Abstract1.assign_texprApronManager.manavtexpNone|>remove_tmplinSome(a',bnd)with|ImpreciseExpression->exec(mk_forget_varvarstmt.srange)manctx(a,bnd)|>enforce_sign_constraintvarman.askctxstmt.srange|UnsupportedExpression->Noneend|S_assign({ekind=E_var(var,mode)}aslval,e)whenvar_modevarmode=WEAK&&is_var_numeric_typevar->(* let's suppose we have
x:w >= constant in the interval domain but NOT here
and now we do x:w := e
the naive join will not fetch the previous bounds of x:w, which will just keep x:w to top... *)letlval'={lvalwithekind=E_var(var,SomeSTRONG)}inlet(a,bnd)=ifBinding.Equiv.mem_lvarbndthenletitv=man.ask(mk_int_interval_query~fast:truelval)inletrange=erangelvalinexec{stmtwithskind=S_assume(constraints_of_itvlval'itvrange)}manctx(a,bnd)|>OptionExt.none_to_exnelse(a,bnd)inexec{stmtwithskind=S_assign(lval',e)}manctx(a,bnd)|>OptionExt.lift@@fun(a',bnd')->join(a,bnd)(a',bnd')|S_expand({ekind=E_var(v,_)},vl)whenis_var_numeric_typev&&List.for_all(function{ekind=E_var(v,_)}->is_var_numeric_typev|_->false)vl->letvl=List.map(function|{ekind=E_var(v,_)}->v|_->assertfalse)vlinletv,bnd=Binding.mopsa_to_apron_varvbndinletvl,bnd=Binding.mopsa_to_apron_varsvlbndinletabs'=Apron.Abstract1.expandApronManager.manav(Array.of_listvl)inSome(abs',bnd)|S_fold({ekind=E_var(v,_)},vl)whenis_var_numeric_typev&&List.for_all(function{ekind=E_var(v,_)}->is_var_numeric_typev|_->false)vl->letvl=List.map(function|{ekind=E_var(v,_)}->v|_->assertfalse)vlinleta,bnd=add_missing_vars(a,bnd)(v::vl)inletv,bnd=Binding.mopsa_to_apron_varvbndinletvl,bnd=Binding.mopsa_to_apron_varsvlbndinletbnd=Binding.remove_apron_varsvlbndinletabs'=tryApron.Abstract1.foldApronManager.mana(Array.of_list(v::vl))withApron.Manager.Errorexc->panic_atstmt.srange"Apron.Manager.Error(%a)"Apron.Manager.print_exclogexcinSome(abs',bnd)|S_assume(e)whenis_numeric_type(etype)->assumestmtman.ask(a,bnd)|_->Noneletvars(abs,bnd)=fold_env(funvacc->letvv=Binding.apron_to_mopsa_varvbndinvv::acc)(Apron.Abstract1.envabs)[]letreceval_intervalmane(abs,bnd)=matchekindewith|E_var(v,_)->(* we meet with the interval computed by non-relational domains so that the interval of `unsigned x` is positive *)letint_nonrel=man.ask(Q_avalue(e,Common.V_int_interval_fast))inletint_rel=bound_varv(abs,bnd)inSome(ItvUtils.IntItv.meet_botint_nonrelint_rel)|E_binop(O_mult,e1,e2)->letint1=eval_intervalmane1(abs,bnd)inletint2=eval_intervalmane2(abs,bnd)inbeginmatchint1,int2with|Someint1,Someint2->Bot.bot_lift2ItvUtils.IntItv.mulint1int2|>OptionExt.return|_->Noneend|E_binop(O_convex_join,e1,e2)->letint1=eval_intervalmane1(abs,bnd)inletint2=eval_intervalmane2(abs,bnd)inbeginmatchint1,int2with|Someint1,Someint2->ItvUtils.IntItv.join_botint1int2|>OptionExt.return|_->Noneend|_->tryletabs,bnd=add_missing_vars(abs,bnd)(Visitor.expr_varse)inlete,abs,bnd,_=exp_to_apron(funexp->man.ask(Common.mk_float_maybenan_queryexp))e(abs,bnd)[]inletenv=Apron.Abstract1.envabsinlete=Apron.Texpr1.of_exprenveinApron.Abstract1.bound_texprApronManager.manabse|>Values.Intervals.Integer.Value.of_apron|>OptionExt.returnwithImpreciseExpression->Some(Values.Intervals.Integer.Value.top)|UnsupportedExpression->Noneletask:typer.('a,r)query->('a,t)simplified_man->'actx->t->roption=funquerymanctx(abs,bnd)->matchquerywith|Q_avalue(e,Common.V_int_interval)->eval_intervalmane(abs,bnd)|Q_related_varsv->related_varsv(abs,bnd)|>OptionExt.return|Q_constant_vars->constant_vars(abs,bnd)|>OptionExt.return|_->Noneletprint_exprmanctxaprinterexp=ifexists_expr(fune->not(is_numeric_typee.etyp))(funs->false)expthen()elseletvars=expr_varsexp|>VarSet.of_listinletvars'=VarSet.fold(funvacc->all_related_varsva|>VarSet.of_list|>VarSet.unionacc)varsvarsinmatchVarSet.elementsvars'with|[]->()|l->matchexec(mk_project_varslexp.erange)manctxawith|None->()|Somea->print_stateprinteraend