123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* 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). *)(* *)(**************************************************************************)openCil_typesopenCilopenCil_datatypeopenAbstract_interpopenLocationsexceptionCall_did_not_take_placemoduleRecord_From_Callbacks=Hook.Build(structtypet=(Kernel_function.tStack.t)*Function_Froms.Memory.tStmt.Hashtbl.t*(Kernel_function.t*Function_Froms.Memory.t)listStmt.Hashtbl.tend)moduletypeTo_Use=sigvalget_from_call:kernel_function->stmt->Function_Froms.tvalstmt_request:stmt->Eva.Results.requestvalkeep_base:kernel_function->Base.t->boolvalcleanup_and_save:kernel_function->Function_Froms.t->Function_Froms.tendletcompute_using_prototype_for_statestatekfassigns=letvarinfo=Kernel_function.get_vikfinletreturn_deps,deps=matchassignswith|WritesAny->From_parameters.warning"@[no assigns clauses@ for function %a.@]@ \
Results will be imprecise."Kernel_function.prettykf;Function_Froms.Memory.(top_return,top)|Writesassigns->let(rt_typ,_,_,_)=splitFunctionTypeVIvarinfoinletinput_zoneoutins=(* Technically out is unused, but there is a signature problem *)Eva.Logic_inout.assigns_inputs_to_zonestate(Writes[out,ins])inlettreat_assignacc(out,ins)=letoutput=Eva.Logic_inout.assigns_tlval_to_zonesstateWriteout.it_contentinmatchoutputwith|None->From_parameters.result~once:true~current:true"Unable to extract assigns in %a"Kernel_function.prettykf;acc|Someoutput->letinput_zone=input_zoneoutinsin(* assign clauses do not let us specify address
dependencies for now, so we assume it is all data
dependencies *)letinput_deps=Function_Froms.Deps.from_data_depsinput_zonein(* Weak update of the over-approximation of the zones assigned *)letacc=Function_Froms.Memory.add_binding~exact:falseaccoutput.overinput_depsin(* Now, perform a strong update on the zones that are guaranteed
to be assigned (under-approximation) AND that do not depend
on themselves.
Note: here we remove an overapproximation from an
underapproximation to get an underapproximation, which is not
the usual direction. It works here because diff on non-top
zones is an exact operation. *)letsure_out_zone=Zone.(ifequaltopinput_zonethenbottomelsediffoutput.underinput_zone)inletacc=Function_Froms.Memory.add_binding~exact:trueaccsure_out_zoneinput_depsinaccinlettreat_ret_assignacc(out,from)=letzone_from=input_zoneoutfromin(* assign clauses do not let us specify address dependencies for
now, so we assume it is all data dependencies *)letinputs_deps=Function_Froms.Deps.from_data_depszone_fromintryletcoffs=Logic_to_c.loc_to_offsetout.it_contentinList.fold_left(funacccoff->let(base,width)=bitsOffsetrt_typcoffinletsize=Int_Base.inject(Int.of_intwidth)inFunction_Froms.Memory.(add_to_return~start:base~size~m:accinputs_deps))acccoffswithLogic_to_c.No_conversion|SizeOfError_->From_parameters.result~once:true~current:true"Unable to extract a proper offset. \
Using FROM for the whole \\result";letsize=Bit_utils.sizeofrt_typinFunction_Froms.(Memory.add_to_return~size~m:accinputs_deps)inletreturn_assigns,other_assigns=List.fold_left(fun(ra,oa)(loc,_asa)->ifLogic_utils.is_resultloc.it_contentthena::ra,oaelsera,a::oa)([],[])assignsinletreturn_assigns=matchreturn_assignswith|[]whenCil.isVoidTypert_typ->Function_Froms.Memory.default_return|[]->(* \from unspecified. *)letsize=Bit_utils.sizeofrt_typinFunction_Froms.Memory.top_return_sizesize|_->List.fold_lefttreat_ret_assignFunction_Froms.Memory.default_returnreturn_assignsinreturn_assigns,List.fold_lefttreat_assignFunction_Froms.Memory.emptyother_assignsin{deps_return=return_deps;Function_Froms.deps_table=deps}moduleZoneStmtMap=structincludeHptmap.Make(Stmt_Id)(Zone)(Hptmap.Comp_unused)(structletv=[[]]end)(structletl=[Ast.self]end)letjoin=letdecide_kz1z2=Zone.joinz1z2injoin~cache:(Hptmap_sig.PersistentCache"From_compute.ZoneStmtMap.join")~symmetric:true~idempotent:true~decideendmoduleMake(To_Use:To_Use)=structtypet'={additional_deps_table:ZoneStmtMap.t;(** Additional control dependencies to add to all modified variables,
coming from the control statements encountered so far (If, Switch).
The statement information is used to remove the dependencies that
are no longer useful, when we reach a statement that post-dominates
the statement that gave rise to the dependency. *)additional_deps:Zone.t;(** Union of the sets in {!additional_deps_table} *)deps_table:Function_Froms.Memory.t(** dependency table *)}letcall_stack:kernel_functionStack.t=Stack.create()(** Stack of function being processed *)(** Recreate the [additional_deps] field from [additional_deps_table] *)letrebuild_additional_depsmap=ZoneStmtMap.fold(fun_zaccz->Zone.joinzaccz)mapZone.bottom(** given a [Function_Froms.Deps.t], apply [f] on both components and merge
the result:
depending directly on an indirect dependency -> indirect,
depending indirectly on a direct dependency -> indirect *)letmerge_depsfdeps=letopenFunction_Froms.Depsinletind=fdeps.indirectinletdata=fdeps.datainletind=Zone.joindata.indirect(to_zoneind)inletdata=data.datain{data=data;indirect=ind}(** Bind all the variables of [b] to [Assigned \from \nothing]. This function
is always called on local variables. We do *not* want to bind a local
variable [v] to Unassigned, as otherwise we could get some dependencies
that refer to [v] (when [v] is not guaranteed to be always assigned, or
for padding in local structs), and that would need to be removed when v
goes out of scope. Moreover, semantically, [v] *is* assigned (albeit to
"uninitialized", which represents an indefinite part of the stack). We
do not attempts to track this "uninitialized" information in From, as this
is redundant with the work done by Value -- hence the use of [\nothing].*)letbind_localsmb=letaux_localaccvi=Cil.CurrentLoc.setvi.vdecl;(* Consider that local are initialized to a constant value *)Function_Froms.Memory.bind_varviFunction_Froms.Deps.bottomaccinletloc=Cil.CurrentLoc.get()inletr=List.fold_leftaux_localmb.blocalsinCil.CurrentLoc.setloc;rletunbind_localsmb=letaux_localaccvi=Function_Froms.Memory.unbind_varviaccinList.fold_leftaux_localmb.blocalsletfindstmtdeps_tblexpr=letrequest=To_Use.stmt_requeststmtinletpre_trans=Eva.Results.expr_dependenciesexprrequestinmerge_deps(fund->Function_Froms.Memory.find_precisedeps_tbld)pre_transletlval_to_zone_with_depsstmtlv=letrequest=To_Use.stmt_requeststmtinEva.Results.lval_depslvrequestletlval_to_precise_loc_with_depsstmt~for_writinglv=letrequest=To_Use.stmt_requeststmtinletdeps=Eva.Results.address_depslvrequestinletaddress=Eva.Results.eval_address~for_writinglvrequestinletloc=Eva.Results.as_precise_locaddressandexact=Eva.Results.(is_singletonaddress||is_bottomaddress)indeps,loc,exactletempty_from={additional_deps_table=ZoneStmtMap.empty;additional_deps=Zone.bottom;deps_table=Function_Froms.Memory.empty}letbottom_from={additional_deps_table=ZoneStmtMap.empty;additional_deps=Zone.bottom;deps_table=Function_Froms.Memory.bottom}moduleComputer=structtypet=t'letbottom=bottom_from;;letcallwise_states_with_formals=Stmt.Hashtbl.create7letsubstitutecall_site_fromsextra_locdeps=letsubst_deps=Function_Froms.Memory.substitutecall_site_fromsdepsinFunction_Froms.Deps.add_indirect_depsubst_depsextra_locletdisplay_one_fromfmtv=Function_Froms.Memory.prettyfmtv.deps_table;Format.fprintffmt"Additional Variable Map : %a@\n"ZoneStmtMap.prettyv.additional_deps_table;Format.fprintffmt"Additional Variable Map Set : %a@\n"Zone.prettyv.additional_depsletprettyfmt(v:t)=display_one_fromfmtvlettransfer_conditional_expsexpstate=letadditional=findsstate.deps_tableexpinletadditional=Function_Froms.Deps.to_zoneadditionalin{statewithadditional_deps_table=ZoneStmtMap.addsadditionalstate.additional_deps_table;additional_deps=Zone.joinadditionalstate.additional_deps}letjoin_and_is_includednew_old=letadditional_map,additional_zone,included=letmold=old.additional_deps_tableinletmnew=new_.additional_deps_tableinletzold=old.additional_depsinletm=ZoneStmtMap.joinmnewmoldinifZoneStmtMap.equalmmoldthenmold,zold,trueelseletnew_z=Zone.joinold.additional_depsnew_.additional_depsinm,new_z,falseinletmap=Function_Froms.Memory.joinnew_.deps_tableold.deps_tableinletincluded'=Function_Froms.Memory.is_includednew_.deps_tableold.deps_tablein{deps_table=map;additional_deps_table=additional_map;additional_deps=additional_zone;},included&&included'letjoinoldnew_=fst(join_and_is_includedoldnew_)letis_includedoldnew_=snd(join_and_is_includedoldnew_)(** Handle an assignment [lv = ...], the dependencies of the right-hand
side being stored in [deps_right]. [init] is true for a local
initialization, in which case the left location is not reduced to its
valid part for a writing, in order to keep the const local variables. *)lettransfer_assignstmt~initlvdeps_rightstate=(* The assigned location is [loc], whose address is computed from
[deps]. *)letdeps,loc,exact=lval_to_precise_loc_with_depsstmt~for_writing:(notinit)lvinletdeps_of_deps=Function_Froms.Memory.findstate.deps_tabledepsinletall_indirect=Zone.joinstate.additional_depsdeps_of_depsinletdeps=Function_Froms.Deps.add_indirect_depdeps_rightall_indirectinletaccess=ifinitthenReadelseWritein{statewithdeps_table=Function_Froms.Memory.add_binding_precise_loc~exactaccessstate.deps_tablelocdeps}lettransfer_callstmtdestfargs_locstate=Db.yield();letrequest=To_Use.stmt_requeststmtinletcalled_vinfos=Eva.Results.(eval_calleefrequest|>default[])inletf_deps=Eva.Results.expr_depsfrequestin(* dependencies for the evaluation of [f] *)letf_deps=Function_Froms.Memory.findstate.deps_tablef_depsinletadditional_deps=Zone.joinstate.additional_depsf_depsinletargs_froms=List.map(funarg->(* TODO : dependencies on subfields for structs *)findstmtstate.deps_tablearg)argsinletstates_with_formals=ref[]inletdo_onkf=letcalled_vinfo=Kernel_function.get_vikfinifAst_info.is_frama_c_builtincalled_vinfo.vnamethenstateelseletfroms_call=To_Use.get_from_callkfstmtinletfroms_call_table=froms_call.Function_Froms.deps_tableinifFunction_Froms.Memory.is_bottomfroms_call_tablethenbottom_fromelseletformal_args=Kernel_function.get_formalskfinletstate_with_formals=refstate.deps_tableinbegintryList.iter2(funvifrom->state_with_formals:=Function_Froms.Memory.bind_varvifrom!state_with_formals;)formal_argsargs_froms;withInvalid_argument_->From_parameters.warning~once:true~current:true"variadic call detected. Using only %d argument(s)."(min(List.lengthformal_args)(List.lengthargs_froms))end;ifnot(Record_From_Callbacks.is_empty())thenstates_with_formals:=(kf,!state_with_formals)::!states_with_formals;letsubst_before_call=substitute!state_with_formalsadditional_depsin(* From state just after the call,
but before the result assignment *)letdeps_after_call=letbefore_call=state.deps_tableinletopenFunction_Fromsinletsubstd=DepsOrUnassigned.substsubst_before_calldinletcall_substituted=Memory.mapsubstfroms_call_tableinMemory.composecall_substitutedbefore_callinletstate={statewithdeps_table=deps_after_call}in(* Treatement for the possible assignment
of the call result *)matchdestwith|None->state|Somelv->letreturn_from=froms_call.Function_Froms.deps_returninletdeps_ret=subst_before_callreturn_frominletinit=Cil.is_mutable_or_initializedlvintransfer_assignstmt~initlvdeps_retstateinletfaccf=letp=do_onfinmatchaccwith|None->Somep|Someacc_memory->Some{statewithdeps_table=Function_Froms.Memory.joinp.deps_tableacc_memory.deps_table}inletresult=try(matchList.fold_leftfNonecalled_vinfoswith|None->state|Somes->s);withCall_did_not_take_place->stateinifnot(Record_From_Callbacks.is_empty())thenStmt.Hashtbl.replacecallwise_states_with_formalsstmt!states_with_formals;resultlettransfer_instrstmt(i:instr)(state:t)=Db.yield();matchiwith|Set(lv,exp,_)->letcomp_vars=findstmtstate.deps_tableexpinletinit=Cil.is_mutable_or_initializedlvintransfer_assignstmt~initlvcomp_varsstate|Local_init(v,AssignIniti,_)->letrecauxlviacc=letdoinitoi_state=aux(Cil.addOffsetLvalolv)istateinmatchiwith|SingleInite->letcomp_vars=findstmtacc.deps_tableeintransfer_assignstmt~init:truelvcomp_varsacc|CompoundInit(ct,initl)->(* To avoid a performance issue, do not fold implicit initializers
of scalar or large arrays. We still use implicit initializers
for small struct arrays, as this may be more precise in case of
padding bits. The 100 limit is arbitrary. *)letimplicit=not(Cil.isArrayTypect&&(Cil.isArithmeticOrPointerType(Cil.typeOf_array_elemct)||Ast_info.array_sizect>(Integer.of_int100)))inletr=Cil.foldLeftCompound~implicit~doinit~ct~initl~accinifimplicitthenrelse(* If implicit zero-initializers have been skipped, also mark
the entire array as initialized from no dependency (nothing
is read by the implicit zero-initializers). *)transfer_assignstmt~init:truelvFunction_Froms.Deps.bottomrinaux(Cil.varv)istate|Call(lvaloption,funcexp,argl,loc)->transfer_callstmtlvaloptionfuncexpargllocstate|Local_init(v,ConsInit(f,args,kind),loc)->Cil.treat_constructor_as_func(transfer_callstmt)vfargskindlocstate|Asm_|Code_annot_|Skip_->statelettransfer_guardsed=letrequest=To_Use.stmt_requestsinletinterpreted_e=Eva.Results.(eval_experequest|>as_cvalue)inlett1=unrollType(typeOfe)inletdo_then,do_else=ifisIntegralTypet1||isPointerTypet1thenCvalue.V.contains_non_zerointerpreted_e,Cvalue.V.contains_zerointerpreted_eelsetrue,true(* TODO: a float condition is true iff != 0.0 *)in(ifdo_thenthendelsebottom),(ifdo_elsethendelsebottom);;(* Eliminate additional variables originating from a control-flow branching
statement closing at [s]. *)leteliminate_additionalsdata=letkf=Stack.topcall_stackinletmap=data.additional_deps_tableinletmap'=ZoneStmtMap.fold(funk_vacc_map->if!Db.Postdominators.is_postdominatorkf~opening:k~closing:sthenZoneStmtMap.removekacc_mapelseacc_map)mapmapinifnot(map==map')then{datawithadditional_deps_table=map';additional_deps=rebuild_additional_depsmap';}elsedatalettransfer_stmtsdata=letdata=eliminate_additionalsdatainletmap_on_all_succsnew_data=List.map(funx->(x,new_data))s.succsinmatchs.skindwith|Instri->map_on_all_succs(transfer_instrsidata)|If(exp,_,_,_)->letdata=transfer_conditional_expsexpdatainDataflows.transfer_if_from_guardtransfer_guardsdata|Switch(exp,_,_,_)->letdata=transfer_conditional_expsexpdatainDataflows.transfer_switch_from_guardtransfer_guardsdata|Return_|Throw_->[]|UnspecifiedSequence_|Loop_|Block_|Goto_|Break_|Continue_|TryExcept_|TryFinally_|TryCatch_->map_on_all_succsdata;;(* Filter out unreachable values. *)lettransfer_stmtsd=ifEva.Results.is_reachables&¬(Function_Froms.Memory.is_bottomd.deps_table)thentransfer_stmtsdelse[]letdoEdgessuccd=ifEva.Results.is_reachablesuccthenletdt=d.deps_tableinletopened=Kernel_function.blocks_opened_by_edgessuccinletclosed=Kernel_function.blocks_closed_by_edgessuccinletdt=List.fold_leftbind_localsdtopenedinletdt=List.fold_leftunbind_localsdtclosedin{dwithdeps_table=dt}elsebottom_from(* Filter the outgoing data using doEdge. *)lettransfer_stmtsd=letds=transfer_stmtsdinList.map(fun(succ,d)->(succ,doEdgessuccd))ds;;end(* Remove all local variables and formals from table *)letexternalizereturnkfstate=letdeps_return=(matchreturn.skindwith|Return(Some({enode=Lvalv}),_)->letzone=lval_to_zone_with_depsreturnvinletdeps=Function_Froms.Memory.find_precisestate.deps_tablezoneinletsize=Bit_utils.sizeof(Cil.typeOfLvalv)inFunction_Froms.(Memory.add_to_return~sizedeps)|Return(None,_)->Function_Froms.Memory.default_return|_->assertfalse)inletaccept=To_Use.keep_basekfinletdeps_table=Function_Froms.Memory.filter_baseacceptstate.deps_tablein{deps_return=deps_return;Function_Froms.deps_table=deps_table}letcompute_using_cfgkf=matchkf.fundecwith|Declaration_->assertfalse|Definition(f,_)->ifnot(Eva.Analysis.save_resultskf)thenFunction_Froms.topelsetryStack.iter(fung->ifkf==gthenraiseExit)call_stack;Stack.pushkfcall_stack;letstate={empty_fromwithdeps_table=bind_localsempty_from.deps_tablef.sbody}inletmoduleFenv=(valDataflows.function_envkf:Dataflows.FUNCTION_ENV)inletmoduleDataflow_arg=structincludeComputerletinit=[(Kernel_function.find_first_stmtkf,state)]endinletmoduleCompute=Dataflows.Simple_forward(Fenv)(Dataflow_arg)inletret_id=Kernel_function.find_returnkfinifnot(Record_From_Callbacks.is_empty())thenbeginFrom_parameters.feedback"Now calling From callbacks";letstates=Stmt.Hashtbl.createFenv.nb_stmtsinCompute.iter_on_result(funkrecord->Stmt.Hashtbl.addstateskrecord.deps_table);Record_From_Callbacks.apply(call_stack,states,Dataflow_arg.callwise_states_with_formals)end;let_poped=Stack.popcall_stackinletlast_from=tryifEva.Results.is_reachableret_idthenexternalizeret_idkfCompute.before.(Fenv.to_orderedret_id)elseraiseNot_foundwithNot_found->beginFrom_parameters.result"Non-terminating function %a (no dependencies)"Kernel_function.prettykf;{Function_Froms.deps_return=Function_Froms.Memory.default_return;deps_table=Function_Froms.Memory.bottom}endinlast_fromwithExit(* Recursive call *)->{Function_Froms.deps_return=Function_Froms.Memory.default_return;deps_table=Function_Froms.Memory.empty}letcompute_using_prototypekf=letstate=Eva.Results.(at_start_ofkf|>get_cvalue_model)inletbehaviors=Eva.Logic_inout.valid_behaviorskfstateinletassigns=Ast_info.merge_assignsbehaviorsincompute_using_prototype_for_statestatekfassignsletcompute_and_returnkf=letcall_site_loc=CurrentLoc.get()inFrom_parameters.feedback"Computing for function %a%s"Kernel_function.prettykf(lets=ref""inStack.iter(funkf->s:=!s^" <-"^(Format.asprintf"%a"Kernel_function.prettykf))call_stack;!s);Db.yield();letresult=ifEva.Analysis.use_spec_instead_of_definitionkfthencompute_using_prototypekfelsecompute_using_cfgkfinletresult=To_Use.cleanup_and_savekfresultinFrom_parameters.feedback"Done for function %a"Kernel_function.prettykf;Db.yield();CurrentLoc.setcall_site_loc;resultletcomputekf=Eva.Analysis.compute();ignore(compute_and_returnkf)end(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)