123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418(**************************************************************************)(* 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). *)(* *)(**************************************************************************)moduleTbl=Virtual_address.HtblmoduleStrMap=Codex.Utils.Datatype_sig.StringMapmoduleLog=Codex_loggerlet()=Log.channel_set_colortrueLog.error_channelmoduleTypedC=Codex.Types.TypedC(* This module is already defined in Stdlib for versions >= 4.08.0. *)moduleResult=structlet(>>)rf=matchrwith|Ok_->f()|Error_ase->eletiter_errorf=function|Ok_->()|Errore->feendmodulePath:sigtypetvalatom:string->tvalfield:t->string->tvalarray_elem:t->int->tvalderef:t->tvalpp:Format.formatter->t->unitend=structtypet=Atomofstring|PFieldoft*string|PElemoft*int|PDerefoftletatoms=Atomsletfieldpf=PField(p,f)letarray_elempi=PElem(p,i)letderefp=PDerefpletrecpp_auxprecfmt=letopenFormatinfunction|Atoms->pp_print_stringfmts|PField(PDerefp,f)->ifprec>2thenfprintffmt"(%a->@,%s)"(pp_aux2)pfelsefprintffmt"%a->@,%s"(pp_aux2)pf|PField(p,f)->ifprec>2thenfprintffmt"(%a.@,%s)"(pp_aux2)pfelsefprintffmt"%a.@,%s"(pp_aux2)pf|PElem(p,i)->ifprec>3thenfprintffmt"(%a[%d])"(pp_aux3)pielsefprintffmt"%a[%d]"(pp_aux3)pi|PDerefp->ifprec>1thenfprintffmt"(*%a)"(pp_aux1)pelsefprintffmt"*%a"(pp_aux1)pletppfmt=pp_aux0fmtendtypecell_typ=TypedC.typ*Units.In_bytes.tletptr_size=4letpp_cell_typfmt((t,i):cell_typ)=Format.fprintffmt"(%a,@,%d)"TypedC.ppt(i:>int)exceptionIncomparable_typesofstringexceptionFalsified_predicateofstringexceptionType_errorofstringletprint_exc(f:('a,Format.formatter,unit)format->'a)=function|Incomparable_typesmsg->f"Incomparable_types:@ %s"msg|Falsified_predicatemsg->f"Falsified_predicate:@ %s"msg|Type_errormsg->f"Type_error:@ %s"msg|_->assertfalseletprint_exce=print_exc(funpp->Log.alarmOperator.Alarm.Heap_typing;Log.errorpp)eletiter_errorres=Log.check"heap_typing";Result.iter_errorprint_excresleteval_valuesyms=letopenTypedCinfunction|Syms->(tryStrMap.findssymswithNot_found->assertfalse(* raise @@ Pred.Undefined_symbol s *))leteval_array_lengthsyms=letopenTypedCinfunction|Fixed_lengthx->x|Variable_lengths->(tryStrMap.findssymswithNot_found->assertfalse(* raise @@ Pred.Undefined_symbol s *))(** Returns the type at a given offset of a non-scalar type.
@param binary zero a [BR.binary] of value zero and of size
[Type.offset_size].
@return a triple (subtyp, idx, ofs) such that [offset] is in [subtyp] at
index [idx] (if [subtyp] is an array) and offset [ofs].
*)(* TODO: This is a duplicate of the function in type_domain, which should actually go in ctypes. *)lettype_at_offsetsymbolstypoffset:cell_typ=(*
Log.result "type_at_offset.@ typ =@ %a.@ offset =@ %d"
TypedC.pp_descr typ
offset;
*)letopenTypedCinletoffset=offsetinmatch(inlinedtyp).descrwith|Structurest->beginletmembers=List.sort_uniq(fun(x,_,_)(y,_,_)->Stdlib.comparexy)st.st_membersinletstruct_size=st.st_byte_sizeinletrecloopcur_field=function|(ofs,_,_)asfield::fieldswhenoffset>=ofs->loopfieldfields|_->let(prec_ofs,_fname,ftyp)=cur_fieldinifoffset>=struct_sizethenbeginletmsg=Format.asprintf"Offset %d not found in structure %a."(offset:>int)TypedC.pptypinraise(Type_errormsg)end;(*
Logger.result "Access to field %s : %a (offset %d) of struct %a"
fname
TypedC.pp ftyp
prec_ofs
TypedC.pp_descr typ;
*)ftyp,Units.In_bytes.(offset-prec_ofs)inletfst_field=tryList.hdmemberswithFailure_->raise(Invalid_argument"type_at_offset: empty structure")inloopfst_field(List.tlmembers)end|Array(elem_typ,l)->letnb_elem=eval_array_lengthsymbolslinletelem_sz=TypedC.sizeofelem_typinletarray_index=(offset:>int)/(elem_sz:>int)in(* Already emitted in Dba2Codex *)(*Logger.check "array_offset_access";*)ifnot(array_index>=0&&Z.lt(Z.of_intarray_index)nb_elem)thenbeginletmsg=Format.asprintf"@[<hov 2>Out-of-bounds access at index %d in array of \
size %a.@]"array_indexZ.pp_printnb_eleminraise(Type_errormsg)end;letoffset_in_elem=(offset:>int)mod(elem_sz:>int)inelem_typ,(offset_in_elem|>Units.In_bytes.of_int)|_whenoffset=Units.In_bytes.zero->(* This is a scalar type and does not have any fields. *)raiseExit|_->letmsg=Format.asprintf"@[<hov 2>Access in type %a \
with non-zero offset %d is not allowed@]"TypedC.pptyp(offset:>int)inraise(Type_errormsg)letsubtypesymbols((t,i):cell_typ)((u,j):cell_typ)=(*Log.result "subtype@ %a@ %a" pp_cell_typ (t,i) pp_cell_typ (u,j);*)letopenTypedCinifTypedC.equivtu&&i=jthen(Pred.is_trueu.pred)||t.pred=u.predelseifnot(Pred.is_trueu.pred||t.pred=u.pred)thenfalseelseletrecparentsxofs=tryletparent_t,parent_ofs=type_at_offsetsymbolsxofsin(x,ofs)::parentsparent_tparent_ofswith|Exit->[(x,ofs)]|Type_error_->(* If the access is ill-typed, then the next parent is "top", and
* therefore our ascending chain stops here. *)[(x,ofs)]inletparents_t,parents_u=parentsti,parentsujin(* Tells whether [l2] is a prefix of [l1]. *)letrecwalkl1l2=matchl1,l2with|[],[]->(* This should only happen for equal cell types, handled above in
* this function *)assertfalse|[],_->false|_,[]->true|(x,ox)::r1,(y,oy)::r2->ifTypedC.equivxy&&ox==oythenwalkr1r2elsefalseinwalk(List.revparents_t)(List.revparents_u)moduletypeMEMORY=sigtypetvalread_u8:t->Virtual_address.t->Loader_types.u8valread_u32:t->Virtual_address.t->Loader_types.u32valread_u64:t->Virtual_address.t->Loader_types.u64endmoduleMake(Memory:MEMORY)=structletarray_sizesymbolselem_tlength=(* Will raise [Overflow] if result does not fit on 32 bits. *)Int32.to_int@@Z.to_int32@@Z.mul(eval_array_lengthsymbolslength)(Z.of_int@@(TypedC.sizeofelem_t:>int))(* Assign a typ or not to a one-byte cell, depending on the possible types
* already assigned to it. *)lettype_cella(typ:cell_typ)(typing,symbols,to_check)=beginmatchTbl.findtypingawith|typ'whensubtypesymbolstyp'typ->(* The registered type for this address is compatible and more precise.
* This should have been detected earlier. *)assertfalse|typ'whensubtypesymbolstyptyp'->(* The registered type for this address is strictly less precise:
* erase the previous type. *)(*Log.result "Assigning %a the type %a" Virtual_address.pp a pp_cell_typ typ;*)Tbl.replacetypingatyp;|u->(* Address would have two incomparable types. This is an error. *)letmsg=Format.asprintf"incomparable types@ at address@ %a:@ %a and %a"Virtual_address.ppapp_cell_typtyppp_cell_typuinraise@@Incomparable_typesmsg|exceptionNot_found->(* Address not visited yet. Register [t] as its type. *)(*Log.result "Assigning %a the type %a" Virtual_address.pp a pp_cell_typ typ;*)Tbl.addtypingatyp;end;matchTbl.findto_checka,typwith|t,(u,_)whenTypedC.equivtu->Tbl.removeto_checka|_->()|exceptionNot_found->()lettype_regionstart_addrtypsizestate=tryfori=0tosize-1dotype_cell(Virtual_address.add_intistart_addr)(typ,Units.In_bytes.of_inti)statedonewithIncomparable_typesmsg->Log.error"%s"msg(* Check that a 4 KiB memory page may does not intersect the kernel address space. *)letcheck_4k_mem_pagestart_addr=Log.result"check_4k_mem_page@ %a"Virtual_address.ppstart_addr;letc1=Virtual_address.compare(Virtual_address.create0x12000000)start_addr<0inletc2=Virtual_address.compare(Virtual_address.add_int(4096*4)start_addr)(Virtual_address.create0x12031000)>0inifc1||c2thenLog.error"Memory page of size 4 KiB at %a intersects with forbidden space"Virtual_address.ppstart_addrleteval_pred~symbols~selfpred=assertfalselettype_heap~symbols(mem:Memory.t)(init_addr:Virtual_address.t)(init_type:TypedC.typ)=letopenTypedCinlettyping:cell_typTbl.t=Tbl.create4000inletto_check:TypedC.typTbl.t=Tbl.create10inletstate=typing,symbols,to_checkin(* Checking and propagating a type for a memory region:
1. assigning the appropriate types to bytes in the region, if possible,
otherwise it's an error
2. check that values in the region satisfy all the predicates of the type
3. recurse on pointers.
The order of steps 1 and 2 could be changed, but this one seems more
efficient in case of error.
*)letreccheck_regionpathstart_addrtyp=ifPred.is_truetyp.predthenOk()elsematchtyp.descrwith|Void|Function_->assertfalse|Base(size,_)->letsize=(size:>int)inletvalue=ifsize=1thenMemory.read_u8memstart_addrelseifsize=4thenMemory.read_u32memstart_addr(* FIXME: this is wrong because {Loader_types.u64} = {int} *)elseifsize=8thenMemory.read_u64memstart_addrelseassertfalseinifnot@@eval_pred~symbols~self:(Z.of_intvalue)typ.predthenletmsg=Format.asprintf"Path@ %a:@ value@ %x@ at@ %a@ (size %d)@ falsifies@ %a"Path.pppathvalueVirtual_address.ppstart_addrsizePred.pptyp.predinError(Falsified_predicatemsg)elseOk()|Ptr_->letvalue=Memory.read_u32memstart_addrinifnot@@eval_pred~symbolstyp.pred~self:(Z.of_intvalue)thenletmsg=Format.asprintf"Path@ %a:@ pointer@ %x@ at@ %a@ falsifies@ %a"Path.pppathvalueVirtual_address.ppstart_addrPred.pptyp.predinError(Falsified_predicatemsg)elseOk()|Array(elem_t,length)->letnb_elem=Int32.to_int@@Z.to_int32@@eval_array_lengthsymbolslengthinletelem_sz=(TypedC.sizeofelem_t:>int)inletrecloop=function|iwheni>=nb_elem->Ok()|i->letopenResultincheck_region(Path.array_elempathi)(Virtual_address.add_int(elem_sz*i)start_addr)elem_t>>fun()->loop(i+1)inloop0|StructureFAM_->assertfalse|Structure{st_members;_}->letrecloop=function|[]->Ok()|((offset:Units.In_bytes.t),fname,ftyp)::tl->letopenResultincheck_region(Path.fieldpathfname)(Virtual_address.add_int(offset:>int)start_addr)ftyp>>fun()->looptlinloopst_members|Application_->failwith("Use of a non-applied constructor type")|Existential_->failwith("Use of a non-instantiated existential type")|Union_->failwith("Use of an ambiguous union type")|Weak_->failwith("Use of an ambiguous weak type")andpropagate_typepath~mutate(typ:TypedC.typ)a=matchtyp.descrwith|Void|Function_->(* Seems safer to mark this as an unreachable case and treat "pointers
* to void" and "pointer to function" as a special case. *)assertfalse|Base(size,_)->ifmutatethentype_regionatyp(size:>int)state;check_regionpathatyp|>iter_error|Ptr{pointed=pointed_t}->ifmutatethentype_regionatypptr_sizestate;check_regionpathatyp|>iter_error;beginmatchpointed_t.descrwith|Void->(* We need only check that the pointer is valid. *)letv=Memory.read_u32memainifv<>0thenbegintryignore@@Memory.read_u8mem@@Virtual_address.createvwithInvalid_argument_->Log.error"Path@ %a:@ Pointer of type void* is invalid."Path.pppathend|Function_->ifMemory.read_u32mema<>0thenLog.error"Path@ %a:@ Non-null function pointer: not checking address."Path.pppath|Base_|Structure_|StructureFAM_|Array_|Ptr_->letv=Memory.read_u32memainifv<>0thenpropagate_guarded(Path.derefpath)~mutate:truepointed_t(Virtual_address.createv)|Application_->failwith("Use of a non-applied constructor type")|Existential_->failwith("Use of a non-instantiated existential type")|Union_->failwith("Use of an ambiguous union type")|Weak_->failwith("Use of an ambiguous weak type")end|Array(elem_t,sz)->letnb_elem=Int32.to_int@@Z.to_int32@@eval_array_lengthsymbolsszinletelem_sz=TypedC.sizeofelem_tinletar_size=array_sizesymbolselem_tszinifmutatethentype_regionatypar_sizestate;check_regionpathatyp|>iter_error;fori=0tonb_elem-1dopropagate_type(Path.array_elempathi)~mutate:falseelem_t(Virtual_address.add_int(i*(elem_sz:>int))a)done|Structure{st_byte_size=st_sz;st_members;_}->ifmutatethentype_regionatyp(st_sz:>int)state;check_regionpathatyp|>iter_error;st_members|>List.iter(fun((offset:Units.In_bytes.t),fname,ftyp)->propagate_type(Path.fieldpathfname)~mutate:falseftyp(Virtual_address.add_int(offset:>int)a))|StructureFAM_->assertfalse|Application_->failwith("Use of a non-applied constructor type")|Existential_->failwith("Use of a non-instantiated existential type")|Union_->failwith("Use of an ambiguous union type")|Weak_->failwith("Use of an ambiguous weak type")andpropagate_guardedpath~mutatetypa=Log.result"propagate_guarded@ %a@ ~mutate:%B@ %a@ %a"Path.pppathmutateTypedC.pptypVirtual_address.ppa;matchTbl.findtypingawith|c_typ->ifsubtypesymbolsc_typ(typ,Units.In_bytes.zero)thenLog.result"Path@ %a:@ Typing@ %a is@ more@ precise,@ not@ propagating."Path.pppathpp_cell_typc_typelsepropagate_typepath~mutatetypa|exceptionNot_found->propagate_typepath~mutatetypainpropagate_type(Path.atom"runtime")~mutate:trueinit_typeinit_addr;to_check|>Tbl.iter(funaddrtyp->Log.error"unchecked@ address@ %a@ remained@ with@ type@ %a and unknown offset"Virtual_address.ppaddrTypedC.pptyp);ifTbl.lengthto_check=0thenLog.result"No address left to check.";typingend