123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(** Finite sets of possibly included or definitely excluded integer constants *)openMopsaopenSig.Abstraction.Simplified_valueopenAstopenCommonmoduleSimplifiedValue=struct(** {2 Types} *)(** ********* *)moduleSet=SetExt.Make(structtypet=Z.tletcompare=Z.compareletprint=unformatZ.pp_printend)typet=|InofSet.t|NotInofSet.tletprintprintera=letprint_setprefixs=ifSet.is_emptysthenpp_stringprinter(prefix^"∅")elsepp_set(unformatZ.pp_print)printer(Set.to_poly_sets)~sopen:(prefix^"{")~ssep:","~sclose:"}"inmatchawith|Ins->print_set"∈ "s|NotIns->print_set"∉ "s(** {2 Header of the abstraction} *)(** ***************************** *)includeGenValueId(structtypenonrect=tletname="universal.numeric.values.powersets.excluded"letdisplay="excluded"end)letaccept_type=function|T_int|T_bool->true|_->falseletbottom=InSet.emptylettop=NotInSet.emptyletis_bottoma=matchawith|Ins->Set.is_emptys|NotIn_->falseletis_topa=matchawith|In_->false|NotIns->Set.is_emptys(** {2 Options} *)(** *********** *)letopt_max_intset=ref10let()=register_domain_optionname{key="-max-excluded-set-size";category="Numeric";doc=" maximum size of integer sets for the excluded powerset";spec=Set_int(opt_max_intset,ArgExt.empty);default=string_of_int!opt_max_intset;}(** {2 Utilities} *)(** ************* *)letzero=In(Set.singletonZ.zero)letone=In(Set.singletonZ.one)(** [bound a] is [a] if the number of elements in [a] is lesser or equal to
the maximum number of elements allowed, otherwise it is [top]. Does not
bound the size of [NotIn], as there are no infinite ascending chains for
[NotIn]. *)letbound_size(a:t):t=matchawith|NotIn_->a|Ins->ifSet.cardinals<=!opt_max_intsetthenaelsetop(** [of_bounds l r] is [{l, l+1, ..., r}] if this set has size up to the
maximum number of elements, otherwise it is [NotIn {l-1, r+1}].
This is a precision optimization compared to simply return top, as we
already know that [l - 1] and [r + 1] will not be in the set of elements.
Due to this optimization, top is often represented as
[∉{MININT - 1, MAXINT + 1}] in C programs. *)letof_bounds(l:Z.t)(h:Z.t):t=letrecdoitacci=ifZ.gtihthenInaccelsedoit(Set.addiacc)(Z.succi)inifZ.subhl>=Z.of_int!opt_max_intsetthenNotIn(Set.of_list[Z.predl;Z.succh])elsedoitSet.emptylletis_zero=function|Ins->Set.equals(Set.singletonZ.zero)|NotIn_->falseletcontains_zero=function|Ins->Set.memZ.zeros|NotIns->Set.memZ.zeros|>notletcontains_non_zero=function|Ins->Set.removeZ.zeros|>Set.is_empty|>not|NotIns->true(* An infinite set always contains non-zero values. *)letremove_zero=function|Ins->In(Set.removeZ.zeros)|NotIns->NotIn(Set.addZ.zeros)(** [of_bool t f] is:
- [∅] if [t = false] and [f = false]
- [{0}] if [t = false] and [f = true]
- [{1}] if [t = true] and [f = false]
- [{0,1}] if [t = true] and [f = true] *)letof_booltf=matcht,fwith|false,false->bottom|true,false->In(Set.singletonZ.one)|false,true->In(Set.singletonZ.zero)|true,true->In(Set.of_list[Z.zero;Z.one])|>bound_sizeletto_itv(a:t):int_itv=ifis_topathenNbI.minf_infelseifis_bottomathenBOTelsematchawith|Ins->Nb(I.of_z(Set.min_elts)(Set.max_elts))|NotIn_->NbI.minf_infletmapf=function|Ins->In(Set.mapfs)|NotIns->NotIn(Set.mapfs)(** [combine combiner s1 s2] is [{combiner x1 x2 | x1 ∈ s1, x2 ∈ s2}]. *)letcombinecombiner(s1:Set.t)(s2:Set.t)=Set.fold(funn1acc->Set.fold(funn2->Set.add(combinern1n2))s2acc)s1Set.emptyletcombine_optcombiner(s1:Set.t)(s2:Set.t)=Set.fold(funn1acc->Set.fold(funn2acc->matchcombinern1n2with|None->acc|Somer->Set.addracc)s2acc)s1Set.empty(** {2 Lattice operators} *)(** ********************* *)letsubset(a1:t)(a2:t):bool=matcha1,a2with|Ins1,Ins2->Set.subsets1s2|NotIns1,NotIns2->Set.subsets2s1|Ins1,NotIns2->Set.inters1s2|>Set.is_empty(* [NotIn s1] is an infinite set, so that is cannot be smaller than a finite set. *)|NotIns1,Ins2->falseletjoin(a1:t)(a2:t):t=matcha1,a2with|Ins1,Ins2->In(Set.unions1s2)|NotIns1,NotIns2->NotIn(Set.inters1s2)(* From the excluded values, we add those that are possibly included. *)|Ins1,NotIns2->NotIn(Set.diffs2s1)|NotIns1,Ins2->NotIn(Set.diffs1s2)letmeet(a1:t)(a2:t):t=matcha1,a2with|Ins1,Ins2->In(Set.inters1s2)|NotIns1,NotIns2->NotIn(Set.unions1s2)(* From the known values, we remove those that are excluded. *)|Ins1,NotIns2->In(Set.diffs1s2)|NotIns1,Ins2->In(Set.diffs2s1)letwidenctx(a1:t)(a2:t):t=matcha1,a2with|NotIn_,_|_,NotIn_->(* If any of the two is an exclusion set, the join will result in an
exclusion set. Once we have an exclusion set, there are no infinite
ascending chains, as the joins will result in intersections of finite
sets. *)joina1a2|Ins1,Ins2->(* If the size is under the maximum number of finite elements in the
union, we keep it as a set of constants. Otherwise, we go to top. This
might be improved by selecting a finite set of elements that are not in
the union, and returning the excluded set of those elements. *)ifSet.cardinals1+Set.cardinals2<=!opt_max_intsetthenIn(Set.unions1s2)elsetop(** {2 Forward semantics} *)(** ********************* *)letconstant(c:constant)(t:typ):t=matchcwith|C_booltrue->In(Set.singletonZ.one)|C_boolfalse->In(Set.singletonZ.zero)|C_topT_bool->In(Set.of_list[Z.zero;Z.one])|>bound_size|C_topT_int->top|C_intn->In(Set.singletonn)|C_int_interval(Finitei1,Finitei2)->of_boundsi1i2|_->topletunop(op:operator)(t:typ)(a:t)(type_return:typ)=matchopwith|O_plus->a|O_minus->mapZ.nega|O_log_not->of_bool(contains_zeroa)(contains_non_zeroa)|_->top(** [combine_with combiner a1 a2] combines the elements of [a1] and [a2] with
[combiner]. If [a1] and [a2] are finite sets, it applies [combiner] to the
cartesian product of the two. If [a1] and [a2] are both exclued powersets,
returns [top]. If one of the two is a finite set of size exactly one (and
therefore, it is a definite value), returns an excluded set where the
constant is combined with the excluded powerset. Otherwise, returns [top].
*)letcombine_withcombinecombinera1a2=matcha1,a2with|Ins1,Ins2->In(combinecombiners1s2)|>bound_size|NotIns1,NotIns2->top|NotInnotin_set,Inin_set->beginmatchSet.cardinalin_setwith|0->bottom|1->NotIn(combinecombinernotin_setin_set)|_->topend|Inin_set,NotInnotin_set->beginmatchSet.cardinalin_setwith|0->bottom|1->NotIn(combinecombinerin_setnotin_set)|_->topendletbinop(op:operator)(t1:typ)(a1:t)(t2:typ)(a2:t)(type_return:typ)=ifis_bottoma1||is_bottoma2thenbottomelseifis_topa1||is_topa2thentopelseletwith_intfab=fa(Z.to_intb)intrymatchopwith|O_plus->combine_withcombineZ.adda1a2|O_minus->combine_withcombineZ.suba1a2|O_mult->ifis_zeroa1||is_zeroa2thenzeroelsecombine_withcombineZ.mula1a2|O_div->let()=debug"%a O_div %a@."(formatprint)a1(formatprint)a2inifis_zeroa1thena1else(* We remove 0 from both the in and the notin to prevent division by zero. *)leta2=matcha2with|Ins2->In(Set.removeZ.zeros2)|NotIns2->NotIn(Set.removeZ.zeros2)incombine_withcombine_opt(funxy->ifZ.(xmody=zero)thenSome(Z.divxy)elseNone)a1a2|O_pow->beginmatcha1,a2with|Ins1,Ins2->combine_withcombine(with_intZ.pow)a1a2|_->topend|O_bit_and->beginmatcha1,a2with|Ins1,Ins2->combine_withcombineZ.loganda1a2|_->topend|O_bit_or->beginmatcha1,a2with|Ins1,Ins2->combine_withcombineZ.logora1a2|_->topend|O_bit_xor->beginmatcha1,a2with|Ins1,Ins2->combine_withcombineZ.logxora1a2|_->topend|O_bit_lshift->ifis_zeroa1thena1elsebeginmatcha1,a2with|Ins1,Ins2->combine_withcombine(with_intZ.shift_left)a1a2|_->topend|O_bit_rshift->ifis_zeroa1thena1elsebeginmatcha1,a2with|Ins1,Ins2->combine_withcombine(with_intZ.shift_right)a1a2|_->topend|O_mod->beginmatcha1,a2with|Ins1,Ins2->ifis_zeroa1thena1elsecombine_withcombineZ.rema1(remove_zeroa2)|_->topend|O_log_or->of_bool(contains_non_zeroa1||contains_non_zeroa2)(contains_zeroa1&&contains_zeroa2)|O_log_and->of_bool(contains_non_zeroa1&&contains_non_zeroa2)(contains_zeroa1||contains_zeroa2)|O_log_xor->of_bool((contains_non_zeroa1&&contains_zeroa2)||(contains_zeroa1&&contains_non_zeroa2))((contains_zeroa1&&contains_zeroa2)||(contains_non_zeroa1&&contains_non_zeroa2))|_->topwithZ.Overflow->topincludeDefaultValueFunctionsletavalue:typer.ravalue_kind->t->roption=funavala->matchavalwith|Common.V_int_interval->Some(to_itva)|Common.V_int_interval_fast->Some(to_itva)|Common.V_int_congr_interval->Some(to_itva,Bot.NbCommon.C.minf_inf)|_->Noneletreccompare(op:operator)(b:bool)(t1:typ)(a1:t)(t2:typ)(a2:t):(t*t)=ifis_bottoma1||is_bottoma2then(bottom,bottom)elseletop=ifbthenopelsenegate_comparison_opopinmatchopwith|O_eq->beginmatcha1,a2with|Ins1,Ins2->leta=In(Set.inters1s2)ina,a|NotIns1,NotIns2->leta=NotIn(Set.unions1s2)ina,a|Inin_set,NotInnotin_set->In(Set.diffin_setnotin_set),a2|NotInnotin_set,Inin_set->a1,In(Set.diffin_setnotin_set)end|O_ne->beginmatcha1,a2with|Ins1,Ins2->lets1=ifSet.cardinals2=1thenSet.diffs1s2elses1inlets2=ifSet.cardinals1=1thenSet.diffs2s1elses2inIns1,Ins2|NotInnotin_set,Inin_set->ifSet.cardinalin_set=1then(* [x=∉{x1,x2} != y=∈{y1} ⟹ x=∉{x1,x2,y1}]*)letnotin_set'=Set.unionnotin_setin_setinletin_set'=in_setinNotInnotin_set',Inin_set'elsea1,a2|Inin_set,NotInnotin_set->ifSet.cardinalin_set=1thenletnotin_set'=Set.unionnotin_setin_setinletin_set'=in_setinInin_set',NotInnotin_set'elsea1,a2|NotIn_,NotIn_->a1,a2end|O_le->beginmatcha1,a2with|Ins1,Ins2->letmin_s1=Set.min_elts1inletmax_s2=Set.max_elts2inIn(Set.filter(funn->Z.leqnmax_s2)s1),In(Set.filter(funn->Z.geqnmin_s1)s2)|Ins1,NotIns2->(* To the excluded set we can add the successor of the largest set on
the left: [x=∈{1,3} <= y=∉{2} ⟹ y=∉{0,2}], as [y] cannot have value
[0]. This is a simple precision improvement. *)a1,NotIn(Set.add(Set.min_elts1|>Z.pred)s2)|NotIns1,Ins2->NotIn(Set.add(Set.max_elts2|>Z.succ)s1),a2|NotIn_,NotIn_->a1,a2end|O_ge->beginmatcha1,a2with|Ins1,Ins2->letmax_s1=Set.max_elts1inletmin_s2=Set.min_elts2inIn(Set.filter(funn->Z.geqnmin_s2)s1),In(Set.filter(funn->Z.leqnmax_s1)s2)|Ins1,NotIns2->a1,NotIn(Set.add(Set.max_elts1|>Z.succ)s2)|NotIns1,Ins2->NotIn(Set.add(Set.min_elts2|>Z.pred)s1),a2|NotIn_,NotIn_->a1,a2end|O_lt->beginmatcha1,a2with|Ins1,Ins2->letmin_s1=Set.min_elts1inletmax_s2=Set.max_elts2inIn(Set.filter(funn->Z.ltnmax_s2)s1),In(Set.filter(funn->Z.gtnmin_s1)s2)|Ins1,NotIns2->a1,NotIn(Set.add(Set.min_elts1)s2)|NotIns1,Ins2->NotIn(Set.add(Set.max_elts2)s1),a2|NotIn_,NotIn_->a1,a2end|O_gt->beginmatcha1,a2with|Ins1,Ins2->letmax_s1=Set.max_elts1inletmin_s2=Set.min_elts2inIn(Set.filter(funn->Z.gtnmin_s2)s1),In(Set.filter(funn->Z.ltnmax_s1)s2)|Ins1,NotIns2->a1,NotIn(Set.add(Set.max_elts1)s2)|NotIns1,Ins2->NotIn(Set.add(Set.min_elts2)s1),a2|NotIn_,NotIn_->a1,a2end|_->default_compareopbt1a1t2a2endlet()=register_simplified_value_abstraction(moduleSimplifiedValue)