12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118(**************************************************************************)(* This file is part of the Codex semantics library. *)(* *)(* Copyright (C) 2013-2025 *)(* 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 LICENSE). *)(* *)(**************************************************************************)moduleLog=Tracelog.Make(structletcategory="Dba2Codex"end)moduleLogExpression=Tracelog.Make(structletcategory="Expression"end)moduleLogDbaInstr=Tracelog.Make(structletcategory="DBA Instruction"end)moduleIn_bytes=Units.In_bytesmoduleIn_bits=Units.In_bitsletin_bytes=Units.In_bytes.of_intletin_bits=Units.In_bits.of_intmoduleVarMap=Codex.Extstdlib.Map.Make(String)typejump_target=|Jump_InnerofDba.id(** Some instruction in a block *)|Jump_OuterofVirtual_address.t(** Some instruction outside of the block. *)|Jump_Dynamic(* TODO *)openCodexmoduleTypedC=Types.TypedCmoduleLogger=Codex_loggermoduletypeAddress_sig=sigtypetvalcreate:int->tmoduleSet:Stdlib.Set.Swithtypeelt=tmoduleHtbl:Stdlib.Hashtbl.Swithtypekey=tendmoduletypeRegionS=sigmoduleVirtual_address:Address_sigvalwritten_data_addrs:Virtual_address.Set.trefvalread_data_addrs:Virtual_address.Set.trefvalset_untyped_load:bool->unitvalset_check_store_intvl:bool->unitvalset_param_typing:(TypedC.typ*int)Virtual_address.Htbl.t->unitendmoduleM=Codex.Domains.Memory_domainsmoduletypeStateS=sigmoduleDomain:M.With_focusing.S_with_typestypet={ctx:Domain.Context.t;vars:Domain.binaryVarMap.t;memory:Domain.memory;instruction_count:int;is_bottom:bool;never_went_to_user_code:bool;}valinitial:(Loader_elf.Img.t,'a,'b,'c)Loader.t_pack->Domain.Context.t->tvalinitial_concrete:(Loader_elf.Img.t,'a,'b,'c)Loader.t_pack(*-> (Loader_elf.Img.t, 'a, 'b) Loader.t_pack*)->Domain.Context.t->tvalreset:(Loader_elf.Img.t,'a,'b,'c)Loader.t_pack->Domain.Context.t->tvalget:size:In_bits.t->t->string->Domain.binaryvalset:t->string->Domain.binary->tvalassume:Domain.boolean->t->t(* val assume_pred : size:int -> TypedC.Pred.t
-> Weak_shape_domain.BR.binary -> t -> t *)valbottom:Domain.Context.t->tvaldump_state:Format.formatter->t->unitvaldump_state_diff:Format.formatter->t->t->Virtual_address.t->(string,string)Hashtbl.t->unitvaljoin:t->t->tvalis_included:t->t->bool(** Serialize a state's variables and memory into a tuple. [to_tuple ctx
state] returns the result of serialization, along with an inversion
function, to turn a tuple back into a state. This function takes the
original state and a tuple as arguments, and update the state's [vars]
and [memory] fields with the tuple's contents. *)valserialize:widens:bool->t->t->'aDomain.Context.in_acc->(t,'a)Domain.Context.resultendmoduleCreate()=structmoduleBinaryEnumBasis:Single_value_abstraction.Sig.NUMERIC_ENUM=structincludeCodex.Single_value_abstraction.IvalincludeCodex.Single_value_abstraction.BitfieldendmoduleTerms=Terms.Builder.Make(Terms.Condition.ConditionCudd)(Terms.Relations.Additive)()modulePropag_domain=Domains.Term_based.Propagation.Make(Terms)(BinaryEnumBasis)(* module Propag_domain = Domains.Term_based.Nonrelational.Make
(Terms)(Ival_basis) *)moduleNumeric_simple=Domains.Term_domain.Make(Terms)(Propag_domain)moduleNumeric_loop=(valifCodex_options.UseLoopDomain.get()then(moduleDomains.Loop_domain.Make(Terms)(Numeric_simple):Domains.Sig.BASE)else(moduleNumeric_simple))moduleNumeric=Domains.Bitwise.Make(Numeric_loop)(* module Numeric_simple = Loop_domain.Make(Terms)(Propag_domain)
module Numeric = Bitwise_domain.Make(Numeric_simple) *)(*module Numeric = Numeric_simple*)(*
module Region_numeric = Region_numeric_offset.Make(Numeric)
module Numeric_OV : Codex.Memory_sig.Operable_Value_Whole
with module Scalar = Numeric
and type binary = Numeric.binary
and type boolean = Numeric.boolean
= Region_numeric.Address
*)moduleRegion_suffix_tree=M.Region_suffix_tree.Make(Numeric)moduleRegion_separation=M.Region_separation.Make(Region_suffix_tree)moduleOffset=Region_suffix_tree.OffsetmodulerecTyped_address_domain:(Domains.Memory_sig.ADDRESS_AND_MAKE_MEMORYwithmoduleScalar=Numeric)=M.Typed_address.Make(Region_separation)(Flexible_array_member_domain.Value)andWholified:(Domains.Memory_sig.WHOLE_MEMORY_DOMAINwithmoduleScalar=Numeric)=M.Wholify.Make(Typed_address_domain)andValue_union_concatenation_domain:(Domains.Memory_sig.WHOLE_MEMORY_DOMAINwithmoduleScalar=Numeric)=M.Value_union_concatenation.Make(Wholified)(* and Block_smashing_domain : *)(* (Domains.Memory_sig.BLOCK *)(* with module Scalar = Numeric) = *)(* M.Block_smashing.Make (Value_union_concatenation_domain.Address) (Offset) *)andFlexible_array_member_domain:(Domains.Memory_sig.COMPLETE_DOMAINwithmoduleScalar=Numeric)=M.Flexible_array_member.MakeComplete(Value_union_concatenation_domain)(M.Block_smashing.Make(Value_union_concatenation_domain.Address)(Value_union_concatenation_domain.Offset))(* module Numeric_OV : Codex.Memory_sig.Operable_Value_Whole = Wholified *)moduleNumeric_OV=Flexible_array_member_domain.Value(* module Wholified_memory = Wholified.Memory(Numeric_OV) *)moduleRegion:RegionSwithmoduleVirtual_address:=Virtual_address=structmoduleVirtual_address=Virtual_addressletwritten_data_addrs=refVirtual_address.Set.emptyletread_data_addrs=refVirtual_address.Set.emptyletuntyped_load=reffalseletset_untyped_loadb=untyped_load:=bletcheck_store_intvl=reftrueletset_check_store_intvlb=check_store_intvl:=bletparam_typing:(TypedC.typ*int)Virtual_address.Htbl.toptionref=refNoneletset_param_typingtyping=param_typing:=SometypingendmoduleMemD=Flexible_array_member_domainmoduleScalar=NumericmoduleValue=MemD.ValuemoduleBlock=MemD.BlockmoduleMemory=MemD.MemorymoduleWithout_focusing=struct(* include Memory_domain.Make(Numeric)(Weak_shape_domain.Binary_Representation)(Weak_shape_domain_memory) *)includeM.Memory_domain.Make(Value)(Block)(Memory)letflush_cache_ctxmem=memendmoduletypeDSIG=M.With_focusing.S_with_typesletm_domain=ifCodex_options.Focusing.get()then(moduleM.With_focusing.Make_with_types(Without_focusing):DSIG)elseletmoduleWithout_focusing=structincludeWithout_focusingletanalyze_summaryctxfuntypargsmem=letres,ret=analyze_summaryctxfuntypargsinres,ret,memletserialize_memory_and_cache~widensctxamemactxbmembentriesacc=serialize_memory~widensctxamemactxbmembaccendin(moduleWithout_focusing:DSIG)moduleDomain=(valm_domain)moduleIval=Codex.Ext.Framac_ival.IvalmoduletypeRegisters=sigvalregisters:(string*In_bits.t)listvalinitial_value:Domain.Context.t->string*In_bits.t->Domain.binaryendletbunknown~sizectx=Domain.binary_unknown~sizectxmoduleMake(Reg:Registers)=structmoduleState=structmoduleWholified=WholifiedmoduleDomain=Domaintypet={ctx:Domain.Context.t;vars:Domain.binaryVarMap.t;memory:Domain.memory;instruction_count:int;is_bottom:bool;never_went_to_user_code:bool;}letread_u32(mem:Loader_elf.Img.t)(offset:int):Loader_types.u32=letopenLoader_elfinletb0=read_offsetmemoffsetinletb1=read_offsetmem(offset+1)inletb2=read_offsetmem(offset+2)inletb3=read_offsetmem(offset+3)in(b3lsl24)lor(b2lsl16)lor(b1lsl8)lorb0letload_imageptrsizeimgctxmemory=letprogram_headers=Loader_elf.program_headersimginletmemory=Array.fold_left(funmemphdr->letopenLoader_elfinletvaddr=phdr.Phdr.vaddrinletfilesize=phdr.Phdr.fileszinletfilesize_mul_4=filesize-(filesizemod4)inletmemsize=phdr.Phdr.memsz|>in_bytesinLogger.result"Region: address %x filesize %x filesize_mul_4 %x memsize %x"vaddrfilesizefilesize_mul_4(memsize:>int);ifmemsize=In_bytes.zerothenmemelseletinitial=Domain.Binary_Forward.biconst~size:(memsize|>In_bytes.in_bits)Z.zeroctxin(* XXX: to test syscalls individually, and quickly. *)(*
let initial =
Region_binary_Operable_Value.param_unknown ~size:(memsize * 8) (Domain.get_subcontext ctx) in
*)letaddress=Domain.Binary_Forward.biconst~size:ptrsize(Z.of_intvaddr)ctxinletmem=Domain.Memory_Forward.store~size:(memsize|>In_bytes.in_bits)ctxmemaddressinitialin(* Logger.result "Memory %a" (Domain.memory_pretty ctx) mem; *)letrecloopimem=(* Logger.result "loop %x" i; *)(* TODO: lots of time, the loaded value is zero. We just
accumulate the zeroes, and write them as a big chunk,
using an inner loop. *)letrecinner_loopiidxsizemem=(* Logger.result "inner_loop %x %x %x" i idx size; *)letu32=tryread_u32img(phdr.Phdr.offset+i)withInvalid_argument_->0inifu32=0&&i<filesize_mul_4theninner_loop(i+4)idx(size+32)memelseifsize==0then(i,u32,mem)elseletsize=size|>in_bitsinletaddress=Domain.Binary_Forward.biconst~size:ptrsize(Z.of_int@@vaddr+idx)ctxinletzero=Domain.Binary_Forward.biconst~sizeZ.zeroctxinletmem=Domain.Memory_Forward.store~sizectxmemaddresszeroin(i,u32,mem)inlet(i,u32,mem)=inner_loopii0meminifi=filesize_mul_4thenmemelse(* let char = Loader_elf.read_offset img (phdr.p_offset + i) in *)(* Logger.result "Char is %d %x" i char; *)((* Logger.result "Writing %x at address %x" char (vaddr + i); *)letvalue=Domain.Binary_Forward.biconst~size:(32|>in_bits)(Z.of_intu32)ctxinletaddress=Domain.Binary_Forward.biconst~size:ptrsize(Z.of_int@@vaddr+i)ctxinletmem=Domain.Memory_Forward.store~size:(32|>in_bits)ctxmemaddressvalueinloop(i+4)mem)inletmem=loop0meminletrecloop_remimem=(* Logger.result "loop_rem %x" i; *)ifi=filesizethenmemelseletvalue=Domain.Binary_Forward.biconst~size:(8|>in_bits)(Z.of_int@@Loader_elf.read_offsetimgi)ctxinletaddress=Domain.Binary_Forward.biconst~size:ptrsize(Z.of_int@@vaddr+i)ctxinletmem=Domain.Memory_Forward.store~size:(8|>in_bits)ctxmemaddressvalueinloop_rem(i+1)meminloop_remfilesize_mul_4mem(* XXX: Ecrire aussi le contenu de la memoire initiale. *))memoryprogram_headersinmemoryletmk_initial_memoryload_imgctx=letmemory=Domain.Memory_Forward.unknown~level:(Domain.Context.levelctx)ctxinletptrsize=Codex.Codex_config.ptr_size()in(* Weak_shape_domain_memory.set_check_store_intvl false; *)Region.set_check_store_intvlfalse;letmemory=load_imgctxmemoryinLogger.result"*** End of memory initialization. ***";(* Weak_shape_domain_memory.set_check_store_intvl true; *)Region.set_check_store_intvltrue;memoryletinitial_memoryimgctx=letptrsize=Codex_config.ptr_size()inletimg=matchimgwith|Loader.PE_|Loader.Raw_|Loader.TI83_->assertfalse|Loader.ELFimg->imginletload_img=load_imageptrsizeimginletroot_type_sym,root_type=assertfalse(* TSettingC.root *)inletroot_type_addr=matchLoader_utils.address_of_symbol_by_name~name:root_type_sym(Loader.ELFimg)withNone->assertfalse|Somea->Virtual_address.createainmk_initial_memoryload_imgctxletconcrete_initial_memoryimg_kernel(*img_app*)ctx=letptrsize=Codex_config.ptr_size()inletimg_kernel=matchimg_kernelwith|Loader.PE_|Loader.Raw_|Loader.TI83_->assertfalse|Loader.ELFimg->imgin(*
let img_app = match img_app with
| Loader.PE _ | Loader.Raw _ | Loader.TI83 _ -> assert false
| Loader.ELF img -> img in
*)letload_img=load_imageptrsizeimg_kernelinmk_initial_memoryload_imgctxletinitial_varsctx=List.fold_left(funacc(name,size)->letvalue=Reg.initial_valuectx(name,size)in(* Logger.result "creating reg %s of size %d" name size; *)VarMap.addnamevalueacc)VarMap.emptyReg.registersletinitialimgctx=(* initialize symbol table. *){ctx;vars=initial_varsctx;memory=initial_memoryimgctx;instruction_count=0;is_bottom=false;never_went_to_user_code=true;}letinitial_concreteimg_kernel(*img_app*)ctx={ctx;vars=initial_varsctx;memory=concrete_initial_memoryimg_kernel(*img_app*)ctx;instruction_count=0;is_bottom=false;never_went_to_user_code=true;}letempty_varsctx=List.fold_left(funacc(name,size)->VarMap.addname(Domain.binary_empty~sizectx)acc)VarMap.emptyReg.registersletresetimgctx={ctx;vars=initial_varsctx;memory=initial_memoryimgctx;instruction_count=0;is_bottom=false;never_went_to_user_code=true;}letis_singletonctx~sizevalue=value|>Domain.Query.binary~sizectx|>Domain.Query.Binary_Lattice.is_singleton~size|>Option.is_someletget~sizestatereg=ifstate.is_bottomthenDomain.binary_empty~sizestate.ctxelsetryVarMap.findregstate.varswithNot_found->failwith("Could not find "^reg)letsetstateregvalue=ifnot(VarMap.memregstate.vars)thenfailwith("Setting "^reg);{statewithvars=VarMap.addregvaluestate.vars}letsetstateregvalue=letsize=List.assocregReg.registersinLogger.debug~level:2"Setting %s to %a"reg(Domain.binary_pretty~sizestate.ctx)value;ifnotstate.is_bottomthenifreg="sp"thenassert(is_singleton~size:(32|>Units.In_bits.of_int)state.ctxvalue);setstateregvalueletdump_statefmtstate=let_=Domain.Context.levelstate.ctxinifstate.is_bottomthenFormat.fprintffmt"Bottom"elseReg.registers|>List.iter(fun(reg,size)->letvalue=VarMap.findregstate.varsinFormat.fprintffmt"%s -> %a@\n"reg(Domain.binary_pretty~sizestate.ctx)value)letdump_state_difffmtoldstatestateaddressresults=Reg.registers|>List.iter(fun(reg,size)->letvalue=VarMap.findregstate.varsinletoldvalue=VarMap.findregoldstate.varsinifnot(Domain.Binary.equalvalueoldvalue)thenletadd=(Format.asprintf"%a"Virtual_address.ppaddress)inletres=(Format.asprintf"%s -> %a@\n"reg(Domain.binary_pretty~sizestate.ctx)value)inHashtbl.addresultsaddres;Format.fprintffmt"%s"res(* (Domain.binary_pretty ~size ctx) value *))letassumecondstate=matchDomain.assumestate.ctxcondwith|None->Log.debug(funp->p"Cond is %a, setting state to bottom"(Domain.boolean_prettystate.ctx)cond);{statewithis_bottom=true}(* TODO: We should probably replace is_bottom with an option type *)|Somectx->{statewithctx}(* let assume_pred ~size pred v state =
let cond = Type.cond_of_pred_subdomain ~size state.ctx pred v in
assume cond state *)letbottomctx=(* assume ctx (Domain.Boolean_Forward.false_ ctx) *){ctx;instruction_count=0;vars=empty_varsctx;memory=Domain.Memory_Forward.unknown~level:(Domain.Context.levelctx)ctx;is_bottom=true;never_went_to_user_code=true;}(*
let pointer_registers = [
"eax"; "ebx"; "ecx"; "edx"; "edi"; "esp"; "ebp"; "esi";
"res32"; "temp32"; "temp32_0"; "temp32_1"; "temp32_2"; "temp32_3";
(* "gdt";
("ds_base",32); ("cs_base",32); ("ss_base",32); ("es_base",32);
("fs_base",32); ("gs_base",32);
("tr_base",32);
("idt",32); *)
] ;;
*)letserialize~widensstate_astate_b(included,acc)=Log.debug(funp->p"State.serialize %a %a"dump_statestate_adump_statestate_b);(* let state_a = if state_a.is_bottom then bottom state_a.ctx else state_a in
let state_b = if state_b.is_bottom then bottom state_b.ctx else state_b in *)ifstate_a.is_bottomthenDomain.Context.Result(included,acc,functxout->state_b,out)elseifstate_b.is_bottomthenDomain.Context.Result(included,acc,functxout->state_a,out)elseletDomain.Context.Result(included,acc,fvar)=VarMap.fold_on_diff2state_a.varsstate_b.vars(Domain.Context.Result(included,acc,fun_ctxout->state_a.vars,out))(funvarab(Domain.Context.Result(inc,acc,k))->matcha,bwith|Somea,Somebwhena==b->Domain.Context.Result(inc,acc,k)|Somea,Someb->letsize=List.assocvarReg.registersinLogger.result"### register %s:@ a =@ %a,@ b =@ %a"var(Domain.binary_pretty~sizestate_a.ctx)a(Domain.binary_pretty~sizestate_b.ctx)b;letDomain.Context.Result(inc,acc,d)=Domain.serialize_binary~widens~sizestate_a.ctxastate_b.ctxb(inc,acc)inDomain.Context.Result(inc,acc,functxout->letv,out=dctxoutinletmap,out=kctxoutin(VarMap.addvarvmap,out))|_,_->assertfalse(* Variables should be defined in both cases *))in(* let entries = List.map (fun var -> (VarMap.find var state_a.vars, VarMap.find var state_b.vars)) pointer_registers in *)(* let Domain.Context.Result (included, acc, fmem) = Domain.serialize_memory_and_cache state_a.ctx state_a.memory state_b.ctx state_b.memory
entries (included,acc) in *)letDomain.Context.Result(included,acc,fmem)=Domain.serialize_memory~widensstate_a.ctxstate_a.memorystate_b.ctxstate_b.memory(included,acc)inDomain.Context.Result(included,acc,functxout->letmemory,out=fmemctxoutinletvars,out=fvarctxoutin{ctx;memory;vars;instruction_count=maxstate_a.instruction_countstate_b.instruction_count;is_bottom=state_a.is_bottom&&state_b.is_bottom;never_went_to_user_code=state_a.never_went_to_user_code&&state_b.never_went_to_user_code;},out);;letjoinab=ifa.is_bottomthenbelseifb.is_bottomthenaelseletDomain.Context.Result(_,acc,f)=serialize~widens:falseab(true,Domain.Context.empty_tuple())inletctx,out=Domain.typed_nondet2a.ctxb.ctxaccinletr,_=fctxoutin(* {r with ctx} *)rletis_includedab=ifa.is_bottomthentrueelseifb.is_bottomthenfalseelseletDomain.Context.Result(included,_,_)=serialize~widens:falseba(true,Domain.Context.empty_tuple())inincludedendletz_of_bitvectorx=Z.of_string@@Bitvector.to_hexstringxletbitvector_of_zx=letstring=Z.format"#x"xinBitvector.of_hexstringstringletbin_of_boolctxcond=Domain.Binary_Forward.bofbool~size:Units.In_bits.onectxcondletbool_of_binctxbin=letzero=Domain.Binary_Forward.biconst~size:Units.In_bits.oneZ.zeroctxinletcondelse=Domain.Binary_Forward.beq~size:Units.In_bits.onectxbinzeroinletcondthen=Domain.Boolean_Forward.notctxcondelseincondthenletjoin_binary_and_state~sizeab=matcha,bwith|Some(bin_a,state_a),Some(bin_b,state_b)->letDomain.Context.Result(inc,tup,fstate)=State.serialize~widens:falsestate_astate_b(true,Domain.Context.empty_tuple())inletDomain.Context.Result(_,tup,fbin)=Domain.serialize_binary~widens:false~sizestate_a.ctxbin_astate_b.ctxbin_b(inc,tup)inletctx,out=Domain.typed_nondet2state_a.ctxstate_b.ctxtupinletbin,out=fbinctxoutinletstate,_=fstatectxoutinSome(bin,state)|Some_,_->b|_->aletbinop_to_stringbop=letopenDbainletopenBinary_opinmatchbopwith|Plus->"+"|Minus->"-"|Mult->"*"|DivU->"/u"|DivS->"/s"|ModU->"modu"|ModS->"mod"|Or->"||"|And->"&&"|Xor->"^"|Concat->"::"|LShift->"<<"|RShiftU->">>u"|RShiftS->">>"|LeftRotate->"leftRotate"|RightRotate->"rightRotate"|Eq->"="|Diff->"!="|LeqU->"<=u"|LtU->"<u"|GeqU->">=u"|GtU->">u"|LeqS->"<="|LtS->"<"|GeqS->">="|GtS->">"letrecprint_exprfmte=letsize=Dba.Expr.size_ofeinmatchewith|Var{name;_}->Format.fprintffmt"%s"name|Load(size,_,addr,_)->Format.fprintffmt"*(%a)"print_expraddr|Cst(bv)->Bitvector.ppfmtbv|Unary(Dba.Unary_op.Restrict{lo;hi},e1)->Format.fprintffmt"%a[%d..%d]"print_expre1lohi|Unary(Dba.Unary_op.Uextsize,e1)->Format.fprintffmt"uext (%a)"print_expre1|Unary(Dba.Unary_op.Sextsize,e1)->Format.fprintffmt"sext (%a)"print_expre1|Unary(Dba.Unary_op.Not,e)->Format.fprintffmt"not (%a)"print_expre|Unary(Dba.Unary_op.UMinus,e)->Format.fprintffmt"-(%a)"print_expre|Binary(bop,e1,e2)->Format.fprintffmt"(%a) %s (%a)"print_expre1(binop_to_stringbop)print_expre2|Ite(cond,e_then,e_else)->Format.fprintffmt"ite(%a, %a, %a)"print_exprcondprint_expre_thenprint_expre_elsemoduleOptionM:sigval(==>):'aoption->('a->'blist)->'blistval(>>=):'aoption->('a->'boption)->'boptionend=structlet(==>)xf=matchxwith|None->[]|Somev->(fv)let(>>=)=Option.bindendletbinop~sizebopv1v2state=letopenDbainletopenBinary_opinletopenStateinletf=matchbopwith|Plus->Domain.Binary_Forward.biadd~flags:(Operator.Flags.Biadd.pack~nsw:false~nuw:false~nusw:false)|Minus->Domain.Binary_Forward.bisub~flags:(Operator.Flags.Bisub.pack~nsw:false~nuw:false~nusw:false)|Mult->Domain.Binary_Forward.bimul~flags:(Operator.Flags.Bimul.pack~nsw:false~nuw:false)|DivU->Domain.Binary_Forward.biudiv|DivS->Domain.Binary_Forward.bisdiv|ModU->Domain.Binary_Forward.biumod|ModS->Domain.Binary_Forward.bismod|Or->Domain.Binary_Forward.bor|And->Domain.Binary_Forward.band|Xor->Domain.Binary_Forward.bxor|Concat->assertfalse|LShift->Domain.Binary_Forward.bshl~flags:(Operator.Flags.Bshl.pack~nsw:false~nuw:false)|RShiftU->Domain.Binary_Forward.blshr|RShiftS->Domain.Binary_Forward.bashr|LeftRotate->assertfalse|RightRotate->fun~sizectx__->(* FIXME: right rotation is fully imprecise *)bunknown~sizectx|Eq->fun~sizectxv1v2->bin_of_boolctx@@Domain.Binary_Forward.beq~sizectxv1v2|Diff->fun~sizectxv1v2->bin_of_boolctx@@Domain.Boolean_Forward.notctx@@Domain.Binary_Forward.beq~sizectxv1v2|LeqU->fun~sizectxv1v2->bin_of_boolctx@@Domain.Binary_Forward.biule~sizectxv1v2|LtU->fun~sizectxv1v2->letcond=Domain.Binary_Forward.biule~sizectxv2v1inletcond=Domain.Boolean_Forward.notctxcondinbin_of_boolctxcond|GeqU->fun~sizectxv1v2->letcond=Domain.Binary_Forward.biule~sizectxv2v1inbin_of_boolctxcond|GtU->fun~sizectxv1v2->letcond=Domain.Binary_Forward.biule~sizectxv1v2inletcond=Domain.Boolean_Forward.notctxcondinbin_of_boolctxcond|LeqS->fun~sizectxv1v2->letcond=Domain.Binary_Forward.bisle~sizectxv1v2inbin_of_boolctxcond|LtS->fun~sizectxv1v2->letcond=Domain.Binary_Forward.bisle~sizectxv2v1inletcond=Domain.Boolean_Forward.notctxcondinbin_of_boolctxcond|GeqS->assertfalse|GtS->fun~sizectxv1v2->letcond=Domain.Binary_Forward.bisle~sizectxv1v2inletcond=Domain.Boolean_Forward.notctxcondinbin_of_boolctxcondinmatchbopwith|Plus->letc=Domain.Binary_Forward.valid_ptr_arith~sizeOperator.Plusstate.ctxv1v2inLogger.check"ptr_arith";beginmatchDomain.query_booleanstate.ctxcwith|Lattices.Quadrivalent.(True|Bottom)->Domain.Binary_Forward.biadd~flags:(Operator.Flags.Biadd.pack~nsw:false~nuw:false~nusw:false)~sizestate.ctxv1v2,state|Lattices.Quadrivalent.(False|Top)->Logger.alarmOperator.Alarm.Ptr_arith;letstate=State.assumecstateinletres=Domain.Binary_Forward.biadd~flags:(Operator.Flags.Biadd.pack~nsw:false~nuw:false~nusw:false)~sizestate.ctxv1v2inres,stateend|Minus->(* let minus_v2 = Domain.Binary_Forward.bisub ~size ~nsw:false ~nuw:false ~nusw:false state.ctx
(Domain.Binary_Forward.biconst ~size Z.zero state.ctx) v2 in
let c = Domain.Binary_Forward.valid_ptr_arith ~size state.ctx v1 minus_v2 in *)letc=Domain.Binary_Forward.valid_ptr_arith~sizeOperator.Minusstate.ctxv1v2inLogger.check"ptr_arith";beginmatchDomain.query_booleanstate.ctxcwith|Lattices.Quadrivalent.(True|Bottom)->Domain.Binary_Forward.bisub~flags:(Operator.Flags.Bisub.pack~nsw:false~nuw:false~nusw:false)~sizestate.ctxv1v2,state|Lattices.Quadrivalent.(False|Top)->Logger.alarmOperator.Alarm.Ptr_arith;letstate=State.assumecstateinletres=Domain.Binary_Forward.bisub~flags:(Operator.Flags.Bisub.pack~nsw:false~nuw:false~nusw:false)~sizestate.ctxv1v2inres,stateend|_->f~sizestate.ctxv1v2,stateletrecexpr'(e,locid)state=letopenOptionMinletopenDba.ExprinletopenDba.Binary_opinletopenStateinletopenBasic_typesinletsize=Dba.Expr.size_ofe|>Units.In_bits.of_intinletexpr~idxtarget_estate=letlocated_e=Binsec2syntax_tree.check_expr_idx(e,locid)~idxtarget_einexprlocated_estateinmatchewith|Var{name;_}->Some(State.get~sizestatename,state)|Load(size_in_bytes,_,addr,_)->letsize=Units.In_bytes.of_intsize_in_bytes|>Units.In_bytes.in_bitsinexpr~idx:0addrstate>>=fun(loc,state)->Logger.check"array_offset_access";letvalid=Domain.Binary_Forward.valid~sizeOperator.Readstate.ctxlocinletstate=beginmatchDomain.query_booleanstate.ctxvalidwith|Lattices.Quadrivalent.(True|Bottom)->state|_->letstate=State.assumevalidstatein(*
(* VERY UGLY *)
(* we also verify (and if not, assume) that the index is smaller than
* the array size *)
let open TypedC in
let state = match loc with
| Region_binary_inst.T {typ = Type.ParamT ({descr = Ptr{pointed={descr = Array (_, ar_sz);_};_};_}, idx, _, _); _} ->
let isize = Type.array_index_size in
let module BR = Region_numeric.Operable_Value in
let binary_of_value ~size ctx = function
| Const x -> BR.Binary_Forward.biconst ~size x ctx
| Sym s -> Type.symbol ctx s
in
begin match ar_sz with
| Some sz ->
(* [i <u s] implies that [i <s s], provided that [s >=s 0].
* Here we assume [s >=s 0].
* [i <u s] also implies [i <=s s], which is what we
* need to verify. Incidentally, [i <u s] is the
* condition that will be the easiest to verify with our
* term based domain and hypotheses on array sizes. *)
let sz_b = binary_of_value ~size:isize ctx sz in
let c3 = BR.Scalar.Boolean_Forward.not ctx @@
BR.Binary_Forward.biule ~size:isize ctx sz_b idx in
begin match Domain.Query.(convert_to_quadrivalent (boolean ctx c3)) with
| Lattices.Quadrivalent.True | Lattices.Quadrivalent.Bottom -> state
| _ ->
*)Emit_alarm.emit_alarmOperator.Alarm.Invalid_load_access;(*
State.assume ctx c3 state
end
| None -> state
end
| _ -> assert false (* should not happen if condition was not verified *)
in
*)stateendinletres,mem=Domain.Memory_Forward.load~sizestate.ctxstate.State.memorylocinSome(res,{statewithState.memory=mem})|Cst(bv)->letsize=Bitvector.size_ofbv|>in_bitsinletvalue=z_of_bitvectorbvinSome(Domain.Binary_Forward.biconst~sizevaluestate.ctx,state)|Unary(Dba.Unary_op.Restrict{lo;hi},e1)->letoldsize=Dba.Expr.size_ofe1|>in_bitsinassert((size:>int)==1+hi-lo);expr~idx:0e1state>>=fun(v,state)->Some(Domain.Binary_Forward.bextract~size~index:(lo|>in_bits)~oldsizestate.ctxv,state)|Unary(Dba.Unary_op.Uextsize,e1)->letoldsize=Dba.Expr.size_ofe1|>in_bitsinexpr~idx:0e1state>>=fun(v1,state)->Some(Domain.Binary_Forward.buext~size:(size|>in_bits)~oldsizestate.ctxv1,state)|Unary(Dba.Unary_op.Sextsize,e1)->letoldsize=Dba.Expr.size_ofe1|>in_bitsinexpr~idx:0e1state>>=fun(v1,state)->Some(Domain.Binary_Forward.bsext~size:(size|>in_bits)~oldsizestate.ctxv1,state)|Unary(Dba.Unary_op.Not,e)->expr~idx:0estate>>=fun(v,state)->if(size:>int)=1thenletv=Domain.Boolean_Forward.notstate.ctx(bool_of_binstate.ctxv)inSome(bin_of_boolstate.ctxv,state)elseletzero=Domain.Binary_Forward.biconst~sizeZ.zerostate.ctxinletv=Domain.Binary_Forward.bxor~sizestate.ctxvzeroinSome(v,state)|Unary(Dba.Unary_op.UMinus,e)->expr~idx:0estate>>=fun(v,state)->letv=Domain.Binary_Forward.bisub~size~flags:(Operator.Flags.Bisub.pack~nsw:false~nuw:false~nusw:false)state.ctx(Domain.Binary_Forward.biconst~sizeZ.zerostate.ctx)vinSome(v,state)|Binary((RightRotate|LeftRotate),e1,Dba.Expr.Cst(bv))whenBitvector.is_zerosbv->expr~idx:0e1state|Binary(Dba.Binary_op.Concat,e1,e2)->expr~idx:0e1state>>=fun(v1,state)->expr~idx:1e2state>>=fun(v2,state)->letsize1=Dba.Expr.size_ofe1|>in_bitsinletsize2=Dba.Expr.size_ofe2|>in_bitsinSome(Domain.Binary_Forward.bconcat~size1~size2state.ctxv1v2,state)|Binary(bop,(Cst(bv1)ase1),e2)->expr~idx:0e1state>>=fun(v1,state)->expr~idx:1e2state>>=fun(v2,state)->letsize1=Dba.Expr.size_ofe1|>in_bitsinletsize2=Dba.Expr.size_ofe2|>in_bitsinletv1'=ifsize1<>size2thenletvalue1=z_of_bitvectorbv1inDomain.Binary_Forward.biconst~size:size2value1state.ctxelsev1inSome(binop~size:size2bopv1'v2state)|Binary(bop,e1,(Cst(bv2)ase2))->expr~idx:0e1state>>=fun(v1,state)->expr~idx:1e2state>>=fun(v2,state)->letsize1=Dba.Expr.size_ofe1|>in_bitsinletsize2=Dba.Expr.size_ofe2|>in_bitsinletv2'=ifsize1<>size2thenletvalue2=z_of_bitvectorbv2inDomain.Binary_Forward.biconst~size:size1value2state.ctxelsev2inSome(binop~size:size1bopv1v2'state)|Binary(bop,e1,e2)->expr~idx:0e1state>>=fun(v1,state)->expr~idx:1e2state>>=fun(v2,state)->letsize1=Dba.Expr.size_ofe1|>in_bitsinletsize2=Dba.Expr.size_ofe2|>in_bitsinassert(size1==size2);assert(size==In_bits.one||size==size1);(* Predicate, or regular binary operator. *)Some(binop~size:size1bopv1v2state)|Ite(cond,e_then,e_else)->expr~idx:0condstate>>=fun(cond_abs,state)->letzero=Domain.Binary_Forward.biconst~size:In_bits.oneZ.zerostate.ctxinletcondelse=Domain.Binary_Forward.beq~size:In_bits.onestate.ctxcond_abszeroinletcondthen=Domain.Boolean_Forward.notstate.ctxcondelseinbeginmatchDomain.query_booleanstate.ctxcondelsewith|Lattices.Quadrivalent.Bottom->None|Lattices.Quadrivalent.True->(* Careful: we tested the value of the "else" condition, so in this
* case we must take the "else" branch *)expr~idx:2e_elsestate|Lattices.Quadrivalent.False->expr~idx:1e_thenstate|Lattices.Quadrivalent.Top->(* Codex_log.debug "passing by If Then Else expression : joining paths" ; *)letstate_then=State.assumecondthenstateinletstate_else=State.assumecondelsestateinjoin_binary_and_state~size(expr~idx:1e_thenstate_then)(expr~idx:2e_elsestate_else)endandexpr(e,locid)state=LogExpression.trace(funp->p"%a"Dba_printer.Ascii.pp_expre)~loc:(Codex_options.Location.Dba_expr{locid;dba_expr=e})~bintrace:locid~pp_ret:(funfmtres->letsize=Dba.Expr.size_ofe|>in_bitsinmatchreswith|None->Format.fprintffmt"None"|Some(x,state)->letctx=state.State.ctxinDomain.binary_pretty~sizectxfmtx)(fun()->expr'(e,locid)state)letstatic_target_to_address=letopenDbainfunction|JInnerid->Jump_Innerid|JOuter{base;id}->assert(id==0);Jump_Outerbaselet_start=letcache=refNoneinfunctionimg->match!cachewith|Somea->a|None->beginmatchLoader_utils.address_of_symbol_by_name~name:"_start"(Lazy.forceimg)with|None->assertfalse|Somea->cache:=Somea;aendlet_end_of_kernel=letcache=refNoneinfunctionimg->match!cachewith|Somea->a|None->beginmatchLoader_utils.address_of_symbol_by_name~name:"_end_of_kernel"(Lazy.forceimg)with|None->assertfalse|Somea->cache:=Somea;aendlet_idle=letcache=refNoneinfunctionimg->match!cachewith|Somea->a|None->beginmatchLoader_utils.address_of_symbol_by_name~name:"idle"(Lazy.forceimg)with|None->assertfalse|Somea->cache:=Somea;aendletinstr'state(instr,locid)=letopenDbainletopenStateinletopenOptionMinletopenDba.Instrinletexpr~idxestate=(* Check that the idx correspond to the expression obtained by
pattern matching. *)letlocated_e=Binsec2syntax_tree.check_instr_idx(instr,locid)~idxeinexprlocated_estateinmatchinstrwith|Assign(LValue.Var{name=v;_},e,id)->(*let bin,state = expr ctx e state in*)expr~idx:0estate==>fun(bin,state)->[Jump_Innerid,State.setstatevbin]|Assign(LValue.Store(size,_,addr,_),e,id)->expr~idx:1estate==>fun(v,state)->expr~idx:0addrstate==>fun(loc,state)->letsize=((size*8)|>in_bits)inletvalid=Domain.Binary_Forward.valid~sizeOperator.Writestate.ctxlocinletstate=beginmatchDomain.query_booleanstate.ctxvalidwith|Lattices.Quadrivalent.(True|Bottom)->state|_->letstate=State.assumevalidstateinEmit_alarm.emit_alarmOperator.Alarm.Invalid_store_access;(*
(* VERY UGLY *)
(* we also verify (and if not, assume) that the index is smaller than
* the array size *)
let open TypedC in
let state = match loc with
| Region_binary_inst.T {typ = Type.ParamT ({descr = Ptr{pointed={descr = Array (_, ar_sz);_};_};_}, idx, _, _); _} ->
let isize = Type.array_index_size in
let module BR = Region_numeric.Operable_Value in
let binary_of_value ~size ctx = function
| Const x -> BR.Binary_Forward.biconst ~size x ctx
| Sym s -> Type.symbol ctx s
in
begin match ar_sz with
| Some sz ->
(* [i <u s] implies that [i <s s], provided that [s >=s 0].
* Here we assume [s >=s 0].
* [i <u s] also implies [i <=s s], which is what we
* need to verify. Incidentally, [i <u s] is the
* condition that will be the easiest to verify with our
* term based domain and hypotheses on array sizes. *)
let sz_b = binary_of_value ~size:isize ctx sz in
let c3 = BR.Scalar.Boolean_Forward.not ctx @@
BR.Binary_Forward.biule ~size:isize ctx sz_b idx in
begin match Domain.Query.(convert_to_quadrivalent (boolean ctx c3)) with
| Lattices.Quadrivalent.True | Lattices.Quadrivalent.Bottom -> state
| _ ->
Logger.error "-alarm- array_offset_access";
State.assume ctx c3 state
end
| None -> state
end
| _ -> assert false (* should not happen if condition was not verified *)
in
*)stateendinletmemory=Domain.Memory_Forward.store~sizestate.ctxstate.State.memorylocvin[Jump_Innerid,{statewithState.memory}]|Assign(LValue.Restrict({name=v;_},Interval.({lo;hi})),e_rval,id)->letsize_v=List.assocvReg.registersinexpr~idx:0e_rvalstate==>fun(rval,state)->Logger.debug~level:3"%s{%d,%d} := %a"vlohi(Domain.binary_pretty~size:size_vstate.ctx)rval;letlo=lo|>in_bitsandhi=hi|>in_bitsinletinitial=State.get~size:size_vstatevinletwritten_size=In_bits.(one+hi-lo)inletnew_v=iflo=In_bits.zerothenrvalelseletlsb=Domain.Binary_Forward.bextract~size:lo~index:In_bits.zero~oldsize:size_vstate.ctxinitialinDomain.Binary_Forward.bconcat~size1:written_size~size2:lostate.ctxrvallsbinletnew_v=ifhi=In_bits.(size_v-one)thennew_velseletmsb=Domain.Binary_Forward.bextract~size:In_bits.(size_v-hi-one)~index:In_bits.(hi+one)~oldsize:size_vstate.ctxinitialinDomain.Binary_Forward.bconcat~size1:In_bits.(size_v-hi-one)~size2:In_bits.(hi+one)state.ctxmsbnew_vin[Jump_Innerid,State.setstatevnew_v]|If(cond,target,id)->expr~idx:0condstate==>fun(bin,state)->letzero=Domain.Binary_Forward.biconst~size:In_bits.oneZ.zerostate.ctxinletcondelse=Domain.Binary_Forward.beq~size:In_bits.onestate.ctxbinzeroinletcondthen=Domain.Boolean_Forward.notstate.ctxcondelsein(matchDomain.query_booleanstate.ctxcondelsewith|Lattices.Quadrivalent.Bottom->[]|Lattices.Quadrivalent.True->(* Careful: we tested the value of the "else" condition, so in this
* case we must take the "else" branch *)[Jump_Innerid,state]|Lattices.Quadrivalent.False->[static_target_to_addresstarget,state]|_->letstate_then=State.assumecondthenstateinletstate_else=State.assumecondelsestatein[(static_target_to_addresstarget,state_then);(Jump_Innerid,state_else)])|Stop_->Logger.result"Stop called";[]|Assert(Dba.Expr.Cstx,_)whenBitvector.is_zerox->Logger.result"Warning: assert false";[]|Assert(cond,id)->expr~idx:0condstate==>fun(cond_v,state)->letzero=Domain.Binary_Forward.biconst~size:In_bits.oneZ.zerostate.ctxinletnotc=Domain.Binary_Forward.beq~size:In_bits.onestate.ctxcond_vzeroinletcond=Domain.Boolean_Forward.notstate.ctxnotcinletc=Domain.query_booleanstate.ctxcondinletopenLattices.Quadrivalentinbeginmatchcwith|False->Logger.fatal"assert %a may be false"(Domain.binary_pretty~size:In_bits.onestate.ctx)cond_v;|Top->Log.debug(funp->p"assert %a may be false"(Domain.binary_pretty~size:In_bits.onestate.ctx)cond_v);Emit_alarm.emit_alarmOperator.Alarm.Possibly_false_assertion;[Jump_Innerid,State.assumecondstate]|Bottom->[]|True->[Jump_Innerid,state]end|Assume_->assertfalse|SJump(jt,_)->lettarget_addr=static_target_to_addressjtin(*
begin match target_addr with
| Jump_Outer target_addr ->
let lazy_img = lazy (Kernel_functions.get_img ()) in
if Virtual_address.to_int target_addr = idle lazy_img then
(* Go to mock address where the abstract task simulator should be hooked *)
[Jump_Outer (Virtual_address.create 0xcafecaf0), state]
else [Jump_Outer target_addr, state]
| Jump_Inner _ | Jump_Dynamic ->*)[target_addr,state](*end*)|DJump(e,_)->letjump(v,state)=letsize=Dba.Expr.size_ofe|>in_bitsinletinf=Z.zeroandsup=Z.shift_leftZ.one(size:>int)in(* Codex_log.debug "jump expression : %a" Dba_printer.Unicode.pp_expr e ; *)(* Codex_log.debug "in dynamic jump : %a" (Domain.binary_pretty ~size ctx) v ; *)letres=(Domain.Query.binary~sizestate.ctxv)inLogger.check"unresolved_dynamic_jump";beginDomain.Query.Binary_Lattice.fold_crop_unsigned~sizeres~inf~sup[](funiacc->(Jump_Outer(Virtual_address.of_bitvector@@bitvector_of_zi),state)::acc)endin(* Codex_log.debug "passing in DJump instruction" ; *)letptr_size=Codex_config.ptr_size()inbeginmatchewith|Load(size,_,(Binary(Plus,e1,e2)asplus_e),_)whensize=4->(* Possibly reading from a jump table. Do not join before going to each target,
and assume that the index is equal to the corresponding value in each case. *)Log.debug(funp->p"Possibly reading from a jump table");(* Log.trace ~loc:(Codex_options.Location.Dba_expr {dba_expr = plus_e}) (fun p -> p "In expression") @@ fun () -> *)(* TODO: the idx is wrong here. *)expr~idx:(-1)e1state==>fun(idx,state1)->letres=Domain.Query.binary~size:(size*8|>in_bits)state.ctxidx|>Domain.Query.Binary_Lattice.to_unsigned_interval~size:ptr_size|>fun(min,max)->Ival.inject_range(Somemin)(Somemax)in(* let res = Domain.Query.binary_to_ival ~signed:false ~size:32 @@ Domain.Query.binary ~size state.ctx idx in *)if(not@@Ival.cardinal_zero_or_oneres)&&Ival.cardinal_is_less_thanres8(* 1 < cardinal < 8 *)&&Ival.fold_int(funeltacc->(Z.equalZ.zero@@Z.remeltZ.(of_int4))&&acc)restruethenletcases=Ival.fold_int(funeltcases->letvalue=Domain.Binary_Forward.biconst~size:ptr_sizeeltstate.ctxinletcond=Domain.Binary_Forward.beq~size:ptr_sizestate.ctxvalueidxin(* let new_state = State.assume ctx cond state1 in *)letnew_state=State.assumecondstatein(expr~idx:0enew_state==>jump)@cases)res[]incaseselseexpr~idx:0estate==>jump|_->expr~idx:0estate==>jumpend|Undef(Dba.LValue.Var{name=v;_},id)->letsize=List.assocvReg.registersinletnew_v=bunknown~sizestate.ctxin[Jump_Innerid,State.setstatevnew_v]|_->assertfalseletinstrstate(instruction,locid)=LogDbaInstr.trace(funp->p"%a"Dba_printer.Ascii.pp_instructioninstruction)~loc:(Codex_options.Location.Dba_instr{locid;dba_instr=instruction})~bintrace:locid(fun_->instr'state(instruction,locid))endend