123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857(**************************************************************************)(* *)(* SPDX-License-Identifier LGPL-2.1 *)(* Copyright (C) *)(* CEA (Commissariat à l'énergie atomique et aux énergies alternatives) *)(* *)(**************************************************************************)openEva_astopenMt_cilopenMt_memory.TypesopenMt_typesopenMt_shared_vars_typesopenMt_threadletwrap_resres=Some(Mt_memory.int_to_valueres)letno_res=(None:valueoption)typehook_sig=(exp*value)list->state*valueoptionletcurrent_positionanalysis=matchCallstack.top_callsiteanalysis.curr_stackwith|Kglobal->assertfalse(* The current stack must contain the call to the builting creating the thread *)|Kstmtstmt->stmt,Option.get(Callstack.popanalysis.curr_stack)(* -------------------------------------------------------------------------- *)(* --- Specialized logging functions --- *)(* -------------------------------------------------------------------------- *)(* Returns [source] and [append] arguments for log functions used in hooks,
according to the the current stack. As builtins are called inside stubbed
function for pthreads library, we use the position of the penultimate call
site, which should correspond to the call to the pthreads function. *)letlog_arganalysis=letstack=analysis.curr_stackinletstack=Option.value(Callstack.popstack)~default:stackinletsource=kinstr_to_source(Callstack.top_callsitestack)inletappendfmt=ifMt_options.PrintCallstacks.get()||Mt_self.Debug.get()>1thenFormat.fprintffmt"@.%a"Callstack.prettystackinsource,appendletresultanalysis=letsource,append=log_arganalysisinMt_self.result~once:true?source~appendletwarninganalysis=letsource,append=log_arganalysisinMt_self.warning~once:true?source~appendleterroranalysis=letsource,append=log_arganalysisinMt_self.error?source~appendexceptionHook_failureofintletdefault_err_code=-255lethook_fail?(code=default_err_code)()=raise(Hook_failurecode)(* Auxiliary function that aborts a hook when a conversion of something
into a proper value fails *)letcatch_conversionanalysis~prefixvmsg=matchvwith|Okv->v|Errorerror->warninganalysis"@[%s: %s. %s. Ignoring.@]"prefixmsgerror;hook_fail()(* -------------------------------------------------------------------------- *)(* --- Specialization of id function *)(* -------------------------------------------------------------------------- *)letfind_idkindfindid=matchfindidwith|Someelt->Okelt|None->leterror=Format.sprintf"Id %d for %s does not exists (incrementation inside program?)."idkindinErrorerrorletfind_thread=find_id"thread"Thread.findletfind_mutex=find_id"mutex"Mutex.findletfind_queue=find_id"queue"Mqueue.find(* -------------------------------------------------------------------------- *)(* --- Constants written in memory to store states --- *)(* -------------------------------------------------------------------------- *)(* Auxiliary function which converts a cvalue to a singleton integer from the
list of expected values [possible_values], or return None otherwise. *)letproject_singleton_intcvaluepossible_values=letfindi=List.find_opt(fun(x,_)->Datatype.Int.equalxi)possible_valuesintryCvalue.V.project_ivalcvalue|>Ival.project_int|>Integer.to_int_exn|>findwithCvalue.V.Not_based_on_null|Ival.Not_Singleton_Int|Z.Overflow->None(* Information about an operation on a thread, mutex or message queue. *)typeoperation={name:string;(* Name of the operation. *)before:intlist;(* List of possible legal values before the operation. *)after:int;(* Value after the operation. *)}(* Checks that [value] is a singleton integer as expected before [operation],
or emit a warning by calling [warn]. [possible_values] is the list of
possible values for [value] associated with a precise message. If [value]
is not a singleton integer from [possible_values], an imprecise warning
is emitted. *)letcheck_valuewarnpossible_valuesoperationvalue=matchproject_singleton_intvaluepossible_valueswith|Some(i,_msg)whenList.memioperation.before->()|Some(_i,msg)->warnmsg|None->letreason=Format.asprintf"unable to check its precise status (internal value %a should be %a)"Cvalue.V.prettyvalue(Pretty_utils.pp_list~sep:" or "Datatype.Int.pretty)operation.beforeinwarnreason(* Operations on threads. *)moduleThreadOp=structletnot_created=0letstarted=1letsuspended=2letcancelled=3letpossible_values=[not_created,"it might not be created yet";started,"it might have already been started by the current thread";suspended,"it might have already been suspended by the current thread";cancelled,"it might have been cancelled by the current thread";]letstart={name="start";before=[suspended];after=started;}letsuspend={name="suspend";before=[started];after=suspended;}letcancel={name="cancel";before=[started;suspended];after=cancelled;}letcheck_and_writeanalysisstatethreadoperation=letid=Mt_ids.of_threadthreadinletvalue=Mt_ids.read_id_statestateidinletwarnfailure_reason=warninganalysis"Trying to %s thread %a, but %s."operation.nameThread.prettythreadfailure_reasonincheck_valuewarnpossible_valuesoperationvalue;Mt_ids.write_id_statestateidoperation.afterend(* Operations on mutexes. *)moduleMutexOp=structletnot_init=0letunlocked=1letlocked=2letpossible_values=[not_init,"it might not be initialized yet";unlocked,"it might already be unlocked (and initialized)";locked,"it might already be locked (and initialized)";]letinitialize={name="initialize";before=[not_init];after=unlocked;}letlock={name="lock";before=[unlocked];after=locked;}letunlock={name="unlock";before=[locked];after=unlocked;}letcheck_and_writeanalysisstatemutexoperation=letid=Mt_ids.of_mutexmutexinletvalue=Mt_ids.read_id_statestateidinletwarnfailure_reason=warninganalysis"Trying to %s mutex %a, but %s."operation.nameMutex.prettymutexfailure_reasonincheck_valuewarnpossible_valuesoperationvalue;Mt_ids.write_id_statestateidoperation.afterend(* Operations on message queues. *)moduleQueueOp=structletnot_init=0letinitialized=1letpossible_values=[not_init,"it might not be initialized yet";initialized,"it might be already initialized";]letinitialize={name="initialize";before=[not_init];after=initialized;}letusename={name;before=[initialized];after=initialized;}letsend=use"send message to"letreceive=use"receive message from"letcheck_and_writeanalysisstatequeueoperation=letid=Mt_ids.of_queuequeueinletvalue=Mt_ids.read_id_statestateidinletwarnfailure_reason=warninganalysis"Trying to %s message queue %a, but %s."operation.nameMqueue.prettyqueuefailure_reasonincheck_valuewarnpossible_valuesoperationvalue;Mt_ids.write_id_statestateidoperation.afterend(** When a thread is created, it must not inherit from its creator the status
of mutexes. This function sets all mutexes passed as argument to 1
(unlocked). *)letreset_mutexesmutexesstate=Mutex.Set.fold(funmutexstate->Mt_ids.replace_id_valuestate(Mt_ids.of_mutexmutex)~before:2~after:1)mutexesstate(* -------------------------------------------------------------------------- *)(* --- External values for shared zones --- *)(* -------------------------------------------------------------------------- *)(* XXX: we should sync values only for the threads that may be alive at this
point *)letsync_valuesanalysisstate=letjoin~written~state=Cvalue.Model.fold(funboffsmstate->letoffsm'=Cvalue.Model.find_base_or_defaultbstateinmatchoffsm'with|`Top->Mt_self.fatal"Top state"|`Bottom->state|`Valueoffsm'->letoffsm''=Cvalue.V_Offsetmap.joinoffsmoffsm'inCvalue.Model.add_baseboffsm''state)writtenstateinletv=Mt_lib.var_thread_created()inletvalue=Results.(in_cvalue_statestate|>eval_varv|>as_cvalue)inifCvalue.V.is_zerovaluethenstate(* As no thread is running, just skip the synchronization. *)elsefold_threadsanalysisstate(funthstate->matchth.th_values_writtenwith|Cvalue.Model.Bottom->state|Cvalue.Model.Top->Cvalue.Model.top|Cvalue.Model.Mapwritten->ifnot(ThreadState.equalanalysis.curr_threadth)thenjoin~written~stateelsestate)lethook_syncanalysisstate:hook_sig=function_->sync_valuesanalysisstate,no_res(* -------------------------------------------------------------------------- *)(* --- Creation of a thread --- *)(* -------------------------------------------------------------------------- *)letbasic_threadeva_threadstackfuncstateparamsparent={th_eva_thread=eva_thread;th_stack=stack;th_init_state=state;th_fun=func;th_params=params;th_parent=parent;th_to_recompute=SetRecomputeReason.empty;th_read_written=AccessesByZone.empty_map;th_amap=Trace.empty;th_cfg=Mt_cfg_types.CfgNode.dead;th_read_written_cfg=Mt_cfg_types.AccessesByZoneNode.empty_map;th_values_written=Cvalue.Model.empty_map;th_projects=[];th_value_results=None;th_priority=PDefault;}letspawn_threadanalysiseva_threadstackfuncstateparamsparent=tryletth'=Thread.Hashtbl.findanalysis.all_threadseva_threadinifOption.equalThreadState.equalparentth'.th_parent=falsethen(letpp_parent=Pretty_utils.pp_opt~none:"<none>"ThreadState.prettyinerroranalysis"Thread '%a' is launched by two different \
threads (%a and %a). Ignoring"Thread.prettyeva_threadpp_parentparentpp_parentth'.th_parent;hook_fail())elseifCallstack.equalstackth'.th_stack=falsethen(erroranalysis"Thread '%a' is launched in two different contexts:@.\
Context 1:@.@[<hov 2> %a@]@.Context 2:@.@[<hov 2> %a@]@.Ignoring"Thread.prettyeva_threadCallstack.prettystackCallstack.prettyth'.th_stack;hook_fail())elseifKernel_function.get_idfunc<>Kernel_function.get_idth'.th_funthen(erroranalysis"Thread '%a' can be two different functions \
(%s and %s). Imprecise pointer? Ignoring."Thread.prettyeva_thread(Kernel_function.get_namefunc)(Kernel_function.get_nameth'.th_fun);hook_fail())else((* Fields that are being joined *)letinit_state',ris=Mt_memory.join_stateth'.th_init_statestateandargs,ra=Mt_memory.join_paramsth'.th_paramsparamsinth'.th_init_state<-init_state';th'.th_params<-args;ifristhenThreadState.recompute_becauseth'InitialEnvChanged;ifrathenThreadState.recompute_becauseth'InitialArgsChanged;lettext=ifris||rathen"New context for"else"Thread"inresultanalysis"@[<hov 2>%s@ %a@]"textThreadState.pretty_detailedth';th')withNot_found->letth=basic_threadeva_threadstackfuncstateparamsparentinth.th_to_recompute<-SetRecomputeReason.singletonFirstIteration;Thread.Hashtbl.addanalysis.all_threadseva_threadth;resultanalysis"@[<hov>New thread: %a@]"ThreadState.pretty_detailedth;thletstandalone_threadthkfinitial_state=matchFunction_calls.analysis_targetkfKglobalwith|`Builtin_|`Spec_->Mt_self.not_yet_implemented"Using an ACSL specification or a builtin to interpret entry point %a \
of thread %a is not supported."Kernel_function.prettykfThread.prettyth|`Body(fundec,_)->letformals=fundec.sformalsinleteval_argvi=Results.(in_cvalue_stateinitial_state|>eval_varvi|>as_cvalue)inletargs=List.mapeval_argformalsinletstack=Callstack.init~thread:(Thread.idth)~entry_point:kfinbasic_threadthstackkfinitial_stateargsNoneletmain_threadk_maininitial_state=standalone_threadThread.maink_maininitial_stateletinterrupt_threadkfinitial_state=letth=Thread.interrupt_handlerkfinstandalone_threadthkfinitial_state(** Set the global variable that indicates that at least one thread is running
to one *)letthread_is_runningstate=letp_thread_running=Mt_lib.var_thread_created(),0inMt_memory.write_int_pointerp_thread_running1state(** Hook registered in the value analysis for the creation of thread *)lethook_thread_creationanalysisstate:hook_sig=function|(_,name)::(_,f)::params->letconv=catch_conversionanalysis~prefix:"During thread creation"in(* We clean the state that will be used by the created thread *)letkf=conv(Mt_memory.extract_funf)"invalid thread function"inletformals=Kernel_function.get_formalskfinletrectrunc_params=function|[],[]->[]|_formal::qf,param::qp->param::trunc_params(qf,qp)|[],(_::_asparams)->ifMt_options.ModerateWarnings.get()thenwarninganalysis"During thread creation, mismatch between function \
'%s' signature and actual arguments. Ignoring last \
%d argument(s) and continuing."(Kernel_function.get_namekf)(List.lengthparams);[]|_::_,[]->erroranalysis"When creating thread from function %s: too few \
arguments, %d expected but %d given. Ignoring.]"(Kernel_function.get_namekf)(List.lengthformals)(List.lengthparams);hook_fail()inletparams=List.mapsnd(trunc_params(formals,params))inleteva_thread=letname=Concurrency.Name.of_cvaluenameinletpos=current_positionanalysisinThread.spawnposnamekfparamsinignore(spawn_threadanalysiseva_threadanalysis.curr_stackkfCvalue.Model.bottomparams(Someanalysis.curr_thread));register_eventanalysis(CreateThreadeva_thread);(* Thread is started as suspended *)Mt_ids.write_id_statestate(Mt_ids.of_threadeva_thread)2,wrap_res(Thread.ideva_thread)|_->Mt_self.fatal"Incorrect mthread binding for thread creation"(* By typing, Frama_C_thread_create must receive at least those
arguments *)letupdate_initial_stateanalysisthstate=(* From now on, at least two threads are running *)letstate=thread_is_runningstatein(* Remove references local to the parent thread *)letstate_started=Mt_memory.clear_non_globalsstatein(* Mutexes should be unlocked in the new threads *)letstate_started=reset_mutexesanalysis.all_mutexesstate_startedinletth=Thread.Hashtbl.findanalysis.all_threadsthinletinitial,changed=Mt_memory.join_stateth.th_init_statestate_startedinifchangedthen(ThreadState.recompute_becausethMt_thread.InitialEnvChanged;ifCvalue.Model.is_reachableth.th_init_statethenresultanalysis"@[<hov 2>New context for@ %a@]"ThreadState.pretty_detailedth;);th.th_init_state<-initial;(* Update the state of the creator too: more than one thread is running,
and the values written by the thread just created become visible. *)sync_valuesanalysisstatelethook_thread_start_suspendoperationaux_stateevtanalysisstate:hook_sig=function|[_,offset]->letprefix="During thread "^operation.nameinletconvv=catch_conversionanalysis~prefixvinletoffset=conv(Mt_memory.extract_intoffset)"invalid thread id"inifoffset<>0thenletth=conv(find_threadoffset)"unknown thread"inletstate=ThreadOp.check_and_writeanalysisstatethoperationinletevt=evtthinresultanalysis"@[%a@]"Event.prettyevt;register_eventanalysisevt;letstate=aux_stateanalysisth(state:state)instate,wrap_res0else(warninganalysis"Trying to %s unknown thread. Ignoring."operation.name;hook_fail~code:(-1)())|_->Mt_self.fatal"Incorrect mthread binding for thread %s"operation.name(** Hook registered in the value analysis when a thread is started *)lethook_thread_start=hook_thread_start_suspendThreadOp.startupdate_initial_state(funi->StartThreadi)lethook_thread_suspend=hook_thread_start_suspendThreadOp.suspend(fun__s->s)(funi->SuspendThreadi)lethook_thread_cancellationanalysisstate:hook_sig=function|[_,offset]->letprefix="During thread cancellation"inletconvv=catch_conversionanalysis~prefixvinletoffset=conv(Mt_memory.extract_intoffset)"invalid thread id"inifoffset<>0thenletth=conv(find_threadoffset)"unknown thread"inregister_eventanalysis(CancelThreadth);letstate=ThreadOp.check_and_writeanalysisstatethThreadOp.cancelinstate,wrap_res0else(warninganalysis"Trying to cancel unknown thread. Ignoring.";hook_fail~code:(-1)())|_->Mt_self.fatal"Incorrect mthread binding for thread cancellation \
(only the thread id is expected)"lethook_thread_exitanalysis(_state:state):hook_sig=function|[_,v]->ifThreadState.is_mainanalysis.curr_threadthen(erroranalysis"Call to thread exit primitive inside main thread. Ignoring";hook_fail())else(register_eventanalysis(ThreadExitv);resultanalysis"Thread exiting with value %a"Cvalue.V.prettyv;Cvalue.Model.bottom,no_res)|_->Mt_self.fatal"Incorrect mthread binding for thread exit \
(only the return value is expected)"lethook_thread_idanalysisstate:hook_sig=fun_->state,wrap_res(Thread.idanalysis.curr_thread.th_eva_thread)lethook_thread_priorityanalysisstate:hook_sig=function|[_,p]->beginletp=catch_conversionanalysis~prefix:"During thread priority definition"(Mt_memory.extract_intp)"invalid thread id"inbeginmatchanalysis.curr_thread.th_prioritywith|PPriorityp'->ifp<>p'thenbeginwarninganalysis"Conflicting priorities (previous: %d, new %d) for thread '%a'."pp'ThreadState.prettyanalysis.curr_thread;(* TODO: add an event + add a recompute reason *)analysis.curr_thread.th_priority<-PUnknown;end|PUnknown->()|PDefault->resultanalysis"Setting priority to %d"p;analysis.curr_thread.th_priority<-PPriorityp;end;state,wrap_res0end|_->Mt_self.fatal"Incorrect mthread binding for thread priority \
(only a non negative integer is expected)"(* -------------------------------------------------------------------------- *)(** --- Hook registered in the value analysis related to messages --- *)(* -------------------------------------------------------------------------- *)lethook_queue_initanalysisstate:hook_sig=function|[_,name;_,size]->letprefix="During queue initialization"inletconvv=catch_conversionanalysis~prefixvinletpos=current_positionanalysisandname=Concurrency.Name.of_cvaluenameandsize=conv(Mt_memory.extract_intsize)"invalid size"inletq=Mqueue.createposnameinanalysis.all_queues<-Mqueue.Set.addqanalysis.all_queues;letstate=QueueOp.check_and_writeanalysisstateqQueueOp.initializeinletsize=ifsize<0thenNoneelseSomesizeinregister_eventanalysis(CreateQueue(q,size));state,wrap_res(Mqueue.idq)|_->Mt_self.fatal"Incorrect mthread binding for queue creation"lethook_send_msganalysisstate:hook_sig=function|[(_,offset);(_exp_content,content);(_exp_size,size)]->letconvv=catch_conversionanalysis~prefix:"During message sending"vinletoffset=conv(Mt_memory.extract_intoffset)"invalid queue id"inifoffset<>0thenletsbytes=conv(Mt_memory.extract_intsize)"invalid message size"inifsbytes<=0thenconv(Error(string_of_intsbytes))"invalid message length";letq=conv(find_queueoffset)"unknown queue"inletcontent=Mt_memory.read_slice~p:content~sbytesstateinletstate=QueueOp.check_and_writeanalysisstateqQueueOp.sendinletaction=SendMsg(q,(content,sbytes))inresultanalysis"@[%a@]"Event.prettyaction;register_eventanalysisaction;state,wrap_res0else(warninganalysis"Trying to send message on uninitialized queue. Ignoring.";state,wrap_res(-1))|_->Mt_self.fatal"Incorrect mthread binding for message sending"letfind_msg_contentanalysisq=letextract_actionthacc=function|SendMsg(q',(v,size))->ifMqueue.equalqq'then(th,v,size)::accelseacc|_->accinfold_threadsanalysis[](fun{th_eva_thread=th;th_amap=m}->Trace.fold'm(funar->extract_actionthra))lethook_receive_msganalysisstate:hook_sig=function|[(_,offset);(_e_size,size);(e_loc,loc)]->letprefix="During message reception"inletconvv=catch_conversionanalysis~prefixvinletoffset=conv(Mt_memory.extract_intoffset)"invalid queue id"inifoffset<>0thenletsmax=conv(Mt_memory.extract_intsize)"invalid size"andp=conv(Mt_memory.extract_pointerloc)"invalid destination buffer"andq=conv(find_queueoffset)"unknown queue"inletstate=QueueOp.check_and_writeanalysisstateqQueueOp.receiveinletaction=ReceiveMsg(q,p,smax)inregister_eventanalysisaction;letcontents=find_msg_contentanalysisqinletstate,res,pp=ifcontents<>[]thenletlength,kept_mess,_,state=List.fold_left(fun(length,kept_mess,exact,state)(_,slice,smessasmess)->letsbytes=minsmesssmaxinletstate'=Mt_memory.write_slice~p~sbytes~slice~exactstateinifCvalue.Model.is_reachablestate'thenletsbytes_val=Cvalue.V.inject_ival(Ival.of_intsbytes)inletlength'=Cvalue.V.joinsbytes_vallengthinlength',mess::kept_mess,false,state'else(warninganalysis"Found message of length %d, which is too long \
for buffer '%a'. Execution will continue without \
those messages.(Ignore \"This path is assumed to \
be dead message if any\".)"smesspp_expe_loc;length,kept_mess,exact,state))(Cvalue.V.bottom,[],true,state)contentsinmatchkept_messwith|[]->Cvalue.Model.bottom,no_res,(funfmt->Format.fprintffmt"No valid value to receive.")|_::_->letppfmt=Format.fprintffmt"Possible %s values:@.%a"(ifList.lengthkept_mess=List.lengthcontentsthen""else"valid ")(Pretty_utils.pp_list~pre:"@[<v>"~sep:"@,"(funfmt(th,v,_)->Format.fprintffmt"@[From thread %a:@ %a@]"Thread.prettythMt_memory.pretty_slicev))kept_messinstate,Somelength,ppelseCvalue.Model.bottom,no_res,(funfmt->Format.fprintffmt"No value to receive (yet?).")inresultanalysis"@[<hov>%a@ %t@]"Event.prettyactionpp;state,reselse(warninganalysis"Trying to receive value on non-initialized queue. Ignoring.";state,wrap_res(-2))|_->Mt_self.fatal"Incorrect mthread binding for message reception"(* Auxiliary functions for the functions that act on mutexes (currently
lock and release). [check] is the function that checks that the state
of the information stored in the mutex is consistent with the action
being performed, and the value with which to update the state.
[evt] returns the corresponding mthread event. *)letaux_mutex~operation:op~eventanalysisstate:hook_sig=function|[_,offset]->letprefix="During mutex "^op.nameinletconvv=catch_conversionanalysis~prefixvinletoffset_conversion=Mt_memory.extract_int_possibly_zerooffsetinletoffset,exact=convoffset_conversion"invalid mutex id"inifexact=`WithZerothenwarninganalysis"Trying to %s a possibly uninitialized mutex."op.name;ifoffset<>0thenletm=conv(find_mutexoffset)"unknown mutex"inletstate=MutexOp.check_and_writeanalysisstatemopinletevt:event=eventminresultanalysis"%a"Event.prettyevt;register_eventanalysisevt;(* XXX: take which mutex is locked into account, and update only
those values *)letwith_external=sync_valuesanalysisstateinwith_external,wrap_res0else(warninganalysis"Trying to %s uninitialized mutex. Ignoring"op.name;state,wrap_res(-1))|_->(* really unlikely unless the code and/or the C binding
are really strange *)Mt_self.fatal"Incorrect mthread binding for mutex function"lethook_init_mutexanalysisstate:hook_sig=function|[_,name]->letpos=current_positionanalysisandname=Concurrency.Name.of_cvaluenameinletmutex=Mutex.createposnameinanalysis.all_mutexes<-Mutex.Set.addmutexanalysis.all_mutexes;letstate=MutexOp.check_and_writeanalysisstatemutexMutexOp.initializeinresultanalysis"Initializing mutex %a"Mutex.prettymutex;state,wrap_res(Mutex.idmutex)|_->(* really unlikely unless the code and/or the C binding
are really strange *)Mt_self.fatal"Incorrect mthread binding for mutex function"lethook_lock_mutex=aux_mutex~operation:MutexOp.lock~event:(funid->MutexLockid)lethook_release_mutex=aux_mutex~operation:MutexOp.unlock~event:(funid->MutexReleaseid)(* -------------------------------------------------------------------------- *)(** --- Misc --- *)(* -------------------------------------------------------------------------- *)lethook_dummy_messageanalysisstate:hook_sig=function|(_,name)::args->letconvv=catch_conversionanalysis~prefix:"During unknown event"vinletname=Mt_memory.extract_constant_stringnameinletname=convname"invalid event name"inletevt=Dummy(name,List.mapsndargs)inregister_eventanalysisevt;resultanalysis"Monitored event: %a"Event.prettyevt;state,no_res|_->Mt_self.fatal"Incorrect mthread binding for unknown event"(* -------------------------------------------------------------------------- *)(** --- Main declarations --- *)(* -------------------------------------------------------------------------- *)(* All the Mthread builtin functions, together with their C name.
The remainder of the conversion to the real type of the callback
{Builtins.register_builtin} occurs in [Mt_main] *)letmthread_builtins=[(* Threads *)"Frama_C_thread_create",hook_thread_creation;"Frama_C_thread_start",hook_thread_start;"Frama_C_thread_suspend",hook_thread_suspend;"Frama_C_thread_cancel",hook_thread_cancellation;"Frama_C_thread_exit",hook_thread_exit;"Frama_C_thread_id",hook_thread_id;"Frama_C_thread_priority",hook_thread_priority;(* Mutexes *)"Frama_C_mutex_init",hook_init_mutex;"Frama_C_mutex_lock",hook_lock_mutex;"Frama_C_mutex_unlock",hook_release_mutex;(* Message queues *)"Frama_C_queue_init",hook_queue_init;"Frama_C_queue_send",hook_send_msg;"Frama_C_queue_receive",hook_receive_msg;(* Misc *)"Frama_C_mthread_show",hook_dummy_message;(* Shared values *)"Frama_C_mthread_sync",hook_sync;];;letis_mthread_builtins=List.exists(fun(s',_)->s=s')mthread_builtins(* Function to register as a callback of the Eva analysis if Mthread
is enabled *)letcatch_functions_callsanalysis(stack:Callstack.callstack)kfstatekind=letf=Kernel_function.get_namekfin(* If an Mthread builtin has been called as main, we fail immediately.
In fact, this case should not happen, because we reject calls to __FRAMA_C_*
functions as main or during thread spawning. We could detect when the stack
has only one element (i.e. pthread_* has been called as main), but the error
message arrives too late, and is not really readable *)ifis_mthread_builtinf&&Option.is_none(Callstack.popstack)thenMt_self.abort"Thread function %s called as starting thread function"f;(* Warn on concurrency library functions without stubs. *)ifkind=`SpecthenMt_lib.warn_on_unsupported_library_functionkf;analysis.curr_stack<-stack;ifCallstack.is_emptystackthen(* This is the entry point of the analysis, the events stack needs to be
empty. *)analysis.curr_events_stack<-[];ifCallstack.is_emptyanalysis.curr_stack&&Thread.is_mainanalysis.curr_thread.th_eva_threadthenbegin(* Beginning of the main thread (kf being the entry point). For the main
thread, it might have not been registered yet if we are at the
first iteration. *)letth=main_threadkfstatein(* This call registers the main thread on the first run, and essentially
does nothing afterwards *)letth=spawn_threadanalysisth.th_eva_threadth.th_stackth.th_funth.th_init_stateth.th_paramsNoneinifanalysis.main_thread!=ththenbegin(* On the first run, the record [th] is created. It is not contained
anywhere else, so we update the fields below. *)analysis.main_thread<-th;analysis.curr_thread<-th;(* We are currently computing this thread (the main one) and we have
just started, no need to recompute it at the next iteration *)th.th_to_recompute<-SetRecomputeReason.empty;(* On the first iteration, also register the interrupt handlers *)letinterrupt_handlers=Mt_options.InterruptHandlers.get()inletinterrupts,state=Kernel_function.Set.fold(funkf_interrupt(interrupts,state)->(* Create and spawn the interrupt thread *)letth=interrupt_threadkf_interruptstateinletth=spawn_threadanalysisth.th_eva_threadth.th_stackth.th_funth.th_init_stateth.th_paramsNonein(* Start the interrupt thread *)letstate=Mt_ids.write_id_statestate(Mt_ids.of_threadth.th_eva_thread)1in(th::interrupts,state))interrupt_handlers([],state)inifinterrupts!=[]thenbegin(* If any interrupt handler has been registered, then their initial
state and the initial state of the main thread need to be updated
so that they all are considered running. *)List.iter(funth->let_=update_initial_stateanalysisth.th_eva_threadstatein())(th::interrupts)endendend;push_function_callanalysis;(* If the function is a leaf one, we register the accesses that occur
through \assigns ACSL specifications, then pop the stack. If there is a
definition, the registering will be done by another hook, at the end of
the execution of the function *)matchkindwith|`Spec|`Builtin->Mt_shared_vars.register_concurrent_var_accessesanalysis(`Leafstate);pop_function_callanalysis;|`Body|`Reuse->()(* Function registered by [Cvalue_callbacks.register_call_results_hook].
Given the end states of a function with a definition, records the variable
accesses it did. *)letcatch_functions_recordanalysisstack_kf_pre_state=function|`Body(Cvalue_callbacks.{before_stmts;after_stmts},i)->analysis.curr_stack<-stack;lethbefore=Lazy.forcebefore_stmtsinlethafter=Lazy.forceafter_stmtsinMt_shared_vars.register_concurrent_var_accessesanalysis(`Finalhbefore);register_memory_statesanalysis~before:hbefore~after:hafter;letcur_events=curr_eventsanalysisinDatatype.Int.Hashtbl.addanalysis.memexec_cacheicur_events;pop_function_callanalysis;|`Reusei->letevents=Datatype.Int.Hashtbl.findanalysis.memexec_cacheiin(* Merge the memoized results in the current analysis *)register_multiple_eventsanalysisevents;pop_function_callanalysis;|`Builtin_|`Spec_->()