
Geração automatizada de drivers de fuzz baseada em UT
UTopia é uma ferramenta para gerar automaticamente drivers de fuzzing a partir de testes de unidade.
UTopia permite que desenvolvedores realizem testes de fuzzing sem conhecimento especial sobre escrita de fuzzers. Até mesmo desenvolvedores familiarizados com fuzzing podem economizar tempo significativo ao gerar drivers de fuzzing automaticamente.
UTopia suporta bibliotecas C/C++ que possuem testes de unidade com GoogleTest, Boost.Test ou Tizen TCT.
Para ver bugs encontrados pelo UTopia, visite a página Trophy. Você também pode ver alguns fuzzers baseados em UTopia lá.
Para facilitar a configuração, fornecemos uma imagem docker para executar o UTopia. Você pode compilar o UTopia e gerar/executar fuzzers dentro do contêiner docker.
Construa a imagem docker com o comando abaixo.
docker buildx build -f docker/Dockerfile -t utopia . #llvm-10
docker buildx build --build-arg LLVM_VERSION=12 -f docker/Dockerfile -t utopia . #llvm-12
UTopia foi projetado para funcionar melhor com a versão 10 do LLVM. Além disso, foi confirmado que ele passa nos testes de unidade na versão 12 do LLVM. Portanto, recomendamos que você use o LLVM 10, mas, se quiser, pode experimentar a versão 12 do LLVM.
UTopia depende de LLVM, Protobuf e GoogleTest. Você pode instalar as dependências manualmente, mas recomendamos usar a imagem docker fornecida.
Após clonar o repositório, inicialize e atualize todos os submódulos:
git submodule update --init --recursive
Para compilar o UTopia, siga o processo cmake abaixo.
cd $UTOPIA_HOME_DIR
cmake -B build -S .
cmake --build build -j$(nproc)
Para alguns projetos selecionados, você pode usar o script auxiliar para executar nossa ferramenta sem esforço adicional. Consulte helper/README.md. Para outros projetos, verifique o manual a seguir.
O Target Analyzer analisa o código da biblioteca alvo e gera um resultado como arquivo json. As opções obrigatórias de linha de comando estão abaixo.
target_analyzer --db ${builddb_path} --extern ${extern_path} --public ${api_json_path} --out ${output_path}
O build db é um arquivo json que contém caminhos dos arquivos AST e IR do código da biblioteca alvo. Seu formato é semelhante ao abaixo.
{
"bc": "/root/fuzz-test-generation/exp/sample/output/bc/libcommon.a.bc",
"ast": [
"/root/fuzz-test-generation/exp/sample/libcommon.a_ast/codec/common/src/ast1.o.ast",
"/root/fuzz-test-generation/exp/sample/libcommon.a_ast/codec/common/src/ast2.o.ast"
],
"project_dir": "/root/fuzz-test-generation/exp/sample"
}
O caminho do arquivo bitcode LLVM de uma biblioteca específica deve ser especificado usando a palavra-chave "bc".
Até agora, aceitamos apenas um arquivo bitcode, portanto, você pode usar llvm-link para vincular vários arquivos bitcode
em um único arquivo bitcode.
Os caminhos dos arquivos AST de uma biblioteca específica devem ser especificados usando a palavra-chave "ast". Observe que o arquivo bitcode especificado e os arquivos ast são gerados a partir dos mesmos códigos-fonte de uma biblioteca específica.
O Target Analyzer aceita relatórios do target analyzer de outras bibliotecas para obter um resultado preciso. Este caminho deve ser um diretório onde outros relatórios estejam armazenados, o que significa que mais de um relatório de bibliotecas é permitido.
Nomes das funções de API a serem analisadas. Deve ser um arquivo json formatado como abaixo.
{
"libcommon.a": [
"API1",
"API2",
"API3"
]
}
Você pode obter a lista de APIs de uma biblioteca específica usando o comando abaixo.
nm --no-demangle --defined-only -g ${librarypath} | awk '$2=="T" {k=""; for(i=3;i<=NF;i++) k=k $i""; print k}'
A propriedade Direction é um parâmetro essencial utilizado pelo target analyzer. Ela indica se um parâmetro é usado para leitura (Dir_In), escrita (Dir_Out), ou tanto leitura quanto escrita (Dir_In | Dir_Out) dentro de uma função. O target analyzer delineia essa propriedade por meio de um tipo de enumeração que compreende os seguintes elementos:
enum Dir {
Dir_NoOp = 0x000, // No operation
Dir_In = 0x100, // Input direction
Dir_Out = 0x010, // Output direction
Dir_Unidentified = 0x001 // Unidentified direction
};
Por exemplo, no trecho de arquivo JSON fornecido:
{
"Direction": {
"BF_crypt(0)": 256,
"BF_crypt(1)": 272,
"BF_crypt(2)": 272,
"BF_decode(1)": 272
}
}
Esta notação ajuda a entender como cada parâmetro dentro de uma função é usado — se para entrada, saída ou ambos — fornecendo insights claros sobre o fluxo de dados e as operações executadas pela função.
O UT Analyzer analisa o código de teste de unidade de uma biblioteca alvo e gera um resultado como arquivo json. As opções obrigatórias de linha de comando estão abaixo.
ut_analyzer --entry ${entry_path} --extern ${extern_path} --ut ${ut_type} --name ${lib_name} --public ${api_json_path} --out ${output_path}
A maioria das opções é igual às do target_analyzer. Observe que entry_path deve especificar os arquivos AST/IR de um executável de teste de unidade, não da biblioteca.
Framework usado pelo projeto alvo; pode ser tct, gtest ou boost.
O fuzz_generator gera drivers de fuzzing usando os arquivos de relatório do target_anlayzer e do ut_analyzer. As opções obrigatórias de linha de comando estão abaixo.
fuzz_generator --src ${src_path} --target ${target_analyzer_report_path} --ut ${ut_analyzer_report_path} --public ${api_json_path} --out ${output_dir}
src_path é o caminho do diretório onde o código-fonte do teste de unidade está armazenado. O fuzz_generator copia esse diretório e gera o driver de fuzzing modificando esses arquivos copiados.
As outras opções são as mesmas do target_analyzer.
Esta seção descreve a funcionalidade e os detalhes de implementação do driver de fuzzing gerado, o que é crucial para entender como o teste de fuzzing é integrado ao código-fonte alvo.
fuzz_entry.cc (Arquivo gerado automaticamente)
DEFINE_PROTO_FUZZER(const AutoFuzz::FuzzArgsProfile &autofuzz_mutation) {
... /* Values are assigned from autofuzz_mutation */
enterAutofuzz();
}
Esta função serve como ponto de entrada para o driver de fuzzing gerado. Ela recebe valores gerados pelo fuzzer e os atribui a variáveis que são subsequentemente usadas para invocar funções da biblioteca. Por fim, a função chama enterAutofuzz(); para prosseguir com o processo de teste de fuzzing.