diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 5cea101f..82717e91 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -370,6 +370,8 @@ typedef struct spv_optimizer_options_t spv_optimizer_options_t; typedef struct spv_reducer_options_t spv_reducer_options_t; +typedef struct spv_fuzzer_options_t spv_fuzzer_options_t; + // Type Definitions typedef spv_const_binary_t* spv_const_binary; @@ -385,6 +387,8 @@ typedef spv_optimizer_options_t* spv_optimizer_options; typedef const spv_optimizer_options_t* spv_const_optimizer_options; typedef spv_reducer_options_t* spv_reducer_options; typedef const spv_reducer_options_t* spv_const_reducer_options; +typedef spv_fuzzer_options_t* spv_fuzzer_options; +typedef const spv_fuzzer_options_t* spv_const_fuzzer_options; // Platform API @@ -590,6 +594,19 @@ SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit( SPIRV_TOOLS_EXPORT void spvReducerOptionsSetFailOnValidationError( spv_reducer_options options, bool fail_on_validation_error); +// Creates a fuzzer options object with default options. Returns a valid +// options object. The object remains valid until it is passed into +// |spvFuzzerOptionsDestroy|. +SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate(); + +// Destroys the given fuzzer options object. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsDestroy(spv_fuzzer_options options); + +// Sets the seed with which the random number generator used by the fuzzer +// should be initialized. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed( + spv_fuzzer_options options, uint32_t seed); + // Encodes the given SPIR-V assembly text to its binary representation. The // length parameter specifies the number of bytes for text. Encoded binary will // be stored into *binary. Any error will be written into *diagnostic if diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp index da27404d..b0fd4a2c 100644 --- a/include/spirv-tools/libspirv.hpp +++ b/include/spirv-tools/libspirv.hpp @@ -191,6 +191,26 @@ class ReducerOptions { spv_reducer_options options_; }; +// A C++ wrapper around a fuzzer options object. +class FuzzerOptions { + public: + FuzzerOptions() : options_(spvFuzzerOptionsCreate()) {} + ~FuzzerOptions() { spvFuzzerOptionsDestroy(options_); } + + // Allow implicit conversion to the underlying object. + operator spv_fuzzer_options() const { // NOLINT(google-explicit-constructor) + return options_; + } + + // See spvFuzzerOptionsSetRandomSeed. + void set_random_seed(uint32_t seed) { + spvFuzzerOptionsSetRandomSeed(options_, seed); + } + + private: + spv_fuzzer_options options_; +}; + // C++ interface for SPIRV-Tools functionalities. It wraps the context // (including target environment and the corresponding SPIR-V grammar) and // provides methods for assembling, disassembling, and validating. diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 0c0820dd..93938f0e 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -198,6 +198,7 @@ set_source_files_properties( add_subdirectory(opt) add_subdirectory(reduce) +add_subdirectory(fuzz) add_subdirectory(link) set(SPIRV_SOURCES @@ -233,6 +234,7 @@ set(SPIRV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/spirv_constant.h ${CMAKE_CURRENT_SOURCE_DIR}/spirv_definition.h ${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_fuzzer_options.h ${CMAKE_CURRENT_SOURCE_DIR}/spirv_optimizer_options.h ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reducer_options.h ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.h @@ -260,6 +262,7 @@ set(SPIRV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/print.cpp ${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_fuzzer_options.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv_optimizer_options.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reducer_options.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.cpp diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt new file mode 100644 index 00000000..af259040 --- /dev/null +++ b/source/fuzz/CMakeLists.txt @@ -0,0 +1,90 @@ +# Copyright (c) 2019 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(SPIRV_BUILD_FUZZER) + set(PROTOBUF_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/protobufs/spvtoolsfuzz.proto) + + add_custom_command( + OUTPUT protobufs/spvtoolsfuzz.pb.cc protobufs/spvtoolsfuzz.pb.h + COMMAND protobuf::protoc + -I=${CMAKE_CURRENT_SOURCE_DIR}/protobufs + --cpp_out=protobufs + ${PROTOBUF_SOURCE} + DEPENDS ${PROTOBUF_SOURCE} + COMMENT "Generate protobuf sources from proto definition file." + ) + + set(SPIRV_TOOLS_FUZZ_SOURCES + fact_manager.h + fuzzer.h + fuzzer_context.h + fuzzer_pass.h + protobufs/spirvfuzz_protobufs.h + pseudo_random_generator.h + random_generator.h + ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h + + fact_manager.cpp + fuzzer.cpp + fuzzer_context.cpp + fuzzer_pass.cpp + pseudo_random_generator.cpp + random_generator.cpp + ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc + ) + + if(MSVC) + # Enable parallel builds across four cores for this lib + add_definitions(/MP4) + endif() + + spvtools_pch(SPIRV_TOOLS_FUZZ_SOURCES pch_source_fuzz) + + add_library(SPIRV-Tools-fuzz ${SPIRV_TOOLS_FUZZ_SOURCES}) + + spvtools_default_compile_options(SPIRV-Tools-fuzz) + target_compile_definitions(SPIRV-Tools-fuzz PUBLIC -DGOOGLE_PROTOBUF_NO_RTTI -DGOOGLE_PROTOBUF_USE_UNALIGNED=0) + + # Compilation of the auto-generated protobuf source file will yield warnings, + # which we have no control over and thus wish to ignore. + if(${COMPILER_IS_LIKE_GNU}) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc PROPERTIES COMPILE_FLAGS -w) + endif() + if(MSVC) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc PROPERTIES COMPILE_FLAGS /w) + endif() + + target_include_directories(SPIRV-Tools-fuzz + PUBLIC ${spirv-tools_SOURCE_DIR}/include + PUBLIC ${SPIRV_HEADER_INCLUDE_DIR} + PRIVATE ${spirv-tools_BINARY_DIR} + PRIVATE ${CMAKE_BINARY_DIR}) + + # The fuzzer reuses a lot of functionality from the SPIRV-Tools library. + target_link_libraries(SPIRV-Tools-fuzz + PUBLIC ${SPIRV_TOOLS} + PUBLIC SPIRV-Tools-opt + PUBLIC protobuf::libprotobuf) + + set_property(TARGET SPIRV-Tools-fuzz PROPERTY FOLDER "SPIRV-Tools libraries") + spvtools_check_symbol_exports(SPIRV-Tools-fuzz) + + if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-fuzz + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif(ENABLE_SPIRV_TOOLS_INSTALL) + +endif(SPIRV_BUILD_FUZZER) diff --git a/source/fuzz/fact_manager.cpp b/source/fuzz/fact_manager.cpp new file mode 100644 index 00000000..1c192878 --- /dev/null +++ b/source/fuzz/fact_manager.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "source/fuzz/fact_manager.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +FactManager::FactManager() = default; + +FactManager::~FactManager() = default; + +bool FactManager::AddFacts(const protobufs::FactSequence& initial_facts, + opt::IRContext* context) { + for (auto& fact : initial_facts.fact()) { + if (!AddFact(fact, context)) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2621) Provide + // information about the fact that could not be added. + return false; + } + } + return true; +} + +bool FactManager::AddFact(const spvtools::fuzz::protobufs::Fact&, + spvtools::opt::IRContext*) { + assert(0 && "No facts are yet supported."); + return true; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fact_manager.h b/source/fuzz/fact_manager.h new file mode 100644 index 00000000..49b6d6dc --- /dev/null +++ b/source/fuzz/fact_manager.h @@ -0,0 +1,53 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_H_ +#define SOURCE_FUZZ_FACT_MANAGER_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/constants.h" + +namespace spvtools { +namespace fuzz { + +// Keeps track of facts about the module being transformed on which the fuzzing +// process can depend. Some initial facts can be provided, for example about +// guarantees on the values of inputs to SPIR-V entry points. Transformations +// may then rely on these facts, can add further facts that they establish. +// Facts are intended to be simple properties that either cannot be deduced from +// the module (such as properties that are guaranteed to hold for entry point +// inputs), or that are established by transformations, likely to be useful for +// future transformations, and not completely trivial to deduce straight from +// the module. +class FactManager { + public: + FactManager(); + + ~FactManager(); + + // Adds all the facts from |facts|, checking them for validity with respect to + // |context|. Returns true if and only if all facts are valid. + bool AddFacts(const protobufs::FactSequence& facts, opt::IRContext* context); + + // Adds |fact| to the fact manager, checking it for validity with respect to + // |context|. Returns true if and only if the fact is valid. + bool AddFact(const protobufs::Fact& fact, opt::IRContext* context); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // #define SOURCE_FUZZ_FACT_MANAGER_H_ diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp new file mode 100644 index 00000000..2f28d79b --- /dev/null +++ b/source/fuzz/fuzzer.cpp @@ -0,0 +1,109 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer.h" + +#include +#include + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "source/opt/build_module.h" +#include "source/spirv_fuzzer_options.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +namespace { +const uint32_t kIdBoundGap = 100; +} + +struct Fuzzer::Impl { + explicit Impl(spv_target_env env) : target_env(env) {} + + const spv_target_env target_env; // Target environment. + MessageConsumer consumer; // Message consumer. +}; + +Fuzzer::Fuzzer(spv_target_env env) : impl_(MakeUnique(env)) {} + +Fuzzer::~Fuzzer() = default; + +void Fuzzer::SetMessageConsumer(MessageConsumer c) { + impl_->consumer = std::move(c); +} + +Fuzzer::FuzzerResultStatus Fuzzer::Run( + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + std::vector* binary_out, protobufs::TransformationSequence*, + spv_const_fuzzer_options options) const { + // Check compatibility between the library version being linked with and the + // header files being used. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + spvtools::SpirvTools tools(impl_->target_env); + if (!tools.IsValid()) { + impl_->consumer(SPV_MSG_ERROR, nullptr, {}, + "Failed to create SPIRV-Tools interface; stopping."); + return Fuzzer::FuzzerResultStatus::kFailedToCreateSpirvToolsInterface; + } + + // Initial binary should be valid. + if (!tools.Validate(&binary_in[0], binary_in.size())) { + impl_->consumer(SPV_MSG_ERROR, nullptr, {}, + "Initial binary is invalid; stopping."); + return Fuzzer::FuzzerResultStatus::kInitialBinaryInvalid; + } + + // Build the module from the input binary. + std::unique_ptr ir_context = BuildModule( + impl_->target_env, impl_->consumer, binary_in.data(), binary_in.size()); + assert(ir_context); + + // Make a PRNG, either from a given seed or from a random device. + PseudoRandomGenerator random_generator( + options->has_random_seed ? options->random_seed + : (uint32_t)std::random_device()()); + + // The fuzzer will introduce new ids into the module. The module's id bound + // gives the smallest id that can be used for this purpose. We add an offset + // to this so that there is a sizeable gap between the ids used in the + // original module and the ids used for fuzzing, as a readability aid. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the + // case where the maximum id bound is reached. + auto minimum_fresh_id = ir_context->module()->id_bound() + kIdBoundGap; + FuzzerContext fuzzer_context(&random_generator, minimum_fresh_id); + + FactManager fact_manager; + if (!fact_manager.AddFacts(initial_facts, ir_context.get())) { + return Fuzzer::FuzzerResultStatus::kInitialFactsInvalid; + } + + // Fuzzer passes will be created and applied here and will populate the + // output sequence of transformations. Currently there are no passes. + // TODO(afd) Implement fuzzer passes and invoke them here. + + // Encode the module as a binary. + ir_context->module()->ToBinary(binary_out, false); + + return Fuzzer::FuzzerResultStatus::kComplete; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer.h b/source/fuzz/fuzzer.h new file mode 100644 index 00000000..f81f2979 --- /dev/null +++ b/source/fuzz/fuzzer.h @@ -0,0 +1,74 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_H_ +#define SOURCE_FUZZ_FUZZER_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Transforms a SPIR-V module into a semantically equivalent SPIR-V module by +// running a number of randomized fuzzer passes. +class Fuzzer { + public: + // Possible statuses that can result from running the fuzzer. + enum class FuzzerResultStatus { + kComplete, + kFailedToCreateSpirvToolsInterface, + kInitialBinaryInvalid, + kInitialFactsInvalid, + }; + + // Constructs a fuzzer from the given target environment. + explicit Fuzzer(spv_target_env env); + + // Disables copy/move constructor/assignment operations. + Fuzzer(const Fuzzer&) = delete; + Fuzzer(Fuzzer&&) = delete; + Fuzzer& operator=(const Fuzzer&) = delete; + Fuzzer& operator=(Fuzzer&&) = delete; + + ~Fuzzer(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Transforms |binary_in| to |binary_out| by running a number of randomized + // fuzzer passes, controlled via |options|. Initial facts about the input + // binary and the context in which it will execute are provided via + // |initial_facts|. The transformation sequence that was applied is returned + // via |transformation_sequence_out|. + FuzzerResultStatus Run( + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + std::vector* binary_out, + protobufs::TransformationSequence* transformation_sequence_out, + spv_const_fuzzer_options options) const; + + private: + struct Impl; // Opaque struct for holding internal data. + std::unique_ptr impl_; // Unique pointer to internal data. +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_H_ diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp new file mode 100644 index 00000000..11de6127 --- /dev/null +++ b/source/fuzz/fuzzer_context.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_context.h" + +namespace spvtools { +namespace fuzz { + +FuzzerContext::FuzzerContext(RandomGenerator* random_generator, + uint32_t min_fresh_id) + : random_generator_(random_generator), next_fresh_id_(min_fresh_id) {} + +FuzzerContext::~FuzzerContext() = default; + +uint32_t FuzzerContext::GetFreshId() { return next_fresh_id_++; } + +RandomGenerator* FuzzerContext::GetRandomGenerator() { + return random_generator_; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h new file mode 100644 index 00000000..5ab0f6f0 --- /dev/null +++ b/source/fuzz/fuzzer_context.h @@ -0,0 +1,52 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_CONTEXT_H_ +#define SOURCE_FUZZ_FUZZER_CONTEXT_H_ + +#include "source/fuzz/random_generator.h" +#include "source/opt/function.h" + +namespace spvtools { +namespace fuzz { + +// Encapsulates all parameters that control the fuzzing process, such as the +// source of randomness and the probabilities with which transformations are +// applied. +class FuzzerContext { + public: + // Constructs a fuzzer context with a given random generator and the minimum + // value that can be used for fresh ids. + FuzzerContext(RandomGenerator* random_generator, uint32_t min_fresh_id); + + ~FuzzerContext(); + + // Provides the random generator used to control fuzzing. + RandomGenerator* GetRandomGenerator(); + + // Yields an id that is guaranteed not to be used in the module being fuzzed, + // or to have been issued before. + uint32_t GetFreshId(); + + private: + // The source of randomness. + RandomGenerator* random_generator_; + // The next fresh id to be issued. + uint32_t next_fresh_id_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_CONTEXT_H_ diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp new file mode 100644 index 00000000..823f2e09 --- /dev/null +++ b/source/fuzz/fuzzer_pass.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPass::FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : ir_context_(ir_context), + fact_manager_(fact_manager), + fuzzer_context_(fuzzer_context), + transformations_(transformations) {} + +FuzzerPass::~FuzzerPass() = default; + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h new file mode 100644 index 00000000..4d0861e6 --- /dev/null +++ b/source/fuzz/fuzzer_pass.h @@ -0,0 +1,61 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_H_ + +#include "source/fuzz/fact_manager.h" +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" + +namespace spvtools { +namespace fuzz { + +// Interface for applying a pass of transformations to a module. +class FuzzerPass { + public: + FuzzerPass(opt::IRContext* ir_context, FactManager* fact_manager, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + virtual ~FuzzerPass(); + + // Applies the pass to the module |ir_context_|, assuming and updating + // facts from |fact_manager_|, and using |fuzzer_context_| to guide the + // process. Appends to |transformations_| all transformations that were + // applied during the pass. + virtual void Apply() = 0; + + protected: + opt::IRContext* GetIRContext() const { return ir_context_; } + + FactManager* GetFactManager() const { return fact_manager_; } + + FuzzerContext* GetFuzzerContext() const { return fuzzer_context_; } + + protobufs::TransformationSequence* GetTransformations() const { + return transformations_; + } + + private: + opt::IRContext* ir_context_; + FactManager* fact_manager_; + FuzzerContext* fuzzer_context_; + protobufs::TransformationSequence* transformations_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // #define SOURCE_FUZZ_FUZZER_PASS_H_ diff --git a/source/fuzz/protobufs/spirvfuzz_protobufs.h b/source/fuzz/protobufs/spirvfuzz_protobufs.h new file mode 100644 index 00000000..b801626b --- /dev/null +++ b/source/fuzz/protobufs/spirvfuzz_protobufs.h @@ -0,0 +1,52 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_SPIRVFUZZ_PROTOBUFS_H_ +#define SOURCE_FUZZ_SPIRVFUZZ_PROTOBUFS_H_ + +// This header file serves to act as a barrier between the protobuf header +// files and files that include them. It uses compiler pragmas to disable +// diagnostics, in order to ignore warnings generated during the processing +// of these header files without having to compromise on freedom from warnings +// in the rest of the project. + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4244) +#endif + +// The following should be the only place in the project where protobuf files +// are directly included. This is so that they can be compiled in a manner +// where warnings are ignored. + +#include "google/protobuf/util/json_util.h" +#include "source/fuzz/protobufs/spvtoolsfuzz.pb.h" + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif // SOURCE_FUZZ_SPIRVFUZZ_PROTOBUFS_H_ diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto new file mode 100644 index 00000000..e87819a5 --- /dev/null +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -0,0 +1,38 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file is specifically named spvtools_fuzz.proto so that the string +// 'spvtools_fuzz' appears in the names of global-scope symbols that protoc +// generates when targeting C++. This is to reduce the potential for name +// clashes with other globally-scoped symbols. + +syntax = "proto3"; + +package spvtools.fuzz.protobufs; + +message FactSequence { + repeated Fact fact = 1; +} + +message Fact { + // Currently there are no facts. +} + +message TransformationSequence { + repeated Transformation transformation = 1; +} + +message Transformation { + // Currently there are no transformations. +} diff --git a/source/fuzz/pseudo_random_generator.cpp b/source/fuzz/pseudo_random_generator.cpp new file mode 100644 index 00000000..773b89df --- /dev/null +++ b/source/fuzz/pseudo_random_generator.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "source/fuzz/pseudo_random_generator.h" + +namespace spvtools { +namespace fuzz { + +PseudoRandomGenerator::PseudoRandomGenerator(uint32_t seed) : mt_(seed) {} + +PseudoRandomGenerator::~PseudoRandomGenerator() = default; + +uint32_t PseudoRandomGenerator::RandomUint32(uint32_t bound) { + assert(bound > 0 && "Bound must be positive"); + return static_cast( + std::uniform_int_distribution<>(0, bound - 1)(mt_)); +} + +bool PseudoRandomGenerator::RandomBool() { + return static_cast(std::uniform_int_distribution<>(0, 1)(mt_)); +} + +uint32_t PseudoRandomGenerator::RandomPercentage() { + // We use 101 because we want a result in the closed interval [0, 100], and + // RandomUint32 is not inclusive of its bound. + return RandomUint32(101); +} + +double PseudoRandomGenerator::RandomDouble() { + return std::uniform_real_distribution(0.0, 1.0)(mt_); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/pseudo_random_generator.h b/source/fuzz/pseudo_random_generator.h new file mode 100644 index 00000000..d2f52920 --- /dev/null +++ b/source/fuzz/pseudo_random_generator.h @@ -0,0 +1,47 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_PSEUDO_RANDOM_GENERATOR_H_ +#define SOURCE_FUZZ_PSEUDO_RANDOM_GENERATOR_H_ + +#include + +#include "source/fuzz/random_generator.h" + +namespace spvtools { +namespace fuzz { + +// Generates random data from a pseudo-random number generator. +class PseudoRandomGenerator : public RandomGenerator { + public: + explicit PseudoRandomGenerator(uint32_t seed); + + ~PseudoRandomGenerator() override; + + uint32_t RandomUint32(uint32_t bound) override; + + uint32_t RandomPercentage() override; + + bool RandomBool() override; + + double RandomDouble() override; + + private: + std::mt19937 mt_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_PSEUDO_RANDOM_GENERATOR_H_ diff --git a/source/fuzz/random_generator.cpp b/source/fuzz/random_generator.cpp new file mode 100644 index 00000000..9ec4845d --- /dev/null +++ b/source/fuzz/random_generator.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/random_generator.h" + +namespace spvtools { +namespace fuzz { + +RandomGenerator::RandomGenerator() = default; + +RandomGenerator::~RandomGenerator() = default; + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/random_generator.h b/source/fuzz/random_generator.h new file mode 100644 index 00000000..9c467983 --- /dev/null +++ b/source/fuzz/random_generator.h @@ -0,0 +1,45 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_RANDOM_GENERATOR_H_ +#define SOURCE_FUZZ_RANDOM_GENERATOR_H_ + +#include + +namespace spvtools { +namespace fuzz { + +class RandomGenerator { + public: + RandomGenerator(); + + virtual ~RandomGenerator(); + + // Returns a value in the half-open interval [0, bound). + virtual uint32_t RandomUint32(uint32_t bound) = 0; + + // Returns a value in the closed interval [0, 100]. + virtual uint32_t RandomPercentage() = 0; + + // Returns a boolean. + virtual bool RandomBool() = 0; + + // Returns a double in the closed interval [0, 1] + virtual double RandomDouble() = 0; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_RANDOM_GENERATOR_H_ diff --git a/source/spirv_fuzzer_options.cpp b/source/spirv_fuzzer_options.cpp new file mode 100644 index 00000000..f6319ca3 --- /dev/null +++ b/source/spirv_fuzzer_options.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/spirv_fuzzer_options.h" + +spv_fuzzer_options_t::spv_fuzzer_options_t() = default; + +SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate() { + return new spv_fuzzer_options_t(); +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsDestroy(spv_fuzzer_options options) { + delete options; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed( + spv_fuzzer_options options, uint32_t seed) { + options->has_random_seed = true; + options->random_seed = seed; +} diff --git a/source/spirv_fuzzer_options.h b/source/spirv_fuzzer_options.h new file mode 100644 index 00000000..1193bfd7 --- /dev/null +++ b/source/spirv_fuzzer_options.h @@ -0,0 +1,33 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_FUZZER_OPTIONS_H_ +#define SOURCE_SPIRV_FUZZER_OPTIONS_H_ + +#include "spirv-tools/libspirv.h" + +#include +#include + +// Manages command line options passed to the SPIR-V Fuzzer. New struct +// members may be added for any new option. +struct spv_fuzzer_options_t { + spv_fuzzer_options_t(); + + // See spvFuzzerOptionsSetRandomSeed. + bool has_random_seed = false; + uint32_t random_seed = 0; +}; + +#endif // SOURCE_SPIRV_FUZZER_OPTIONS_H_ diff --git a/utils/check_symbol_exports.py b/utils/check_symbol_exports.py index d4c85794..a8f3785a 100755 --- a/utils/check_symbol_exports.py +++ b/utils/check_symbol_exports.py @@ -56,6 +56,13 @@ def check_library(library): # _ZN : something in a namespace # _Z[0-9]+spv[A-Z_] : C++ symbol starting with spv[A-Z_] symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_Z[0-9]+spv[A-Z_])') + + # In addition, the following pattern whitelists global functions that are added + # by the protobuf compiler: + # - AddDescriptors_spvtoolsfuzz_2eproto() + # - InitDefaults_spvtoolsfuzz_2eproto() + symbol_whitelist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov') + seen = set() result = 0 for line in command_output(['objdump', '-t', library], '.').split('\n'): @@ -65,7 +72,7 @@ def check_library(library): if symbol not in seen: seen.add(symbol) #print("look at '{}'".format(symbol)) - if not symbol_ok_pattern.match(symbol): + if not (symbol_whitelist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)): print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol)) result = 1 return result