Coverage Report

Created: 2026-08-13 06:43

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
using ::fuzztest::BitFlagCombinationOf;
18
19
namespace avif {
20
namespace testutil {
21
namespace {
22
23
//------------------------------------------------------------------------------
24
25
struct DecoderInput {
26
  const uint8_t* available_bytes;
27
  size_t available_size;
28
  size_t read_size;
29
};
30
31
// A custom reader is necessary to get the number of bytes read by libavif.
32
// See avifIOReadFunc() documentation.
33
avifResult AvifIoRead(struct avifIO* io, uint32_t read_flags, uint64_t offset,
34
92.1k
                      size_t size, avifROData* out) {
35
92.1k
  DecoderInput* data = reinterpret_cast<DecoderInput*>(io->data);
36
92.1k
  if (read_flags != 0 || !data || data->available_size < offset) {
37
0
    return AVIF_RESULT_IO_ERROR;
38
0
  }
39
92.1k
  out->data = data->available_bytes + offset;
40
92.1k
  out->size =
41
92.1k
      std::min(size, data->available_size - static_cast<size_t>(offset));
42
92.1k
  data->read_size =
43
92.1k
      std::max(data->read_size, static_cast<size_t>(offset) + out->size);
44
92.1k
  return AVIF_RESULT_OK;
45
92.1k
}
46
47
//------------------------------------------------------------------------------
48
49
void DecodeIncr(const std::string& arbitrary_bytes, bool is_persistent,
50
                bool give_size_hint,
51
                avifImageContentTypeFlags content_to_decode,
52
13.3k
                bool use_nth_image_api) {
53
13.3k
  ASSERT_FALSE(GetSeedDataDirs().empty());  // Make sure seeds are available.
54
55
13.3k
  ImagePtr reference(avifImageCreateEmpty());
56
13.3k
  ASSERT_NE(reference.get(), nullptr);
57
58
13.3k
  DecoderInput data = {reinterpret_cast<const uint8_t*>(arbitrary_bytes.data()),
59
13.3k
                       arbitrary_bytes.size(), 0};
60
13.3k
  avifIO io = {.destroy = nullptr,
61
13.3k
               .read = AvifIoRead,
62
13.3k
               .write = nullptr,
63
13.3k
               .sizeHint = arbitrary_bytes.size(),
64
13.3k
               .persistent = AVIF_TRUE,
65
13.3k
               .data = &data};
66
67
13.3k
  DecoderPtr decoder(avifDecoderCreate());
68
13.3k
  ASSERT_NE(decoder.get(), nullptr);
69
13.3k
  avifDecoderSetIO(decoder.get(), &io);
70
  // OSS-Fuzz limits the allocated memory to 2560 MB.
71
13.3k
  constexpr uint32_t kMaxMem = 2560u * 1024 * 1024;
72
  // Consider at most four planes of 16-bit samples.
73
13.3k
  constexpr uint32_t kMaxImageSize =
74
13.3k
      kMaxMem / (AVIF_PLANE_COUNT_YUV + 1) / sizeof(uint16_t);
75
  // Reduce the limit further to include pixel buffer copies and other memory
76
  // allocations.
77
13.3k
  constexpr uint32_t kImageSizeLimit = kMaxImageSize / 4;
78
13.3k
  decoder->imageSizeLimit = kImageSizeLimit;
79
  // This can lead to AVIF_RESULT_NO_CONTENT.
80
13.3k
  decoder->imageContentToDecode = content_to_decode;
81
82
13.3k
  avifResult result = avifDecoderRead(decoder.get(), reference.get());
83
  // AVIF_RESULT_INTERNAL_ERROR means a broken invariant and should not happen.
84
13.3k
  ASSERT_NE(result, AVIF_RESULT_INTERNAL_ERROR);
85
13.3k
  if (result != AVIF_RESULT_OK) return;
86
87
  // Avoid timeouts by discarding big images decoded many times.
88
3.90k
  if (reference->width * reference->height * data.read_size > 8 * 1024 * 1024) {
89
715
    return;
90
715
  }
91
  // decodeIncrementally() will fail if there are leftover bytes.
92
3.18k
  const avifRWData encoded_data = {const_cast<uint8_t*>(data.available_bytes),
93
3.18k
                                   data.read_size};
94
  // No clue on whether encoded_data is tiled so use a lower bound of a single
95
  // tile for the whole image.
96
  // Note that an AVIF tile is at most as high as an AV1 frame
97
  // (aomediacodec.github.io/av1-spec says max_frame_height_minus_1 < 65536)
98
  // but libavif successfully decodes AVIF files with dimensions unrelated to
99
  // the underlying AV1 frame (for example a 1x1000000 AVIF for a 1x1 AV1).
100
  // Otherwise we could use the minimum of reference->height and 65536u below.
101
3.18k
  const uint32_t max_cell_height = reference->height;
102
3.18k
  result = DecodeIncrementally(
103
3.18k
      encoded_data, decoder.get(), is_persistent, give_size_hint,
104
3.18k
      use_nth_image_api, *reference, max_cell_height,
105
3.18k
      /*enable_fine_incremental_check=*/false, /*expect_whole_file_read=*/true,
106
3.18k
      /*expect_parse_success_from_partial_file=*/false);
107
  ASSERT_NE(result, AVIF_RESULT_INTERNAL_ERROR);
108
3.18k
}
109
110
FUZZ_TEST(DecodeAvifFuzzTest, DecodeIncr)
111
    .WithDomains(ArbitraryImageWithSeeds({AVIF_APP_FILE_FORMAT_AVIF}),
112
                 /*is_persistent=*/Arbitrary<bool>(),
113
                 /*give_size_hint=*/Arbitrary<bool>(),
114
                 BitFlagCombinationOf<avifImageContentTypeFlags>(
115
                     {AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA,
116
                      AVIF_IMAGE_CONTENT_GAIN_MAP,
117
                      AVIF_IMAGE_CONTENT_SAMPLE_TRANSFORMS}),
118
                 /*use_nth_image_api=*/Arbitrary<bool>());
119
120
//------------------------------------------------------------------------------
121
122
}  // namespace
123
}  // namespace testutil
124
}  // namespace avif