123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495(**************************************************************************)(* *)(* This file is part of WP plug-in of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* CEA (Commissariat a l'energie atomique et aux energies *)(* alternatives) *)(* *)(* 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, version 2.1. *)(* *)(* It 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. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file licenses/LGPLv2.1). *)(* *)(**************************************************************************)(* -------------------------------------------------------------------------- *)(* --- Variable Partitionning --- *)(* -------------------------------------------------------------------------- *)typevalidity=Valid|Nullabletypeparam=|NotUsed|ByAddr|ByValue|ByShift|ByRef|InContextofvalidity|InArrayofvalidityletpp_nullablefmt=function|Valid->()|Nullable->Format.pp_print_stringfmt" (nullable)"letpp_paramfmt=function|NotUsed->Format.pp_print_stringfmt"not used"|ByAddr->Format.pp_print_stringfmt"in heap"|ByValue->Format.pp_print_stringfmt"by value"|ByShift->Format.pp_print_stringfmt"by value with shift"|ByRef->Format.pp_print_stringfmt"by ref"|InContextn->Format.fprintffmt"in context%a"pp_nullablen|InArrayn->Format.fprintffmt"in array%a"pp_nullablen(* -------------------------------------------------------------------------- *)(* --- Separation Hypotheses --- *)(* -------------------------------------------------------------------------- *)openCil_typestypezone=|Varofvarinfo(* &x - the cell x *)|Ptrofvarinfo(* p - the cell pointed by p *)|Arrofvarinfo(* p+(..) - the cell and its neighbors pointed by p *)typepartition={globals:zonelist;(* [ &G , G[...], ... ] *)to_heap:zonelist;(* [ p, ... ] *)context:zonelist;(* [ p+(..), ... ] *)nullable:zonelist;(* [ p+(..), ... ] but can be NULL *)by_addr:zonelist;(* [ &(x + ..), ... ] *)}(* -------------------------------------------------------------------------- *)(* --- Partition --- *)(* -------------------------------------------------------------------------- *)letempty={globals=[];context=[];nullable=[];to_heap=[];by_addr=[];}letsetxpw=matchpwith|NotUsed->w|ByAddr->{wwithby_addr=Varx::w.by_addr}|ByRef->ifCil.isFunctionTypex.vtypethenwelse{wwithcontext=Ptrx::w.context}|InContextv->ifCil.isFunctionTypex.vtypethenwelsebeginmatchvwith|Nullable->{wwithnullable=Ptrx::w.nullable}|Valid->{wwithcontext=Ptrx::w.context}end|InArrayv->ifCil.isFunctionTypex.vtypethenwelsebeginmatchvwith|Nullable->{wwithnullable=Arrx::w.nullable}|Valid->{wwithcontext=Arrx::w.context}end|ByValue|ByShift->ifx.vghostthenwelseifCil.isFunctionTypex.vtypethenwelseifx.vglob&&(x.vstorage<>Static||x.vaddrof)thenletz=ifCil.isArrayTypex.vtypethenArrxelseVarxin{wwithglobals=z::w.globals}elseifx.vformal&&Cil.isPointerTypex.vtypethenletz=ifp=ByShiftthenArrxelsePtrxin{wwithto_heap=z::w.to_heap}elsew(* -------------------------------------------------------------------------- *)(* --- Building Annotations --- *)(* -------------------------------------------------------------------------- *)openLogic_constletrecptr_of=function|Ctypet->Ctype(TPtr(t,[]))|twhenLogic_typing.is_set_typet->lett=Logic_typing.type_of_set_elemtinLogic_const.make_set_type(ptr_oft)|_->assertfalseletrecaddr_of_lval?locterm=lettyp=ptr_ofterm.term_typeinmatchterm.term_nodewith|TLvallv->Logic_utils.mk_logic_AddrOf?loclvtyp|TCastE(_,t)|TLogic_coerce(_,t)->addr_of_lval?loct|Tif(c,t,e)->lett=addr_of_lval?loctinlete=addr_of_lval?loceinLogic_const.term?loc(Tif(c,t,e))typ|Tat(_,_)->term|Tunionl->letl=List.map(addr_of_lval?loc)linLogic_const.term?loc(Tunionl)typ|Tinterl->letl=List.map(addr_of_lval?loc)linLogic_const.term?loc(Tinterl)typ|Tcomprehension(t,qs,p)->lett=addr_of_lval?loctinLogic_const.term?loc(Tcomprehension(t,qs,p))typ|_->termlettype_of_zone=function|Ptrvi->vi.vtype|Varvi->TPtr(vi.vtype,[])|ArrviwhenCil.isPointerTypevi.vtype->vi.vtype|Arrvi->TPtr(Cil.typeOf_array_elemvi.vtype,[])letzone_to_term?(to_char=false)loczone=lettyp=Ctype(type_of_zonezone)inletlvalvi=TVar(Cil.cvar_to_lvarvi),TNoOffsetinletloc_rangeptr=ifnotto_charthenptrelseletpointed=matchtypwith|(Ctype(TPtr(t,[])))->t|_->assertfalse(* typ has been generated by type_of_zone *)inletlen=Logic_utils.expr_to_term(Cil.sizeOf~locpointed)inletlast=term(TBinOp(MinusA,len,tinteger~loc1))len.term_typeinletrange=trange~loc(Some(tinteger~loc0),Somelast)inletptr=Logic_utils.mk_cast~locCil.charPtrTypeptrinterm~loc(TBinOp(PlusPI,ptr,range))ptr.term_typeinmatchzonewith|Varvi->loc_range(term~loc(TAddrOf(lvalvi))typ)|Ptrvi->loc_range(term~loc(TLval(lvalvi))typ)|Arrvi->letptr=ifCil.isArrayTypevi.vtypethenterm~loc(TStartOf(lvalvi))typelseterm~loc(TLval(lvalvi))typinletptr=ifnotto_charthenptrelseLogic_utils.mk_cast~locCil.charPtrTypeptrinletrange=trange~loc(None,None)interm~loc(TBinOp(PlusPI,ptr,range))ptr.term_typeletregion_to_termloc=function|[]->term~locTempty_set(CtypeCil.charPtrType)|[z]->zone_to_termlocz|x::tlasl->letfst=type_of_zonexinlettl=List.maptype_of_zonetlinletto_char=not(List.for_all(Cil_datatype.Typ.equalfst)tl)inletset_typ=make_set_type(Ctype(ifto_charthenCil.charPtrTypeelsefst))interm~loc(Tunion(List.map(zone_to_term~to_charloc)l))set_typletseparated_list?loc=function|[]|[_]->ptrue|l->letcomp=Cil_datatype.Term.compareinpseparated?loc(List.sortcompl)letterm_separated_from_regionslocassignedl=separated_list~loc(assigned::List.map(region_to_termloc)l)letvalid_regionlocr=lett=region_to_termlocrinpvalid~loc(here_label,t)letvalid_or_null_regionlocr=lett=region_to_termlocrinletnull=term~locTnullt.term_typeinpor(valid_regionlocr,prel~loc(Req,t,null))letrecrankp=matchp.pred_contentwith|Pvalid_->1|Pseparated_->2|Pimplies(_,p)->rankp|Por(p,q)|Pand(p,q)->max(rankp)(rankq)|_->0letcomparepq=letr=rankp-rankqinifr<>0thenrelseLogic_utils.compare_predicatepqletnormalizeps=List.sort_uniqcompare@@List.filter(funp->not(Logic_utils.is_trivially_truep))psletptrset{term_type=t}=letopenLogic_typinginis_pointer_typet||(is_set_typet&&is_pointer_type(type_of_elementt))(* -------------------------------------------------------------------------- *)(* --- Partition Helpers --- *)(* -------------------------------------------------------------------------- *)letwelltypedzones=letrecpartition_by_typetaccl=matchl,accwith|[],_->acc|x::l,[]->partition_by_type(type_of_zonex)[[x]]l|x::l,p::acc'whenCil_datatype.Typ.equalt(type_of_zonex)->partition_by_typet((x::p)::acc')l|x::l,acc->partition_by_type(type_of_zonex)([x]::acc)linletcompare_zoneab=Cil_datatype.Typ.compare(type_of_zonea)(type_of_zoneb)inpartition_by_typeCil.voidType[](List.sortcompare_zonezones)letglobal_zonespartition=List.map(funz->[z])partition.globalsletcontext_zonespartition=List.map(funz->[z])partition.contextletnullable_zonespartition=List.map(funz->[z])partition.nullableletheapspartition=welltypedpartition.to_heapletaddr_of_varspartition=welltypedpartition.by_addr(* -------------------------------------------------------------------------- *)(* --- Computing Separation --- *)(* -------------------------------------------------------------------------- *)(* Memory regions shall be separated with each others *)letmain_separationlocglobalscontextnullableheaps=ifcontext=[]&&nullable=[]&&heaps=[]then[]elseletl_zones=matchheapswith|[]->[globals@context]|heaps->List.map(funh->h::(globals@context))heapsinletregions_to_terms=List.map(region_to_termloc)inletguard_nullabletnsep=pimplies~loc(prel~loc(Rneq,tn,term~locTnulltn.term_type),sep)inletacc_with_nullabletnzones=List.cons@@guard_nullabletn(separated_list~loc(tn::regions_to_termszones))inletno_nullablezones=separated_list~loc@@regions_to_termszonesinletnullable_internullable=letseparated_nullable(p,q)=guard_nullablep@@guard_nullableq@@pseparated~loc[p;q]inletreccollect_pairs=function(* trivial cases *)|[]->[]|[_]->[]|p::l->letacc_sepq=List.cons@@separated_nullable(p,q)inList.fold_rightacc_sepl@@collect_pairslincollect_pairsnullableinmatchnullablewith|[]->List.mapno_nullablel_zones|nullable->lett_nullable=regions_to_termsnullableinletsep_nullable=nullable_intert_nullableinletfoldn=List.fold_right(acc_with_nullablen)l_zonesinList.fold_rightfoldt_nullablesep_nullable(* Filter assigns *)letassigned_locationskffilter=letadd_froml(e,_ds)=iffiltere.it_contentthene::lelselinletadd_assign_emitterassignsl=matchassignswith|WritesAny->l|Writesfroms->List.fold_leftadd_fromlfromsinAnnotations.fold_assignsadd_assignkfCil.default_behavior_name[](* Locations assigned by pointer from a call *)letassigned_via_pointerskf=letrecassigned_via_pointert=matcht.term_nodewith|TLval(TMem_,_)->true|TCastE(_,t)|TLogic_coerce(_,t)|Tcomprehension(t,_,_)|Tat(t,_)->assigned_via_pointert|Tunionl|Tinterl->List.existsassigned_via_pointerl|Tif(_,t1,t2)->assigned_via_pointert1||assigned_via_pointert2|_->falseinassigned_locationskfassigned_via_pointer(* Checks whether a term refers to Post *)letpost_termt=letexceptionPost_valueinletv=objectinheritCil.nopCilVisitormethod!vlogic_label=function|BuiltinLabelPost->raisePost_value|_->Cil.SkipChildrenmethod!vterm_lval=function|TResult_,_->raisePost_value|_->Cil.DoChildrenendintryignore(Cil.visitCilTermvt);falsewithPost_value->true(* Computes conditions from call assigns *)letassigned_separationkflocglobals=letaddr_oft=addr_of_lval~loct.it_contentinletasgnd_ptrs=List.mapaddr_of(assigned_via_pointerskf)inletfolder(req,ens)t=letsep=term_separated_from_regionsloctglobalsinifpost_termtthen(req,sep::ens)else(sep::req,ens)inList.fold_leftfolder([],[])asgnd_ptrs(* Computes conditions from partition *)letclauses_of_partitionkflocp=letglobals=global_zonespinletmain_sep=main_separationlocglobals(context_zonesp)(nullable_zonesp)(heapsp)inletassigns_sep_req,assigns_sep_ens=assigned_separationkflocglobalsinletcontext_valid=List.map(valid_regionloc)(context_zonesp)inletnullable_valid=List.map(valid_or_null_regionloc)(nullable_zonesp)inletreqs=main_sep@assigns_sep_req@context_valid@nullable_validinletreqs=normalizereqsinletens=normalizeassigns_sep_ensinreqs,ens(* Computes conditions from return *)letout_pointers_separationkflocp=letret_t=Kernel_function.get_return_typekfinletaddr_oft=addr_of_lval~loct.it_contentinletassigned_ptrt=(* Search assigned pointers via a pointer,
e.g. 'assigns *p ;' with '*p' of type pointer or set of pointers *)ifptrsett.it_contentthenSome(addr_oft)elseNoneinletasgnd_ptrs=List.filter_mapassigned_ptr(assigned_via_pointerskf)inletasgnd_ptrs=ifCil.isPointerTyperet_tthentresult~locret_t::asgnd_ptrselseasgnd_ptrsinletformals_separation=letformal_zone=functionVarv->v.vformal|_->falseinletformal_partition={pwithby_addr=List.filterformal_zonep.by_addr}inletformals=addr_of_varsformal_partitioninList.map(funt->term_separated_from_regionsloctformals)asgnd_ptrsinletglobals_separation=letglobals=global_zonespinList.map(funt->term_separated_from_regionsloctglobals)asgnd_ptrsinnormalize(formals_separation@globals_separation)(* Computes all conditions from behavior *)letcompute_behaviorkfnamehypotheses_computer=letpartition=hypotheses_computerkfinletloc=Kernel_function.get_locationkfinletreqs,ens=clauses_of_partitionkflocpartitioninletens=out_pointers_separationkflocpartition@ensinletreqs=List.mapnew_predicatereqsinletens=List.map(funp->Normal,new_predicatep)ensinmatchreqs,enswith|[],[]->None|reqs,ens->Some{b_name=Annotations.fresh_behavior_namekf("wp_"^name);b_requires=reqs;b_assumes=[];b_post_cond=ens;b_assigns=WritesAny;b_allocation=FreeAllocAny;b_extended=[]}(* -------------------------------------------------------------------------- *)(* --- Memoization --- *)(* -------------------------------------------------------------------------- *)moduleTable=State_builder.Hashtbl(Cil_datatype.Kf.Hashtbl)(Datatype.Option(Cil_datatype.Funbehavior))(structletname="MemoryContext.Table"letsize=17letdependencies=[Ast.self]end)moduleRegisteredHypotheses=State_builder.Set_ref(Cil_datatype.Kf.Set)(structletname="Wp.MemoryContext.RegisteredHypotheses"letdependencies=[Ast.self]end)letcomputenamehypotheses_computer=Globals.Functions.iter(funkf->ignore(compute_behaviorkfnamehypotheses_computer))letget_behaviorkfnamehypotheses_computer=Table.memobeginfunkf->AssignsCompleteness.warnkf;compute_behaviorkfnamehypotheses_computerendkf(* -------------------------------------------------------------------------- *)(* --- External API --- *)(* -------------------------------------------------------------------------- *)letprint_memory_contextkfbhvfmt=beginletprinter=newPrinter.extensible_printer()inletpp_vdecl=printer#without_annotprinter#vdeclinFormat.fprintffmt"@[<hv 0>@[<hv 3>/*@@@ %a"Cil_printer.pp_behaviorbhv;letvkf=Kernel_function.get_vikfinFormat.fprintffmt"@ @]*/@]@\n@[<hov 2>%a;@]@\n"pp_vdeclvkf;endletwarnkfnamehyp_computer=matchget_behaviorkfnamehyp_computerwith|None->()|Somebhv->Wp_parameters.warning~current:false~once:true~source:(fst(Kernel_function.get_locationkf))"@[<hv 0>Memory model hypotheses for function '%s':@ %t@]"(Kernel_function.get_namekf)(print_memory_contextkfbhv)letemitter=Emitter.(create"Wp.Hypotheses"[Funspec]~correctness:[]~tuning:[])letadd_behaviorkfnamehypotheses_computer=ifRegisteredHypotheses.memkfthen()elsebeginbeginmatchget_behaviorkfnamehypotheses_computerwith|None->()|Somebhv->Annotations.add_behaviorsemitterkf[bhv]end;RegisteredHypotheses.addkfend(* -------------------------------------------------------------------------- *)