
Fuzzea las implementaciones de CPU generando entradas de prueba a partir de proxies de software, para luego ejecutarlas en hardware real y detectar defectos y erratas de microarquitectura.
SiliFuzz es un sistema que encuentra defectos en CPUs fuzzing de proxies de software, como simuladores de CPU o desensambladores, y luego ejecuta las entradas de prueba acumuladas (conocidas como el corpus) en CPUs reales a gran escala. SiliFuzz es un trabajo en curso; consulte el paper para más detalles.
El fuzzing es una técnica para probar un objetivo (una aplicación o una API) con una gran cantidad de entradas de prueba generadas sobre la marcha. El objetivo es hacer que estas entradas sean lo más interesantes y diversas posible para provocar casos límite. En otras palabras, el fuzzing busca maximizar la cobertura de código combinada. La cobertura de código puede tener diferentes significados, por ejemplo, qué bloques básicos se ejecutan o qué rutas se toman en el programa.
Para los propósitos de SiliFuzz, un proxy es cualquier sistema de software o hardware que se comporta de manera similar a algunos aspectos de la CPU objetivo. Por ejemplo, un emulador de CPU o un desensamblador. Se necesita un proxy cuando no podemos recopilar información de cobertura directamente del objetivo.
Al aplicar técnicas de fuzzing a un proxy, podemos generar una serie de entradas de prueba (corpus) que producen un comportamiento interesante en el proxy. Nuestra suposición subyacente es que esto se traduce en un comportamiento igualmente interesante en el objetivo. Lea más en los docs.
Una colección de entradas utilizadas para probar el objetivo se conoce como corpus.
Un corpus razonablemente grande contiene millones de entradas y generalmente se divide en múltiples fragmentos no superpuestos llamados shards.
Un snapshot de SiliFuzz describe una secuencia corta de instrucciones de CPU más un estado inicial de registros y memoria de la CPU para ejecutar esa secuencia de manera determinista. Un snapshot típico contiene menos de 100 bytes de código y se ejecuta en microsegundos, pero puede ser arbitrariamente grande. Los snapshots se almacenan como protocol buffers silifuzz.proto.Snapshot.
Los snapshots se crean típicamente a partir de las entradas generadas por un motor de fuzzing. Para propósitos de prueba de CPU, estas entradas se filtran para eliminar todos los snapshots no deterministas. Lea más en los docs.
Un estado final describe el contenido de los registros y la memoria que se espera que existan al final de la ejecución de un Snapshot. Si el snapshot se ejecuta de manera diferente en distintas microarquitecturas de CPU, tendrá múltiples estados finales esperados.
Un Snap es una representación en memoria de un Snapshot que puede cargarse y ejecutarse fácilmente mediante el Runner. Los Snaps se cargan típicamente desde el disco mediante un lector runner. El formato en disco de Snap es esencialmente el mismo que el formato en memoria, excepto que los punteros nativos se reemplazan por offsets. Consulte este header para más detalles. Este formato a menudo se denomina relocatable. Cada Snap contiene exactamente un estado final esperado, es decir, los Snaps son específicos de la microarquitectura. Lea más en los docs.
Runner es un binario para probar un solo núcleo de CPU. Un runner consume un fragmento de corpus, ejecuta repetidamente Snaps aleatorios en él y verifica que se alcanza el estado final esperado. Runner es un proceso de un solo hilo.
El orchestrator es un proceso que controla múltiples runners. En una configuración típica, el orchestrator ejecutará continuamente un runner por núcleo de CPU lógico, y acumulará y reportará cualquier fallo producido por los procesos runner individuales.
Consulte este archivo para la lista de microarquitecturas soportadas.
SiliFuzz se ejecuta en sistemas Linux x86_64 y aarch64. Ha sido probado con las versiones 5.x y 6.x del kernel de Linux. No hay garantía de que sea compatible con versiones anteriores del kernel. El ABI legacy vsyscall debe estar desactivado para evitar falsos positivos.
Una lista no exhaustiva de bugs y defectos que SiliFuzz ha encontrado.
Un bug lógico es un comportamiento inválido de la CPU inherente a una microarquitectura o stepping particular de una CPU. SiliFuzz ha identificado los siguientes bugs:
Un defecto (eléctrico) es un comportamiento inválido de la CPU que ocurre solo en uno o varios chips. SiliFuzz ha encontrado los siguientes defectos que describimos en el paper
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}"
NOTA: Puede usar un contenedor Docker para evitar contaminar el sistema anfitrión: 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 ..."
Para Bazel, use los siguientes comandos.
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
# Fuzz the Unicorn proxy under Centipede 1000 times with parallelism of 30.
"${SILIFUZZ_BIN_DIR}/external/fuzztest+/centipede/centipede" \
--binary="${SILIFUZZ_BIN_DIR}/proxies/unicorn_x86_64" \
--workdir=/tmp/wd \
-j=30 --num_runs=1000