123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(** Common utility definitions used by combiners *)openCore.AllopenMopsa_utils(** Kinds of domain combiners *)typecombiner=|Sequence|Compose|Product(** GADT identifiers used by domain combiners *)type_id+=|C_empty:unitid|C_pair:combiner*'aid*'bid->('a*'b)idlet()=register_id{eq=(letf:typeab.witness->aid->bid->(a,b)Eq.eqoption=funnextid1id2->matchid1,id2with|C_empty,C_empty->SomeEq|C_pair(c1,x1,y1),C_pair(c2,x2,y2)->ifc1=c2thenmatchequal_idx1x2with|None->None|SomeEq->matchequal_idy1y2with|None->None|SomeEq->SomeEqelseNone|_->next.eqid1id2inf);}(** GADT identifiers used by value combiners *)type_id+=V_empty:unitidtype_id+=V_pair:'aid*'bid->('a*'b)idlet()=register_id{eq=(letf:typeab.witness->aid->bid->(a,b)Eq.eqoption=funnextid1id2->matchid1,id2with|V_empty,V_empty->SomeEq|V_pair(x1,y1),V_pair(x2,y2)->beginmatchequal_idx1x2with|None->None|SomeEq->matchequal_idy1y2with|None->None|SomeEq->SomeEqend|_->next.eqid1id2inf);}(** [sat_targets ~domains ~targets] checks whether a combiner containing [domains] satisifies some route [targets] *)letsat_targets~targets~domains=matchtargetswith|None->true|Somes->not(DomainSet.is_empty(DomainSet.intersdomains))typeaccessor+=|Ax_pair_left|Ax_pair_rightlet()=register_accessor{print=(funnextfmt->function|Ax_pair_right->Format.pp_print_stringfmt"right"|Ax_pair_left->Format.pp_print_stringfmt"left"|t->nextfmtt);compare=(funnextt1t2->matcht1,t2with|Ax_pair_right,Ax_pair_right->0|Ax_pair_left,Ax_pair_left->0|_->comparet1t2);}(** Manager of the left argument in a pair of domains *)letfst_pair_man(man:('a,'b*'c)man):('a,'b)man={manwithget=get_pair_fstman;set=set_pair_fstman;add_change=(funstmtpathchange_map->man.add_changestmt(Ax_pair_left::path)change_map);}(** Manager of the right argument in a pair of domains *)letsnd_pair_man(man:('a,'b*'c)man):('a,'c)man={manwithget=get_pair_sndman;set=set_pair_sndman;add_change=(funstmtpathchange_map->man.add_changestmt(Ax_pair_right::path)change_map);}(** Find the manager of a domain given the manager of a combiner containing it *)letrecfind_domain_man:typebc.target:bid->tree:cid->('a,c)man->('a,b)man=fun~target~treeman->matchtreewith|C_empty->raiseNot_found|C_pair(_,left,right)->begintryfind_domain_man~target~tree:left(fst_pair_manman)withNot_found->find_domain_man~target~tree:right(snd_pair_manman)end|_->matchequal_idtargettreewith|SomeEq->man|None->raiseNot_found(** Check whether a domain is part of a combiner tree *)letrecmem_domain:typebc.target:bid->tree:cid->bool=fun~target~tree->matchtreewith|C_empty->false|C_pair(_,left,right)->mem_domain~target~tree:left||mem_domain~target~tree:right|_->matchequal_idtargettreewith|SomeEq->true|None->falseletpp_domainsfmts=Format.(pp_print_list~pp_sep:(funfmt()->pp_print_stringfmt", ")pp_print_string)fmt(DomainSet.elementss)(** Apply transfer functions [f1] and [f2] in cascade. Function [f1]
is called first. When [f1] returns [None] or not-handled cases,
[f2] is called. Note that not-handled cases are joined in order to
call [f2] only once. *)letcascade_calltargetsf1domains1f2domains2=matchsat_targets~targets~domains:domains1,sat_targets~targets~domains:domains2with|false,false->(* Both domains do not provide an [exec] for such targets *)Exceptions.panic"switch: targets '%a' not found in %a nor %a"(OptionExt.printpp_domains)targetspp_domainsdomains1pp_domainsdomains2|true,false->(* Only [D1] provides a transfer function for such targets *)letf=f1targetsin(funcmdmanflow->fcmd(fst_pair_manman)flow)|false,true->(* Only [D2] provides a transfer function for such targets *)letf=f2targetsin(funcmdmanflow->fcmd(snd_pair_manman)flow)|true,true->(* Both [D1] and [D2] provide a transfer function for such targets *)letff1=f1targetsinletff2=f2targetsin(funcmdmanflow->matchff1cmd(fst_pair_manman)flowwith|None->ff2cmd(snd_pair_manman)flow|Someret1->(* Collect cases not handled by [D1] and pass them to [D2] *)matchCases.partition(funcflow->matchcwithNotHandled->true|_->false)ret1with|None,_->Someret1|Somenot_handled1,handled1->(* Fusion all not-handled cases to speedup the analysis *)letnot_handled1'=Cases.remove_duplicatesman.latticenot_handled1in(* Call [D2] on not-handled cases *)letret2=not_handled1'>>=?fun_flow->ff2cmd(snd_pair_manman)flowinOptionExt.neutral2Cases.joinhandled1ret2)letcascade_stateless_calltargetsf1domains1f2domains2=matchsat_targets~targets~domains:domains1,sat_targets~targets~domains:domains2with|false,false->(* Both domains do not provide an [exec] for such targets *)Exceptions.panic"switch: targets '%a' not found in %a nor %a"(OptionExt.printpp_domains)targetspp_domainsdomains1pp_domainsdomains2|true,false->(* Only [D1] provides a transfer function for such targets *)f1targets|false,true->(* Only [D2] provides a transfer function for such targets *)f2targets|true,true->(* Both [D1] and [D2] provide a transfer function for such targets *)letff1=f1targetsinletff2=f2targetsin(funcmdmanflow->matchff1cmdmanflowwith|None->ff2cmdmanflow|Someret1->(* Collect cases not handled by [D1] and pass them to [D2] *)matchCases.partition(funcflow->matchcwithNotHandled->true|_->false)ret1with|None,_->Someret1|Somenot_handled1,handled1->(* Fusion all not-handled cases to speedup the analysis *)letnot_handled1'=Cases.remove_duplicatesman.latticenot_handled1in(* Call [D2] on not-handled cases *)letret2=not_handled1'>>=?fun_flow->ff2cmdmanflowinOptionExt.neutral2Cases.joinhandled1ret2)letbroadcast_calltargetsf1domains1f2domains2=matchsat_targets~targets~domains:domains1,sat_targets~targets~domains:domains2with|false,false->(* Both domains do not provide an [exec] for such targets *)Exceptions.panic"broadcast_call: targets '%a' not found in %a nor %a"(OptionExt.printpp_domains)targetspp_domainsdomains1pp_domainsdomains2|true,false->(* Only [D1] provides a transfer function for such targets *)letf=f1targetsin(funcmdmanflow->fcmd(fst_pair_manman)flow)|false,true->(* Only [D2] provides a transfer function for such targets *)letf=f2targetsin(funcmdmanflow->fcmd(snd_pair_manman)flow)|true,true->(* Both [D1] and [D2] provide a transfer function for such targets *)letff1=f1targetsinletff2=f2targetsin(funcmdmanflow->OptionExt.neutral2Cases.join(ff1cmd(fst_pair_manman)flow)(ff2cmd(snd_pair_manman)flow))letbroadcast_stateless_calltargetsf1domains1f2domains2=matchsat_targets~targets~domains:domains1,sat_targets~targets~domains:domains2with|false,false->(* Both domains do not provide an [exec] for such targets *)Exceptions.panic"broadcast_stateless_call: targets '%a' not found in %a nor %a"(OptionExt.printpp_domains)targetspp_domainsdomains1pp_domainsdomains2|true,false->(* Only [D1] provides a transfer function for such targets *)f1targets|false,true->(* Only [D2] provides a transfer function for such targets *)f2targets|true,true->(* Both [D1] and [D2] provide a transfer function for such targets *)letff1=f1targetsinletff2=f2targetsin(funcmdmanflow->OptionExt.neutral2Cases.join(ff1cmdmanflow)(ff2cmdmanflow))letbroadcast_initf1f2progmanflow=matchf1prog(fst_pair_manman)flowwith|None->f2prog(snd_pair_manman)flow|Somer1->matchr1>>%?f2prog(snd_pair_manman)with|None->Somer1|Somer2->Somer2letbroadcast_stateless_initf1f2progmanflow=matchf1progmanflowwith|None->f2progmanflow|Somer1->matchr1>>%?f2progmanwith|None->Somer1|Somer2->Somer2