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 <afdx@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Alastair Donaldson <afdx@google.com>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
Alastair Donaldson 2021-07-27 16:32:59 +00:00 committed by Tint LUCI CQ
parent dee93c6b9a
commit 3647df3fb7
1 changed files with 48 additions and 13 deletions

View File

@ -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<Mutator> CreateMutator(const std::vector<uint32_t>& 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<uint32_t>& 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;
}