diff options
Diffstat (limited to 'tests/fuzzer/decoder_fuzzer_frame_parallel.cc')
-rw-r--r-- | tests/fuzzer/decoder_fuzzer_frame_parallel.cc | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/tests/fuzzer/decoder_fuzzer_frame_parallel.cc b/tests/fuzzer/decoder_fuzzer_frame_parallel.cc new file mode 100644 index 0000000..d1b1c54 --- /dev/null +++ b/tests/fuzzer/decoder_fuzzer_frame_parallel.cc @@ -0,0 +1,139 @@ +// Copyright 2020 The libgav1 Authors +// +// 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 <cstddef> +#include <cstdint> +#include <deque> +#include <memory> +#include <vector> + +#include "examples/file_reader.h" +#include "examples/file_reader_constants.h" +#include "examples/file_reader_interface.h" +#include "src/gav1/decoder.h" +#include "src/gav1/status_code.h" +#include "tests/fuzzer/fuzzer_temp_file.h" + +namespace { + +#if defined(LIBGAV1_EXHAUSTIVE_FUZZING) +// Set a large upper bound to give more coverage of a single input; this value +// should be larger than most of the frame counts in the corpus. +constexpr size_t kMaxDataSize = 400 * 1024; +#else +constexpr size_t kMaxDataSize = 200 * 1024; +#endif + +using InputBuffer = std::vector<uint8_t>; + +struct InputBuffers { + ~InputBuffers() { + for (auto& buffer : free_buffers) { + delete buffer; + } + } + std::deque<InputBuffer*> free_buffers; +}; + +void ReleaseInputBuffer(void* callback_private_data, + void* buffer_private_data) { + auto* const test = static_cast<InputBuffers*>(callback_private_data); + test->free_buffers.push_back(static_cast<InputBuffer*>(buffer_private_data)); +} + +} // namespace + +// Always returns 0. Nonzero return values are reserved by libFuzzer for future +// use. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Reject large chunks of data to improve fuzzer throughput. + if (size > kMaxDataSize) return 0; + + // Note that |input_buffers| has to outlive the |decoder| object since the + // |release_input_buffer| callback could be called on the |decoder|'s + // destructor. + InputBuffers input_buffers; + + libgav1::Decoder decoder; + libgav1::DecoderSettings settings = {}; + // Use the 33 + low byte of the width to seed the number of threads. This + // ensures that we will trigger the frame parallel path in most cases. + // We use both nibbles of the lower byte as this results in values != 1 much + // more quickly than using the lower nibble alone. + settings.threads = + 33 + ((size >= 13) ? ((data[12] >> 4 | data[12]) & 0xF) + 1 : 1); + + settings.frame_parallel = true; + settings.blocking_dequeue = true; + settings.callback_private_data = &input_buffers; + settings.release_input_buffer = ReleaseInputBuffer; + if (decoder.Init(&settings) != libgav1::kStatusOk) return 0; + + FuzzerTemporaryFile tempfile(data, size); + auto file_reader = + libgav1::FileReader::Open(tempfile.filename(), /*error_tolerant=*/true); + if (file_reader == nullptr) return 0; + + InputBuffer* input_buffer = nullptr; + bool dequeue_finished = false; + + do { + if (input_buffer == nullptr && !file_reader->IsEndOfFile()) { + if (input_buffers.free_buffers.empty()) { + auto* const buffer = new (std::nothrow) InputBuffer(); + if (buffer == nullptr) { + break; + } + input_buffers.free_buffers.push_back(buffer); + } + input_buffer = input_buffers.free_buffers.front(); + input_buffers.free_buffers.pop_front(); + if (!file_reader->ReadTemporalUnit(input_buffer, nullptr)) { + break; + } + } + + if (input_buffer != nullptr) { + libgav1::StatusCode status = + decoder.EnqueueFrame(input_buffer->data(), input_buffer->size(), + /*user_private_data=*/0, + /*buffer_private_data=*/input_buffer); + if (status == libgav1::kStatusOk) { + input_buffer = nullptr; + // Continue to enqueue frames until we get a kStatusTryAgain status. + continue; + } + if (status != libgav1::kStatusTryAgain) { + break; + } + } + + const libgav1::DecoderBuffer* buffer; + libgav1::StatusCode status = decoder.DequeueFrame(&buffer); + if (status == libgav1::kStatusNothingToDequeue) { + dequeue_finished = true; + } else if (status == libgav1::kStatusOk) { + dequeue_finished = false; + } else { + break; + } + } while (input_buffer != nullptr || !file_reader->IsEndOfFile() || + !dequeue_finished); + + if (input_buffer != nullptr) { + input_buffers.free_buffers.push_back(input_buffer); + } + + return 0; +} |