From 5710f9d5be56b427ccfa48f6a730d70396817efe Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Sat, 19 Dec 2015 21:22:14 +0100 Subject: Fixed several bugs when processing concurrent delayed works. --- ChangeLog | 17 ++++ src/analysis/disass/fetch.c | 88 ++++++++++++++++--- src/arch/context-int.h | 2 + src/arch/context.c | 55 +++++------- src/arch/context.h | 6 +- src/glibext/delayed.c | 203 ++++++++++++++++++++++++++++++++++++++++---- src/glibext/delayed.h | 11 +++ 7 files changed, 318 insertions(+), 64 deletions(-) diff --git a/ChangeLog b/ChangeLog index 31dca7f..a6c2c2c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +15-12-19 Cyrille Bagard + + * src/analysis/disass/fetch.c: + Track the remaining drop points for the delayed work group. + + * src/arch/context-int.h: + * src/arch/context.c: + * src/arch/context.h: + Use an external counter to track the added drop points. + + * src/glibext/delayed.c: + * src/glibext/delayed.h: + Fix several bugs when processing concurrent delayed works. Count precisely + the remaining works to wait for. Avoid to generate group identifiers equal + to DEFAULT_WORK_GROUP. Fix a bug by handling the "no status bar" case. Fix + an Out-Of-Bound access when creating new groups. + 15-12-17 Cyrille Bagard * src/arch/arm/context.c: diff --git a/src/analysis/disass/fetch.c b/src/analysis/disass/fetch.c index 5caf573..29d8923 100644 --- a/src/analysis/disass/fetch.c +++ b/src/analysis/disass/fetch.c @@ -309,7 +309,8 @@ static void g_delayed_fetching_process(GDelayedFetching *fetching, GtkExtStatusB /* Poursuit l'analyse à partir des points d'entrée découverts. */ static void follow_execution_flow_v2(GProcContext *, const GDelayedFetching *); - +/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */ +static bool check_if_extra_wait_is_needed(GWorkQueue *, wgroup_id_t, GProcContext *); @@ -331,11 +332,14 @@ static void follow_execution_flow_v2(GProcContext *, const GDelayedFetching *); static void follow_execution_flow_v2(GProcContext *ctx, const GDelayedFetching *template) { GWorkQueue *queue; /* Gestionnaire de différés */ + gint *remaining_counter; /* Compteur à considérer */ virt_t virt; /* Adresse de départ dépilée */ GDelayedFetching *fetching; /* Récupération à mener */ queue = get_work_queue(); + remaining_counter = (gint *)g_object_get_data(G_OBJECT(ctx), "remaining_counter"); + while (g_proc_context_pop_drop_point(ctx, &virt)) { fetching = g_delayed_fetching_new(template, virt); @@ -351,6 +355,16 @@ static void follow_execution_flow_v2(GProcContext *ctx, const GDelayedFetching * g_work_queue_schedule_work(queue, G_DELAYED_WORK(fetching), template->gid); + /** + * Le décompte n'est réalisé qu'après la programmation de la tâche. + * Ainsi, lors de l'attente de la fin des traitements, on a la garantie + * de ne pas avoir de trou entre le dépilement des points et la programmation + * des tâches de traitement associées. + */ + + if (g_atomic_int_dec_and_test(remaining_counter)) + g_work_queue_wake_up_waiters(queue, template->gid); + } } @@ -358,6 +372,45 @@ static void follow_execution_flow_v2(GProcContext *ctx, const GDelayedFetching * /****************************************************************************** * * +* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * +* id = identifiant d'un groupe de travail. * +* ctx = contexte de désass. avec une nouvelle entrée. * +* * +* Description : Etudie le besoin d'attendre d'avantage de prochaines tâches. * +* * +* Retour : true pour attendre d'avantage, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool check_if_extra_wait_is_needed(GWorkQueue *queue, wgroup_id_t id, GProcContext *ctx) +{ + bool result; /* Bilan à retourner */ + gint *remaining_counter; /* Compteur à considérer */ + + remaining_counter = (gint *)g_object_get_data(G_OBJECT(ctx), "remaining_counter"); + + result = (g_atomic_int_get(remaining_counter) > 0); + + return result; + +} + + + + + + + + + + + + + +/****************************************************************************** +* * * Paramètres : binary = représentation de binaire chargé. * * ctx = contexte offert en soutien à un désassemblage. * * areas = liste de zones contenant des données à traiter. * @@ -435,15 +488,9 @@ GArchInstruction *disassemble_binary_content(const GLoadedBinary *binary, wgroup GBinContent *content; /* Contenu binaire à manipuler */ phys_t length; /* Taille des données à lire */ GWorkQueue *queue; /* Gestionnaire de différés */ + gint remaining_counter; /* Quantité de points restants */ double done; /* Portion de travail accompli */ - - - - - - - /* Constitution du modèle de référence */ template.gid = gid; @@ -463,24 +510,33 @@ GArchInstruction *disassemble_binary_content(const GLoadedBinary *binary, wgroup /* Amorce des traitements */ - g_signal_connect(template.ctx, "drop-point-pushed", G_CALLBACK(follow_execution_flow_v2), &template); - queue = get_work_queue(); + g_atomic_int_set(&remaining_counter, 0); + + g_object_set_data(G_OBJECT(template.ctx), "remaining_counter", &remaining_counter); + + g_proc_context_attach_counter(template.ctx, &remaining_counter); + /** * Première phase de désassemblage : suivi des chemins tracés. */ + + g_work_queue_set_extra_wait_callback(queue, gid, + (wait_for_incoming_works_cb)check_if_extra_wait_is_needed, + template.ctx); + + g_signal_connect(template.ctx, "drop-point-pushed", G_CALLBACK(follow_execution_flow_v2), &template); + template.info = init_progessive_status(statusbar, _("Disassembling following the execution flow..."), 0, length); - + g_binary_format_setup_disassembling_context(format, template.ctx); g_work_queue_wait_for_completion(queue, gid); - printf("===================== DONE !\n"); - done = get_current_progessive_status(template.info); fini_progessive_status(template.info); @@ -495,6 +551,12 @@ GArchInstruction *disassemble_binary_content(const GLoadedBinary *binary, wgroup ensure_all_mem_areas_are_filled(template.areas, template.count, template.ctx, template.info); + g_work_queue_wait_for_completion(queue, gid); + + g_work_queue_set_extra_wait_callback(queue, gid, NULL, NULL); + + g_object_set_data(G_OBJECT(template.ctx), "remaining_counter", NULL); + fini_progessive_status(template.info); /** diff --git a/src/arch/context-int.h b/src/arch/context-int.h index 885c2f6..59a06bb 100644 --- a/src/arch/context-int.h +++ b/src/arch/context-int.h @@ -54,6 +54,8 @@ struct _GProcContext size_t esyms_count; /* Nombres de nouveautés */ GMutex es_access; /* Accès à cette même liste */ + gint *counter; + }; diff --git a/src/arch/context.c b/src/arch/context.c index bb1d80a..98eebb2 100644 --- a/src/arch/context.c +++ b/src/arch/context.c @@ -104,6 +104,26 @@ static void g_proc_context_init(GProcContext *ctx) /****************************************************************************** * * +* Paramètres : ctx = contexte de désassemblage à mettre à jour. * +* counter = adresse du compteur à modifier. * +* * +* Description : Enregistre un compteur pour le décompte des points à traiter.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_proc_context_attach_counter(GProcContext *ctx, gint *counter) +{ + ctx->counter = counter; + +} + + +/****************************************************************************** +* * * Paramètres : ctx = contexte de désassemblage à compléter. * * level = indication de priorité et d'origine de l'adresse. * * addr = adresse d'un nouveau point de départ à traiter. * @@ -155,6 +175,9 @@ void g_proc_context_push_drop_point(GProcContext *ctx, unsigned int level, virt_ g_mutex_lock(&ctx->dp_access); + if (ctx->counter != NULL) + g_atomic_int_inc(ctx->counter); + G_PROC_CONTEXT_GET_CLASS(ctx)->push_point(ctx, level, addr, ap); g_mutex_unlock(&ctx->dp_access); @@ -168,38 +191,6 @@ void g_proc_context_push_drop_point(GProcContext *ctx, unsigned int level, virt_ /****************************************************************************** * * -* Paramètres : ctx = contexte de désassemblage à consulter. * -* addr = adresse de mémoire virtuelle à rechercher. * -* * -* Description : Précise si une adresse donnée figure comme point de départ. * -* * -* Retour : true si l'adresse est connue en interne, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_proc_context_has_addr_as_drop_points(GProcContext *ctx, virt_t addr) -{ - bool result; /* Bilan à retourner */ - size_t i; /* Boucle de parcours */ - - result = false; - - g_mutex_lock(&ctx->dp_access); - - for (i = 0; i < ctx->dp_count && !result; i++) - result = (ctx->drop_points[i] == addr); - - g_mutex_unlock(&ctx->dp_access); - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : ctx = contexte de désassemblage à compléter. * * virt = adresse d'un point de départ de code à traiter. * * * diff --git a/src/arch/context.h b/src/arch/context.h index 5dffd6a..334eaf1 100644 --- a/src/arch/context.h +++ b/src/arch/context.h @@ -51,12 +51,12 @@ typedef struct _GProcContextClass GProcContextClass; /* Indique le type définit par la GLib pour le contexte de processeur. */ GType g_proc_context_get_type(void); +/* Enregistre un compteur pour le décompte des points à traiter. */ +void g_proc_context_attach_counter(GProcContext *, gint *); + /* Ajoute une adresse virtuelle comme point de départ de code. */ void g_proc_context_push_drop_point(GProcContext *, unsigned int, virt_t, ...); -/* Précise si une adresse donnée figure comme point de départ. */ -bool g_proc_context_has_addr_as_drop_points(GProcContext *, virt_t); - /* Fournit une adresse virtuelle comme point de départ de code. */ bool g_proc_context_pop_drop_point(GProcContext *, virt_t *); diff --git a/src/glibext/delayed.c b/src/glibext/delayed.c index 967865b..a83fae4 100644 --- a/src/glibext/delayed.c +++ b/src/glibext/delayed.c @@ -78,11 +78,15 @@ typedef struct _GWorkGroup GMutex mutex; /* Verrou pour l'accès */ GCond cond; /* Réveil pour un traitement */ GCond wait_cond; /* Réveil d'attente de fin */ + gint pending; /* Tâches en cours d'exécution */ GThread **threads; /* Procédure de traitement */ guint threads_count; /* Nombre de procédures */ bool force_exit; /* Procédure d'arrêt */ + wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */ + void *data; /* Données à associer */ + } GWorkGroup; /* File de traitement pour un type donné (classe) */ @@ -121,7 +125,13 @@ static void g_work_group_schedule(GWorkGroup *, GDelayedWork *); static void *g_work_group_process(GWorkGroup *); /* Attend que toutes les tâches d'un groupe soient traitées. */ -static void g_work_group_wait_for_completion(GWorkGroup *); +static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *); + +/* Modifie les conditions d'attente des fins d'exécutions. */ +static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *); + +/* Force un réveil d'une attente en cours pour la confirmer. */ +static void g_work_group_wake_up_waiters(GWorkGroup *); @@ -166,6 +176,9 @@ static void g_work_queue_finalize(GWorkQueue *); /* Donne l'assurance de l'existence d'un groupe de travail. */ static GWorkGroup *g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t); +/* Fournit le groupe de travail correspondant à un identifiant. */ +static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t); + /* ---------------------------------------------------------------------------------- */ @@ -379,6 +392,8 @@ static void g_work_group_init(GWorkGroup *group) g_cond_init(&group->cond); g_cond_init(&group->wait_cond); + g_atomic_int_set(&group->pending, 0); + group->threads_count = g_get_num_processors(); group->threads = (GThread **)calloc(group->threads_count, sizeof(GThread *)); @@ -401,6 +416,9 @@ static void g_work_group_init(GWorkGroup *group) group->force_exit = false; + group->callback = NULL; + group->data = NULL; + } @@ -491,8 +509,11 @@ static GWorkGroup *g_work_group_new(wgroup_id_t id, GtkExtStatusBar *statusbar) result->id = id; - result->statusbar = statusbar; - g_object_ref(statusbar); + if (statusbar != NULL) + { + result->statusbar = statusbar; + g_object_ref(statusbar); + } return result; @@ -535,6 +556,8 @@ static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work) { g_mutex_lock(&group->mutex); + g_atomic_int_inc(&group->pending); + delayed_work_list_add_tail(work, &group->works); g_cond_signal(&group->cond); @@ -568,7 +591,10 @@ static void *g_work_group_process(GWorkGroup *group) g_cond_wait(&group->cond, &group->mutex); if (group->force_exit) + { + g_mutex_unlock(&group->mutex); break; + } work = group->works; delayed_work_list_del(work, &group->works); @@ -579,7 +605,8 @@ static void *g_work_group_process(GWorkGroup *group) g_object_unref(G_OBJECT(work)); - g_cond_broadcast(&group->wait_cond); + if (g_atomic_int_dec_and_test(&group->pending)) + g_cond_broadcast(&group->wait_cond); } @@ -591,6 +618,7 @@ static void *g_work_group_process(GWorkGroup *group) /****************************************************************************** * * * Paramètres : group = groupe dont les conclusions sont attendues. * +* queue = queue d'appartenance pour les appels externes. * * * * Description : Attend que toutes les tâches d'un groupe soient traitées. * * * @@ -600,18 +628,78 @@ static void *g_work_group_process(GWorkGroup *group) * * ******************************************************************************/ -static void g_work_group_wait_for_completion(GWorkGroup *group) +static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queue) { + wait_for_incoming_works_cb callback; /* Procédure complémentaire */ + + bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data) + { + return false; + } + + callback = group->callback != NULL ? group->callback : no_extra_check; + g_mutex_lock(&group->mutex); - while (dl_list_empty(group->works) && !group->force_exit) + /** + * On attend que : + * - la liste des tâches programmées soit vide. + * - il n'existe plus de tâche en cours. + * - rien n'indique que de nouvelles tâches supplémentaires vont arriver. + */ + + while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data)) + && !group->force_exit) + { g_cond_wait(&group->wait_cond, &group->mutex); + } g_mutex_unlock(&group->mutex); } +/****************************************************************************** +* * +* Paramètres : group = groupe dont les paramètres sont à modifier. * +* callback = éventuelle fonction à appeler ou NULL. * +* data = données devant accompagner l'appel. * +* * +* Description : Modifie les conditions d'attente des fins d'exécutions. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data) +{ + group->callback = callback; + group->data = data; + +} + + +/****************************************************************************** +* * +* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* +* id = identifiant d'un groupe de travail. * +* * +* Description : Force un réveil d'une attente en cours pour la confirmer. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_work_group_wake_up_waiters(GWorkGroup *group) +{ + g_cond_broadcast(&group->wait_cond); + +} + /* ---------------------------------------------------------------------------------- */ @@ -816,7 +904,7 @@ static GWorkGroup *g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id queue->groups_count * sizeof(GWorkGroup *)); result = g_work_group_new(id, queue->statusbar); - queue->groups[i] = result; + queue->groups[queue->groups_count - 1] = result; } @@ -843,7 +931,7 @@ wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue) g_mutex_lock(&queue->mutex); - result = queue->generator++; + result = ++queue->generator; g_work_queue_ensure_group_exists(queue, result); @@ -932,35 +1020,118 @@ void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id * Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * * id = identifiant d'un groupe de travail. * * * -* Description : Attend que toutes les tâches d'un groupe soient traitées. * +* Description : Fournit le groupe de travail correspondant à un identifiant. * * * -* Retour : - * +* Retour : Eventuel groupe existant trouvé ou NULL si aucun. * * * * Remarques : - * * * ******************************************************************************/ -void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) +static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t id) { + GWorkGroup *result; /* Trouvaille à retourner */ size_t i; /* Boucle de parcours */ - GWorkGroup *group; /* Groupe de travail à attendre*/ - group = NULL; + result = NULL; g_mutex_lock(&queue->mutex); for (i = 0; i < queue->groups_count; i++) if (g_work_group_get_id(queue->groups[i]) == id) { - group = queue->groups[i]; - g_object_ref(G_OBJECT(group)); + result = queue->groups[i]; + g_object_ref(G_OBJECT(result)); + break; } g_mutex_unlock(&queue->mutex); + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * +* id = identifiant d'un groupe de travail. * +* * +* Description : Attend que toutes les tâches d'un groupe soient traitées. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) +{ + GWorkGroup *group; /* Groupe de travail à attendre*/ + + group = g_work_queue_find_group_for_id(queue, id); + + if (group != NULL) + { + g_work_group_wait_for_completion(group, queue); + g_object_unref(G_OBJECT(group)); + } + +} + + +/****************************************************************************** +* * +* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* +* id = identifiant d'un groupe de travail. * +* callback = éventuelle fonction à appeler ou NULL. * +* data = données devant accompagner l'appel. * +* * +* Description : Modifie les conditions d'attente des fins d'exécutions. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data) +{ + GWorkGroup *group; /* Groupe de travail à traiter */ + + group = g_work_queue_find_group_for_id(queue, id); + + if (group != NULL) + { + g_work_group_set_extra_wait_callback(group, callback, data); + g_object_unref(G_OBJECT(group)); + } + +} + + +/****************************************************************************** +* * +* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* +* id = identifiant d'un groupe de travail. * +* * +* Description : Force un réveil d'une attente en cours pour la confirmer. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id) +{ + GWorkGroup *group; /* Groupe de travail à traiter */ + + group = g_work_queue_find_group_for_id(queue, id); + if (group != NULL) { - g_work_group_wait_for_completion(group); + g_work_group_wake_up_waiters(group); g_object_unref(G_OBJECT(group)); } diff --git a/src/glibext/delayed.h b/src/glibext/delayed.h index 0a27c93..4902e68 100644 --- a/src/glibext/delayed.h +++ b/src/glibext/delayed.h @@ -106,5 +106,16 @@ void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *, wgroup_id_t); void g_work_queue_wait_for_completion(GWorkQueue *, wgroup_id_t); +/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */ +typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *); + + +/* Modifie les conditions d'attente des fins d'exécutions. */ +void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *); + +/* Force un réveil d'une attente en cours pour la confirmer. */ +void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t); + + #endif /* _GLIBEXT_DELAYED_H */ -- cgit v0.11.2-87-g4458