123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259(**************************************************************************)(* This file is part of BINSEC. *)(* *)(* Copyright (C) 2016-2026 *)(* 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 licenses/LGPLv2.1). *)(* *)(**************************************************************************)openTypesopenIrletexpr_of_addraddrsize=Dba.Expr.constant(Bitvector.create(Virtual_address.to_bigintaddr)size)letname="shadow-stack"moduleLogger=Logger.Sub(structletname=nameend)typeScript.Ast.t+=Initial_stackofScript.Ast.Expr.tScript.Ast.loclisttype('value,'model,'state,'path,'a)field_id+=|Stack:('value,'model,'state,'path,'valuelist)field_idletgrammar_extension=[Dyp.Add_rules[(("decl",[Dyp.Regexp(RE_String"initial");Dyp.Regexp(RE_String"call");Dyp.Regexp(RE_String"stack");Dyp.Non_ter("comma_separated_expr_rev_list",No_priority);],"default_priority",[]),fun_->function|[_;_;_;Binsec_script.Syntax.Obj(Script.Expr_listvalues)]->(Binsec_script.Syntax.Decl(Initial_stack(List.revvalues)),[])|_->assertfalse);];]letcommand_printerppf=function|Initial_stackvalues->Format.fprintfppf"initial call stack %a"(Format.pp_print_list~pp_sep:(funppf()->Format.pp_print_stringppf", ")(funppf(addr,_)->Script.Expr.ppppfaddr))values;true|_->falsemoduleInline_plugin=structletname=nameletfields:(modulePATH)->fieldlist=fun_->[]letextensions:typea.(moduleENGINEwithtypePath.t=a)->aextensionlist=funengine->letmoduleE=(valengine)inletmoduleP=E.Pathinletpointer_size=Machine.ISA.word_sizeE.isainletarray=Some"shadow_stack"inletptr=Dba.Var.create"shadow_stack_pointer"~bitsize:(Size.Bit.createpointer_size)~tag:Tempandwitness=Dba.Var.create"shadow_stack_witness"~bitsize:(Size.Bit.createpointer_size)~tag:Tempinletptr_r=Dba.Expr.vptrandwitness_r=Dba.Expr.vwitnessinletzero=Dba.Expr.constant(Bitvector.zerospointer_size)andoffset=Dba.Expr.constant(Bitvector.of_int~size:pointer_size(pointer_size/8))inletinsert_return_checkgraphvertextarget=Revision.insert_list_beforegraphvertex[Assert(Dba.Expr.ugtptr_rzero);Assign{var=ptr;rval=Dba.Expr.subptr_roffset};Load{var=witness;base=array;dir=LittleEndian;addr=ptr_r};Assert(Dba.Expr.equalwitness_rtarget);]in[Grammar_extensiongrammar_extension;Initialization_callback(funpath->P.assignpathptrzero);Command_handler(fundecl(env:Script.env)path->matchdeclwith|Initial_stackvalues->letptr_v=List.fold_left(funptr_vaddr->letaddr=Script.eval_expr~size:env.wordsizeaddrenvinP.storepatharray~addr:ptr_vaddrLittleEndian;Dba.Expr.addptr_voffset)zerovaluesinP.assignpathptrptr_v;true|_->false);Command_printercommand_printer;Instrumentation_routine(fungraph->Revision.iter_new_vertex(funvertex->matchRevision.nodegraphvertexwith|Fallthrough{kind=Goto{tag=Call{base;_};_};_}|Terminator{kind=Goto{tag=Call{base;_};_};_}|Terminator{kind=Jump{tag=Call{base;_};_};_}->Revision.insert_list_beforegraphvertex[Store{base=array;dir=LittleEndian;addr=ptr_r;rval=expr_of_addrbasepointer_size;};Assign{var=ptr;rval=Dba.Expr.addptr_roffset};]|Terminator{kind=Goto{target;tag=Return;_};_}->insert_return_checkgraphvertex(expr_of_addrtargetpointer_size)|Terminator{kind=Jump{target;tag=Return;_};_}->insert_return_checkgraphvertextarget|_->())graph);]endmoduleBuiltin(E:ENGINE):EXTENSIONSwithtypepath=E.Path.t=structmoduleP=E.PathmoduleV=P.ValuemoduleM=P.Modeltypepath=P.tletkey=E.lookupStackletpointer_size=Machine.ISA.word_sizeE.isatypeIr.builtin+=PushofDba.Expr.t|PopofDba.Expr.tletpushtargetpath=Logger.debug~level:2"%a: push(%a)"Virtual_address.pp(P.pcpath)Dba_printer.Ascii.pp_bl_termtarget;P.setpathkey(P.get_valuepathtarget::P.getpathkey)letreportaddrtargetwitnessmodel=Logger.error"@[<v>%a: Stack tampering@ (goto %a, expected %a)@ %a@]"Virtual_address.ppaddrBitvector.pp_hex_or_bin(M.evaltargetmodel)Bitvector.pp_hex_or_bin(M.evalwitnessmodel)M.ppmodel(* TODO pp_with_section ?*)letpoptargetpath=letaddr=P.pcpathinLogger.debug~level:2"%a: pop(%a)"Virtual_address.ppaddrDba_printer.Ascii.pp_bl_termtarget;matchP.getpathkeywith|[]->Logger.error"%a: return does not match any call"Virtual_address.ppaddr;SignalAssertion_failure|witness_v::tail->(P.setpathkeytail;lettarget_v=P.get_valuepathtargetin(matchP.check_sat_assuming_v~retain:falsepath(V.binaryDifftarget_vwitness_v)with|None->()|Somemodel->reportaddrtarget_vwitness_vmodel);matchP.assume_vpath(V.binaryEqtarget_vwitness_v)with|None->SignalAssertion_failure|Some_->Return)letlist:P.textensionlist=[Grammar_extensiongrammar_extension;Command_handler(fundecl(env:Script.env)path->matchdeclwith|Initial_stackvalues->P.setpathkey(List.rev_map(funaddr->P.get_valuepath(Script.eval_expr~size:env.wordsizeaddrenv))values);true|_->false);Command_printercommand_printer;Instrumentation_routine(fungraph->Revision.iter_new_vertex(funvertex->matchRevision.nodegraphvertexwith|Fallthrough{kind=Goto{tag=Call{base;_};_};_}|Terminator{kind=Goto{tag=Call{base;_};_};_}|Terminator{kind=Jump{tag=Call{base;_};_};_}->Revision.insert_beforegraphvertex(Builtin(Push(expr_of_addrbasepointer_size)))|Terminator{kind=Goto{target;tag=Return;_};_}->Revision.insert_beforegraphvertex(Builtin(Pop(expr_of_addrtargetpointer_size)))|Terminator{kind=Jump{target;tag=Return;_};_}->Revision.insert_beforegraphvertex(Builtin(Poptarget))|_->())graph);Builtin_resolver(function|Pushtarget->Apply(pushtarget)|Poptarget->Call(poptarget)|_->Unknown);Builtin_printer(funppf->function|Pushtarget->Format.fprintfppf"shadow push %a"Dba_printer.Ascii.pp_bl_termtarget;true|Poptarget->Format.fprintfppf"shadow pop %a"Dba_printer.Ascii.pp_bl_termtarget;true|_->false);]endmoduleBuiltin_plugin=structletname=nameletfields:(modulePATH)->fieldlist=fun_->[Field{id=Stack;default=[];copy=None;merge=None}]letextensions:typea.(moduleENGINEwithtypePath.t=a)->aextensionlist=funengine->letmoduleExtensions=Builtin((valengine))inExtensions.listend