
Boîte à outils pour explorer les conditions de course du noyau Linux à l'aide des traces KCOV : visionneuses GUI et terminal pour l'exécution concurrente et l'accès mémoire, plus des tests automatiques d'ordonnancement A-B-A.
Ceci est un outillage pour explorer les conditions de course du noyau Linux et pour le débogage général du noyau.
Voir aussi l'article de blog d'annonce https://projectzero.google/2026/09/maccconc-race-condition.html.
Il existe actuellement trois outils :
Ce n'est pas un produit Google officiellement supporté. Ce projet n'est pas éligible au Google Open Source Software Vulnerability Rewards Program.
Tout d'abord, obtenez une version de LLVM qui inclut le commit dc5c6d008f48 ; c'est-à-dire soit une compilation depuis HEAD, plutôt que depuis une branche de release, soit une compilation en version >=23. De telles compilations sont, par exemple, disponibles sur https://apt.llvm.org/ . Si vous êtes un googler, voir http://go/maccconc-kernel-build-notes .
Obtenez une arborescence de noyau avec les patches requis depuis https://github.com/thejh/linux , branche kcov-tracing-full.
Lors de la configuration et de la compilation du noyau, définissez les variables make
CC / LLVM / LLVM_PREFIX comme documenté sur
https://docs.kernel.org/kbuild/llvm.html pour vous assurer que la bonne chaîne d'outils LLVM est utilisée.
Définissez cette variable d'environnement pour obtenir des informations plus claires sur les blocs de base exécutés (pour l'interface graphique) et éviter les piles d'appels confuses dues à l'optimisation des appels terminaux :
export KCFLAGS="-fno-optimize-sibling-calls -mllvm -sanitizer-coverage-prune-blocks=false"
Configurez le noyau comme d'habitude ; il peut être utile de partir de
make [...] kvm_guest.config si vous ne partez pas d'une configuration existante.
Assurez-vous que les options de configuration du noyau suivantes sont définies (par exemple via l'interface de configuration ncurses make [...] nconfig ou en les collant à la fin de .config) :
# for core functionality
CONFIG_SMP=y
CONFIG_NR_CPUS=4
CONFIG_KASAN=y
CONFIG_KASAN_OUTLINE=y
CONFIG_KCOV=y
CONFIG_KCOV_EXT_RECORDS=y
CONFIG_KCOV_MEMORY=y
CONFIG_KALLSYMS_ALL=y
# to give the GUI information about source lines and inlining
CONFIG_DEBUG_INFO_DWARF5=y
# for communicating with the GUI
CONFIG_VSOCKETS=y
CONFIG_VIRTIO_VSOCKETS=y
CONFIG_VIRTIO_PCI=y
# for maximizing the potential for race conditions
CONFIG_PREEMPT=y
# for making virtual addresses at runtime the same as in vmlinux
CONFIG_RANDOMIZE_BASE=n
# needed for several samples
CONFIG_TMPFS=y
Vous pouvez également activer ce qui suit si vous souhaitez tester des conditions de course impliquant RCU, mais notez que cela entraînera un important ralentissement et ne fonctionne actuellement correctement que si vous utilisez l'interface graphique.
CONFIG_RCU_EXPERT=y
CONFIG_RCU_STRICT_GRACE_PERIOD=y
Veuillez vous assurer que toutes les fonctionnalités du noyau que vous souhaitez tester sont compilées dans le noyau, et non en tant que modules.
Il est recommandé de compiler l'outillage en espace utilisateur sur la machine hôte ; en particulier l'interface graphique, qui est conçue pour s'exécuter sur l'hôte, et non dans l'invité.
Installez git et les dépendances de compilation ; pour Debian :
sudo apt install git build-essential pkg-config libcapstone-dev libdw-dev libglfw3-dev
Après avoir cloné ce dépôt, téléchargez les sous-modules avec :
git submodule update --init --recursive
Compilez avec make.
Vous pouvez démarrer le noyau compilé dans une VM QEMU normale si vous activez les options de configuration du noyau requises et utilisez une image disque avec une distribution Linux, ou quelque chose de similaire ; mais l'approche recommandée est plutôt d'installer kvmtool comme ceci :
git clone https://git.kernel.org/pub/scm/linux/kernel/git/will/kvmtool.git
cd kvmtool
make
make install
Ensuite, vous pouvez démarrer le noyau compilé comme suit (en supposant que $HOME/bin est dans votre $PATH) :
lkvm run --kernel [path to kernel tree]/arch/x86/boot/bzImage --vsock 5 --console virtio
Cela vous donnera un shell dans un environnement où une vue en lecture seule du système de fichiers de l'hôte est montée sur /host, avec un rootfs minimal qui consiste principalement en des liens symboliques vers ce système de fichiers hôte pour /bin, /lib, /usr, etc. / et /host sont tous deux des systèmes de fichiers 9p.
Veuillez monter manuellement debugfs et tmpfs dans l'invité après chaque démarrage :
sh-5.3# mount -t debugfs none /sys/kernel/debug
sh-5.3# mount -t tmpfs none /tmp
sh-5.3#
Les cas de test sont du code C qui définit quatre fonctions :
void test_setup(void) { [...] }
void test_thread1(void) { [...] }
void test_thread2(void) { [...] }
void test_end(void) { [...] }
Pour chaque exécution du cas de test, test_setup() s'exécutera en premier ; puis test_thread1() et test_thread2() s'exécuteront en parallèle ; et enfin, test_end() s'exécutera.
Les cas de test doivent être compilés en tant que bibliothèques partagées, comme ceci :
$ cc -shared -o [name].so [name].c -fPIC
Les cas de test d'exemple dans le dossier testcase/ peuvent également être compilés via make, comme :
$ make testcase/demo-dup-vs-close.so
cc -shared -o testcase/demo-dup-vs-close.so testcase/demo-dup-vs-close.c -Wall
L'outil kcov-autorace peut explorer automatiquement les ordres d'exécution A-B-A. Les ordres A-B-A sont ceux où le thread A s'exécute jusqu'à un certain point, puis le thread B s'exécute entièrement, puis le thread A termine son exécution.
Après avoir compilé un cas de test sur l'hôte, vous pouvez l'exécuter dans l'invité à l'aide de l'assistant kcov-autorace. Par exemple :
sh-5.3# cd /host/{path to checkout on the host}
sh-5.3# ./kcov-autorace testcase/demo-dup-vs-close.so
loading kallsyms
RCU state (excluded): base=ffffffff82770100 len=500
loading testcase
initializing kcov
collecting A-B coverage
dup(5) = 6 (success)
testing candidates
dup(5) = -1 (Bad file descriptor)
dup(5) = -1 (Bad file descriptor)
dup(5) = -1 (Bad file descriptor)
dup(5) = 5 (success)
dup(5) = 6 (success)
dup(5) = 6 (success)
dup(5) = 6 (success)
dup(5) = 6 (success)
dup(5) = 6 (success)
dup(5) = 6 (success)
dup(5) = 6 (success)
stats: injection-failed:0 wait-timeout:7 reordered:4
sh-5.3#
Cela montre qu'il existe un ordre A-B-A de close(5) et dup(5) qui fait que dup(5) renvoie 5.
Notez que kcov-autorace et les autres outils utilisent un délai d'attente d'attente active codé en dur SPIN_LIMIT.
L'outil kcov-terminal peut être utilisé pour exécuter un cas de test avec des contraintes d'ordre spécifiées manuellement. Celles-ci ne spécifient pas un ordre complet ; au lieu de cela, elles constituent un ensemble de règles « A doit se produire avant B ».
Cet outil est utilisé dans l'invité, de manière similaire à kcov-autorace.
Exemple d'utilisation avec le cas de test demo-inode-attr-change pour une exécution avec des contraintes d'ordre qui démontrent que la lecture de l'UID et du GID par fstat() n'est pas atomique vis-à-vis de fchown() :
sh-5.3# ./kcov-terminal testcase/demo-inode-attr-change.so
uid=0 gid=0
===== filtered to interference set, no RCU core =====
LEGEND:
type: R=read W=write M=modify(read+write) F=free A=atomic