123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260(**************************************************************************)(* *)(* This file is part of the Frama-C's Lannotate plug-in. *)(* *)(* Copyright (C) 2012-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) *)(* *)(**************************************************************************)openCil_typesopenAst_const(** RCC Visitor **)classvisitormk_label=object(self)inheritVisitor.frama_c_inplacevalunk_loc=Cil_datatype.Location.unknown(* Used to know if we are inside an inlined block *)valis_inlined_block=Stack.create()(* List of last temporary variables used to store mutations
for each critical block *)valseen_vinfos=Stack.create()(* Remember if a macro double was seen for the next if *)valmutableseen_double:bool=false(* Remember if a macro ignore was seen for the next if *)valmutableseen_ignore:bool=false(* Id used to create temporary variable names *)valmutableid:int=0valmutableto_add=[](* Get the next id to use *)methodprivatenext():int=id<-id+1;idvalstart_inline="__LANNOTATE_START_INLINE"valend_inline="__LANNOTATE_END_INLINE"valstart="__CM_START"valend_crit="__CM_END"valdouble_if="__CM_DOUBLE_IF"valignore_if="__CM_IGNORE_IF"valtarget="__CM_TARGET"methodprivatein_crit_zone()=not(Stack.is_emptyseen_vinfos)&&Stack.is_emptyis_inlined_blockmethodprivateis_lannotate_builting=matchgwith|GFunDecl(_,vi,_)->List.exists(funbuiltin->String.equalvi.vnamebuiltin)[start;double_if;target;start_inline;end_inline]|_->falsemethodprivateadd_to_seenvi=(* we add the new variable to all currently opened critical zones *)letl=Stack.fold(funacce->e::acc)[]seen_vinfosinStack.clearseen_vinfos;List.iter(fune->Stack.push(vi::e)seen_vinfos)l(* Creates a new temporary variable and adds it to seen_vinfos *)methodprivateget_new_tmp_var():varinfo=letkf=Option.getself#current_kfinletfdec=Kernel_function.get_definitionkfinletname="lannot_mut_"^string_of_int(self#next())inletvi=Cil.makeTempVar~namefdecCil_const.intTypeinself#add_to_seenvi;to_add<-vi::to_add;vi(* For a given if expression, creates the corresponding mutated expression
of the form (mut_exp && !e || !mut_exp && e), and assigns the temporary
variable with mutate function (cf. Printer in utils.ml)
*)methodprivategenerate_exp(exp:exp):(stmt*exp)=letmutate_lval=Cil.var(self#get_new_tmp_var())inletloc=exp.elocinletmutate_exp=Cil.new_exp~loc(Lvalmutate_lval)inletmutation_side=Cil.mkBinOp~locLAndmutate_exp(Exp_builder.lnotexp)inletno_mutation_side=Cil.mkBinOp~locLAnd(Exp_builder.lnotmutate_exp)expinletmutate_call=Utils.mk_call~result:mutate_lval!Annotators.mutated[]inmutate_call,Cil.mkBinOp~locLOrmutation_sideno_mutation_side(* Depending on seen_double value, changes the form of the mutated expression
and returns it along with assigns of temporary variables *)methodprivategenerate_if_exp(exp:exp):(stmtlist*exp)=matchexp.enodewith|BinOp(LAnd|LOrasop,exp1,exp2,_)whenseen_double->letmutate_call,new_exp1=self#generate_expexp1inletmutate_call',new_exp2=self#generate_expexp2inletconcat_exp=Cil.mkBinOp~loc:exp.elocopnew_exp1new_exp2in[mutate_call;mutate_call'],concat_exp|_->ifseen_doublethenbeginOptions.warning"If at %a wrongly marked as double.\ Parsing can split \
if statement if it contains side effects. \
Ignoring macro..."Printer.pp_locationexp.eloc;end;letmutate_call,new_exp=self#generate_expexpin[mutate_call],new_exp(* Creates a disjunction of all seen_vinfos *)methodprivategenerate_disj(loc:location)(vinfos:varinfolist)=letrecauxvinfosacc=matchvinfos,accwith|[],Someacc->acc|vInfo::tl,None->letnew_exp=Cil.new_exp~loc(Lval(Cil.varvInfo))inauxtl(Some(new_exp))|vInfo::tl,Someacc->letnew_exp=Cil.new_exp~loc(Lval(Cil.varvInfo))inletnew_disj=Cil.mkBinOp~locLOrnew_expaccinauxtl(Some(new_disj))|_->assertfalseinauxvinfosNone(* Clears all parameters after each function *)method!vfuncdec=ifnot@@Annotators.shouldInstrumentFundec.svarthenCil.SkipChildrenelseCil.DoChildrenPost(funfdec->Stack.clearis_inlined_block;letfvi=letzero_init=Cil.makeZeroInit~loc:unk_locvi.vtypeinletlocal_init=AssignInitzero_initinletinstr_init=Local_init(vi,local_init,unk_loc)invi.vdefined<-true;Stmt_builder.instrinstr_initinletinits=List.rev@@List.mapfto_addinfdec.sbody.bstmts<-inits@fdec.sbody.bstmts;Stack.clearseen_vinfos;to_add<-[];seen_double<-false;seen_ignore<-false;fdec)(* Handles inlined block to avoid annotating them *)method!vblockb=ifnot(Options.InlinedBlock.get())&&Ast_attributes.(containsframa_c_inlinedb.battrs)thenbeginStack.pushtrueis_inlined_block;Cil.DoChildrenPost(funb'->ignore(Stack.popis_inlined_block);b')endelseCil.DoChildren(* Handles lannotate macro and generates mutations and labels as such :
- START, handles all if statements between START and END
- END, ends "annotating" zone
- TARGET, generates labels
- DOUBLE IF, remember for the next instrumented if form
- If, mutate if expression
*)method!vstmt_auxstmt=matchstmt.skindwith|Instr(Call(_,{enode=Lval(Varv,NoOffset)},[],_))whenString.equalv.vnamestart->ifStack.is_emptyis_inlined_blockthenStack.push[]seen_vinfos;stmt.skind<-Instr(Skip(Cil_datatype.Stmt.locstmt));Cil.SkipChildren|Instr(Call(_,{enode=Lval(Varv,NoOffset)},[],_))whenString.equalv.vnameend_crit->ifself#in_crit_zone()thenbeginignore(Stack.popseen_vinfos);seen_double<-false;seen_ignore<-false;end;stmt.skind<-Instr(Skip(Cil_datatype.Stmt.locstmt));Cil.SkipChildren|Instr(Call(_,{enode=Lval(Varv,NoOffset)},[],loc))whenString.equalv.vnametarget->ifself#in_crit_zone()thenbeginlettops=List.rev@@Stack.fold(funacce->e::acc)[]seen_vinfosinlettops=List.sort_uniqcomparetopsinletlabels=List.filter_map(funvis->ifvis=[]thenNoneelseSome(mk_label(self#generate_disjlocvis)[]loc))topsiniflabels<>[]thenbeginletb=Ast_const.Stmt_builder.blocklabelsinb.labels<-stmt.labels;Cil.ChangeTobendelsebeginOptions.warning"Success point reached without preceding if at %a, \
annotation ignored."Printer.pp_locationloc;stmt.skind<-Instr(Skip(Cil_datatype.Stmt.locstmt));Cil.SkipChildrenendendelsebeginstmt.skind<-Instr(Skip(Cil_datatype.Stmt.locstmt));Cil.SkipChildrenend|Instr(Call(_,{enode=Lval(Varv,NoOffset)},[],_))whenString.equalv.vnamedouble_if->ifself#in_crit_zone()thenseen_double<-true;stmt.skind<-Instr(Skip(Cil_datatype.Stmt.locstmt));Cil.SkipChildren|Instr(Call(_,{enode=Lval(Varv,NoOffset)},[],_))whenString.equalv.vnameignore_if->ifself#in_crit_zone()thenseen_ignore<-true;stmt.skind<-Instr(Skip(Cil_datatype.Stmt.locstmt));Cil.SkipChildren|If_whenself#in_crit_zone()&&seen_ignore->seen_ignore<-false;Cil.DoChildren|If(exp,thenb,elseb,loc)whenself#in_crit_zone()->letmutate_calls,new_exp=self#generate_if_expexpinseen_double<-false;letthenb'=Cil.visitCilBlock(self:>Cil.cilVisitor)thenbinletelseb'=Cil.visitCilBlock(self:>Cil.cilVisitor)elsebinletnew_if=Stmt_builder.mk(If(new_exp,thenb',elseb',loc))instmt.skind<-Block(Cil.mkBlock(mutate_calls@[new_if]));Cil.SkipChildren|_->Cil.DoChildrenmethod!vfile_=Cil.DoChildrenPost(funf->letclean_globals=Cil.foldGlobalsf(funaccg->ifself#is_lannotate_builtingthenaccelseg::acc)[]inf.globals<-List.revclean_globals;f)endmoduleRedundantCheckCountermeasures=Annotators.Register(structletname="RCC"lethelp="Redundant Check Countermeasures Coverage"letapplymk_labelfile=Visitor.visitFramacFileSameGlobals(newvisitormk_label:>Visitor.frama_c_visitor)fileend)