
UT-basierte automatisierte Fuzz-Treiber-Generierung
UTopia ist ein Tool zur automatischen Generierung von Fuzz-Treibern aus Unit-Tests.
UTopia ermöglicht es Entwicklern, Fuzzing durchzuführen, ohne spezielle Kenntnisse über das Schreiben von Fuzzern zu haben. Selbst Entwickler, die mit Fuzzing vertraut sind, können erheblich Zeit sparen, indem sie Fuzz-Treiber automatisch generieren.
UTopia unterstützt C/C++-Bibliotheken, die Unit-Tests mit GoogleTest, Boost.Test oder Tizen TCT besitzen.
Um von UTopia gefundene Fehler zu sehen, besuchen Sie die Seite Trophy. Dort können Sie auch einige auf UTopia basierende Fuzzer sehen.
Für eine einfache Einrichtung stellen wir ein Docker-Image zum Ausführen von UTopia bereit. Sie können UTopia im Docker-Container erstellen und Fuzzer generieren/ausführen.
Erstellen Sie das Docker-Image mit dem folgenden Befehl.
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 ist für die beste Funktion mit LLVM-Version 10 ausgelegt. Es wurde bestätigt, dass es die Unit-Tests mit LLVM-Version 12 besteht. Daher empfehlen wir Ihnen, LLVM 10 zu verwenden, aber wenn Sie möchten, können Sie LLVM-Version 12 ausprobieren.
UTopia hängt von LLVM, Protobuf und GoogleTest ab. Sie können die Abhängigkeiten manuell installieren, aber wir empfehlen, das bereitgestellte Docker-Image zu verwenden.
Nach dem Klonen des Repositorys initialisieren und aktualisieren Sie alle Submodule:
git submodule update --init --recursive
Um UTopia zu erstellen, folgen Sie dem untenstehenden CMake-Prozess.
cd $UTOPIA_HOME_DIR
cmake -B build -S .
cmake --build build -j$(nproc)
Für einige ausgewählte Projekte können Sie das Hilfsskript verwenden, um unser Tool ohne weiteren Aufwand auszuführen. Bitte beachten Sie helper/README.md. Für andere Projekte beachten Sie bitte die folgende Anleitung.
Der Target Analyzer analysiert den Code der Zielbibliothek und erzeugt ein Ergebnis als JSON-Datei. Die Pflichtoptionen für die Befehlszeile sind unten aufgeführt.
target_analyzer --db ${builddb_path} --extern ${extern_path} --public ${api_json_path} --out ${output_path}
Die Build-DB ist eine JSON-Datei, die Pfade zu AST- und IR-Dateien des Codes der Zielbibliothek enthält. Das Format sieht wie folgt aus.
{
"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"
}
Der Pfad zur LLVM-Bitcode-Datei einer bestimmten Bibliothek sollte mit dem Schlüsselwort "bc" angegeben werden.
Wir akzeptieren bisher nur eine Bitcode-Datei. Daher können Sie llvm-link verwenden, um mehrere Bitcode-Dateien zu einer Bitcode-Datei zu verknüpfen.
Die Pfade zu den AST-Dateien einer bestimmten Bibliothek sollten mit dem Schlüsselwort "ast" angegeben werden. Beachten Sie, dass die angegebene Bitcode-Datei und die AST-Dateien für eine bestimmte Bibliothek aus denselben Quellcodes erzeugt werden.
Der Target Analyzer akzeptiert Target-Analyzer-Berichte anderer Bibliotheken, um ein genaues Ergebnis zu erzielen. Dieser Pfad sollte ein Verzeichnispfad sein, in dem andere Berichte gespeichert sind; das bedeutet, dass Berichte mehrerer Bibliotheken zulässig sind.
Namen der zu analysierenden API-Funktionen. Es sollte eine JSON-Datei sein, die wie folgt formatiert ist.
{
"libcommon.a": [
"API1",
"API2",
"API3"
]
}
Sie können die API-Liste einer bestimmten Bibliothek mit dem folgenden Befehl abrufen.
nm --no-demangle --defined-only -g ${librarypath} | awk '$2=="T" {k=""; for(i=3;i<=NF;i++) k=k $i""; print k}'
Die Eigenschaft Direction ist ein wesentlicher Parameter, den der Target Analyzer verwendet. Sie gibt an, ob ein Parameter innerhalb einer Funktion zum Lesen (Dir_In), zum Schreiben (Dir_Out) oder sowohl zum Lesen als auch zum Schreiben (Dir_In | Dir_Out) verwendet wird. Der Target Analyzer beschreibt diese Eigenschaft durch einen Aufzählungstyp, der die folgenden Elemente umfasst:
enum Dir {
Dir_NoOp = 0x000, // No operation
Dir_In = 0x100, // Input direction
Dir_Out = 0x010, // Output direction
Dir_Unidentified = 0x001 // Unidentified direction
};
Zum Beispiel in dem folgenden JSON-Ausschnitt:
{
"Direction": {
"BF_crypt(0)": 256,
"BF_crypt(1)": 272,
"BF_crypt(2)": 272,
"BF_decode(1)": 272
}
}
Diese Notation hilft zu verstehen, wie jeder Parameter in einer Funktion verwendet wird, ob für Eingabe, Ausgabe oder beides, und bietet klare Einblicke in den Datenfluss und die von der Funktion ausgeführten Operationen.
Der UT Analyzer analysiert den Unit-Test-Code für eine Zielbibliothek und erzeugt ein Ergebnis als JSON-Datei. Die Pflichtoptionen für die Befehlszeile sind unten aufgeführt.
ut_analyzer --entry ${entry_path} --extern ${extern_path} --ut ${ut_type} --name ${lib_name} --public ${api_json_path} --out ${output_path}
Die meisten Optionen sind dieselben wie beim target_analyzer. Beachten Sie, dass entry_path die AST-/IR-Dateien für eine ausführbare Unit-Test-Datei angeben sollte, nicht für eine Bibliothek.
Das vom Zielprojekt verwendete Framework kann tct, gtest oder boost sein.
fuzz_generator erzeugt Fuzz-Treiber mithilfe der Berichtsdateien von target_anlayzer und ut_analyzer. Die Pflichtoptionen für die Befehlszeile sind unten aufgeführt.
fuzz_generator --src ${src_path} --target ${target_analyzer_report_path} --ut ${ut_analyzer_report_path} --public ${api_json_path} --out ${output_dir}
src_path ist der Verzeichnispfad, in dem der Quellcode der Unit-Tests gespeichert ist. fuzz_generator kopiert dieses Verzeichnis und erzeugt den Fuzz-Treiber, indem es diese kopierten Dateien modifiziert.
Die übrigen Optionen sind dieselben wie die Optionen von target_analyzer.
Dieser Abschnitt beschreibt die Funktionalität und die Implementierungsdetails des erzeugten Fuzz-Treibers, die entscheidend dafür sind, zu verstehen, wie Fuzzing in den Quellcode der Zielbibliothek integriert wird.
fuzz_entry.cc (automatisch generierte Datei)
DEFINE_PROTO_FUZZER(const AutoFuzz::FuzzArgsProfile &autofuzz_mutation) {
... /* Values are assigned from autofuzz_mutation */
enterAutofuzz();
}
Diese Funktion dient als Einstiegspunkt für den erzeugten Fuzz-Treiber. Sie übernimmt vom Fuzzer generierte Werte und weist sie Variablen zu, die anschließend zum Aufrufen von Bibliotheksfunktionen verwendet werden. Schließlich ruft die Funktion enterAutofuzz(); auf, um den Fuzzing-Prozess fortzusetzen.
Quellcode, der den Ziel-Testfall definiert
#ifdef __cplusplus
extern "C" {
#endif
void enterAutofuzz() {
class AutofuzzTest : public ::Parser_TestArray_Test {
public:
void runTest() {
try {
SetUpTestCase();
} catch (std::exception &E) {}
try {
SetUp();
} catch (std::exception &E) {}
try {
TestBody();
} catch (std::exception &E) {}
try {
TearDown();
} catch (std::exception &E) {}
try {
TearDownTestCase();
} catch (std::exception &E) {}
}
};
AutofuzzTest Fuzzer;
Fuzzer.runTest();
}
#ifdef __cplusplus
}
#endif
Im letzten Teil des Quellcodes, der den Ziel-Testfall definiert, injiziert UTopia die Funktion enterAutofuzz. Innerhalb dieser Funktion wird die Klasse AutofuzzTest deklariert, die von ::Parser_TestArray_Test erbt. Diese Basisklasse wird vom GoogleTest-Framework definiert und ist spezifisch für den behandelten Testfall, wie unten gezeigt:
TEST(Parser, TestArray)
{
...
}
Die Methode runTest() führt den Testfall unabhängig von GoogleTest aus, indem sie fünf Funktionen aufruft, die GoogleTest normalerweise für jeden Testfall aufruft. Dieser Ansatz ermöglicht die direkte Testausführung, ohne sich auf das GoogleTest-Framework zu verlassen.
Die Fuzz-Treiber werden in ${output_dir} erzeugt, das fuzz_generator als Befehlszeilenoption übergeben wird.
Sie können sie mit demselben Compilerbefehl erstellen, der auch für die ausführbare Unit-Test-Datei verwendet wird.
Beachten Sie, dass Sie die von fuzz_generator erzeugten Dateien fuzz_entry.cc, FuzzArgsProto.pb.cc einbinden sollten. Sie finden diese Dateien in ${output_dir}.
python3 -m helper.make {library name}
python3 -m helper.build {library name}
Sie finden alle Ausgaben der gesamten Pipeline von 'UTopia' in den folgenden beiden Verzeichnissen:
exp/{library name}/output,result/test/{library name}