123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* 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). *)(* *)(**************************************************************************)openCil_typesmoduleM=Alarms.Maptypealarm=Alarms.ttypestatus=Abstract_interp.Comp.result=True|False|Unknowntypes=statusM.ttypet=Justofs|AllButofstype'aif_consistent=[`Valueof'a|`Inconsistent]letstring_of_predicate_status=function|Unknown->"unknown"|True->"valid"|False->"invalid"letpretty_statusfmtv=Format.fprintffmt"%s"(string_of_predicate_statusv)moduleStatus=structincludeDatatype.Make_with_collections(structtypet=statusincludeDatatype.Serializable_undefinedletname="Alarmset.status"letreprs=[True;False;False;Unknown]letmem_project=Datatype.never_any_projectletpretty=pretty_statusletcompare(s1:t)(s2:t)=Stdlib.compares1s2letequal(s1:t)(s2:t)=s1=s2lethash(s:t)=Hashtbl.hashsend)letjoinxy=matchx,ywith|True,True->True|False,False->False|True,False|False,True|Unknown,_|_,Unknown->Unknownletinterxy=matchx,ywith|Unknown,_->`Valuey|_,Unknown->`Valuex|True,True->`ValueTrue|False,False->`ValueFalse|True,False|False,True->`InconsistentexceptionStopletjoin_listl=tryletr=List.fold_left(funacce->matche,accwith|Unknown,_->raiseStop|e,None->Somee|e,Someeacc->Some(joineacce))Nonelinmatchrwith|None->True|Somer->rwithStop->UnknownendmoduleD=Alarms.Map.Make(Status)letprettyfmt=function|Justm->Format.fprintffmt"Just %a"D.prettym|AllButm->Format.fprintffmt"AllBut %a"D.prettymletnone=JustM.emptyletall=AllButM.emptyletequalab=matcha,bwith|Justm,Justn|AllButm,AllButn->M.equalStatus.equalmn|_,_->falseletget=functionJustm|AllButm->mletis_empty=function|AllBut_->false|Justm->M.is_emptymletsingleton?(status=Unknown)a=ifstatus=TruethenJustM.emptyelseJust(M.singletonastatus)letsetalarmstatus=function|Justm->ifstatus=TruethenJust(M.removealarmm)elseJust(M.addalarmstatusm)|AllButm->ifstatus=UnknownthenAllBut(M.removealarmm)elseAllBut(M.addalarmstatusm)letdefault=function|Just_->True|AllBut_->Unknownletfindalarmset=trymatchsetwith|Justm|AllButm->M.findalarmmwithNot_found->defaultset(* Merges two alarm maps [s1] and [s2], using [merge_status] to merge merge the
statuses bound to a same alarm.
If [combine] is true, both alarm maps [s1] and [s2] have been produced in
the same state but for different expressions [e1] and [e2]: they may contain
different alarms, but should always have the same status bound to a same
alarm. Do not use the default status for alarms in [s1] missing in [s2] (and
vice versa): such alarms may have no meaning for [e2], and should have the
same status in [s2] than in [s1] anyway.
If [combine] is false, both maps come from the evaluation of the same
expression in different states. In this case, use the default status for any
alarm missing in one side. *)letmerge~combinemerge_statuss1s2=letd1=defaults1andd2=defaults2inletm1=gets1andm2=gets2inletclosed_set=matchmerge_statusd1d2with|True->true|Unknown->false|False->assertfalseinletreturn=ifclosed_setthenfunctionTrue->None|p->SomepelsefunctionUnknown->None|p->Somepinletmerge_p1p2=matchp1,p2with|None,None->assertfalse|Somep,None->ifcombinethenSomepelsereturn(merge_statuspd2)|None,Somep->ifcombinethenSomepelsereturn(merge_statusd1p)|Somep1,Somep2->return(merge_statusp1p2)inifclosed_setthenJust(M.mergemergem1m2)elseAllBut(M.mergemergem1m2)letunion=merge~combine:falseStatus.joinexceptionInconsistentletintersectstatus1status2=matchStatus.interstatus1status2with|`Valuestatus->status|`Inconsistent->raiseInconsistentletinters1s2=try`Value(merge~combine:falseintersects1s2)withInconsistent->`Inconsistentletcombines1s2=(* [intersect] is only applied if both maps explicitely contain the same
alarm. As both maps have been produced in the same state, the statuses for
this alarm should be consistent. *)trymerge~combine:trueintersects1s2withInconsistent->Self.fatal~current:true"Inconsistent combination of two alarm maps %a and %a."prettys1prettys2letiterf=function|Justm->M.iterfm|AllBut_->assertfalseletfoldfacc=function|Justm->M.foldfmacc|AllBut_->assertfalseletexiststest~default=function|Justm->M.existstestm||defaultTrue|AllButm->M.existstestm||defaultUnknownletfor_alltest~default=function|Justm->M.for_alltestm&&defaultTrue|AllButm->M.for_alltestm&&defaultUnknown(* --------------------------------------------------------------------------
Alarms
------------------------------------------------------------------------ *)letemitter=Eva_utils.emitter(* Printer that shows additional information about temporaries *)letlocal_printer:Printer.extensible_printer=letopenCil_typesinobject(self)inheritPrinter.extensible_printer()assuper(* Temporary variables for which we want to print more information *)valmutabletemporaries=Cil_datatype.Varinfo.Set.emptymethod!code_annotationfmtca=temporaries<-Cil_datatype.Varinfo.Set.empty;matchca.annot_contentwith|AAssert(_,p)->(* ignore the ACSL name *)letp=p.tp_statement.pred_contentinFormat.fprintffmt"@[<v>@[assert@ %a;@]"self#predicate_nodep;(* print temporary variables information *)ifnot(Cil_datatype.Varinfo.Set.is_emptytemporaries)thenbeginFormat.fprintffmt"@ @[(%t)@]"self#pp_temporariesend;Format.fprintffmt"@]";|_->assertfalsemethodprivatepp_temporariesfmt=letpp_varfmtvi=Format.fprintffmt"%s from@ @[%s@]"vi.vname(Option.getvi.vdescr)inPretty_utils.pp_iterCil_datatype.Varinfo.Set.iter~pre:""~suf:""~sep:",@ "pp_varfmttemporariesmethod!logic_varfmtlvi=(matchlvi.lv_originwith|None|Some{vdescr=None}->()|Some({vdescr=Some_}asvi)->temporaries<-Cil_datatype.Varinfo.Set.addvitemporaries);super#logic_varfmtlviendletpr_annot=local_printer#code_annotation(* Default behaviour: print one alarm per kinstr. *)moduleAlarm_key=Datatype.Pair_with_collections(Cil_datatype.Kinstr)(Alarms)(structletmodule_name="Alarm_key"end)moduleAlarm_cache=State_builder.Hashtbl(Alarm_key.Hashtbl)(Datatype.Unit)(structletname="Value_messages.Alarm_cache"letdependencies=[Self.state]letsize=35end)letloc=function|Cil_types.Kglobal->(* can occur in case of obscure bugs (already happened)
with wacky initializers. Module Initial_state of
value analysis correctly positions the loc *)Cil.CurrentLoc.get()|Cil_types.Kstmts->Cil_datatype.Stmt.locsletreport_alarmkiannotmsg=Eva_utils.alarm_report~source:(fst(locki))"@[%s.@ @[<hov 2>%a@]@]%t"msgpr_annotannotEva_utils.pp_callstackletstring_fkind=function|Cil_types.FFloat->"float"|Cil_types.FDouble->"double"|Cil_types.FLongDouble->"long double"(** Emitting alarms *)letregister_alarmkialarmstatusstr=letstatus=matchstatuswith|True->Property_status.True|False->(* We cannot soundly emit a red status on an alarm, because we may have
emitted a green status earlier on. Thus we store the information
for logging purposes, and emit an Unknown status. *)Red_statuses.add_red_alarmkialarm;Property_status.Dont_know|Unknown->Property_status.Dont_knowinletannot,_is_new=Alarms.register~loc:(locki)~statusemitterkialarmin(* Report each alarm only once per analysis. The boolean [is_new] returned
by {{Alarms.register}} is inadequate, as an alarm emitted by another
plugin or by a previous run of Eva would be considered as not new. *)Alarm_cache.memo(fun(_ki,_alarm)->report_alarmkiannotstr)(ki,alarm)letemit_alarmkinstralarm(status:status)=letregister_alarm=register_alarmkinstralarmstatusinmatchalarmwith|Alarms.Pointer_comparison(_,_)->register_alarm"pointer comparison"|Alarms.Division_by_zero_->register_alarm"division by zero"|Alarms.Overflow(kind,_,_,_)->letstr=matchkindwith|Alarms.Signed->"signed overflow"|Alarms.Unsigned->"unsigned overflow"|Alarms.Signed_downcast->"signed downcast"|Alarms.Unsigned_downcast->"unsigned downcast"|Alarms.Pointer_downcast->"pointer downcast"inregister_alarmstr|Alarms.Float_to_int_->register_alarm"overflow in conversion from floating-point to integer"|Alarms.Invalid_shift(_,Some_)->register_alarm"invalid RHS operand for shift"|Alarms.Invalid_shift(_,None)->register_alarm"invalid LHS operand for left shift"|Alarms.Memory_access(_,access_kind)->letaccess=matchaccess_kindwith|Alarms.For_reading->"read"|Alarms.For_writing->"write"inregister_alarm(Format.sprintf"out of bounds %s"access)|Alarms.Index_out_of_bound_->register_alarm"accessing out of bounds index"|Alarms.Invalid_pointer_->register_alarm"invalid pointer creation"|Alarms.Differing_blocks_->register_alarm"pointer subtraction"|Alarms.Is_nan_or_infinite(_,fkind)->letsfkind=string_fkindfkindinregister_alarm(Format.sprintf"non-finite %s value"sfkind)|Alarms.Is_nan(_,fkind)->letsfkind=string_fkindfkindinregister_alarm(Format.sprintf"NaN %s value"sfkind)|Alarms.Uninitialized_->register_alarm"accessing uninitialized left-value"|Alarms.Dangling_->register_alarm"accessing left-value that contains escaping addresses"|Alarms.Not_separated_->register_alarm"undefined multiple accesses in expression"|Alarms.Overlap_->register_alarm"partially overlapping lvalue assignment"|Alarms.Function_pointer_->register_alarm"pointer to function with incompatible type"|Alarms.Invalid_bool_->register_alarm"trap representation of a _Bool lvalue"letheight_alarm=letopenEva_utilsinfunction|Alarms.Division_by_zeroe|Alarms.Index_out_of_bound(e,_)|Alarms.Invalid_pointere|Alarms.Invalid_shift(e,_)|Alarms.Overflow(_,e,_,_)|Alarms.Float_to_int(e,_,_)|Alarms.Function_pointer(e,_)|Alarms.Pointer_comparison(None,e)->height_expre+2|Alarms.Memory_access(lv,_)|Alarms.Danglinglv|Alarms.Invalid_boollv->height_lvallv+1|Alarms.Uninitializedlv->height_lvallv|Alarms.Pointer_comparison(Somee1,e2)->max(height_expre1)(height_expre2)+2|Alarms.Differing_blocks(e1,e2)->max(height_expre1)(height_expre2)+1|Alarms.Not_separated(lv1,lv2)|Alarms.Overlap(lv1,lv2)->max(height_lvallv1)(height_lvallv2)+1|Alarms.Is_nan_or_infinite(e,fkind)|Alarms.Is_nan(e,fkind)->lettrivial=matchCil.typeOfewith|TFloat(fk,_)->fk=fkind|_->falseiniftrivialthenheight_expreelseheight_expre+1letcmpa1a2=Datatype.Int.compare(height_alarm(fsta1))(height_alarm(fsta2))letremove_redundant_alarmsmap=letfilteralarmstatus=matchalarmwith|Alarms.Invalid_pointerexpr->letlval=Memexpr,NoOffsetinletimpliesalarms=status=s&&matchalarmwith|Alarms.Memory_access(lv,_access_kind)->Cil_datatype.LvalStructEq.equallvlval|_->falseinnot(M.existsimpliesmap)|_->trueinM.filterfiltermapletemit_alarmskinstrmap=letmap=remove_redundant_alarmsmapinletlist=M.bindingsmapinletsorted_list=List.sortcmplistinList.iter(fun(alarm,status)->emit_alarmkinstralarmstatus)sorted_list;ifAlarm_cache.length()>=Parameters.StopAtNthAlarm.get()thenSelf.abort"Stopping at nth alarm"letemitkinstr=function|Justmap->ifnot(M.is_emptymap)thenemit_alarmskinstrmap(* TODO: use GADT to avoid this assert false ? *)|AllBut_->Self.abort~current:true~once:true"All alarms may arise: \
abstract state too imprecise to continue the analysis."(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)