Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file engine.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432(****************************************************************************)(* *)(* 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/>. *)(* *)(****************************************************************************)(** Engine for interactive analysis sessions *)openMopsa_utilsopenCore.AllopenToplevelopenFormatopenLocationopenCallstackopenBreakpointopenQuerymoduleI=InterfaceopenInterfaceopenActionopenEnvdbopenTrace(** {2 Interactive engine} *)(** ********************** *)moduleMake(Toplevel:TOPLEVEL)(InterfaceFunctor:I.INTERFACE)=structmoduleInterface=InterfaceFunctor(Toplevel)typet=Toplevel.tletdebugfmt=Debug.debug~channel:"framework.engines.interactive"fmt(** Catch Ctrl+C interrupts as a Break exception*)let()=Sys.catch_breaktrue(** {2 Actions on the abstract domain} *)(** ********************************** *)(** Actions on abstract domain *)type_return_action=|Exec:stmt*route->Toplevel.tpostreturn_action|Eval:expr*route*semantic->Toplevel.tevalreturn_action(* Actions with hidden return type *)typexaction=Action:'areturn_action->xaction(** Get the program location related to an action *)letaction_range:typea.areturn_action->range=function|Exec(stmt,_)->stmt.srange|Eval(exp,_,_)->exp.erangeletinterface_action(typea)(action:areturn_action):Action.action=matchactionwith|Exec(stmt,route)->Exec(stmt,route)|Eval(expr,route,tran)->Eval(expr,route,tran)(** {2 Interaction detection} *)(** ************************* *)(* Test if the currest state corresponds to a function call *)letis_calloldcur=callstack_begins_withcurold(* Test if the currest state corresponds to a function return *)letis_returnoldcur=callstack_begins_witholdcur(* Test if an action corresponds to a new line of code *)letis_new_loc_action:typea.state->areturn_action->bool=funoldaction->matchactionwith|Eval_->false|Exec({skind=S_block_},_)->false|Exec(stmt,_)->letrange=untag_rangestmt.srangeinis_orig_rangerange&&(matchold.locwith|None->true|Somerange'->letp=get_range_startrangeinletp'=get_range_startrange'inp.pos_file<>p'.pos_file||p.pos_line<>p'.pos_line)(* Check if the analyzer reached an interaction point *)letis_range_breakpoint()=matchstate.locwith|None->false|Somerange->BreakpointSet.exists(function|B_line(file,line)->match_range_filefilerange&&match_range_linelinerange|_->false)!breakpoints(** Test if there is a breakpoint at a given function *)letis_function_breakpoint()=not(is_empty_callstackstate.callstack)&&(letcall=callstack_topstate.callstackinBreakpointSet.exists(function|B_functionf->f=call.call_fun_orig_name|_->false)!breakpoints)letis_named_breakpointname=BreakpointSet.exists(function|B_namedname'->name=name'|_->false)!breakpointsletis_alarm_breakpoint_active()=BreakpointSet.memB_alarm!breakpoints(* Check if the analyzer reached an interaction point *)letis_interaction_point(typea)old(action:areturn_action)=matchactionwith|Exec({skind=S_breakpointname},_)whenis_named_breakpointname->true|_->matchstate.commandwith(* Always interact with [StepI] *)|StepI->true(* [NextI] stops only if the current depth is less than
the depth when the user entered the [NextI] command *)|NextI->state.depth<=state.command_depth(* [Step] stops at any new line of codes *)|Step->is_new_loc_actionoldaction(* [Next] stops at new lines of codes that are not in inner calls (unless if
there is a breakpoint) *)|Next->is_new_loc_actionoldaction&&(not(is_callstate.command_callstackstate.callstack)||is_range_breakpoint()||(state.call_preamble&&is_function_breakpoint()))(* [Continue] stops at new lines of code with attached breakpoints *)|Continue->is_new_loc_actionoldaction&&(is_range_breakpoint()||(state.call_preamble&&is_function_breakpoint()))(* [Finish] stops at new lines of code after function return or at breakpoints *)|Finish->is_new_loc_actionoldaction&&(is_returnstate.command_callstackstate.callstack||is_range_breakpoint()||is_function_breakpoint())|Backward->assertfalseletget_new_alarms(typea)(action:areturn_action)(ret:a)=letdoitcases=letalarms=Cases.fold(funacc_flow->letreport=Flow.get_reportflowinfold_report(fundiagacc->matchdiag.diag_kindwith|Error|Warning->AlarmSet.uniondiag.diag_alarmsacc|Safe|Unreachable->acc)reportacc)AlarmSet.emptycasesinAlarmSet.diffalarmsstate.alarmsinmatchactionwith|Exec_->doitret|Eval_->doitret(*************************)(** Environment database *)(*************************)letenvdb:tenvdbref=refempty_envdb(** {2 Interactive engine} *)(** ********************** *)(** Apply an action on a flow and return its result *)letrecapply_action:typea.areturn_action->tflow->a=funactionflow->matchactionwith|Exec(stmt,route)->state.depth<-state.depth+1;letret=Toplevel.exec~routestmtmanflowinstate.depth<-state.depth-1;ret|Eval(exp,route,translate)->state.depth<-state.depth+1;letret=Toplevel.eval~route~translateexpmanflowinstate.depth<-state.depth-1;ret(** Wait for user input and process it *)andinteract:typea.areturn_action->tflow->a=funactionflow->letcmd=Interface.read_command(interface_actionaction)!envdbmanflowinstate.command<-cmd;state.command_depth<-state.depth;state.command_callstack<-(Flow.get_callstackflow);matchcmdwith|Backward->raiseToplevel.GoBackward|_->apply_actionactionflow(** Interact with the user input or apply the action *)andinteract_or_apply_action:typea.areturn_action->Location.range->Toplevel.tflow->a=funactionrangeflow->letold=copy_statestateinstate.depth<-state.depth+1;state.callstack<-Flow.get_callstackflow;try(* When entering a function, we push the old loc to locstack, so that we
can retrieve it when returning from the function to check if we
encounter a new loc after the call. *)(ifis_callold.callstackstate.callstackthen(state.call_preamble<-true;state.locstack<-old.loc::old.locstack)else(* When returning from a function, we pop the loc from the stack,
and we consider it as the old loc *)ifis_returnold.callstackstate.callstackthen(state.call_preamble<-false;matchold.locstackwith|[]->old.loc<-None;state.loc<-None|hd::tl->old.loc<-hd;state.loc<-hd;state.locstack<-tl));letcur=Flow.getT_curman.latticeflowin(* Check if we reached a new loc *)letnew_loc=is_new_loc_actionoldactioninletlast_trace_element_id=ref0in(ifnew_locthenletrange=action_rangeactioninstate.loc<-Somerange;state.trace<-begin_trace_element(interface_actionaction)state.trace;last_trace_element_id:=get_last_trace_element_idstate.trace;letctx=Flow.get_ctxflowinenvdb:=add_envdb(interface_actionaction)ctxcurman!envdb);(* Check if we reached an interaction point *)letinteraction=is_interaction_pointoldaction&¬(man.lattice.is_bottomcur)in(* Reset call flag if we reached a new loc *)ifnew_locthenstate.call_preamble<-false;letret=ifinteractionthen(Interface.reach(interface_actionaction)manflow;interactactionflow)elseapply_actionactionflowin(* Check if there are new alarms *)ifnew_locthen(state.trace<-end_trace_element!last_trace_element_id(interface_actionaction)state.trace;letnew_alarms=get_new_alarmsactionretinstate.alarms<-AlarmSet.unionstate.alarmsnew_alarms;ifnot(AlarmSet.is_emptynew_alarms)then(Interface.alarm(AlarmSet.elementsnew_alarms)(interface_actionaction)manflow;ifis_alarm_breakpoint_active()then(Interface.reach(interface_actionaction)manflow;let_=interactactionflowin())););(* Return *)state.depth<-state.depth-1;retwith|Toplevel.SysBreak_|Sys.Break->interactactionflow|Toplevel.GoBackward->letcs=Flow.get_callstackflowinifis_callcsstate.callstackthen(state.depth<-old.depth+1;state.callstack<-Flow.get_callstackflow;state.alarms<-AlarmSet.of_list(Alarm.report_to_alarms(Flow.get_reportflow));Interface.reach(interface_actionaction)manflow;interactactionflow)elseraiseToplevel.GoBackward|Exit->raiseExit|e->Interface.errore;ifPrintexc.backtrace_status()thenPrintexc.print_backtracestderr;Debug.warn"Error encountered (see above); jumping back to allow looking around";Interface.reach(interface_actionaction)manflow;interactactionflow(** {2 Engine functions} *)(** ******************** *)andsubsetctxaa'=state.depth<-state.depth+1;letb=Toplevel.subsetmanctxaa'instate.depth<-state.depth-1;bandjoinctxaa'=state.depth<-state.depth+1;letaa=Toplevel.joinmanctxaa'instate.depth<-state.depth-1;aaandmeetctxaa'=state.depth<-state.depth+1;letaa=Toplevel.meetmanctxaa'instate.depth<-state.depth-1;aaandwidenctxaa'=state.depth<-state.depth+1;letaa=Toplevel.widenmanctxaa'instate.depth<-state.depth-1;aaandinitprog=Interface.init();Toplevel.initprogman;andanalyzestmtflow=tryinit_statestate;letret=(execstmtflow>>%funflow->exec(mk_stmt(S_block([],[]))(tag_rangestmt.srange"end"))flow)|>post_to_flowmaninInterface.finishmanret;retwith|Exit->raiseExit|Toplevel.GoBackward->analyzestmtflow|e->Interface.errore;raiseeandexec?(route=toplevel)stmtflow=interact_or_apply_action(Exec(stmt,route))stmt.srangeflowandeval?(route=toplevel)?(translate=any_semantic)?(translate_when=[])expflow=interact_or_apply_action(Eval(exp,route,translate))exp.erangeflowandask:typer.?route:route->(Toplevel.t,r)query->Toplevel.tflow->(Toplevel.t,r)cases=fun?(route=toplevel)queryflow->Toplevel.ask~routequerymanflowandprint_expr?(route=toplevel)flowprinterexp=Toplevel.print_expr~routemanflowprinterexpandlattice:Toplevel.tlattice={bottom=Toplevel.bottom;top=Toplevel.top;is_bottom=Toplevel.is_bottom;subset;join;meet;widen;merge=Toplevel.merge;print=Toplevel.print_state;}andman:(Toplevel.t,Toplevel.t)man={lattice;get=(funtkflow->letabs=Flow.gettklatticeflowinCases.singletonabsflow);set=(funtkabsflow->letflow=Flow.settkabslatticeflowinPost.returnflow);add_change=(funstmtpathflowchange_map->add_stmt_to_change_mapstmt(List.revpath)change_map);exec=exec;eval=eval;ask=ask;print_expr=print_expr;}end