
Toolkit per esplorare le race condition del kernel Linux utilizzando le tracce KCOV: visualizzatori GUI e da terminale per l'esecuzione concorrente e l'accesso alla memoria, più test automatici di ordinamento A-B-A.
Questo è un insieme di strumenti per esplorare le race condition del kernel Linux e per il debug generale del kernel.
Vedi anche il post di annuncio sul blog https://projectzero.google/2026/09/maccconc-race-condition.html.
Attualmente ci sono tre strumenti:
Questo non è un prodotto Google ufficialmente supportato. Questo progetto non è idoneo al Google Open Source Software Vulnerability Rewards Program.
Per prima cosa, procurati una versione di LLVM che includa il commit dc5c6d008f48; ovvero una build da HEAD, anziché da un branch di release, oppure una build alla versione >=23. Tali build sono, ad esempio, disponibili su https://apt.llvm.org/ . Se sei un googler, vedi http://go/maccconc-kernel-build-notes .
Ottieni un albero del kernel con le patch richieste da https://github.com/thejh/linux , branch kcov-tracing-full.
Quando configuri e compili il kernel, imposta le variabili make
CC / LLVM / LLVM_PREFIX come documentato su
https://docs.kernel.org/kbuild/llvm.html per assicurarti che venga usata la toolchain LLVM corretta.
Imposta questa variabile d'ambiente per ottenere informazioni più chiare sui basic block eseguiti (per la GUI) ed evitare call stack confusi a causa dell'ottimizzazione delle tail call:
export KCFLAGS="-fno-optimize-sibling-calls -mllvm -sanitizer-coverage-prune-blocks=false"
Configura il kernel come al solito; potrebbe essere utile partire da
make [...] kvm_guest.config se non parti da una configurazione esistente.
Assicurati che i seguenti flag di configurazione del kernel siano impostati (ad esempio tramite la UI di configurazione ncurses make [...] nconfig o incollandoli in fondo a .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
Puoi anche abilitare quanto segue se vuoi testare race condition che coinvolgono RCU, ma nota che questo causerà un forte rallentamento e attualmente funziona correttamente solo se usi la GUI.
CONFIG_RCU_EXPERT=y
CONFIG_RCU_STRICT_GRACE_PERIOD=y
Assicurati che tutte le funzionalità del kernel che vuoi testare siano compilate nel kernel, non come moduli.
Si consiglia di compilare il tooling userspace sulla macchina host; specialmente la GUI, che è progettata per essere eseguita sull'host, non nel guest.
Installa git e le dipendenze di build; per Debian:
sudo apt install git build-essential pkg-config libcapstone-dev libdw-dev libglfw3-dev
Dopo aver clonato questo repository, scarica i sottomoduli con:
git submodule update --init --recursive
Compila con make.
Puoi avviare il kernel compilato in una normale VM QEMU se abiliti i flag di configurazione del kernel richiesti e usi un'immagine disco con una distribuzione Linux, o qualcosa del genere; ma l'approccio consigliato è invece installare kvmtool in questo modo:
git clone https://git.kernel.org/pub/scm/linux/kernel/git/will/kvmtool.git
cd kvmtool
make
make install
Poi puoi avviare il kernel compilato come segue (assumendo che $HOME/bin sia nel tuo $PATH):
lkvm run --kernel [path to kernel tree]/arch/x86/boot/bzImage --vsock 5 --console virtio
Questo ti darà una shell in un ambiente in cui una vista in sola lettura del filesystem host è montata su /host, con un rootfs minimale che consiste principalmente di symlink verso questo filesystem host per /bin, /lib, /usr e così via. Sia / che /host sono filesystem 9p.
Monta manualmente debugfs e tmpfs nel guest dopo ogni avvio:
sh-5.3# mount -t debugfs none /sys/kernel/debug
sh-5.3# mount -t tmpfs none /tmp
sh-5.3#
I test case sono codice C che definisce quattro funzioni:
void test_setup(void) { [...] }
void test_thread1(void) { [...] }
void test_thread2(void) { [...] }
void test_end(void) { [...] }
Per ogni esecuzione del test case, test_setup() verrà eseguita per prima; poi test_thread1() e test_thread2() verranno eseguite in parallelo; e infine, test_end() verrà eseguita.
I test case dovrebbero essere compilati come librerie condivise, in questo modo:
$ cc -shared -o [name].so [name].c -fPIC
I test case di esempio nella cartella testcase/ possono anche essere compilati tramite make, così:
$ make testcase/demo-dup-vs-close.so
cc -shared -o testcase/demo-dup-vs-close.so testcase/demo-dup-vs-close.c -Wall
Lo strumento kcov-autorace può esplorare automaticamente gli ordinamenti di esecuzione A-B-A. Gli ordinamenti A-B-A sono quelli in cui il thread A viene eseguito fino a un certo punto, poi il thread B viene eseguito completamente, e infine il thread A termina l'esecuzione.
Dopo aver compilato un test case sull'host, puoi eseguirlo nel guest usando
l'helper kcov-autorace. Ad esempio:
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#
Questo mostra che esiste un ordinamento A-B-A di close(5) e dup(5) che porta dup(5) a restituire 5.
Nota che kcov-autorace e gli altri strumenti usano un timeout di spin-wait hardcoded
SPIN_LIMIT.
Lo strumento kcov-terminal può essere usato per eseguire un testcase con vincoli di ordinamento specificati manualmente. Questi non specificano un ordinamento completo; invece,
sono un insieme di regole "A dovrebbe avvenire prima di B".
Questo strumento viene usato nel guest, in modo simile a kcov-autorace.
Esempio d'uso con il testcase demo-inode-attr-change per l'esecuzione con vincoli
di ordinamento che dimostrano che la lettura di UID e GID da parte di fstat() non è
atomica rispetto a 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