Coverage Report

Created: 2025-11-11 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libavif/tests/gtest/avif_fuzztest_dec_incr.cc
Line
Count
Source
1
// Copyright 2022 Google LLC
2
// SPDX-License-Identifier: BSD-2-Clause
3
// Compare non-incremental and incremental decode results of an arbitrary byte
4
// sequence.
5
6
#include <algorithm>
7
#include <cstdint>
8
#include <string>
9
10
#include "avif/avif.h"
11
#include "avif_fuzztest_helpers.h"
12
#include "avifincrtest_helpers.h"
13
#include "fuzztest/fuzztest.h"
14
#include "gtest/gtest.h"
15
16
using ::fuzztest::Arbitrary;
17
18
namespace avif {
19
namespace testutil {
20
namespace {
21
22
//------------------------------------------------------------------------------
23
24
struct DecoderInput {
25
  const uint8_t* available_bytes;
26
  size_t available_size;
27
  size_t read_size;
28
};
29
30
// A custom reader is necessary to get the number of bytes read by libavif.
31
// See avifIOReadFunc() documentation.
32
avifResult AvifIoRead(struct avifIO* io, uint32_t read_flags, uint64_t offset,
33
86.3k
                      size_t size, avifROData* out) {
34
86.3k
  DecoderInput* data = reinterpret_cast<DecoderInput*>(io->data);
35
86.3k
  if (read_flags != 0 || !data || data->available_size < offset) {
36
0
    return AVIF_RESULT_IO_ERROR;
37
0
  }
38
86.3k
  out->data = data->available_bytes + offset;
39
86.3k
  out->size =
40
86.3k
      std::min(size, data->available_size - static_cast<size_t>(offset));
41
86.3k
  data->read_size =
42
86.3k
      std::max(data->read_size, static_cast<size_t>(offset) + out->size);
43
86.3k
  return AVIF_RESULT_OK;
44
86.3k
}
45
46
//------------------------------------------------------------------------------
47
48
void DecodeIncr(const std::string& arbitrary_bytes, bool is_persistent,
49
12.2k
                bool give_size_hint, bool use_nth_image_api) {
50
12.2k
  ASSERT_FALSE(GetSeedDataDirs().empty());  // Make sure seeds are available.
51
52
12.2k
  ImagePtr reference(avifImageCreateEmpty());
53
12.2k
  ASSERT_NE(reference.get(), nullptr);
54
55
12.2k
  DecoderInput data = {reinterpret_cast<const uint8_t*>(arbitrary_bytes.data()),
56
12.2k
                       arbitrary_bytes.size(), 0};
57
12.2k
  avifIO io = {.destroy = nullptr,
58
12.2k
               .read = AvifIoRead,
59
12.2k
               .write = nullptr,
60
12.2k
               .sizeHint = arbitrary_bytes.size(),
61
12.2k
               .persistent = AVIF_TRUE,
62
12.2k
               .data = &data};
63
64
12.2k
  DecoderPtr decoder(avifDecoderCreate());
65
12.2k
  ASSERT_NE(decoder.get(), nullptr);
66
12.2k
  avifDecoderSetIO(decoder.get(), &io);
67
  // OSS-Fuzz limits the allocated memory to 2560 MB.
68
12.2k
  constexpr uint32_t kMaxMem = 2560u * 1024 * 1024;
69
  // Consider at most four planes of 16-bit samples.
70
12.2k
  constexpr uint32_t kMaxImageSize =
71
12.2k
      kMaxMem / (AVIF_PLANE_COUNT_YUV + 1) / sizeof(uint16_t);
72
  // Reduce the limit further to include pixel buffer copies and other memory
73
  // allocations.
74
12.2k
  constexpr uint32_t kImageSizeLimit = kMaxImageSize / 4;
75
  // avifDecoderParse returns AVIF_RESULT_NOT_IMPLEMENTED if kImageSizeLimit is
76
  // bigger than AVIF_DEFAULT_IMAGE_SIZE_LIMIT.
77
12.2k
  static_assert(kImageSizeLimit <= AVIF_DEFAULT_IMAGE_SIZE_LIMIT,
78
12.2k
                "Too big an image size limit");
79
12.2k
  decoder->imageSizeLimit = kImageSizeLimit;
80
81
12.2k
  if (avifDecoderRead(decoder.get(), reference.get()) == AVIF_RESULT_OK) {
82
    // Avoid timeouts by discarding big images decoded many times.
83
    // TODO(yguyon): Increase this arbitrary threshold but decode incrementally
84
    //               fewer times than as many bytes.
85
3.71k
    if (reference->width * reference->height * data.read_size >
86
3.71k
        8 * 1024 * 1024) {
87
639
      return;
88
639
    }
89
    // decodeIncrementally() will fail if there are leftover bytes.
90
3.07k
    const avifRWData encoded_data = {const_cast<uint8_t*>(data.available_bytes),
91
3.07k
                                     data.read_size};
92
    // No clue on whether encoded_data is tiled so use a lower bound of a single
93
    // tile for the whole image.
94
    // Note that an AVIF tile is at most as high as an AV1 frame
95
    // (aomediacodec.github.io/av1-spec says max_frame_height_minus_1 < 65536)
96
    // but libavif successfully decodes AVIF files with dimensions unrelated to
97
    // the underlying AV1 frame (for example a 1x1000000 AVIF for a 1x1 AV1).
98
    // Otherwise we could use the minimum of reference->height and 65536u below.
99
3.07k
    const uint32_t max_cell_height = reference->height;
100
3.07k
    const avifResult result = DecodeIncrementally(
101
3.07k
        encoded_data, decoder.get(), is_persistent, give_size_hint,
102
3.07k
        use_nth_image_api, *reference, max_cell_height,
103
3.07k
        /*enable_fine_incremental_check=*/false,
104
3.07k
        /*expect_whole_file_read=*/true,
105
3.07k
        /*expect_parse_success_from_partial_file=*/false);
106
    // The result does not matter, as long as we do not crash.
107
3.07k
    (void)result;
108
3.07k
  }
109
12.2k
}
110
111
FUZZ_TEST(DecodeAvifFuzzTest, DecodeIncr)
112
    .WithDomains(ArbitraryImageWithSeeds({AVIF_APP_FILE_FORMAT_AVIF}),
113
                 Arbitrary<bool>(), Arbitrary<bool>(), Arbitrary<bool>());
114
115
//------------------------------------------------------------------------------
116
117
}  // namespace
118
}  // namespace testutil
119
}  // namespace avif