123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636(**************************************************************************)(* *)(* SPDX-License-Identifier LGPL-2.1 *)(* Copyright (C) *)(* CEA (Commissariat à l'énergie atomique et aux énergies alternatives) *)(* *)(**************************************************************************)(** Server requests about the values inferred by the Eva analysis. *)openServeropenDataopenCil_typesmoduleKmap=Kernel_function.HashtblmoduleSmap=Cil_datatype.Stmt.HashtblmoduleCSet=Callstack.SetmoduleCSmap=Callstack.HashtblmoduleMd=MarkdownmoduleJdecl=Kernel_ast.DeclmoduleJstmt=Kernel_ast.StmtmoduleJmarker=Kernel_ast.Markerletpackage=lettitle="Values inferred by the Eva analysis"inPackage.package~plugin:"eva"~name:"values"~title()(* ----- Simple values information for the Inspector ------------------------ *)typeevaluation_point=|Initial|Preofkernel_function|Stmtofkernel_function*stmtletpostkf=ifAnalysis.use_spec_instead_of_definitionkfthenraiseNot_foundelsetryStmt(kf,Kernel_function.find_returnkf)withKernel_function.No_Statement->raiseNot_foundletrequest_at=function|Initial->Results.at_start|Stmt(_,stmt)->Results.beforestmt|Prekf->Results.at_start_ofkfletproperty_evaluation_point=function|Property.IPCodeAnnot{ica_kf=kf;ica_stmt=stmt}|IPPropertyInstance{ii_kf=kf;ii_stmt=stmt}->Stmt(kf,stmt)|IPPredicate{ip_kf;ip_kind=PKEnsures(_,Normal)}->postip_kf|IPPredicate{ip_kf=kf;ip_kind=PKRequires_|PKAssumes_|PKTerminates}|IPAssigns{ias_kf=kf}|IPFrom{if_kf=kf}->Prekf|IPPredicate_|IPComplete_|IPDisjoint_|IPDecrease_|IPAxiomatic_|IPModule_|IPLemma_|IPTypeInvariant_|IPGlobalInvariant_|IPOther_|IPAllocation_|IPReachable_|IPExtended_|IPBehavior_->raiseNot_foundletmarker_evaluation_point=function|Printer_tag.PGlobal_->Initial|PStmt(kf,stmt)|PStmtStart(kf,stmt)->Stmt(kf,stmt)|PVDecl(kf,kinstr,v)whennot(v.vformal||v.vglob)->begin(* Only evaluate declaration of local variable if it is initialized. *)matchkf,kinstrwith|Somekf,Kstmt({skind=Instr(Local_init_)}ass)->Stmt(kf,s)|_->raiseNot_foundend|PLval(kf,ki,_)|PExp(kf,ki,_)|PVDecl(kf,ki,_)->beginmatchkf,kiwith|Somekf,Kstmtstmt->Stmt(kf,stmt)|Somekf,Kglobal->Prekf|None,Kglobal->Initial|None,Kstmt_->assertfalseend|PTermLval(_,_,prop,_)|PIPprop->property_evaluation_pointprop|PType_->raiseNot_foundletterm_lval_to_lvalkftlval=tryletresult=Option.bindEva_utils.find_return_varkfinLogic_to_c.term_lval_to_lval?resulttlvalwithLogic_to_c.No_conversion->raiseNot_found(* Returns the server identifier of marker corresponding to varinfo [vi]
at [eval_point]. *)lettag_varinfo_as_lval_ateval_point=letkf,kinstr=matcheval_pointwith|Initial->None,Kglobal|Prekf->Somekf,Kglobal|Stmt(kf,stmt)->Somekf,Kstmtstmtinfunvi->letmarker=Printer_tag.PLval(kf,kinstr,Cil.varvi)inServer.Kernel_ast.Marker.indexmarker(* Executes function [f] with an updated global printer which prints
any varinfo as a lvalue marker at the given evaluation point. *)letwith_updated_varinfo_printereval_pointf=lettag_vi=tag_varinfo_as_lval_ateval_pointinletmodulePrinter_class(X:Printer.PrinterClass)=structclassprinter()=objectinheritX.printer()assupermethod!varinfofmtvi=Format.fprintffmt"@{<%s>%a@}"(tag_vivi)super#varinfovi;endendinletprinter=Printer.current_printer()inletfinally()=Printer.set_printerprinterinPrinter.update_printer(modulePrinter_class:Printer.PrinterExtension);Fun.protect~finallyfletprint_valuefmtloc=letis_scalar=Ast_types.is_scalarinletevaluation_point=marker_evaluation_pointlocinletrequest=request_atevaluation_pointinleteval=matchlocwith|Printer_tag.PLval(_,_,lval)whenis_scalar(Cil.typeOfLvallval)->Results.eval_lvallval|Printer_tag.PExp(_,_,expr)whenis_scalar(Cil.typeOfexpr)->Results.eval_expexpr|PVDecl(_,_,vi)whenis_scalarvi.vtype->Results.eval_varvi|PTermLval(kf,_,_ip,tlval)->letlval=term_lval_to_lvalkftlvalinifis_scalar(Cil.typeOfLvallval)thenResults.eval_lvallvalelseraiseNot_found|_->raiseNot_foundinletpretty=Cvalue.V_Or_Uninitialized.prettyinleteval_cvalueat=Results.(evalat|>as_cvalue_or_uninitialized)inletbefore=eval_cvaluerequestinletprint=matchevaluation_pointwith|Initial|Pre_->fun()->prettyfmtbefore|Stmt(_,stmt)->letafter=eval_cvalue(Results.afterstmt)inifCvalue.V_Or_Uninitialized.equalbeforeafterthenfun()->prettyfmtbeforeelsefun()->Format.fprintffmt"Before: %a@\nAfter: %a"prettybeforeprettyafterinwith_updated_varinfo_printerevaluation_pointprintlet()=Server.Kernel_ast.Information.register~id:"eva.value"~label:"Value"~title:"Possible values inferred by Eva"~enable:Analysis.is_computedprint_value(* ----- Detailed values by callstack for the values table ------------------ *)typeterm=Pexprofexp|Plvaloflval|Ppredofpredicate(* A term and the program point where it should be evaluated. *)typeprobe=term*evaluation_pointtypetruth=Abstract_interp.truth(* The result of an evaluation:
- the resulting value as a text to be printed;
- the alarms emitted for the evaluation;
- the variables pointed by the resulting value, if any. *)typeevaluation={value:Jtext.t;alarms:(truth*string)list;pointed_vars:(string*Printer_tag.localizable)list;}(* Evaluations after the given statement. If the statement is a conditional
branch, evaluations in the [then] and [else] branch. *)type'vnext=|Afterof'v|Condof'v*'v|Nothingtypeevaluations={here:evaluation;next:evaluationnext;}(* -------------------------------------------------------------------------- *)(* --- Marker Utilities --- *)(* -------------------------------------------------------------------------- *)letnext_steps=function|Initial|Pre_->`None|Stmt(_,stmt)->matchstmt.skindwith|If(cond,_,_,_)->`Condition(stmt,cond)|Instr(Set_|Call_|Local_init_)->`Effectstmt|Instr_whenAnnotations.has_code_annotstmt->`Effectstmt|Instr(Asm_|Code_annot_|Skip_)|Switch_|Loop_|Block_|UnspecifiedSequence_|TryCatch_|TryFinally_|TryExcept_|Return_|Break_|Continue_|Goto_|Throw_->`Noneletprobe_stmtstmt=matchstmt.skindwith|Instr(Set(lv,_,_))|Instr(Call(Somelv,_,_,_))->Plvallv|Instr(Local_init(v,_,_))->Plval(Varv,NoOffset)|Return(Somee,_)|If(e,_,_,_)|Switch(e,_,_,_)->Pexpre|_->raiseNot_foundletprobe_code_annot=function|AAssert(_,p)|AInvariant(_,true,p)->Ppredp.tp_statement|_->raiseNot_foundletprobe_property=function|Property.IPCodeAnnotica->probe_code_annotica.ica_ca.annot_content|IPPropertyInstance{ii_pred=Somepred}|IPPredicate{ip_pred=pred}->Ppred(Logic_const.pred_of_id_predpred)|_->raiseNot_foundletprobe_marker=function|Printer_tag.PLval(_,_,lval)whenAst_types.is_fun(Cil.typeOfLvallval)->raiseNot_found|PVDecl(_,_,vi)whenAst_types.is_funvi.vtype->raiseNot_found|PLval(_,_,l)->Plvall|PExp(_,_,e)->Pexpre|PStmt(_,s)|PStmtStart(_,s)->probe_stmts|PVDecl(_,_,v)->Plval(Varv,NoOffset)|PTermLval(kf,_,_,tlval)->Plval(term_lval_to_lvalkftlval)|PIPproperty->probe_propertyproperty|_->raiseNot_foundletprobemarker=trySome(probe_markermarker,marker_evaluation_pointmarker)withNot_found->None(* -------------------------------------------------------------------------- *)(* --- Domain Utilities --- *)(* -------------------------------------------------------------------------- *)moduleJtruth:Data.Swithtypet=truth=structtypet=truthletjtype=Package.(Junion[Jtag"True";Jtag"False";Jtag"Unknown"])letto_json=function|Abstract_interp.Unknown->`String"Unknown"|True->`String"True"|False->`String"False"letof_json=function|`String"True"->Abstract_interp.True|`String"False"->Abstract_interp.False|_->Abstract_interp.Unknownend(* -------------------------------------------------------------------------- *)(* --- Utility functions for cvalue and offsetmaps --- *)(* -------------------------------------------------------------------------- *)typeoffsetmap=|OffsetmapofCvalue.V_Offsetmap.t|Bottom|Empty|Top|InvalidLocletpp_offsetmaptypfmt=function|Bottom->Format.fprintffmt"<BOTTOM>"|Empty->Format.fprintffmt"<EMPTY>"|Top->Format.fprintffmt"<NO INFORMATION>"|InvalidLoc->Format.fprintffmt"<INVALID LOCATION>"|Offsetmapoffsm->Cvalue.V_Offsetmap.pretty_generic~typ()fmtoffsm;Eval_op.pretty_stitched_offsetmapfmttypoffsmletextract_single_varvistate=letb=Base.of_varinfoviintrymatchCvalue.Model.find_basebstatewith|`Bottom->Bottom|`Valuem->Offsetmapm|`Top->TopwithNot_found->InvalidLocletreduce_loc_and_evalstateloc=ifCvalue.Model.is_topstatethenTopelseifnot(Cvalue.Model.is_reachablestate)thenBottomelseifZ_or_top.is_zeroloc.Locations.sizethenEmptyelseletloc'=Locations.(valid_partReadloc)inifLocations.is_bottomloc'thenInvalidLocelsetryletsize=Z_or_top.projectloc'.Locations.sizeinmatchCvalue.Model.copy_offsetmaploc'.Locations.addrsizestatewith|`Bottom->InvalidLoc|`Valueoffsm->OffsetmapoffsmwithAbstract_interp.Error_Top->Topletfind_offsetmapcvalue_stateprecise_loc=letflocacc=matchacc,reduce_loc_and_evalcvalue_statelocwith|Offsetmapo1,Offsetmapo2->Offsetmap(Cvalue.V_Offsetmap.joino1o2)|Bottom,v|v,Bottom->v|Empty,v|v,Empty->v|Top,Top->Top|InvalidLoc,InvalidLoc->InvalidLoc|InvalidLoc,(Offsetmap_asres)->res|Offsetmap_,InvalidLoc->acc|Top,r|r,Top->r(* cannot happen, we should get Top everywhere *)inPrecise_locs.foldfprecise_locBottom(* Get pointed bases from a cvalue. *)letget_basescvalue=tryBase.SetLattice.project(Cvalue.V.get_basescvalue)withAbstract_interp.Error_Top->Base.Hptset.empty(* Get pointed bases from an offsetmap. *)letget_pointed_bases=function|Offsetmapoffsm->letget_basesv=Cvalue.V_Or_Uninitialized.get_vv|>get_basesinletfvacc=get_basesv|>Base.Hptset.unionaccinCvalue.V_Offsetmap.fold_on_valuesfoffsmBase.Hptset.empty|Bottom|Empty|Top|InvalidLoc->Base.Hptset.empty(* Only keep a list of C variables from both previous functions. *)letfilter_variablesbases=letadd_varbaseacc=tryBase.to_varinfobase::accwithBase.Not_a_C_variable->accinletvars=List.rev(Base.Hptset.foldadd_varbases[])inList.filter(funvi->not(Ast_types.is_funvi.vtype))vars(* -------------------------------------------------------------------------- *)(* --- EVA Proxy --- *)(* -------------------------------------------------------------------------- *)moduletypeEvaProxy=sigvalevaluate:probe->Callstack.toption->evaluationsendmoduleProxy(A:Engine_sig.S_with_results):EvaProxy=structopenEvaltypedstate=A.Dom.stateor_top_bottomletget_precise_loc=letdefault=fun_->Precise_locs.loc_topinOption.value~default(A.Loc.getMain_locations.PLoc.key)letget_cvalue=letdefault=fun_->Cvalue.V.topinOption.value~default(A.Val.getMain_values.CVal.key)letdomain_statecallstack=function|Initial->A.get_stateInitial|Prekf->A.get_state?callstack(Startkf)|Stmt(_,stmt)->A.get_state?callstack(Beforestmt)(* --- Converts an evaluation [result] into an exported [value]. ---------- *)(* Result of an evaluation: a generic value for scalar types, or an offsetmap
for struct and arrays. *)typeresult=|ValueofA.Val.tEval.flagged_value|Offsetmapofoffsetmap|Statusoftruthletpp_resulttypfmt=function|Valuev->(Eval.pretty_value_with_flags(A.Val.pretty_typ(Sometyp)))fmtv|Offsetmapoffsm->pp_offsetmaptypfmtoffsm|Statustruth->Alarmset.Status.prettyfmttruthletget_pointed_bases=function|Valuev->get_basesBottom.(mapget_cvaluev.v|>value~bottom:Cvalue.V.bottom)|Offsetmapoffsm->get_pointed_basesoffsm|Status_->Base.Hptset.emptyletget_pointed_markerseval_pointresult=letbases=get_pointed_basesresultinletvars=filter_variablesbasesinletkf,kinstr=matcheval_pointwith|Initial->None,Kglobal|Prekf->Somekf,Kglobal|Stmt(kf,stmt)->Somekf,Kstmtstmtinletto_markervi=lettext=Pretty_utils.to_stringPrinter.pp_varinfoviinletmarker=Printer_tag.PLval(kf,kinstr,Cil.varvi)intext,markerinList.mapto_markervars(* Creates an exported [value] from an evaluation result. *)letmake_valuetypeval_point(result,alarms)=letdescr=Format.asprintf"@[<hov 2>%a@]"Alarms.prettyinletfalarmstatusacc=(status,descralarm)::accinletalarms=Alarmset.foldf[]alarms|>List.revinletpretty_eval=Bottom.pretty(pp_resulttyp)inletresult_to_json()=Data.jprettypretty_evalresultinletvalue=with_updated_varinfo_printereval_pointresult_to_jsoninletpointed_markers=get_pointed_markerseval_pointinletpointed_vars=Bottom.(mappointed_markersresult|>value~bottom:[])in{value;alarms;pointed_vars}(* --- Evaluates an expression or lvalue into an evaluation [result]. ----- *)letlval_to_offsetmap(lval:Eva_ast.lval)state=letcvalue_state=A.Dom.get_cvalue_or_topstateinmatchlval.nodewith|Varvi,NoOffset->letr=extract_single_varvicvalue_statein`Valuer,Alarmset.none|_->A.eval_lval_to_locstatelval>>=:funloc->letprecise_loc=get_precise_loclocinfind_offsetmapcvalue_stateprecise_locleteval_lval(lval:Eva_ast.lval)state=matchAst_types.unroll_nodelval.typwith|TInt_|TEnum_|TPtr_|TFloat_->A.copy_lvaluestatelval>>=:funvalue->Valuevalue|_->lval_to_offsetmaplvalstate>>=:funoffsm->Offsetmapoffsmleteval_exprexprstate=A.eval_exprstateexpr>>=:funvalue->Value{v=`Valuevalue;initialized=true;escaping=false}leteval_predeval_pointpredicatestate=letresult=matcheval_pointwith|Initial|Pre_->None|Stmt(kf,_)->Eva_utils.find_return_varkfinletenv=Abstract_domain.{states=(function_->A.Dom.top);result}inlettruth=A.Dom.evaluate_predicateenvstatepredicatein`Value(Statustruth),Alarmset.none(* --- Evaluates all steps (before/after the statement). ------------------ *)letdo_nextevalstateeval_pointcallstack=matchnext_stepseval_pointwith|`Condition(stmt,cond)->letcond'=Eva_ast.translate_expcondinletkf=Kernel_function.find_englobing_kfstmtinletdummy_callstack=Callstack.init~thread:0~entry_point:kfinletpos=Position.localstmtdummy_callstackinletthen_state=(A.assume_cond~posstatecond'true:>dstate)inletelse_state=(A.assume_cond~posstatecond'false:>dstate)inCond(evalthen_state,evalelse_state)|`Effectstmt->letafter_state=A.get_state?callstack(Afterstmt)inAfter(evalafter_state)|`None->Nothingleteval_stepstypevaleval_pointcallstack=letdefaultstr=letvalue=Data.jtextstrin{value;alarms=[];pointed_vars=[];}inleteval=function|`Bottom->default"Unreachable"|`Top->default"No information"|`Valuestate->make_valuetypeval_point(evalstate)inletbefore=domain_statecallstackeval_pointinlethere=evalbeforeinletnext=matchbeforewith|`Valuestate->do_nextevalstateeval_pointcallstack|_->Nothingin{here;next;}letevaluate(term,eval_point)callstack=matchtermwith|Plvallval->letlval'=Eva_ast.translate_lvallvalineval_stepslval'.typ(eval_lvallval')eval_pointcallstack|Pexprexpr->letexpr'=Eva_ast.translate_expexprineval_stepsexpr'.typ(eval_exprexpr')eval_pointcallstack|Ppredpred->eval_stepsCil_const.intType(eval_predeval_pointpred)eval_pointcallstackendletproxy=letmake(a:(moduleEngine_sig.S_with_results))=(moduleProxy(vala):EvaProxy)inletcurrent=ref(make@@Engine.current())inlethooka=current:=makeainEngine.register_hookhook;fun()->!current(* -------------------------------------------------------------------------- *)(* --- Request getProbeInfo --- *)(* -------------------------------------------------------------------------- *)letis_reachable=function|Stmt(_,stmt)->Results.is_reachablestmt|Prekf->Results.is_calledkf|Initial->Results.is_reachable_kinstrKgloballet()=letgetProbeInfo=Request.signature~input:(moduleJmarker)()inletset_evaluable=Request.resultgetProbeInfo~name:"evaluable"~descr:(Md.plain"Can the probe be evaluated?")(moduleJbool)andset_code=Request.result_optgetProbeInfo~name:"code"~descr:(Md.plain"Probe source code")(moduleJstring)andset_stmt=Request.result_optgetProbeInfo~name:"stmt"~descr:(Md.plain"Probe statement")(moduleJstmt)andset_effects=Request.resultgetProbeInfo~name:"effects"~descr:(Md.plain"Effectfull statement")~default:false(moduleJbool)andset_condition=Request.resultgetProbeInfo~name:"condition"~descr:(Md.plain"Conditional statement")~default:false(moduleJbool)inletset_proberqpppeval_point=letcomputed=Analysis.is_computed()inletreachable=is_reachableeval_pointinset_evaluablerq(computed&&reachable);set_coderq(Some(Pretty_utils.to_stringppp));beginmatcheval_pointwith|Initial|Pre_->()|Stmt(_kf,stmt)->set_stmtrq(Somestmt)end;matchnext_stepseval_pointwith|`None->()|`Condition_->set_conditionrqtrue|`Effect_->set_effectsrqtrueinRequest.register_sig~packagegetProbeInfo~kind:`GET~name:"getProbeInfo"~descr:(Md.plain"Probe information")beginfunrqmarker->matchprobemarkerwith|None->set_evaluablerqfalse|Some(term,eval_point)->matchtermwith|Plvall->set_proberqPrinter.pp_lvalleval_point|Pexpre->set_proberqPrinter.pp_expeeval_point|Ppredp->set_proberqPrinter.pp_predicatepeval_pointend(* -------------------------------------------------------------------------- *)(* --- Request getValues --- *)(* -------------------------------------------------------------------------- *)moduleJEvaluation=structopenServer.Datatyperecordletrecord:recordRecord.signature=Record.signature()letvalue=Record.fieldrecord~name:"value"~descr:(Markdown.plain"Textual representation of the value")(moduleData.Jtext)letalarms=Record.fieldrecord~name:"alarms"~descr:(Markdown.plain"Alarms raised by the evaluation")(moduleJlist(Jpair(Jtruth)(Jstring)))letpointed_vars=Record.fieldrecord~name:"pointedVars"~descr:(Markdown.plain"List of variables pointed by the value")(moduleJlist(Jpair(Jstring)(Jmarker)))letdata=Record.publishrecord~package~name:"evaluation"~descr:(Markdown.plain"Evaluation of an expression or lvalue")moduleR:Record.Swithtyper=record=(valdata)typet=evaluationletjtype=R.jtypeletto_jsont=R.default|>R.setvaluet.value|>R.setalarmst.alarms|>R.setpointed_varst.pointed_vars|>R.to_jsonendlet()=letgetValues=Request.signature()inletget_tgt=Request.paramgetValues~name:"target"~descr:(Md.plain"Works with all markers containing an expression")(moduleJmarker)andget_cs=Request.param_optgetValues~name:"callstack"~descr:(Md.plain"Callstack to collect (defaults to none)")(moduleCallstack_requests.JCallstack)andset_before=Request.result_optgetValues~name:"vBefore"~descr:(Md.plain"Domain values before execution")(moduleJEvaluation)andset_after=Request.result_optgetValues~name:"vAfter"~descr:(Md.plain"Domain values after execution")(moduleJEvaluation)andset_then=Request.result_optgetValues~name:"vThen"~descr:(Md.plain"Domain values for true condition")(moduleJEvaluation)andset_else=Request.result_optgetValues~name:"vElse"~descr:(Md.plain"Domain values for false condition")(moduleJEvaluation)inRequest.register_sig~packagegetValues~kind:`GET~name:"getValues"~descr:(Md.plain"Abstract values for the given marker")~signals:Update.signalsbeginfunrq()->letmoduleA:EvaProxy=(valproxy())inletmarker=get_tgtrqandcallstack=get_csrqinmatchprobemarkerwith|None->()|Someprobe->letdomain=A.evaluateprobecallstackinset_beforerq(Somedomain.here);matchdomain.nextwith|Aftervalue->set_afterrq(Somevalue)|Cond(v_then,v_else)->set_thenrq(Somev_then);set_elserq(Somev_else)|Nothing->()end(* -------------------------------------------------------------------------- *)