123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249openCore_kernelopenBap.StdopenMonads.StdopenBap_primus.StdopenFormatincludeSelf()letpackage="bap"(*
In a general case a block is terminated by a sequence of jumps:
{v
when C1 jmp D1
when C2 jmp D2
...
when Cm jmp Dm
v}
The IR requires the following invariant:
{v C1 \/ C2 \/ ... \/ Cm v}.
The infeasible interpreter is a non-deterministic interperter, that
for every block B that is terminated with m jumps, will fork m-1
context after the last definition, so that under the n-th context
the Dn destination will be taken by the interpreter.
For the Dm-th destination to be taken, the following condition must
hold: {v ~C1 /\ ~C2 /\ ... ~C(n-1) /\ Cn v}, where [~] symbol
denotes logical negation.
However, we would require an SMT solver to find such contexts. So,
the interpreter requires a program to be in a trivial condition form
(TCF). In TCF every jmp condition must be a single variable or a
constant true.
*)typeassn={use:tid;var:var;res:bool;}typeid=Primus.Machine.idtypestate={conflicts:assnlist;visited:Tid.Set.t;}letinspect_assn{use;var;res}=letvar=sprintf"%s: %s"(Tid.to_stringuse)(Var.to_stringvar)inSexp.List[Sexp.Atomvar;sexp_of_boolres]letinspect_conflict(assn)=Sexp.List(List.mapassn~f:inspect_assn)letinspect_conflictscs=Sexp.List(List.map~f:inspect_conflictcs)letinit_visitedprog=(objectinherit[Tid.Set.t]Term.visitormethod!enter_blkblkvisited=ifTerm.has_attrblkTerm.visitedthenSet.addvisited(Term.tidblk)elsevisitedend)#runprogTid.Set.emptyletstate=Primus.Machine.State.declare~name:"conflicts"~uuid:"58bb35f4-f259-4712-8d15-bdde1be3caa8"@@funproj->{conflicts=[];visited=init_visited(Project.programproj)}letneg=List.map~f:(funassn->{assnwithres=notassn.res})letassumptionsblk=Term.enumjmp_tblk|>Seq.fold~init:([],[])~f:(fun(assns,assms)jmp->matchJmp.condjmpwith|Bil.Varc->letassn={use=Term.tidjmp;var=c;res=true}inassn::assns,(assn::negassns)::assms|Bil.Int_->assns,(negassns)::assms|_->failwith"Not in TCF")|>sndmoduleTrapPageFault(Machine:Primus.Machine.S)=structmoduleCode=Primus.Linker.Make(Machine)letexec=Code.unlink(`symbolPrimus.Interpreter.pagefault_handler)endmoduleDoNothing(Machine:Primus.Machine.S)=structletexec=Machine.return()endmoduleId=Monad.State.Multi.IdmoduleForker(Machine:Primus.Machine.S)=structopenMachine.SyntaxmoduleEval=Primus.Interpreter.Make(Machine)moduleEnv=Primus.Env.Make(Machine)moduleMem=Primus.Memory.Make(Machine)moduleLinker=Primus.Linker.Make(Machine)letassumeassns=Machine.List.iterassns~f:(funassn->Eval.const(Word.of_boolassn.res)>>=funr->Eval.getassn.var>>=funr'->letop=ifassn.resthenBil.ORelseBil.ANDinEval.binopoprr'>>=Eval.setassn.var)letunsat_assumptionsblk=Machine.List.map(assumptionsblk)~f:(Machine.List.filter~f:(fun{var;res}->Env.getvar>>|Primus.Value.to_word>>|funr->Word.(r<>of_boolres)))letpp_id=Monad.State.Multi.Id.ppletdo_forkblk~child=Machine.current()>>=funpid->Machine.Global.getstate>>=funt->ifSet.memt.visited(Term.tidblk)thenMachine.returnpidelseMachine.fork()>>=fun()->Machine.current()>>=funid->ifId.(pid=id)thenMachine.returnidelsechild()>>=fun()->Machine.switchpid>>|fun()->idletforkblk=unsat_assumptionsblk>>=Machine.List.iter~f:(function|[]->Machine.return()|conflicts->Machine.ignore_m@@do_forkblk~child:(fun()->assumeconflicts>>=fun()->Machine.Local.updatestate~f:(funt->{twithconflicts})))letassume_returnsblkcall=matchCall.returncallwith|Some(Directdst)->Machine.current()>>=funpid->do_forkblk~child:Machine.return>>=funid->ifId.(id=pid)thenMachine.return()elseMachine.Global.getstate>>=fun{visited}->ifSet.memvisiteddstthenEval.halt>>=never_returnselseLinker.exec(`tiddst)>>=fun()->Eval.halt>>=never_returns|_->Machine.return()letfork_on_callsblkjmp=matchJmp.kindjmpwith|Callc->assume_returnsblkc|_->Machine.return()letis_lastblkdef=matchTerm.lastdef_tblkwith|None->true|Somelast->Term.samedeflastlethas_no_defblk=Term.lengthdef_tblk=0letsteplevel=letopenPrimus.Posinmatchlevelwith|Blk{me=blk}whenhas_no_defblk->forkblk|Def{up={me=blk};me=def}whenis_lastblkdef->forkblk|Jmp{up={me=blk};me=jmp}->fork_on_callsblkjmp|_->Machine.return()letmark_visitedblk=Machine.Global.updatestate~f:(funt->{twithvisited=Set.addt.visited(Term.tidblk)})letinit()=Machine.sequence[Primus.Interpreter.leave_pos>>>step;Primus.Interpreter.leave_blk>>>mark_visited;]endmoduleEnableDivisionByZero(Machine:Primus.Machine.S)=structmoduleLinker=Primus.Linker.Make(Machine)letinit()=Linker.link~name:Primus.Interpreter.division_by_zero_handler(moduleDoNothing)endletlegacy_promiscous_mode_components=["var-randomizer";"mem-randomizer";"arg-randomizer";"promiscuous-path-explorer";"division-by-zero-handler";"limit";]letenable_legacy_promiscuous_mode()=Primus.System.Repository.update~package"legacy-main"~f:(funinit->List.foldlegacy_promiscous_mode_components~init~f:(funsystemcomponent->Primus.System.add_componentsystem~packagecomponent))openConfig;;letdesc="When this mode is enabled the Primus Machine will venture into \
paths with unsatisfied constraints. Basically, it means that on \
every branch the state is duplicated.";;manpage[`S"DESCRIPTION";`Pdesc;`P"The program will be translated into the Trivial Condition Form,
where each compound condition expression is trivialized to a
variable, that is bound earlier in the block."]letenabled=flag"mode"~doc:"(DEPRECATED) Enable the mode."let()=when_ready(fun{get=(!!)}->Primus.Components.register_generic"promiscuous-path-explorer"(moduleForker)~package~desc:"Forces execution of all linearly independent paths \
by forcefully flipping the branch conditions.";Primus.Components.register_generic"division-by-zero-handler"(moduleEnableDivisionByZero)~package~desc:"Disables division by zero errors.";if!!enabledthenenable_legacy_promiscuous_mode());