diff --git a/cmake/onnxruntime_fuzz_test.cmake b/cmake/onnxruntime_fuzz_test.cmake index 26d41e98687..eea411d9381 100644 --- a/cmake/onnxruntime_fuzz_test.cmake +++ b/cmake/onnxruntime_fuzz_test.cmake @@ -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") + 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) + # 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") + # 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}) - # 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") @@ -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) + # 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") + # 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 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) + # compile with c++17 + target_compile_features(onnxruntime_security_fuzz PUBLIC cxx_std_17) - # Security fuzzing engine header file reference - onnxruntime_add_include_to_target(onnxruntime_security_fuzz onnx onnxruntime) + # Security fuzzing engine header file reference + onnxruntime_add_include_to_target(onnxruntime_security_fuzz onnx onnxruntime) - # Assign all include to one variable - set(SEC_FUZ_INC "${SEC_FUZZ_ROOT}/include") - set(INCLUDE_FILES ${SEC_FUZ_INC} "$") + # Assign all include to one variable + set(SEC_FUZ_INC "${SEC_FUZZ_ROOT}/include") + set(INCLUDE_FILES ${SEC_FUZ_INC} "$") - # add all these include directory to the Fuzzing engine - target_include_directories(onnxruntime_security_fuzz PRIVATE ${INCLUDE_FILES}) + # add all these include directory to the Fuzzing engine + target_include_directories(onnxruntime_security_fuzz PRIVATE ${INCLUDE_FILES}) - # add link libraries the project - target_link_libraries(onnxruntime_security_fuzz onnx_proto onnxruntime protobuf-mutator ${PROTOBUF_LIB}) + # add link libraries to the project + target_link_libraries(onnxruntime_security_fuzz onnx_proto onnxruntime protobuf-mutator ${PROTOBUF_LIB}) - # add the dependencies - add_dependencies(onnxruntime_security_fuzz onnx_proto onnxruntime protobuf-mutator ${PROTOBUF_LIB}) + # add the dependencies + add_dependencies(onnxruntime_security_fuzz onnx_proto onnxruntime protobuf-mutator ${PROTOBUF_LIB}) - # copy the dlls to the execution directory - add_custom_command(TARGET onnxruntime_security_fuzz POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) + # 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 $ $ + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) + + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + # Add a second fuzzer that uses libFuzzer in fuzzer/libfuzzer + message(STATUS "Building libProtoBufFuzzer-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/OrtProtoLibfuzzer.cpp") + + # 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}) + + # 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 $ $ + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) + # 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 $ $ + COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) + endif() endif() diff --git a/onnxruntime/test/fuzzing/ort_libfuzzer/OrtLibfuzzer.cpp b/onnxruntime/test/fuzzing/ort_libfuzzer/OrtLibfuzzer.cpp new file mode 100644 index 00000000000..406aca722bb --- /dev/null +++ b/onnxruntime/test/fuzzing/ort_libfuzzer/OrtLibfuzzer.cpp @@ -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(size))) { + return 0; // Ignore invalid inputs + } + predict(msg, data_provider.ConsumeIntegral(), 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; +} diff --git a/onnxruntime/test/fuzzing/ort_libfuzzer/OrtProtoLibfuzzer.cpp b/onnxruntime/test/fuzzing/ort_libfuzzer/OrtProtoLibfuzzer.cpp new file mode 100644 index 00000000000..607d9cfd9c7 --- /dev/null +++ b/onnxruntime/test/fuzzing/ort_libfuzzer/OrtProtoLibfuzzer.cpp @@ -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 + +Ort::Env env; + +std::string wstring_to_string(const std::wstring& wstr) { + std::wstring_convert> 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 +using PostProcessor = + protobuf_mutator::libfuzzer::PostProcessorRegistration; + +// 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 dist(0.0f, 1.0f); + return dist(rng); +} + +// PostProcessor for ONNX ModelProto with random values +static PostProcessor 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(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; + } +}