• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <cstddef>
16 #include <cstdint>
17 #include <deque>
18 #include <memory>
19 #include <new>
20 #include <vector>
21 
22 #include "examples/file_reader.h"
23 #include "examples/file_reader_constants.h"
24 #include "examples/file_reader_interface.h"
25 #include "src/gav1/decoder.h"
26 #include "src/gav1/status_code.h"
27 #include "tests/fuzzer/fuzzer_temp_file.h"
28 
29 namespace {
30 
31 #if defined(LIBGAV1_EXHAUSTIVE_FUZZING)
32 // Set a large upper bound to give more coverage of a single input; this value
33 // should be larger than most of the frame counts in the corpus.
34 constexpr size_t kMaxDataSize = 400 * 1024;
35 #else
36 constexpr size_t kMaxDataSize = 200 * 1024;
37 #endif
38 
39 using InputBuffer = std::vector<uint8_t>;
40 
41 struct InputBuffers {
~InputBuffers__anone81d57180111::InputBuffers42   ~InputBuffers() {
43     for (auto& buffer : free_buffers) {
44       delete buffer;
45     }
46   }
47   std::deque<InputBuffer*> free_buffers;
48 };
49 
ReleaseInputBuffer(void * callback_private_data,void * buffer_private_data)50 void ReleaseInputBuffer(void* callback_private_data,
51                         void* buffer_private_data) {
52   auto* const test = static_cast<InputBuffers*>(callback_private_data);
53   test->free_buffers.push_back(static_cast<InputBuffer*>(buffer_private_data));
54 }
55 
56 }  // namespace
57 
58 // Always returns 0. Nonzero return values are reserved by libFuzzer for future
59 // use.
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)60 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
61   // Reject large chunks of data to improve fuzzer throughput.
62   if (size > kMaxDataSize) return 0;
63 
64   // Note that |input_buffers| has to outlive the |decoder| object since the
65   // |release_input_buffer| callback could be called on the |decoder|'s
66   // destructor.
67   InputBuffers input_buffers;
68 
69   libgav1::Decoder decoder;
70   libgav1::DecoderSettings settings = {};
71   // Use the 33 + low byte of the width to seed the number of threads. This
72   // ensures that we will trigger the frame parallel path in most cases.
73   // We use both nibbles of the lower byte as this results in values != 1 much
74   // more quickly than using the lower nibble alone.
75   settings.threads =
76       33 + ((size >= 13) ? ((data[12] >> 4 | data[12]) & 0xF) + 1 : 1);
77 
78   settings.frame_parallel = true;
79   settings.blocking_dequeue = true;
80   settings.callback_private_data = &input_buffers;
81   settings.release_input_buffer = ReleaseInputBuffer;
82   if (decoder.Init(&settings) != libgav1::kStatusOk) return 0;
83 
84   FuzzerTemporaryFile tempfile(data, size);
85   auto file_reader =
86       libgav1::FileReader::Open(tempfile.filename(), /*error_tolerant=*/true);
87   if (file_reader == nullptr) return 0;
88 
89   InputBuffer* input_buffer = nullptr;
90   bool dequeue_finished = false;
91 
92   do {
93     if (input_buffer == nullptr && !file_reader->IsEndOfFile()) {
94       if (input_buffers.free_buffers.empty()) {
95         auto* const buffer = new (std::nothrow) InputBuffer();
96         if (buffer == nullptr) {
97           break;
98         }
99         input_buffers.free_buffers.push_back(buffer);
100       }
101       input_buffer = input_buffers.free_buffers.front();
102       input_buffers.free_buffers.pop_front();
103       if (!file_reader->ReadTemporalUnit(input_buffer, nullptr)) {
104         break;
105       }
106     }
107 
108     if (input_buffer != nullptr) {
109       libgav1::StatusCode status =
110           decoder.EnqueueFrame(input_buffer->data(), input_buffer->size(),
111                                /*user_private_data=*/0,
112                                /*buffer_private_data=*/input_buffer);
113       if (status == libgav1::kStatusOk) {
114         input_buffer = nullptr;
115         // Continue to enqueue frames until we get a kStatusTryAgain status.
116         continue;
117       }
118       if (status != libgav1::kStatusTryAgain) {
119         break;
120       }
121     }
122 
123     const libgav1::DecoderBuffer* buffer;
124     libgav1::StatusCode status = decoder.DequeueFrame(&buffer);
125     if (status == libgav1::kStatusNothingToDequeue) {
126       dequeue_finished = true;
127     } else if (status == libgav1::kStatusOk) {
128       dequeue_finished = false;
129     } else {
130       break;
131     }
132   } while (input_buffer != nullptr || !file_reader->IsEndOfFile() ||
133            !dequeue_finished);
134 
135   if (input_buffer != nullptr) {
136     input_buffers.free_buffers.push_back(input_buffer);
137   }
138 
139   return 0;
140 }
141