123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(** Intra-procedural iterator for blocks, assignments and tests *)openMopsaopenSig.Abstraction.StatelessopenAstopenNumeric.Common(******************)(** Trace markers *)(******************)typemarker+=M_ifofbool*exprlet()=register_marker{marker_print=(funnextfmt->function|M_if(true,cond)->Format.fprintffmt"if (%a)"pp_exprcond|M_if(false,cond)->Format.fprintffmt"if (!%a)"pp_exprcond|m->nextfmtm);marker_compare=(funnextm1m2->matchm1,m2with|M_if(branch1,cond1),M_if(branch2,cond2)->Compare.pairBool.comparecompare_expr(branch1,cond1)(branch2,cond2)|_->nextm1m2);marker_print_name=(funnext->function|M_if_->"if"|m->nextm);marker_name="if";}(**********************)(** Domain definition *)(**********************)moduleDomain=structincludeGenStatelessDomainId(structletname="universal.iterators.intraproc"end)letchecks=[]letinitprogmanflow=Noneletrecnegate_bool_expre=matchekindewith|E_constant(C_booltrue)->mk_falsee.erange|E_constant(C_boolfalse)->mk_truee.erange|E_constant(C_topT_bool)->e|E_unop(O_log_not,ee)->ee|E_binop(O_log_and,e1,e2)->mk_log_or(negate_bool_expre1)(negate_bool_expre2)e.erange|E_binop(O_log_or,e1,e2)->mk_log_and(negate_bool_expre1)(negate_bool_expre2)e.erange|E_binop(O_log_xor,e1,e2)->mk_log_xor(negate_bool_expre1)e2e.erange|E_binop(op,e1,e2)whenis_comparison_opop->mk_binope1(negate_comparison_opop)e2e.erange~etyp:T_bool|_->mk_notee.erangeletrecto_bool_expre=matchekindewith|E_constant(C_bool_)->e|E_constant(C_topT_bool)->e|E_var_->e|E_unop(O_log_not,e)->negate_bool_expr(to_bool_expre)|E_unop(op,_)whenis_predicate_opop->e|E_binop(op,_,_)whenis_comparison_opop->e|E_binop(op,e1,e2)whenis_logic_opop->mk_binop(to_bool_expre1)op(to_bool_expre2)e.erange~etyp:T_bool|_->assertfalseletreceval_bool_expre~ftrue~ffalse~fbothrangemanflow=letee=matchexpr_to_constewith|Somec->{ewithekind=E_constantc}|None->einmatchekindeewith|E_constant(C_booltrue)->ftrueflow|E_constant(C_boolfalse)->ffalseflow|E_constant(C_intn)->ifZ.(n<>zero)thenftrueflowelseffalseflow|E_constant(C_topT_bool)->fbothflow|E_constant(C_topT_int)->fbothflow|E_unop(O_log_not,ee)->eval_bool_expree~ftrue:ffalse~ffalse:ftrue~fbothrangemanflow|_->assume(to_bool_expree)manflow~route:(Belowname)~translate:"Universal"~fthen:ftrue~felse:ffalseletexecstmtmanflow=matchskindstmtwith|S_expressione->man.evaleflow>>$?funeflow->Post.returnflow|>OptionExt.return|S_assign(x,e)whenis_universal_type(etypx)->man.evaleflow~translate:"Universal">>$?funeflow->man.exec(mk_assignxestmt.srange)flow~route:(Belowname)|>OptionExt.return|S_assume{ekind=E_constant(C_boolb)}->Post.return(ifbthenflowelseFlow.removeT_curflow)|>OptionExt.return|S_assume{ekind=E_unop(O_log_not,{ekind=E_constant(C_boolb)})}->Post.return(ifnotbthenflowelseFlow.removeT_curflow)|>OptionExt.return|S_assume{ekind=E_constant(C_intn)}->Post.return(ifZ.(n<>zero)thenflowelseFlow.removeT_curflow)|>OptionExt.return|S_assume{ekind=E_unop(O_log_not,{ekind=E_constant(C_intn)})}->Post.return(ifZ.(n=zero)thenflowelseFlow.removeT_curflow)|>OptionExt.return|S_assumeewhenis_universal_type(etype)->man.evaleflow~translate:"Universal">>$?fune'flow->man.exec(mk_assumee'stmt.srange)flow~route:(Belowname)|>OptionExt.return(* Skip the analysis of the block if there is no indirect flow and the
current environment is empty *)|S_block(b,cleaner)whenFlow.is_emptyflow||(* no indirect flow *)(Flow.is_singletonflow&&Flow.memT_curflow&&(* empty environment *)man.lattice.is_bottom(Flow.getT_curman.latticeflow))->Post.returnflow|>OptionExt.return|S_block(block,local_vars)->Some(letpost=List.fold_left(funaccstmt->acc>>%man.execstmt)(Post.returnflow)blockinletend_range=ifis_orig_rangestmt.srangethenset_range_startstmt.srange(get_range_endstmt.srange)elsestmt.srangeinletpost=List.fold_left(funaccvar->acc>>%man.exec(mk_remove_varvarend_range))postlocal_varsinpost)(* Skip the analysis of if there is no flow *)|S_if(cond,s1,s2)whenFlow.is_emptyflow->Post.returnflow|>OptionExt.return(* Use [assume], that skips the analyis of a branch if its input environment is empty. *)(* This is sound if there is no inderct flow, because [assume] will not
execute the branch if its [cur] environment is empty, while an indirect
flow may have an empty [cur] environment. *)|S_if(cond,s1,s2)whenFlow.is_singletonflow&&Flow.memT_curflow->assumecondmanflow~fthen:(funflow->man.exec(mk_add_marker(M_if(true,cond))stmt.srange)flow>>%man.execs1)~felse:(funflow->man.exec(mk_add_marker(M_if(false,cond))stmt.srange)flow>>%man.execs2)|>OptionExt.return|S_if(cond,s1,s2)->(* Use this function to execute a branch when the other one is not
reachable. In addition to the execution of the body of the reachable branch,
this function executes the unreachable branch with an empty T_cur
environment. This ensures that indirect flows in the branch are
executed. *)letexec_one_branchstmtotherbranchflow=letpost1=man.exec(mk_add_marker(M_if(branch,cond))stmt.srange)flow>>%man.execstmtinletctx1=Cases.get_ctxpost1inletflow2=Flow.set_ctxctx1flow|>Flow.removeT_curinletpost2=man.execotherflow2inPost.joinpost1post2in(* Execute both branches and ensure proper propagation of the context *)letexec_both_branchesflow1flow2=letpost1=man.exec(mk_add_marker(M_if(true,cond))stmt.srange)flow1>>%man.execs1inletctx1=Cases.get_ctxpost1inletflow2=Flow.set_ctxctx1flow2inletpost2=man.exec(mk_add_marker(M_if(false,cond))stmt.srange)flow2>>%man.execs2inPost.joinpost1post2inassumecondmanflow~fthen:(exec_one_branchs1s2true)~felse:(exec_one_branchs2s1false)~fboth:(exec_both_branches)(* When both environment are empty, we still need to execute both
branches because of eventual indirect flows *)~fnone:(exec_both_branches)|>OptionExt.return|S_print_state->letprinter=empty_printer()inFlow.printman.lattice.printprinterflow;Framework.Output.Factory.printprinter(srangestmt);Some(Post.returnflow)|S_print_exprel->letprinter=empty_printer()inList.iter(man.print_exprflowprinter)el;Framework.Output.Factory.printprinter(srangestmt);Some(Post.returnflow)|_->Noneletis_not_universale=not(is_universal_typee.etyp)letevalexpmanflow=matchekindexpwith|E_binop(O_log_and,e1,e2)whenis_universal_typeexp.etyp->assume_nume1manflow~fthen:(funflow->(* Since we didn't check the type of the sub-expression [e1], we
need to translate to Universal (if this isn't the case already).
That way, we can handle expressions from other semantics, as long
as they can be translated to Universal.
Note that we need to do that because we checked that the type of
the whole expression is Universal. *)man.evale2flow~translate:"Universal")~felse:(funflow->Eval.singleton(mk_falseexp.erange)flow)|>OptionExt.return|E_binop(O_log_or,e1,e2)whenis_universal_typeexp.etyp->assume_nume1manflow~fthen:(funflow->Eval.singleton(mk_trueexp.erange)flow)~felse:(funflow->man.evale2flow~translate:"Universal")|>OptionExt.return|E_binop(O_log_xor,e1,e2)whenis_universal_typeexp.etyp->lets1=assume_nume1manflow~fthen:(funflow->man.eval(mk_note2exp.erange)~translate:"Universal"flow)~felse:(funflow->man.evale2flow~translate:"Universal")inlets2=assume_num(mk_note1exp.erange)manflow~fthen:(funflow->man.evale2flow~translate:"Universal")~felse:(funflow->man.eval(mk_note2exp.erange)~translate:"Universal"flow)inEval.joins1s2|>OptionExt.return|E_unop(O_log_not,{ekind=E_binop(O_log_and,e1,e2)})whenis_universal_typeexp.etyp->man.eval(mk_log_or(mk_note1e1.erange)(mk_note2e2.erange)exp.erange)flow|>OptionExt.return|E_unop(O_log_not,{ekind=E_binop(O_log_or,e1,e2)})whenis_universal_typeexp.etyp->man.eval(mk_log_and(mk_note1e1.erange)(mk_note2e2.erange)exp.erange)flow|>OptionExt.return|E_binop(op,e1,e2)whenis_comparison_opop&&is_universal_typeexp.etyp->eval_bool_exprexpexp.erangemanflow~ftrue:(funflow->Eval.singleton(mk_trueexp.erange)flow)~ffalse:(funflow->Eval.singleton(mk_falseexp.erange)flow)~fboth:(funflow->Eval.singleton(mk_topT_boolexp.erange)flow)|>OptionExt.return|E_unop(op,ee)whenis_predicate_opop&&is_universal_typeexp.etyp->eval_bool_exprexpexp.erangemanflow~ftrue:(funflow->Eval.singleton(mk_trueexp.erange)flow)~ffalse:(funflow->Eval.singleton(mk_falseexp.erange)flow)~fboth:(funflow->Eval.singleton(mk_topT_boolexp.erange)flow)|>OptionExt.return|_->Noneletaskquerymanflow=Noneletprint_exprmanflowprinterexp=()endlet()=register_stateless_domain(moduleDomain)