Traquer les conditions de concurrence dans Linux avec MAccConc et KCOV
Reproduire de manière fiable une condition de concurrence relève souvent du défi en raison du non-déterminisme de l'ordonnancement des threads. Un chercheur de Project Zero dévoile MAccConc, un outil open source permettant de tracer les accès mémoire et d'injecter des délais ciblés dans le noyau Linux.
Fil « Détection des conditions de concurrence par traçage mémoire et injection de délais »

Les bugs de concurrence, ou race conditions (failles où le résultat dépend de l'ordre d'exécution imprévisible de plusieurs threads, les unités d'exécution d'un processus), figurent parmi les plus complexes à analyser et à corriger. Jusqu'à présent, la validation d'un correctif reposait souvent sur l'ajout manuel de boucles d'attente temporaires dans le code du noyau Linux ou l'utilisation d'outils de traçage comme DTrace, dont l'impact reste limité sur les fonctions intégrées (inline). Pour résoudre ce manque de déterminisme, l'outil MAccConc (Memory Access Concurrency) propose une approche automatisée fondée sur le traçage fin des accès mémoire et l'injection de verrous d'attente.
Identifier les accès avec des traces d'appel augmentées
Pour rejouer un scénario de concurrence, il est nécessaire d'identifier un accès mémoire de manière stable d'une exécution à l'autre. S'appuyer sur l'adresse mémoire brute est impossible car les allocations changent à chaque passage. Se baser uniquement sur l'adresse de l'instruction ne suffit pas non plus si l'accès a lieu dans une fonction utilitaire générique comme memcpy(). MAccConc introduit donc les traces d'appel augmentées d'un compteur (count-augmented stack traces). Chaque niveau de la pile d'appels indique précisément combien d'appels à une fonction cible doivent être ignorés avant d'intercepter l'accès désiré.
Collecte des accès mémoire via KCOV et ASAN
Sur le plan matériel et logiciel, l'outil s'appuie sur deux briques internes du noyau Linux. D'une part, l'instrumentation du compilateur via ASAN (AddressSanitizer, un outil de détection des erreurs mémoire) génère un rappel système à chaque accès en lecture ou écriture. D'autre part, KCOV (Kernel Coverage, le mécanisme d'analyse de couverture du noyau) est détourné pour enregistrer ces événements et les transmettre à l'espace utilisateur. Cette intégration nécessite le support du compilateur LLVM 23.1.0 pour remonter les événements d'entrée et de sortie de fonction.
Contrôle de l'ordonnancement par injection de délais
Une fois les points d'accès mémoire repérés, MAccConc permet de piloter l'ordre d'exécution des threads grâce à un nouvel appel système via ioctl (une commande de contrôle d'un périphérique ou du noyau) nommé KCOV_SET_DI. L'espace utilisateur peut associer des actions explicites aux adresses mémoire repérées :
// Exemple conceptuel d'action transmise via l'ioctl KCOV_SET_DI
struct kcov_di_action action = {
.type = DI_STACK_WAIT, // Met le thread en attente active (spin-wait)
.flag_index = 0, // Index du drapeau partagé à vérifier
.max_spin_iterations = 1000
};
ioctl(kcov_fd, KCOV_SET_DI, &action);
Trois primitives sont proposées : DI_STACK_WAKE_PRE (lever un drapeau avant l'accès), DI_STACK_WAIT (attendre qu'un drapeau soit levé) et DI_STACK_WAKE_POST (lever un drapeau après l'accès). Grâce à ce mécanisme, MAccConc propose deux modes d'exploration : une approche par contraintes de causalité (A-happens-before-B) avec interface graphique pour l'analyse manuelle, et un mode de bascule de contexte strict permettant d'exécuter automatiquement des entrelacements alternés (A-B-A) pour la recherche automatisée de bugs.