TL;DR — En résumé
Guide expert race conditions kernel : double-fetch, TOCTOU, userfaultfd, Dirty COW et Pipe
Les race conditions kernel sont parmi les vulnérabilités les plus complexes et les plus puissantes en exploitation système. Contrairement aux buffer overflows qui corrompent la mémoire par débordement, les race conditions exploitent les fenêtres temporelles entre deux opérations qui devraient être atomiques mais ne le sont pas. Les deux catégories principales — Double-Fetch (le kernel lit deux fois une valeur en mémoire partagée, l'attaquant la modifie entre les deux lectures) et TOCTOU (Time-of-Check-Time-of-Use, le kernel vérifie une condition puis agit dessus, l'attaquant modifie l'état entre la vérification et l'utilisation) — permettent l'escalade de privilèges, le contournement des vérifications de sécurité et la corruption de données kernel. Ce guide technique couvre les mécanismes d'exploitation, les techniques de synchronisation (race winning), les CVE historiques (Dirty COW, Dirty Pipe), les outils de détection et les primitifs avancés d'exploitation de race conditions dans le noyau Linux et Windows.
\nEn bref
- Double-Fetch : le kernel lit deux fois une valeur userspace — l'attaquant la modifie entre les lectures
- TOCTOU : Time-of-Check-Time-of-Use — l'état change entre la vérification et l'utilisation
- Exploitation : timing manipulation, multi-threading, CPU pinning et userfaultfd
- CVE historiques : Dirty COW (CVE-2016-5195), Dirty Pipe (CVE-2022-0847), io_uring races
- Détection : KCSAN, Thread Sanitizer, analyse statique et fuzzing concurrentiel (Syzkaller)
Double-Fetch : Mécanisme et Exploitation
\nUn double-fetch se produit quand le kernel accède deux fois au même emplacement en mémoire userspace : une première fois pour vérifier (validation de taille, type, permissions) et une seconde fois pour utiliser la valeur. L'attaquant utilise un second thread pour modifier la valeur entre les deux accès :
\n// KERNEL CODE VULNÉRABLE (simplifié)\n// Le kernel lit la taille depuis l'espace utilisateur DEUX FOIS\n\nstruct user_request __user *req = (void *)arg;\n\n// 1ère lecture : vérification de la taille\nif (copy_from_user(&size, &req->size, sizeof(size)))\n return -EFAULT;\nif (size > MAX_SIZE)\n return -EINVAL; // Vérification OK\n\n// ⚠️ FENÊTRE DE RACE — l'attaquant modifie req->size ici\n\n// 2ème lecture : utilisation de la taille (implicite dans copy_from_user)\nbuf = kmalloc(size, GFP_KERNEL); // Alloue avec la taille vérifiée\nif (copy_from_user(buf, req->data, req->size)) // ❌ Re-lit req->size !\n // req->size peut maintenant être > MAX_SIZE → buffer overflow kernel\n return -EFAULT;\nTOCTOU : Time-of-Check-Time-of-Use
\nLes vulnérabilités TOCTOU surviennent quand le kernel vérifie une condition (check) puis agit dessus (use), mais l'état peut changer entre les deux opérations. L'exemple classique est la vérification d'accès à un fichier :
\n// TOCTOU classique sur le filesystem\n// Thread 1 (programme setuid) :\nif (access("/tmp/config", R_OK) == 0) {\n // CHECK : l'utilisateur a le droit de lire /tmp/config\n \n // ⚠️ FENÊTRE DE RACE\n // Thread 2 : symlink("/etc/shadow", "/tmp/config")\n \n fd = open("/tmp/config", O_RDONLY);\n // USE : ouvre le fichier — mais c'est maintenant /etc/shadow !\n read(fd, buf, sizeof(buf)); // Lit /etc/shadow avec les privilèges root\n}\nDirty COW (CVE-2016-5195) : La Race Condition Légendaire
\nDirty COW est la race condition kernel la plus célèbre : elle exploite une race dans le mécanisme de Copy-on-Write (COW) du gestionnaire de mémoire Linux. Quand un processus écrit dans une page COW, le kernel doit copier la page avant l'écriture. Dirty COW exploite une race entre le thread de fault handling et le thread d'écriture pour modifier directement la page originale (partagée) sans la copier — permettant l'écriture dans des fichiers en lecture seule, y compris /etc/passwd et les binaires setuid.
Dirty Pipe (CVE-2022-0847) : Race sur les Pipes
\nDirty Pipe exploite un bug dans la gestion des pipes Linux : le flag PIPE_BUF_FLAG_CAN_MERGE n'est pas correctement initialisé quand un pipe buffer est recyclé depuis le page cache. L'attaquant peut écrire dans n'importe quel fichier lisible (même en lecture seule) via un pipe, sans aucune race condition temporelle — le bug est déterministe. Dirty Pipe est considéré comme encore plus puissant que Dirty COW car il est fiable à 100%.
Techniques de Race Winning
\nGagner une race condition kernel nécessite un contrôle précis du timing entre les threads :
\n- \n
- CPU Pinning : utiliser
sched_setaffinity()pour forcer les threads attaquant et victime sur des cœurs CPU spécifiques, réduisant la variabilité de scheduling \n - userfaultfd : intercepter les page faults userspace pour bloquer le kernel au milieu d'un
copy_from_user()— agrandit la fenêtre de race à l'infini \n - FUSE (Filesystem in Userspace) : monter un filesystem FUSE et bloquer les opérations de lecture pour contrôler le timing des accès fichier du kernel \n
- io_uring : les opérations asynchrones io_uring créent naturellement des fenêtres de race dans le kernel \n
- Flooding / contention : saturer les caches, les locks ou les schedulers pour augmenter la latence entre les opérations kernel \n
userfaultfd : L'Arme Ultime pour les Races Kernel
\n// userfaultfd — contrôler le timing d'un copy_from_user kernel\n#include <linux/userfaultfd.h>\n#include <sys/ioctl.h>\n\n// 1. Créer un userfaultfd\nint uffd = syscall(SYS_userfaultfd, O_CLOEXEC | O_NONBLOCK);\n\n// 2. Mapper une page et l'enregistrer avec userfaultfd\nvoid *page = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,\n MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);\n\nstruct uffdio_register reg = {\n .range = {.start = (unsigned long)page, .len = PAGE_SIZE},\n .mode = UFFDIO_REGISTER_MODE_MISSING\n};\nioctl(uffd, UFFDIO_REGISTER, ®);\n\n// 3. Passer 'page' au syscall vulnérable\n// Quand le kernel fait copy_from_user(page) → PAGE FAULT\n// Le kernel se bloque et attend notre handler userfaultfd\n\n// 4. Dans le handler (thread séparé) :\n// - Modifier l'état kernel (autre thread/syscall)\n// - Puis résoudre le fault en fournissant les données\nstruct uffdio_copy copy = {\n .dst = (unsigned long)page,\n .src = (unsigned long)malicious_data,\n .len = PAGE_SIZE,\n};\nioctl(uffd, UFFDIO_COPY, ©);\n// → Le kernel reprend avec nos données malveillantes\nDétection et Prévention
\n- \n
- KCSAN (Kernel Concurrency SANitizer) : détecteur de data races dynamique intégré au kernel Linux depuis 5.8 \n
- Syzkaller : fuzzer kernel de Google qui génère automatiquement des séquences de syscalls concurrentes pour déclencher des races \n
- copy_from_user_once() : pattern kernel recommandé — copier les données userspace une seule fois dans un buffer kernel, puis utiliser uniquement le buffer kernel \n
- Restriction userfaultfd : Linux 5.11+ restreint userfaultfd aux processus avec CAP_SYS_PTRACE (sysctl vm.unprivileged_userfaultfd=0) \n
À retenir
- Les race conditions kernel exploitent les fenêtres temporelles entre opérations non-atomiques
- Double-fetch : le kernel lit 2x la mémoire userspace — l'attaquant modifie entre les lectures
- TOCTOU : l'état change entre la vérification (check) et l'utilisation (use) — bypass de permissions
- userfaultfd bloque le kernel au milieu de copy_from_user() — agrandit la fenêtre de race à l'infini
- Dirty COW et Dirty Pipe sont les race conditions kernel les plus célèbres — LPE fiable sur Linux
- KCSAN et Syzkaller détectent automatiquement les data races dans le kernel Linux
FAQ — Questions Fréquentes
\nLes race conditions kernel sont-elles exploitables de manière fiable ?
Historiquement non — les races étaient considérées comme non-fiables car dépendantes du timing CPU. Mais les techniques modernes (userfaultfd, FUSE, CPU pinning) permettent de contrôler le timing avec une précision suffisante pour une exploitation fiable. Dirty COW avait un taux de succès >90% avec la bonne configuration de threads. Dirty Pipe est déterministe (pas réellement une race condition temporelle).
Comment trouver des race conditions dans le kernel Linux ?
Les approches principales : Syzkaller (fuzzer kernel concurrentiel — le plus efficace), KCSAN (détection dynamique de data races pendant l'exécution), audit de code (rechercher les double-fetch dans copy_from_user et les TOCTOU dans les vérifications de permissions), et analyse statique (outils comme Coccinelle avec des patterns de détection de races).
userfaultfd est-il toujours disponible pour les attaquants ?
Sur les kernels récents (5.11+), userfaultfd nécessite CAP_SYS_PTRACE pour les processus non privilégiés si vm.unprivileged_userfaultfd=0 est configuré (défaut sur la plupart des distributions récentes). Cependant, l'alternative FUSE (Filesystem in Userspace) fournit des capacités similaires pour le contrôle de timing et est accessible sans privilèges spéciaux via fusermount.
Besoin d'un accompagnement expert ?
Nos consultants spécialisés en sécurité système et exploitation kernel vous accompagnent dans l'évaluation de votre posture de sécurité.
Contactez-nous? Articles connexes
? Références externes
Exploitation des race conditions dans les pilotes de périphériques modernes
Les pilotes de périphériques représentent une surface d'attaque privilégiée pour les race conditions noyau, car ils s'exécutent en ring 0 tout en interagissant avec des espaces utilisateur non fiables. Les double-fetch vulnerabilities dans les pilotes surviennent lorsque le code noyau lit deux fois un pointeur ou une valeur depuis la mémoire utilisateur sans verrouillage intermédiaire — entre les deux lectures, un thread concurrent peut modifier la valeur via un `mmap()` de la région partagée. Le CVE-2022-29582 (pilote io_uring Linux) et le CVE-2023-33246 (pilote GPU Qualcomm) illustrent la persistance de ce pattern même dans du code maintenu activement.
Les outils de détection statique comme Coccinelle (semantic patch engine du kernel Linux) et cppcheck avec ses règles de concurrence permettent d'identifier les patterns double-fetch dans du code C. Pour la détection dynamique, KCSAN (Kernel Concurrency Sanitizer), disponible dans le kernel Linux depuis 5.8, instrumente les accès mémoire au niveau noyau et détecte les data races en temps réel — son déploiement en environnement de fuzzing (syzkaller) est une pratique standard dans les programmes de bug bounty noyau de Google et du Linux Kernel Security Team.

Besoin d'un expert cybersécurité ?
\nAudit, pentest, formation, IA — plus de 25 ans d'expérience, 100+ missions réalisées.
\n\nUn projet cybersécurité ?
Expert dispo · Réponse 24h