From 3647df3fb79050346c4eec85053b956f38e4626f Mon Sep 17 00:00:00 2001 From: Alastair Donaldson Date: Tue, 27 Jul 2021 16:32:59 +0000 Subject: [PATCH] SPIR-V Tools fuzzer: validate before mutation ClusterFuzz will provide inputs to a fuzzer that did not necessarily come from the current fuzzing run, thus the SPIR-V Tools mutator can be presented with arbitrary inputs. This change causes it to validate inputs before mutation, and reject invalid inputs. Change-Id: Ic90e62e4f80f38826765b0d815e4f41de915b5df Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/59661 Auto-Submit: Alastair Donaldson Kokoro: Kokoro Commit-Queue: Alastair Donaldson Commit-Queue: Ryan Harrison Reviewed-by: Ryan Harrison Reviewed-by: Ben Clayton --- fuzzers/tint_spirv_tools_fuzzer/fuzzer.cc | 61 ++++++++++++++++++----- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/fuzzers/tint_spirv_tools_fuzzer/fuzzer.cc b/fuzzers/tint_spirv_tools_fuzzer/fuzzer.cc index be6fd898d0..5910e985c6 100644 --- a/fuzzers/tint_spirv_tools_fuzzer/fuzzer.cc +++ b/fuzzers/tint_spirv_tools_fuzzer/fuzzer.cc @@ -25,6 +25,7 @@ #include "fuzzers/tint_spirv_tools_fuzzer/spirv_opt_mutator.h" #include "fuzzers/tint_spirv_tools_fuzzer/spirv_reduce_mutator.h" #include "fuzzers/tint_spirv_tools_fuzzer/util.h" +#include "spirv-tools/libspirv.hpp" namespace tint { namespace fuzzers { @@ -96,15 +97,45 @@ std::unique_ptr CreateMutator(const std::vector& binary, } } +void CLIMessageConsumer(spv_message_level_t level, + const char*, + const spv_position_t& position, + const char* message) { + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + std::cerr << "error: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_WARNING: + std::cout << "warning: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_INFO: + std::cout << "info: line " << position.index << ": " << message + << std::endl; + break; + default: + break; + } +} + +bool IsValid(const std::vector& binary) { + spvtools::SpirvTools tools(context->params.mutator_params.target_env); + tools.SetMessageConsumer(CLIMessageConsumer); + return tools.IsValid() && tools.Validate(binary.data(), binary.size(), + spvtools::ValidatorOptions()); +} + extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, size_t max_size, unsigned seed) { - if ((size % 4) != 0) { - // A valid SPIR-V binary's size must be a multiple of 4, and the SPIR-V - // Tools fuzzer should only work with valid binaries. - // TODO(afdx): Change this to an assertion once sure that this fuzzer is - // configured correctly in ClusterFuzz. + if ((size % sizeof(uint32_t)) != 0) { + // A valid SPIR-V binary's size must be a multiple of the size of a 32-bit + // word, and the SPIR-V Tools fuzzer is only designed to work with valid + // binaries. return 0; } @@ -114,13 +145,18 @@ extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, MutatorCache dummy_cache(1); auto* mutator_cache = context->mutator_cache.get(); if (!mutator_cache) { - // Use a dummy cache if the user has decided not to use a real cache. - // The dummy cache will be destroyed when we return from this function but - // it will save us from writing all the `if (mutator_cache)` below. + // Use a placeholder cache if the user has decided not to use a real cache. + // The placeholder cache will be destroyed when we return from this function + // but it will save us from writing all the `if (mutator_cache)` below. mutator_cache = &dummy_cache; } if (!mutator_cache->Get(binary)) { + // This is an unknown binary, so its validity must be checked before + // proceeding. + if (!IsValid(binary)) { + return 0; + } // Assign a mutator to the binary if it doesn't have one yet. mutator_cache->Put(binary, CreateMutator(binary, seed)); } @@ -178,11 +214,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return 0; } - if ((size % 4) != 0) { - // By design, the SPIR-V Tools fuzzer should only ever test using valid - // SPIR-V binaries, whose sizes should be multiples of 4 bytes. - // TODO(afdx): Change this to an assertion once sure that this fuzzer is - // configured correctly in ClusterFuzz. + if ((size % sizeof(uint32_t)) != 0) { + // The SPIR-V Tools fuzzer has been designed to work with valid + // SPIR-V binaries, whose sizes should be multiples of the size of a 32-bit + // word. return 0; }