
Fuzz les implémentations de CPU en générant des entrées de test à partir de proxies logiciels, puis les exécute sur du matériel réel pour détecter les défauts et errata de la microarchitecture.
SiliFuzz est un système qui détecte les défauts des CPU en appliquant le fuzzing à des proxies logiciels, comme les simulateurs de CPU ou les désassembleurs, puis en exécutant les entrées de test accumulées (appelées le corpus) sur de véritables CPU à grande échelle. SiliFuzz est un travail en cours, veuillez consulter l'article pour plus de détails.
Le fuzzing est une technique qui consiste à tester une cible (une application ou une API) avec un grand nombre d'entrées de test générées à la volée. L'objectif est de rendre ces entrées aussi intéressantes et aussi diverses que possible afin de déclencher des cas limites. En d'autres termes, le fuzzing vise à maximiser la couverture de code combinée. La couverture de code peut avoir différentes significations, par exemple quels blocs de base sont exécutés ou quels chemins sont pris dans le programme.
Dans le cadre de SiliFuzz, un proxy est tout système logiciel ou matériel qui se comporte de manière similaire à certains aspects du CPU cible. Par exemple, un émulateur de CPU ou un désassembleur. Un proxy est nécessaire lorsque nous ne pouvons pas collecter directement les informations de couverture de la cible.
En appliquant les techniques de fuzzing à un proxy, nous pouvons générer une série d'entrées de test (corpus) qui produisent un comportement intéressant dans le proxy. Notre hypothèse sous-jacente est que cela se traduit par un comportement tout aussi intéressant dans la cible. En savoir plus dans la documentation.
Un ensemble d'entrées utilisé pour tester la cible est appelé corpus.
Un corpus raisonnablement grand contient des millions d'entrées et est généralement divisé en plusieurs morceaux non chevauchants appelés shards.
Un snapshot SiliFuzz décrit une courte séquence d'instructions CPU ainsi qu'un état initial des registres du CPU et de la mémoire pour exécuter cette séquence de manière déterministe. Un snapshot typique contient moins de 100 octets de code et s'exécute en microsecondes, mais il peut être arbitrairement grand. Les snapshots sont stockés sous forme de protobufs silifuzz.proto.Snapshot.
Les snapshots sont généralement créés à partir des entrées générées par un moteur de fuzzing. À des fins de test de CPU, ces entrées sont filtrées pour éliminer tous les snapshots non déterministes. En savoir plus dans la documentation.
Un état final décrit le contenu des registres et de la mémoire censé exister à la fin de l'exécution d'un snapshot. Si le snapshot s'exécute différemment sur différentes microarchitectures de CPU, il aura plusieurs états finaux attendus.
Un Snap est une représentation en mémoire d'un Snapshot qui peut être facilement chargée et exécutée par le Runner. Les Snaps sont généralement chargés depuis le disque par un reading runner. Le format sur disque d'un Snap est essentiellement le même que le format en mémoire, à l'exception du fait que les pointeurs natifs sont remplacés par des offsets. Voir ce fichier d'en-tête pour plus de détails. Ce format est souvent appelé relocatable. Chaque Snap contient exactement un état final attendu, c'est-à-dire que les Snaps sont spécifiques à une microarchitecture. En savoir plus dans la documentation.
Runner est un binaire permettant de tester un seul cœur de CPU. Un runner consomme un shard de corpus, exécute à plusieurs reprises des Snaps aléatoires qui s'y trouvent et vérifie que l'état final attendu est atteint. Runner est un processus monothread.
L'orchestrateur est un processus qui pilote plusieurs runners. Dans une configuration typique, l'orchestrateur exécute en continu un runner par cœur de CPU logique, et accumule et signale toute défaillance produite par les processus runners individuels.
Voir ce fichier pour la liste des microarchitectures prises en charge.
SiliFuzz fonctionne sur les systèmes Linux x86_64 et aarch64. Il a été testé avec les versions 5.x et 6.x du noyau Linux. Rien ne garantit qu'il soit compatible avec les versions plus anciennes du noyau. L'ABI legacy vsyscall doit être désactivée pour éviter les faux positifs.
Une liste non exhaustive des bogues et défauts découverts par SiliFuzz.
Un bogue logique est un comportement CPU invalide inhérent à une microarchitecture ou à un stepping particulier du CPU. SiliFuzz a identifié les bogues suivants :
Un défaut (électrique) est un comportement CPU invalide qui ne se produit que sur une ou plusieurs puces. SiliFuzz a trouvé les défauts suivants, décrits dans l'article
git clone https://github.com/google/silifuzz.git && cd silifuzz
SILIFUZZ_SRC_DIR=`pwd`
./install_build_dependencies.sh # Currently, works for the latest Ubuntu only.
bazel build -c opt @silifuzz//tools:{snap_corpus_tool,fuzz_filter_tool,snap_tool,silifuzz_platform_id,simple_fix_tool_main} \
@silifuzz//runner:reading_runner_main_nolibc \
@silifuzz//orchestrator:silifuzz_orchestrator_main
SILIFUZZ_BIN_DIR=`pwd`/bazel-bin
cd "${SILIFUZZ_BIN_DIR}"
REMARQUE : vous pouvez utiliser un conteneur Docker pour éviter de polluer le système hôte : docker run -it --tty --security-opt seccomp=unconfined --mount type=bind,source=${SILIFUZZ_SRC_DIR},target=/app ubuntu:noble /bin/bash -c "cd /app && ./install_build_dependencies.sh && bazel build ... && bazel test ..."
Pour Bazel, utilisez les commandes suivantes.
cd "${SILIFUZZ_SRC_DIR}"
COV_FLAGS_FILE="$(bazel info output_base)/external/fuzztest+/centipede/clang-flags.txt"
bazel build -c opt --copt=-UNDEBUG --dynamic_mode=off \
--per_file_copt=unicorn/.*@$(xargs < "${COV_FLAGS_FILE}" |sed -e 's/,/\\,/g' -e 's/ /,/g') @//proxies:unicorn_x86_64
bazel build -c opt @fuzztest//centipede:centipede
mkdir -p /tmp/wd