Skip to content

Commit

Permalink
[Fuzzer] Add two new ORT libfuzzer (Linux clang support for now) (mic…
Browse files Browse the repository at this point in the history
…rosoft#22055)

### Description
This PR adds two new libfuzzer in fuzzer project.
1. Binary libfuzzer 
2. libprotobuf-fuzzer

To compile run below cmd on linux:
```
LLVM_PROFILE_FILE="%p.profraw" CFLAGS="-g -fsanitize=address,fuzzer-no-link -shared-libasan -fprofile-instr-generate -fcoverage-mapping" CXXFLAGS="-g -shared-libasan -fsanitize=address,fuzzer-no-link -fprofile-instr-generate -fcoverage-mapping" CC=clang CXX=clang++ ./build.sh --update --build --config Debug --compile_no_warning_as_error --build_shared_lib --skip_submodule_sync --use_full_protobuf  --parallel --fuzz_testing --build_dir build/
```
Run fuzzer:
```
LD_PRELOAD=$(clang -print-file-name=libclang_rt.asan-x86_64.so) build/Debug/onnxruntime_libfuzzer_fuzz  testinput -rss_limit_mb=8196 -max_total_time=472800 -fork=2 -jobs=4 -workers=4 -ignore_crashes=1 -max_len=2097152 2>&1 | grep -v "\[libprotobuf ERROR"
```


### Motivation and Context
The existing custom fuzzer is not coverage guided and it's slow and it
will work on one model mutation at a time. The new fuzzers are coverage
guided, and we can use more models' files as a corpus to increase the
coverage.
  • Loading branch information
0xdr3dd authored Sep 12, 2024
1 parent d539c27 commit 5c36110
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 45 deletions.
145 changes: 100 additions & 45 deletions cmake/onnxruntime_fuzz_test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@
# Check that the options are properly set for
# the fuzzing project
if (onnxruntime_FUZZ_ENABLED)
message(STATUS "Building dependency protobuf-mutator and libfuzzer")

# set the options used to control the protobuf-mutator build
set(PROTOBUF_LIBRARIES ${PROTOBUF_LIB})
set(LIB_PROTO_MUTATOR_TESTING OFF)

# include the protobuf-mutator CMakeLists.txt rather than the projects CMakeLists.txt to avoid target clashes
# with google test
add_subdirectory("external/libprotobuf-mutator/src")

# add the appropriate include directory and compilation flags
# needed by the protobuf-mutator target and the libfuzzer
set(PROTOBUF_MUT_INCLUDE_DIRS "external/libprotobuf-mutator")
onnxruntime_add_include_to_target(protobuf-mutator ${PROTOBUF_LIB})
onnxruntime_add_include_to_target(protobuf-mutator-libfuzzer ${PROTOBUF_LIB})
target_include_directories(protobuf-mutator PRIVATE ${INCLUDE_DIRECTORIES} ${PROTOBUF_MUT_INCLUDE_DIRS})
target_include_directories(protobuf-mutator-libfuzzer PRIVATE ${INCLUDE_DIRECTORIES} ${PROTOBUF_MUT_INCLUDE_DIRS})
message(STATUS "Building dependency protobuf-mutator and libfuzzer")

# set the options used to control the protobuf-mutator build
set(PROTOBUF_LIBRARIES ${PROTOBUF_LIB})
set(LIB_PROTO_MUTATOR_TESTING OFF)

# include the protobuf-mutator CMakeLists.txt rather than the projects CMakeLists.txt to avoid target clashes
# with google test
add_subdirectory("external/libprotobuf-mutator/src")

# add the appropriate include directory and compilation flags
# needed by the protobuf-mutator target and the libfuzzer
set(PROTOBUF_MUT_INCLUDE_DIRS "external/libprotobuf-mutator")
onnxruntime_add_include_to_target(protobuf-mutator ${PROTOBUF_LIB})
onnxruntime_add_include_to_target(protobuf-mutator-libfuzzer ${PROTOBUF_LIB})
target_include_directories(protobuf-mutator PRIVATE ${INCLUDE_DIRECTORIES} ${PROTOBUF_MUT_INCLUDE_DIRS})
target_include_directories(protobuf-mutator-libfuzzer PRIVATE ${INCLUDE_DIRECTORIES} ${PROTOBUF_MUT_INCLUDE_DIRS})

if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
# MSVC-specific compiler options
target_compile_options(protobuf-mutator PRIVATE "/wd4244" "/wd4245" "/wd4267" "/wd4100" "/wd4456")
Expand All @@ -44,42 +45,96 @@ if (onnxruntime_FUZZ_ENABLED)
)
endif()

# add Fuzzing Engine Build Configuration
message(STATUS "Building Fuzzing engine")
# add Fuzzing Engine Build Configuration
message(STATUS "Building Fuzzing engine")

# set Fuzz root directory
set(SEC_FUZZ_ROOT ${TEST_SRC_DIR}/fuzzing)

# Security fuzzing engine src file reference
set(SEC_FUZ_SRC "${SEC_FUZZ_ROOT}/src/BetaDistribution.cpp"
"${SEC_FUZZ_ROOT}/src/OnnxPrediction.cpp"
"${SEC_FUZZ_ROOT}/src/testlog.cpp"
"${SEC_FUZZ_ROOT}/src/test.cpp")

# compile the executables
onnxruntime_add_executable(onnxruntime_security_fuzz ${SEC_FUZ_SRC})

# compile with c++17
target_compile_features(onnxruntime_security_fuzz PUBLIC cxx_std_17)

# set Fuzz root directory
set(SEC_FUZZ_ROOT ${TEST_SRC_DIR}/fuzzing)
# Security fuzzing engine header file reference
onnxruntime_add_include_to_target(onnxruntime_security_fuzz onnx onnxruntime)

# Security fuzzing engine src file reference
set(SEC_FUZ_SRC "${SEC_FUZZ_ROOT}/src/BetaDistribution.cpp"
"${SEC_FUZZ_ROOT}/src/OnnxPrediction.cpp"
"${SEC_FUZZ_ROOT}/src/testlog.cpp"
"${SEC_FUZZ_ROOT}/src/test.cpp")
# Assign all include to one variable
set(SEC_FUZ_INC "${SEC_FUZZ_ROOT}/include")
set(INCLUDE_FILES ${SEC_FUZ_INC} "$<TARGET_PROPERTY:protobuf-mutator,INCLUDE_DIRECTORIES>")

# compile the executables
onnxruntime_add_executable(onnxruntime_security_fuzz ${SEC_FUZ_SRC})
# add all these include directory to the Fuzzing engine
target_include_directories(onnxruntime_security_fuzz PRIVATE ${INCLUDE_FILES})

# compile with c++17
target_compile_features(onnxruntime_security_fuzz PUBLIC cxx_std_17)
# add link libraries to the project
target_link_libraries(onnxruntime_security_fuzz onnx_proto onnxruntime protobuf-mutator ${PROTOBUF_LIB})

# Security fuzzing engine header file reference
onnxruntime_add_include_to_target(onnxruntime_security_fuzz onnx onnxruntime)
# add the dependencies
add_dependencies(onnxruntime_security_fuzz onnx_proto onnxruntime protobuf-mutator ${PROTOBUF_LIB})

# Assign all include to one variable
set(SEC_FUZ_INC "${SEC_FUZZ_ROOT}/include")
set(INCLUDE_FILES ${SEC_FUZ_INC} "$<TARGET_PROPERTY:protobuf-mutator,INCLUDE_DIRECTORIES>")
# copy the shared libraries (DLLs on Windows, SOs on Linux) to the execution directory
add_custom_command(TARGET onnxruntime_security_fuzz POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:onnxruntime> $<TARGET_FILE_DIR:onnxruntime_security_fuzz>
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:${PROTOBUF_LIB}> $<TARGET_FILE_DIR:onnxruntime_security_fuzz>)

# add all these include directory to the Fuzzing engine
target_include_directories(onnxruntime_security_fuzz PRIVATE ${INCLUDE_FILES})
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# Add a second fuzzer that uses libFuzzer in fuzzer/libfuzzer
message(STATUS "Building libProtoBufFuzzer-based fuzzer")

# add link libraries the project
target_link_libraries(onnxruntime_security_fuzz onnx_proto onnxruntime protobuf-mutator ${PROTOBUF_LIB})
# Set source files for the libFuzzer
set(LIBFUZZER_SRC "${SEC_FUZZ_ROOT}/src/OnnxPrediction.cpp"
"${SEC_FUZZ_ROOT}/src/testlog.cpp"
"${SEC_FUZZ_ROOT}/ort_libfuzzer/OrtProtoLibfuzzer.cpp")

# add the dependencies
add_dependencies(onnxruntime_security_fuzz onnx_proto onnxruntime protobuf-mutator ${PROTOBUF_LIB})
# Compile the libFuzzer-based fuzzer
onnxruntime_add_executable(onnxruntime_proto_libfuzzer ${LIBFUZZER_SRC})
# Security fuzzing engine header file reference
onnxruntime_add_include_to_target(onnxruntime_proto_libfuzzer onnx onnxruntime)
# Set include directories for libFuzzer
target_include_directories(onnxruntime_proto_libfuzzer PRIVATE ${INCLUDE_FILES})

# copy the dlls to the execution directory
add_custom_command(TARGET onnxruntime_security_fuzz POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:onnxruntime> $<TARGET_FILE_DIR:onnxruntime_security_fuzz>
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:${PROTOBUF_LIB}> $<TARGET_FILE_DIR:onnxruntime_security_fuzz>)
# Add link libraries for libFuzzer
target_link_libraries(onnxruntime_proto_libfuzzer onnx_proto onnxruntime protobuf-mutator protobuf-mutator-libfuzzer -fsanitize=fuzzer,address ${PROTOBUF_LIB})

# Add the dependencies for libFuzzer
add_dependencies(onnxruntime_proto_libfuzzer onnx_proto onnxruntime protobuf-mutator protobuf-mutator-libfuzzer ${PROTOBUF_LIB})

# Copy shared libraries for libFuzzer
add_custom_command(TARGET onnxruntime_proto_libfuzzer POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:onnxruntime> $<TARGET_FILE_DIR:onnxruntime_proto_libfuzzer>
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:${PROTOBUF_LIB}> $<TARGET_FILE_DIR:onnxruntime_proto_libfuzzer>)
# Add a second fuzzer that uses libFuzzer in fuzzer/libfuzzer
message(STATUS "Building libBufFuzzer-based fuzzer")

# Set source files for the libFuzzer
set(LIBFUZZER_SRC "${SEC_FUZZ_ROOT}/src/OnnxPrediction.cpp"
"${SEC_FUZZ_ROOT}/src/testlog.cpp"
"${SEC_FUZZ_ROOT}/ort_libfuzzer/OrtLibfuzzer.cpp")

# Compile the libFuzzer-based fuzzer
onnxruntime_add_executable(onnxruntime_libfuzzer_fuzz ${LIBFUZZER_SRC})
# Security fuzzing engine header file reference
onnxruntime_add_include_to_target(onnxruntime_libfuzzer_fuzz onnx onnxruntime)
# Set include directories for libFuzzer
target_compile_definitions(onnxruntime_libfuzzer_fuzz PRIVATE GOOGLE_PROTOBUF_NO_LOGGING=1)
target_include_directories(onnxruntime_libfuzzer_fuzz PRIVATE ${INCLUDE_FILES})

# Add link libraries for libFuzzer
target_link_libraries(onnxruntime_libfuzzer_fuzz onnx_proto onnxruntime protobuf-mutator protobuf-mutator-libfuzzer -fsanitize=fuzzer,address ${PROTOBUF_LIB})

# Add the dependencies for libFuzzer
add_dependencies(onnxruntime_libfuzzer_fuzz onnx_proto onnxruntime protobuf-mutator protobuf-mutator-libfuzzer ${PROTOBUF_LIB})

# Copy shared libraries for libFuzzer
add_custom_command(TARGET onnxruntime_libfuzzer_fuzz POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:onnxruntime> $<TARGET_FILE_DIR:onnxruntime_libfuzzer_fuzz>
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:${PROTOBUF_LIB}> $<TARGET_FILE_DIR:onnxruntime_libfuzzer_fuzz>)
endif()
endif()
42 changes: 42 additions & 0 deletions onnxruntime/test/fuzzing/ort_libfuzzer/OrtLibfuzzer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include "OnnxPrediction.h"
#include "onnxruntime_session_options_config_keys.h"
#include "src/libfuzzer/libfuzzer_macro.h"
#include "fuzzer/FuzzedDataProvider.h"

Ort::Env env;

void predict(onnx::ModelProto& msg, unsigned int seed, Ort::Env& env) {
// Create object for prediction
//
OnnxPrediction predict(msg, env);

// Give predict a function to generate the data
// to run prediction on.
//
predict.SetupInput(GenerateDataForInputTypeTensor, seed);

// Run the prediction on the data
//
predict.RunInference();
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider data_provider(data, size);
onnx::ModelProto msg;
try {
if (!msg.ParseFromArray(data, static_cast<int>(size))) {
return 0; // Ignore invalid inputs
}
predict(msg, data_provider.ConsumeIntegral<int>(), env);
} catch (const std::exception& e) {
// Optionally log or suppress the exception
// std::cerr << "Caught exception: " << e.what() << std::endl;
} catch (...) {
// Handle any other exceptions
// std::cerr << "Caught unknown exception." << std::endl;
}
return 0;
}
94 changes: 94 additions & 0 deletions onnxruntime/test/fuzzing/ort_libfuzzer/OrtProtoLibfuzzer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include "src/mutator.h"
#include "OnnxPrediction.h"
#include "onnxruntime_session_options_config_keys.h"
#include "src/libfuzzer/libfuzzer_macro.h"
#include "onnx/onnx_pb.h"

#include <type_traits>

Ort::Env env;

std::string wstring_to_string(const std::wstring& wstr) {
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
return converter.to_bytes(wstr);
}

void predict(onnx::ModelProto& msg, unsigned int seed, Ort::Env& env) {
// Create object for prediction
//
OnnxPrediction predict(msg, env);

// Give predict a function to generate the data
// to run prediction on.
//
predict.SetupInput(GenerateDataForInputTypeTensor, seed);

// Run the prediction on the data
//
predict.RunInference();

// View the output
//
predict.PrintOutputValues();
}

template <class Proto>
using PostProcessor =
protobuf_mutator::libfuzzer::PostProcessorRegistration<Proto>;

// Helper function to generate random strings
std::string generate_random_string(size_t length, std::mt19937& rng) {
const std::string characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::uniform_int_distribution<> dist(0, characters.size() - 1);
std::string result;
for (size_t i = 0; i < length; ++i) {
result += characters[dist(rng)];
}
return result;
}

// Helper function to generate random float
float generate_random_float(std::mt19937& rng) {
std::uniform_real_distribution<float> dist(0.0f, 1.0f);
return dist(rng);
}

// PostProcessor for ONNX ModelProto with random values
static PostProcessor<onnx::ModelProto> reg1 = {
[](onnx::ModelProto* model_proto, unsigned int seed) {
std::mt19937 rng(seed);

// Set model's IR version
model_proto->set_ir_version(7);

model_proto->set_producer_name("onnx");
model_proto->set_producer_version("7.0");
model_proto->set_domain("example.com");

// Add a dummy opset import
auto* opset_import = model_proto->add_opset_import();
opset_import->set_version(10);

// Access the graph from the model
auto* graph = model_proto->mutable_graph();

// Set a random name for the graph
graph->set_name(generate_random_string(10, rng));
}};

DEFINE_PROTO_FUZZER(const onnx::ModelProto& msg) {
try {
auto seed = static_cast<unsigned int>(std::chrono::system_clock::now().time_since_epoch().count());
onnx::ModelProto msg_proto = msg;
predict(msg_proto, seed, env);
} catch (const std::exception& e) {
// Optionally log or suppress the exception
// std::cerr << "Caught exception: " << e.what() << std::endl;
} catch (...) {
// Handle any other exceptions
// std::cerr << "Caught unknown exception." << std::endl;
}
}

0 comments on commit 5c36110

Please sign in to comment.