
Fuzza implementações de CPU gerando entradas de teste a partir de proxies de software e, em seguida, executa-as em hardware real para detectar defeitos e errata de microarquitetura.
O SiliFuzz é um sistema que encontra defeitos em CPUs por meio de fuzzing de proxies de software, como simuladores de CPU ou desmontadores, e então executa as entradas de teste acumuladas (conhecidas como corpus) em CPUs reais em grande escala. O SiliFuzz é um trabalho em andamento; consulte o artigo para obter detalhes.
Fuzzing é uma técnica de testar um alvo (um aplicativo ou uma API) com um grande número de entradas de teste geradas em tempo real. O objetivo é tornar essas entradas tão interessantes e diversas quanto possível para acionar casos extremos. Em outras palavras, o fuzzing visa maximizar a cobertura de código combinada. Cobertura de código pode ter diferentes significados, por exemplo, quais blocos básicos são executados ou quais caminhos são percorridos no programa.
Para os propósitos do SiliFuzz, um proxy é qualquer sistema de software ou hardware que se comporte de maneira semelhante a alguns aspectos da CPU alvo. Por exemplo, um emulador de CPU ou um desmontador. Um proxy é necessário quando não podemos coletar diretamente informações de cobertura do alvo.
Ao aplicar técnicas de fuzzing a um proxy, podemos gerar uma série de entradas de teste (corpus) que produzem comportamento interessante no proxy. Nossa suposição subjacente é que isso se traduz em um comportamento igualmente interessante no alvo. Leia mais na documentação.
Uma coleção de entradas usadas para testar o alvo é conhecida como corpus.
Um corpus razoavelmente grande contém milhões de entradas e geralmente é dividido em vários blocos não sobrepostos chamados shards.
Um snapshot do SiliFuzz descreve uma sequência curta de instruções de CPU mais um estado inicial dos registradores e da memória da CPU para executar essa sequência de forma determinística. Um snapshot típico contém menos de 100 bytes de código e é executado em microssegundos, mas pode ser arbitrariamente grande. Os snapshots são armazenados como buffers de protocolo silifuzz.proto.Snapshot.
Os snapshots são tipicamente criados a partir das entradas geradas por um mecanismo de fuzzing. Para fins de teste de CPU, essas entradas são filtradas para eliminar todos os snapshots não determinísticos. Leia mais na documentação.
Um estado final descreve o conteúdo dos registradores e da memória que se espera que exista ao final da execução de um Snapshot. Se o snapshot for executado de forma diferente em diferentes microarquiteturas de CPU, ele terá vários estados finais esperados.
Um Snap é uma representação em memória de um Snapshot que pode ser facilmente carregado e executado pelo Runner. Os Snaps são tipicamente carregados do disco por um reading runner. O formato em disco do Snap é essencialmente o mesmo que o formato em memória, exceto que os ponteiros nativos são substituídos por offsets. Consulte este cabeçalho para obter detalhes. Esse formato é frequentemente chamado de relocatable. Cada Snap contém exatamente um estado final esperado, ou seja, os Snaps são específicos da microarquitetura. Leia mais na documentação.
Runner é um binário para testar um único núcleo de CPU. Um runner consome um shard de corpus, executa repetidamente Snaps aleatórios nele e verifica se o estado final esperado é alcançado. O Runner é um processo de thread única.
O orquestrador é um processo que controla vários runners. Em uma configuração típica, o orquestrador executará continuamente um runner por núcleo lógico de CPU e acumulará e reportará quaisquer falhas produzidas pelos processos runners individuais.
Consulte este arquivo para obter a lista de microarquiteturas suportadas.
O SiliFuzz é executado em sistemas Linux x86_64 e aarch64. Foi testado com versões 5.x e 6.x do kernel Linux. Não há garantia de compatibilidade com versões mais antigas do kernel. O ABI vsyscall legado deve ser desativado para evitar falsos positivos.
Uma lista não exaustiva de bugs e defeitos que o SiliFuzz encontrou.
Um bug lógico é um comportamento inválido da CPU inerente a uma microarquitetura ou stepping específico de CPU. O SiliFuzz identificou os seguintes bugs:
Um defeito (elétrico) é um comportamento inválido da CPU que ocorre apenas em um ou vários chips. O SiliFuzz encontrou os seguintes defeitos que descrevemos no artigo:
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: Você pode usar um contêiner Docker para evitar poluir o sistema host: 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 os seguintes 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