Avoid id bound errors during opt fuzzing (#4658)

Use a very large id bound when fuzzing the optimizer, and check that the
input does not ids that are too close to this bound. This should make it
impossible in practice for an id overflow to occur.

Fixes #4657.
This commit is contained in:
Alastair Donaldson 2021-12-13 10:56:52 +00:00 committed by GitHub
parent ff07cfd86f
commit f0351b7bc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 157 additions and 78 deletions

View File

@ -87,18 +87,21 @@ spvtools_fuzzer("spvtools_dis_fuzzer_src") {
spvtools_fuzzer("spvtools_opt_performance_fuzzer_src") {
sources = [
"spvtools_opt_performance_fuzzer.cpp",
"spvtools_opt_fuzzer_common.cpp",
]
}
spvtools_fuzzer("spvtools_opt_legalization_fuzzer_src") {
sources = [
"spvtools_opt_legalization_fuzzer.cpp",
"spvtools_opt_fuzzer_common.cpp",
]
}
spvtools_fuzzer("spvtools_opt_size_fuzzer_src") {
sources = [
"spvtools_opt_size_fuzzer.cpp",
"spvtools_opt_fuzzer_common.cpp",
]
}

View File

@ -46,9 +46,9 @@ if (${SPIRV_BUILD_LIBFUZZER_TARGETS})
add_spvtools_libfuzzer_target(TARGET spvtools_as_fuzzer SRCS spvtools_as_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_libfuzzer_target(TARGET spvtools_binary_parser_fuzzer SRCS spvtools_binary_parser_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_libfuzzer_target(TARGET spvtools_dis_fuzzer SRCS spvtools_dis_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY})
add_spvtools_libfuzzer_target(TARGET spvtools_val_fuzzer SRCS spvtools_val_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY})
if (${SPIRV_BUILD_FUZZER})
add_spvtools_libfuzzer_target(TARGET spvtools_fuzz_fuzzer SRCS spvtools_fuzz_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-fuzz ${SPIRV_TOOLS_FULL_VISIBILITY})

View File

@ -0,0 +1,92 @@
// Copyright (c) 2021 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 "test/fuzzers/spvtools_opt_fuzzer_common.h"
#include "source/opt/build_module.h"
#include "test/fuzzers/random_generator.h"
namespace spvtools {
namespace fuzzers {
int OptFuzzerTestOneInput(
const uint8_t* data, size_t size,
std::function<void(spvtools::Optimizer&)> register_passes) {
if (size < 1) {
return 0;
}
spvtools::fuzzers::RandomGenerator random_gen(data, size);
auto target_env = random_gen.GetTargetEnv();
spvtools::Optimizer optimizer(target_env);
optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
const spv_position_t&, const char*) {});
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
// The largest possible id bound is used when running the optimizer, to avoid
// the problem of id overflows.
const size_t kFinalIdLimit = UINT32_MAX;
// The input is scanned to check that it does not already use an id too close
// to this limit. This still gives the optimizer a large set of ids to
// consume. It is thus very unlikely that id overflow will occur during
// fuzzing. If it does, then the initial id limit should be decreased.
const size_t kInitialIdLimit = kFinalIdLimit - 1000000U;
// Build the module and scan it to check that all used ids are below the
// initial limit.
auto ir_context =
spvtools::BuildModule(target_env, nullptr, input.data(), input.size());
if (ir_context == nullptr) {
// It was not possible to build a valid module; that's OK - skip this input.
return 0;
}
bool found_excessively_large_id = false;
ir_context->module()->ForEachInst(
[&found_excessively_large_id](spvtools::opt::Instruction* inst) -> void {
if (inst->result_id() && inst->result_id() > kInitialIdLimit) {
found_excessively_large_id = true;
}
},
true);
if (found_excessively_large_id) {
// The input contains a very large id. The input is thus abandoned, to avoid
// the possibility of ending up hitting the id bound limit.
return 0;
}
// Set the optimizer and its validator up with the largest possible id bound
// limit.
spvtools::ValidatorOptions validator_options;
spvtools::OptimizerOptions optimizer_options;
optimizer_options.set_max_id_bound(kFinalIdLimit);
validator_options.SetUniversalLimit(spv_validator_limit_max_id_bound,
kFinalIdLimit);
optimizer_options.set_validator_options(validator_options);
register_passes(optimizer);
optimizer.Run(input.data(), input.size(), &input, optimizer_options);
return 0;
}
} // namespace fuzzers
} // namespace spvtools

View File

@ -0,0 +1,35 @@
// Copyright (c) 2021 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 TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_
#define TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_
#include <cinttypes>
#include <cstddef>
#include <functional>
#include "spirv-tools/optimizer.hpp"
namespace spvtools {
namespace fuzzers {
// Helper function capturing the common logic for the various optimizer fuzzers.
int OptFuzzerTestOneInput(
const uint8_t* data, size_t size,
std::function<void(spvtools::Optimizer&)> register_passes);
} // namespace fuzzers
} // namespace spvtools
#endif // TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_

View File

@ -12,33 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstdint>
#include <vector>
#include <cinttypes>
#include <cstddef>
#include <functional>
#include "spirv-tools/optimizer.hpp"
#include "test/fuzzers/random_generator.h"
#include "test/fuzzers/spvtools_opt_fuzzer_common.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size < 1) {
return 0;
}
spvtools::fuzzers::RandomGenerator random_gen(data, size);
spvtools::Optimizer optimizer(random_gen.GetTargetEnv());
optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
const spv_position_t&, const char*) {});
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
optimizer.RegisterLegalizationPasses();
optimizer.Run(input.data(), input.size(), &input);
return 0;
return spvtools::fuzzers::OptFuzzerTestOneInput(
data, size, [](spvtools::Optimizer& optimizer) -> void {
optimizer.RegisterLegalizationPasses();
});
}

View File

@ -12,33 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstdint>
#include <vector>
#include <cinttypes>
#include <cstddef>
#include <functional>
#include "spirv-tools/optimizer.hpp"
#include "test/fuzzers/random_generator.h"
#include "test/fuzzers/spvtools_opt_fuzzer_common.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size < 1) {
return 0;
}
spvtools::fuzzers::RandomGenerator random_gen(data, size);
spvtools::Optimizer optimizer(random_gen.GetTargetEnv());
optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
const spv_position_t&, const char*) {});
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
optimizer.RegisterPerformancePasses();
optimizer.Run(input.data(), input.size(), &input);
return 0;
return spvtools::fuzzers::OptFuzzerTestOneInput(
data, size, [](spvtools::Optimizer& optimizer) -> void {
optimizer.RegisterPerformancePasses();
});
}

View File

@ -12,33 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstdint>
#include <vector>
#include <cinttypes>
#include <cstddef>
#include <functional>
#include "spirv-tools/optimizer.hpp"
#include "test/fuzzers/random_generator.h"
#include "test/fuzzers/spvtools_opt_fuzzer_common.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size < 1) {
return 0;
}
spvtools::fuzzers::RandomGenerator random_gen(data, size);
spvtools::Optimizer optimizer(random_gen.GetTargetEnv());
optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
const spv_position_t&, const char*) {});
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
optimizer.RegisterSizePasses();
optimizer.Run(input.data(), input.size(), &input);
return 0;
return spvtools::fuzzers::OptFuzzerTestOneInput(
data, size, [](spvtools::Optimizer& optimizer) -> void {
optimizer.RegisterSizePasses();
});
}