123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982(**************************************************************************)(* *)(* SPDX-License-Identifier LGPL-2.1 *)(* Copyright (C) *)(* CEA (Commissariat à l'énergie atomique et aux énergies alternatives) *)(* *)(**************************************************************************)openCil_typesopenCil_datatypeopenSimplifiedmoduleEdgeLabel=structtypet=|Pointer(* represents dereferentiation of pointers as well as arrays *)|Fieldoffieldinfoletcomparelr=matchl,rwith|Pointer,Pointer->0|Pointer,Field_->-1|Fieldlv,Fieldrv->Fieldinfo.comparelvrv|Field_,Pointer->1letdefault=Pointerletis_pointer=functionPointer->true|_->falseletis_field=functionField_->true|_->falseletprettyfmt=function|Pointer->()|Fieldf->Format.fprintffmt"-%s"f.fnameendmoduleG=structincludeGraph.Persistent.Digraph.ConcreteBidirectionalLabeled(Datatype.Int)(EdgeLabel)letpsuccgv=letonly_pointer_succe=ifEdgeLabel.is_pointer(E.labele)thenSome(E.dste)elseNoneinList.filter_maponly_pointer_succ(succ_egv)letppredgv=letpe=matchE.labelewithPointer->Some(E.srce)|_->NoneinList.filter_mapp(pred_egv)letfsucc_optgvf=assert(List.for_all(fune->EdgeLabel.is_field@@E.labele)@@succ_egv);letis_field_fe=matchE.labelewith|Fieldf'->Fieldinfo.equalff'|_->falseinletedges=succ_egvinassert(List.length(List.filteris_field_fedges)<=1);Option.mapE.dst@@List.find_optis_field_fedgesletpsucc_optgv=matchpsuccgvwith|[]->None|[v]->Somev|_->Options.fatal"Invariant violated: more than one successor"letcheck_loopv1v2=ifv1=v2thenOptions.warning~once:true~wkey:Options.Warn.incoherent"loop on vertex %d (following unsafe cast?); analysis may be unsound"v1letadd_edgegv1v2=check_loopv1v2;add_edgegv1v2letadd_edge_eg((v1,_,v2)asedge)=check_loopv1v2;add_edge_egedgeendtypev=G.V.tmoduleV=G.VmoduleE=structincludeG.EincludeEdgeLabelmoduleMap=Stdlib.Map.Make(structtypet=EdgeLabel.tletcompare=EdgeLabel.compareend)endmoduleVMap=Datatype.Int.MapmoduleVSet=Datatype.Int.SetmoduleLSet=Cil_datatype.LvalStructEq.SetmoduleVarSet=Cil_datatype.Varinfo.SetmoduleVarMap=structincludeCil_datatype.Varinfo.Mapletintersect=merge@@fun_lr->matchl,rwith|Somel,Somer->Some(l,r)|_->Noneletpretty=letmoduleM=Make(Datatype.Int)inM.prettyendtypestate={graph:G.t;vmap:VarSet.tVMap.t;(* associate with each node a set of variables *)varmap:V.tVarMap.t(* reverse of varmap *)}letnode_counter=ref0letfresh_node_id()=letid=!node_counterinnode_counter:=!node_counter+1;idletget_varsvs:VarSet.t=tryVMap.findvs.vmapwithNot_found->VarSet.empty(* raises Not_found *)letrecfind_lval_vertex((lhost,offset):lval)s:V.t=letfind_psuccv=matchG.psucc_opts.graphvwithSomev->v|_->raiseNot_foundinletfind_fsuccvfname=matchG.fsucc_opts.graphvfnamewithSomev->v|_->raiseNot_foundinletfind_lhost=function|Varvar->VarMap.findvars.varmap|Meme->matchLvalOrRef.from_expewith|None->Options.fatal"unexpected result: Lval.from (%a) = None"Exp.prettye|Some(LvalOrRef.Reflv1)->find_lval_vertexlv1s|Some(LvalOrRef.Lvallv1)->letv1=find_lval_vertexlv1sinfind_psuccv1inletrecfind_offsetv=function|NoOffset->v|Index(_,o)->letv'=find_psuccvinfind_offsetv'o|Field(f,o)->letv'=find_fsuccvfinfind_offsetv'oinlethv=find_lhostlhostinfind_offsethvoffsetletadd_field_and_castflv=letoff=Field(f,NoOffset)inmatchlvwith|Memp,NoOffsetwhenAst_types.is_void_ptr(Cil.typeOfp)->letnewt=Cil_const.(mk_tptr(mk_tcompf.fcomp))inMem(Cil.mkCast~newtp),off|_->Cil.addOffsetLvalofflvmoduleReadout=struct(* Reconstruct all lvals that are represented by the given node.
Nodes only carry varinfos. In order to obtain lvals we recursively walk
backwards in the graph inductively constructing lvals from scratch.
- The lvals of the current node are the stored varinfos themselves.
- After gathering the lvals for the predecessor, the lvals are
modified according to the edge type used:
* Pointer: add a star (x → *x)
* Field f: add an offset (x -> x.f) *)letget_lval_setvs:LSet.t=assert(G.mem_vertexs.graphv);(* cycles can occur with unsafe casts such as: x->f = (int* ) x; *)letrecchecking_for_cyclessvisitedv=ifVSet.memvvisitedthenlet()=Options.warning~once:true~wkey:Options.Warn.incoherent"cycle during readout of vertex %d, \
(following unsafe cast?); analysis may be unsound"vinLSet.emptyelseletvisited=VSet.addvvisitedinletmodified_predecessors=List.map(fune->letpred_lvals=checking_for_cyclessvisited@@E.srceinletmodify_lvallv=matchE.labelewith|Fieldf->add_field_and_castflv|Pointer->letty=Cil.typeOfLvallvinifAst_types.is_arraytythenCil.addOffsetLval(Index(Simplified.nul_exp,NoOffset))lvelselet()=ifnot@@Ast_types.is_ptrtythenOptions.debug"unexpected type: %a"Printer.pp_typtyinMem(Cil.dummy_exp@@Lvallv),NoOffsetinLSet.mapmodify_lvalpred_lvals)(G.pred_es.graphv)inletlvals_of_v=letmk_lvalvar=(Varvar),NoOffsetinLSet.of_seq@@Seq.mapmk_lval@@VarSet.to_seq@@get_varsvsinList.fold_leftLSet.unionlvals_of_vmodified_predecessorsinchecking_for_cyclessVSet.emptyvletlvals_pointing_to_vertexvs:LSet.t=assert(G.mem_vertexs.graphv);letlist_pred=List.map(funb->get_lval_setbs)(G.ppreds.graphv)inList.fold_leftLSet.unionLSet.emptylist_predletvars_pointing_to_vertexvs:VarSet.t=letpreds=G.ppreds.graphvinletpred_vars=List.map(funp->get_varsps)predsinList.fold_leftVarSet.unionVarSet.emptypred_varsletfind_varslvs=letlv=Lval.simplifylvintryletv=find_lval_vertexlvsinget_varsvswithNot_found->VarSet.emptyletfind_synonymslvs=letlv=Lval.simplifylvintryletv=find_lval_vertexlvsinget_lval_setvswithNot_found->LSet.emptyletalias_varslvs:VarSet.t=tryletv=find_lval_vertexlvsinList.fold_left(funaccsucc->VarSet.unionacc@@vars_pointing_to_vertexsuccs)VarSet.empty(G.psuccs.graphv)withNot_found->VarSet.emptyletalias_lvalslvs:LSet.t=letv_opt=trySome(find_lval_vertexlvs)withNot_found->NoneinmatchOption.bind(G.psucc_opts.graph)v_optwith|None->LSet.empty|Somesucc->lvals_pointing_to_vertexsuccsletpoints_to_varslvs:VarSet.t=letsucc=tryG.psucc_opts.graph@@find_lval_vertexlvswithNot_found->Noneinmatchsuccwith|None->VarSet.empty|Somesucc_v->get_varssucc_vsletpoints_to_lvalslvs:LSet.t=letsucc=tryG.psucc_opts.graph@@find_lval_vertexlvswithNot_found->Noneinmatchsuccwith|None->LSet.empty|Somesucc_v->get_lval_setsucc_vsletalias_sets_varss=letalias_set_of_vertex(i,_)=letaliases=vars_pointing_to_vertexisinifVarSet.cardinalaliases>=2thenSomealiaseselseNoneinList.filter_mapalias_set_of_vertex@@VMap.bindingss.vmapletalias_sets_lvalss=letalias_set_of_vertex(i,_)=letaliases=lvals_pointing_to_vertexisinifLSet.cardinalaliases>=2thenSomealiaseselseNoneinList.filter_mapalias_set_of_vertex@@VMap.bindingss.vmapendmodulePretty=structletpp_internalfmts=Format.fprintffmt"@[<v>";Format.fprintffmt"@[Edges:";G.iter_edges_e(fune->Format.fprintffmt"@;<3 2>@[%d@ @[%a%t@]@ %d@]"(E.srce)E.pretty(E.labele)Unicode.pp_right_arrow(E.dste))s.graph;Format.fprintffmt"@]@;<6>";Format.fprintffmt"@[VarMap:@;<3 2>";VarMap.prettyfmts.varmap;Format.fprintffmt"@]@;<6>";Format.fprintffmt"@[VMap:@;<2>";VMap.iter(funvls->Format.fprintffmt"@;<2 2>@[%d:%a@]"vVarSet.prettyls)s.vmap;Format.fprintffmt"@]";Format.fprintffmt"@]"letlibc_regexp=Str.regexp"^__fc_"letvars_filter_libcvars=ifOptions.is_debug_key_enabledOptions.DebugKeys.show_libc_varsthenvarselseletis_from_libcvi=Str.string_matchlibc_regexpvi.vname0inVarSet.filter(funv->not@@is_from_libcv)varsletlvals_filter_libclvals=ifOptions.is_debug_key_enabledOptions.DebugKeys.show_libc_varsthenlvalselseletrecis_from_libc=function|Varvi,_->Str.string_matchlibc_regexpvi.vname0|Mem{enode=Lvall;_},_->is_from_libcl|Mem_,_->falseinLSet.filter(funlval->not@@is_from_libclval)lvalsletpp_graphfmts=letis_first=reftrueinletpp_nodevfmtvars=Format.fprintffmt"%d:%a"vVarSet.pretty(vars_filter_libcvars)inletpp_edgee=letv1=E.srceandv2=E.dsteinif!is_firstthenis_first:=falseelseFormat.fprintffmt"@;<3>";Format.fprintffmt"@[%a@] %a%t @[%a@]"(pp_nodev1)(VMap.findv1s.vmap)E.pretty(E.labele)Unicode.pp_right_arrow(pp_nodev2)(VMap.findv2s.vmap)inletpp_unconnected_vertexv=ifG.in_degrees.graphv=0&&G.out_degrees.graphv=0thenbeginif!is_firstthenis_first:=falseelseFormat.fprintffmt"@;<3>";pp_nodevfmt(VMap.findvs.vmap)endinifG.nb_vertexs.graph=0thenFormat.fprintffmt"<empty>"elsebeginG.iter_edges_epp_edges.graph;G.iter_vertexpp_unconnected_vertexs.graphend;ifOptions.is_debug_key_enabledOptions.DebugKeys.show_internal_statethenbeginFormat.pp_print_newlinefmt();pp_internalfmtsendletpp_aliasesfmts=letalias_sets=List.maplvals_filter_libc@@Readout.alias_sets_lvalssinletalias_sets=List.stable_sortLSet.compare@@(* sort for more robust oracles *)List.filter(funlvals->LSet.cardinallvals>=2)alias_setsinPretty_utils.pp_list~empty:"<none>"~sep:"@;<2>"LSet.prettyfmtalias_setsend(* invariants of type t must be true before and after each function call *)letassert_invariantss:unit=(* check that all vertex of the graph have entries in vmap,
and are integer between 0 and node_counter, and have at most 1 successor *)assert(!node_counter>=0);letassert_vertexv=Options.debug~level:5"checking coherence of vertex %d"v;assert(v>=0);assert(v<!node_counter);assert(VMap.memvs.vmap);letsucc_e=G.succ_es.graphvinletis_pointer_vertex=List.exists(fune->E.is_pointer@@E.labele)succ_eandis_struct_vertex=List.exists(fune->E.is_field@@E.labele)succ_einassert(not(is_pointer_vertex&&is_struct_vertex));assert(notis_pointer_vertex||List.length(G.succs.graphv)<=1);inG.iter_vertexassert_vertexs.graph;letassert_edgev1v2=Options.debug~level:5"checking coherence of edge %d %t %d"v1Unicode.pp_right_arrowv2;assert(G.mem_vertexs.graphv1);assert(G.mem_vertexs.graphv2)inG.iter_edgesassert_edges.graph;letassert_varmap(var:varinfo)v=assert(G.mem_vertexs.graphv);assert(VarSet.memvar(VMap.findvs.vmap))inVarMap.iterassert_varmaps.varmap;letassert_vmapv(ls:VarSet.t)=assert(G.mem_vertexs.graphv);(* TODO: we removed the invariant because of OSCS*)(* if not (VarSet.is_empty ls)
* then
* begin
* let lv = VarSet.choose ls in
* let is_ptr_lv = Lval.is_pointer lv in
* assert (VarSet.for_all (fun x -> Lval.is_pointer x = is_ptr_lv) ls)
* end; *)assert(VarSet.fold(funlvacc->acc&&V.equal(VarMap.findlvs.varmap)v)lstrue)inVMap.iterassert_vmaps.vmap(* Ensure that assert_invariants is not executed if the -noassert flag is supplied. *)letassert_invariantss=tryassert(assert_invariantss;true)withAssert_failure_asexn->letbt=Printexc.get_raw_backtrace()inOptions.debug"incoherent graph:@ @[%a@]"Pretty.pp_graphs;Printexc.raise_with_backtraceexnbtletasserting_invariantss=assert_invariantss;sletpretty?(debug=false)fmts=assert_invariantss;ifdebugthenPretty.pp_graphfmtselsePretty.pp_aliasesfmts(* NOTE on "constant vertex": a constant vertex represents an unnamed
scalar value (type bottom in steensgaard's paper), or the address
of a variable. It means that in [vmap], its associated VarSet is
empty. By definition, constant vertex cannot be associated to a
lval in [varmap] *)letcreate_empty_vertexs:V.t*state=letnew_v=fresh_node_id()innew_v,{graph=G.add_vertexs.graphnew_v;varmap=s.varmap;vmap=VMap.addnew_vVarSet.emptys.vmap}letcreate_var_vertexvars=assert(not@@VarMap.memvars.varmap);letv=fresh_node_id()inlets={graph=G.add_vertexs.graphv;varmap=VarMap.addvarvs.varmap;vmap=VMap.addv(VarSet.singletonvar)s.vmap}inletreccreate_typ_vertexsvty=matchty.tnodewith|TArray(ty,_)|TPtrty->(* create more vertices for each level of dereferentiation *)letv',s=create_empty_vertexsinlets={swithgraph=G.add_edges.graphvv'}increate_typ_vertexsv'ty|_->s(* until the type becomes scalar *)inv,create_typ_vertexsvvar.vtypeletfind_or_create_var_vertex(var:varinfo)s=tryVarMap.findvars.varmap,swithNot_found->create_var_vertexvarsletrecfind_or_create_lval_vertex((lhost,offset):lval)s:V.t*state=letfind_or_create_psuccvs=matchG.psucc_opts.graphvwith|None->letv',s=create_empty_vertexsin(* finally add a points-to edge between v and v' *)letnew_graph=G.add_edges.graphvv'inv',{swithgraph=new_graph}|Somev'->v',sinletfind_or_create_fsuccvsf=matchG.fsucc_opts.graphvfwith|None->letv',s=create_empty_vertexsin(* finally add a points-to edge between v and v' *)letnew_graph=G.add_edge_es.graph@@E.createv(Fieldf)v'inv',{swithgraph=new_graph}|Somev'->v',sinletfind_or_create_lhosts=function|Varvar->find_or_create_var_vertexvars|Meme->matchLvalOrRef.from_expewith|None->Options.fatal"unexpected result: Lval.from (%a) = None"Exp.prettye|Some(LvalOrRef.Reflv1)->find_or_create_lval_vertexlv1s|Some(LvalOrRef.Lvallv1)->letv1,s=find_or_create_lval_vertexlv1sinfind_or_create_psuccv1sinletrecfind_or_create_offsetvs=function|NoOffset->v,s|Index(_,o)->letv',s=find_or_create_psuccvsinfind_or_create_offsetv'so|Field(f,o)->letv',s=find_or_create_fsuccvsfinfind_or_create_offsetv'soinlethv,s=find_or_create_lhostslhostinletv,s=find_or_create_offsethvsoffsetinOptions.debug~level:5"graph after find_or_create_lval_vertex @[%a@] (%d):@ %a"Printer.pp_lval(lhost,offset)vPretty.pp_graphs;v,sandfind_or_create_ref_vertexlvs:V.t*state=letv1,s=find_or_create_lval_vertexlvsinletva,s=create_empty_vertexsinlets={swithgraph=G.add_edges.graphvav1}inOptions.debug~level:5"graph after find_or_create_ref_vertex @[%a@] (%d):@ %a"LvalOrRef.pretty(LvalOrRef.Reflv)vaPretty.pp_graphs;va,sandfind_or_create_lval_or_ref_vertex(lv:LvalOrRef.t)s:V.t*state=matchlvwith|LvalOrRef.Lvallv->find_or_create_lval_vertexlvs|LvalOrRef.Reflv->find_or_create_ref_vertexlvs(* TODO is there a better way to do it ? *)letfind_vertexlvs=letlv=Lval.simplifylvinletv,x1=find_or_create_lval_vertexlvsinifs==x1thenv(* if s has not been modified, then the vertex was found, not created *)elseraiseNot_found(* merge of two vertices; the first vertex carries both sets, the second is
removed from the graph and from varmap and vmap *)letmergesv1v2=ifV.equalv1v2||not(G.mem_vertexs.graphv1)||not(G.mem_vertexs.graphv2)thenselse(* update varmap : every lval in v2 must now be associated with v1 *)letnew_varmap=VarSet.fold(funlv2->VarMap.addlv2v1)(get_varsv2s)s.varmapinletnew_vmap=letnew_set=VarSet.union(get_varsv1s)(get_varsv2s)inVMap.addv1new_set@@VMap.removev2s.vmapinletnew_graph=(* update the graph *)letf_fold_succeg:G.t=G.add_edge_eg@@E.createv1(E.labele)(E.dste)andf_fold_predeg:G.t=G.add_edge_eg@@E.create(E.srce)(E.labele)v1inletg=s.graphin(* add all new edges *)letg=G.fold_succ_ef_fold_succgv2ginletg=G.fold_pred_ef_fold_predgv2ginG.remove_vertexgv2(* remove v2 *)in{graph=new_graph;varmap=new_varmap;vmap=new_vmap}(* functions join and unify-pointer of steensgaard's paper *)(* join_without_check may break the invariants *)letrecjoin_without_checksv1v2:state=ifV.equalv1v2||not(G.mem_vertexs.graphv1&&G.mem_vertexs.graphv2)thenselseletmk_edge_mapsuccs=letmk_succe=E.labele,E.dsteinE.Map.of_seq@@Seq.mapmk_succ@@List.to_seqsuccsinletsuccs1=mk_edge_map@@G.succ_es.graphv1inletsuccs2=mk_edge_map@@G.succ_es.graphv2inletsucc_pairs=letmk_pair_succ1succ2=matchsucc1,succ2with|Somes1,Somes2->Some(s1,s2)|_->NoneinE.Map.mergemk_pairsuccs1succs2inlets=mergesv1v2inassert(not(G.mem_vertexs.graphv2));letmerge_succs_(succ1,succ2)s=assert(succ1<>v2);assert(succ2<>v1);join_without_checkssucc1succ2inE.Map.foldmerge_succssucc_pairssletjoinsv1v2:state=Options.debug~level:5"graph before join(%d,%d):@;<2>@[%a@]"v1v2Pretty.pp_graphs;assert_invariantss;letres=join_without_checksv1v2inOptions.debug~level:5"graph after join(%d,%d):@;<2>@[%a@]"v1v2Pretty.pp_graphres;begintryassert_invariantsreswithAssert_failure_->Options.debug"join(%d,%d) failed"v1v2;Options.debug"before join:@;<2>@[%a@]"Pretty.pp_internals;Options.debug"after join:@;<2>@[ %a@]"Pretty.pp_internalresend;resletmerge_sets(vs:VSet.t):V.t*state=letv0=VSet.choosevsinifVSet.cardinalvs<2thenv0,selsebeginOptions.debug~level:5"graph before merge_set %a:@;<2>@[%a@]"VSet.prettyvsPretty.pp_internals;assert(G.mem_vertexs.graphv0);letresult=VSet.fold(funvacc->mergeaccv0v)vssinOptions.debug~level:5"graph after merge_set %a:@;<2>@[%a@]"VSet.prettyvsPretty.pp_internalresult;v0,resultend(* may operate on an unsound state, where nodes may have multiple successors
of the same edge type *)letrecjoin_succssv=ifnot@@G.mem_vertexs.graphvthenselseletedge_map=List.fold_left(funme->letadd_dst=function|None->Some(VSet.singleton@@E.dste)|Somevs->Some(VSet.add(E.dste)vs)inE.Map.update(E.labele)add_dstm)E.Map.empty(G.succ_es.graphv)inletmerge_vset_evss=ifVSet.cardinalvs<2thenselseletv0,s=merge_setsvsinjoin_succssv0inE.Map.foldmerge_vsetedge_maps(* in Steensgard's paper, this is written settype(v1,ref(v2,bot)) *)letset_typesv1v2:state=assert_invariantss;(* if v1 points to another node, suppress current outgoing edge (and the node if it is a constant node) *)letg,new_vmap=matchG.psucc_opts.graphv1with|None->s.graph,s.vmap|Somev2->(* if v2 is a constant node suppress it directly *)ifVarSet.is_empty(VMap.findv2s.vmap)thenG.remove_vertexs.graphv2,VMap.removev2s.vmapelseG.remove_edges.graphv1v2,s.vmapinletnew_g=G.add_edgegv1v2inasserting_invariants{swithgraph=new_g;vmap=new_vmap}letassignmentslvexp:state=assert_invariantss;letv1,s=find_or_create_lval_vertex(Lval.simplifylv)sinmatchexpwith|None->s|Somee->matchAst_types.is_ptr(Cil.typeOfe),LvalOrRef.from_expewith|false,_|_,None->s|true,Somey->letv2,s=find_or_create_lval_or_ref_vertexysinifList.memv2(G.psuccs.graphv1)||List.memv1(G.psuccs.graphv2)thenlet()=Options.warning~source:(fste.eloc)"ignoring assignment of the form: %a = %a"Printer.pp_lvallvPrinter.pp_expe;inselseasserting_invariants@@joinsv1v2(* assignment x = allocate(y) *)letassignment_x_allocate_yslv:state=assert_invariantss;letv1,s=find_or_create_lval_vertex(Lval.simplifylv)sinmatchG.psucc_opts.graphv1with|None->letv2,s=create_empty_vertexsinset_typesv1v2|Some_->sletis_includedss'=(* tests if s is included in s', at least as the nodes with lval *)assert_invariantss;assert_invariantss';letexceptionNot_includedintryletiter_varmap(var:varinfo)v:unit=letv'=tryVarMap.findvars'.varmapwithNot_found->raiseNot_includedin(* TODO: render correct for structs *)letsuccs=E.Map.of_seq@@Seq.map(fune->E.labele,E.dste)@@List.to_seq@@G.succ_es.graphvandsuccs'=E.Map.of_seq@@Seq.map(fune->E.labele,E.dste)@@List.to_seq@@G.succ_es'.graphv'inletcheck_succs_succ1succ2=matchsucc1,succ2with|None,_->None|Some_,None->raiseNot_included|Somev1p,Somev2p->ifV.equalv1pv2pthenNoneelseraiseNot_includedinignore@@E.Map.mergecheck_succssuccssuccs'inVarMap.iteriter_varmaps.varmap;truewithNot_included->falseletempty:state={graph=G.empty;varmap=VarMap.empty;vmap=VMap.empty}letis_emptys=comparesempty=0(* refresh vertex values to an interval. *)letrefreshs:state=assert_invariantss;ifis_emptysthenselsebeginletfill_mapidxmap=letmap=VMap.addidx!node_countermapinincrnode_counter;mapinletmap_idx=G.fold_vertexfill_maps.graphVMap.emptyinletconvertidx=VMap.findidxmap_idxinletvmap_convertidxvalueacc=VMap.add(convertidx)valueaccinlet{graph;varmap;vmap}=sinletresult={graph=G.map_vertexconvertgraph;varmap=VarMap.mapconvertvarmap;vmap=VMap.foldvmap_convertvmapVMap.empty}inasserting_invariantsresultendletunion_findvmapintersections=letmoduleStore:UnionFind.STORE=UnionFind.StoreMap.Make(VMap)inletmoduleUF=UnionFind.Make(Store)inletuf=UF.new_store()inletrefs=VMap.mapi(funi_->UF.makeufi)vmapinletput_into_uf(v1,v2)=letr1=VMap.findv1refsinletr2=VMap.findv2refsinignore@@UF.unionufr1r2inlet_vs=Seq.iterput_into_ufintersectionsinletsets_to_be_joined=letadd_to_mapirsets=letrepr=UF.findufrinletadd_to_set=function|None->Some(VSet.singletoni)|Someset->Some(VSet.addiset)inVMap.update(UF.getufrepr)add_to_setsetsinVMap.foldadd_to_maprefsVMap.emptyinsets_to_be_joinedletunions1s2:state=assert_invariantss1;assert_invariantss2;letnew_graph=G.fold_vertex(funv2g->G.add_vertexgv2)s2.graphs1.graphinletnew_graph=G.fold_edges_e(funeg->G.add_edge_ege)s2.graphnew_graphinletnew_vmap=VMap.union(fun_lset1lset2->Option.some@@VarSet.unionlset1lset2)s2.vmaps1.vmapinletsets_to_be_joined=letintersections=VarMap.to_seq@@VarMap.intersects1.varmaps2.varmapinunion_findnew_vmap@@Seq.mapsndintersectionsinletnew_varmap=VarMap.union(fun_l_r->Somel)s1.varmaps2.varmapinlets={graph=new_graph;varmap=new_varmap;vmap=new_vmap}inletmerged_nodes,s=VMap.fold(fun_set(merged_nodes,s)->letv0,s=merge_setssetin(v0::merged_nodes),s)sets_to_be_joined([],s)inlets=List.fold_leftjoin_succssmerged_nodesinOptions.debug~level:4"Union: Result graph:%a"Pretty.pp_graphs;begintryassert_invariantsswithAssert_failure_->Options.debug"union failed";Options.debug"Union: First graph:%a"Pretty.pp_internals1;Options.debug"Union: Second graph:%a"Pretty.pp_internals2;Options.debug"Union: Result graph:%a"Pretty.pp_internals;assert_invariantssend;smoduleSummary=struct(* a type for summaries of functions *)typet={state:stateoption;formals:lvallist;return:expoption}letmakes(kf:kernel_function)=letexp_return:expoption=ifKernel_function.has_definitionkfthenletreturn_stmt=Kernel_function.find_returnkfinmatchreturn_stmt.skindwith|Return(e,_)->e|_->Options.fatal"this should not happen"elseNoneinlets=matchexp_returnwith|None->s|Somee->beginmatchs,LvalOrRef.from_expewith|_,None->s|s,Somelv->let_,new_s=find_or_create_lval_or_ref_vertexlvsinnew_sendin{state=Somes;formals=List.map(funv->(Varv,NoOffset))(Kernel_function.get_formalskf);return=exp_return}letpretty?(debug=false)fmtsummary=letpp_list_lvalsfmt(l:lvallist)=letis_first=reftrueinletpp_elemlv=if!is_firstthenis_first:=falseelseFormat.fprintffmt"@ ";Format.fprintffmt"@[%a"Cil_datatype.Lval.prettylv;letpointees=Readout.points_to_varslvsinifnot@@VarSet.is_emptypointeesthenFormat.fprintffmt"%t%a"Unicode.pp_right_arrowVarSet.prettypointees;Format.fprintffmt"@]";inList.iterpp_elemlinletpp_optionppfmt=function|Somex->ppfmtx|None->Format.fprintffmt"<none>"inmatchsummary.statewith|None->ifdebugthenFormat.fprintffmt"not found"|Someswhenis_emptys->ifdebugthenFormat.fprintffmt"empty"|Somes->Format.fprintffmt"@[formals: @[%a@]@;<4>returns: @[%a@]@;<4>state: @[%a@] "(pp_list_lvals)summary.formals(pp_optionExp.pretty)summary.return(pp_option@@pretty~debug)summary.stateend(* the algorithm:
- unify the two graphs dropping all the variables from the summary
- pair arguments with formals assigning the formal's successor as the argument's successor
*)letcalls(res:lvaloption)(args:explist)(summary:Summary.t):state=assert_invariantss;letformals=summary.Summary.formalsinassert(List.lengthargs=List.lengthformals);letsummary_state=refresh@@Option.getsummary.statein(* pair up formals and their corresponding arguments,
as well as the bound result with the returned value *)letarg_formal_pairs=letres_ret=matchres,summary.returnwith|None,None->[]|Someres,Someret->letsimplify_retx=matchLvalOrRef.from_expxwith|Some(LvalOrRef.Lvallval)->lval|_->Options.fatal"unexpected form of return statement"in[LvalOrRef.Lval(Lval.simplifyres),simplify_retret]|None,Some_->[]|Some_,None->(* Shouldn't happen: Frama-C adds missing returns *)Options.fatal"unexpected case: result without return"inletsimplify_both(arg,formal)=trymatchLvalOrRef.from_expargwith|None->None|Somelv->Some(lv,Lval.simplifyformal)withExplicit_pointer_addressloc->Options.warning~source:(fstloc)~wkey:Options.Warn.unsupported_address"unsupported feature: explicit pointer address: %a; analysis may be unsound"Printer.pp_exparg;Noneinres_ret@List.filter_mapsimplify_both@@List.combineargsformalsin(* for each pair (lv1,lv2) find (or create) the corresponding vertices *)lets,arg_formal_vertex_pairs=lets=refsinletfind_vertex(lv1,lv2)=tryletv2=find_lval_vertexlv2summary_stateinletv1,new_state=find_or_create_lval_or_ref_vertexlv1!sins:=new_state;Some(v1,v2)withNot_found->Noneinletargs=List.filter_mapfind_vertexarg_formal_pairsin!s,argsinletfind_global(vi,v1)=tryignore@@Globals.Vars.findvi;Some(VarMap.findvis.varmap,v1)withNot_found->Noneinletglobal_pairs=List.filter_mapfind_global@@VarMap.bindingssummary_state.varmapinletvertex_pairs=arg_formal_vertex_pairs@global_pairsin(* merge the function graph;
for every arg/formal vertex pair (v1,v2) and every edge v2→v create edge v1→v. *)letg=lettransfer_succsg(v1,v2)=List.fold_left(funge->G.add_edge_eg@@E.createv1(E.labele)(E.dste))g(G.succ_esummary_state.graphv2)inletg=s.graphinletg=G.fold_vertex(funig->G.add_vertexgi)summary_state.graphginletg=G.fold_edges_e(funeg->G.add_edge_ege)summary_state.graphginList.fold_lefttransfer_succsgvertex_pairsin(* garbage collect: remove leaf vertices from g that originate from summary_state *)letvertices_added_to_g,g=letg=refginletremove_if_leafv_=ifG.in_degree!gv=0thenlet()=g:=G.remove_vertex!gvinNoneelseSomeVarSet.emptyinletremaining_vertices=VMap.filter_mapremove_if_leafsummary_state.vmapinremaining_vertices,!ginlets={graph=g;varmap=s.varmap;vmap=letleft_bias_l_=SomelinVMap.unionleft_biass.vmapvertices_added_to_g}inasserting_invariants(List.fold_leftjoin_succss@@List.mapfstvertex_pairs)moduleDot=structletfind_vars_ref=Extlib.mk_fun"find_vars"includeGraph.Graphviz.Dot(structincludeGletedge_attributes_=[]letdefault_edge_attributes_=[]letget_subgraph_=Noneletvertex_attributesv=letlset=!find_vars_refvinletlabel=VarSet.prettyFormat.str_formatterlset;Format.flush_str_formatter()in[`Labellabel]letvertex_namev=string_of_intvletdefault_vertex_attributes_=[`Shape`Box]letgraph_attributes_=[]end)endmoduleAPI=structtypet=statetypesummary=Summary.tletpretty_summary=Summary.prettyletmake_summary=Summary.makeletvidv:int=vletrecclosure_find_lsetvs:(V.t*LSet.t)list=matchG.psucc_opts.graphvwith|None->[v,Readout.get_lval_setvs]|Somev_next->(v,Readout.get_lval_setvs)::closure_find_lsetv_nextsletfind_transitive_closurelvs:(V.t*LSet.t)list=letlv=Lval.simplifylvinassert_invariantss;tryclosure_find_lset(find_lval_vertexlvs)swithNot_found->[](* TODO : what about offsets ? *)letget_lval_set=Readout.get_lval_setletfind_vars=Readout.find_varsletfind_synonyms=Readout.find_synonymsletalias_vars=Readout.alias_varsletalias_lvals=Readout.alias_lvalsletpoints_to_vars=Readout.points_to_varsletpoints_to_lvals=Readout.points_to_lvalsletalias_sets_vars=Readout.alias_sets_varsletalias_sets_lvals=Readout.alias_sets_lvalsletget_graphs=s.graphletprint_dotfilenames=letfile=open_outfilenameinDot.find_vars_ref:=(funv->get_varsvs);Dot.output_graphfiles.graph;close_outfileendincludeAPI