Coverage Report

Created: 2026-01-17 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebp/tests/fuzzer/enc_dec_fuzzer.cc
Line
Count
Source
1
// Copyright 2018 Google Inc.
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
////////////////////////////////////////////////////////////////////////////////
16
17
#include <cstddef>
18
#include <cstdint>
19
#include <cstdio>
20
#include <cstdlib>
21
#include <cstring>
22
#include <memory>
23
24
#include "./fuzz_utils.h"
25
#include "src/dsp/cpu.h"
26
#include "src/utils/rescaler_utils.h"
27
#include "webp/decode.h"
28
#include "webp/encode.h"
29
30
namespace {
31
32
const VP8CPUInfo default_VP8GetCPUInfo = fuzz_utils::VP8GetCPUInfo;
33
34
void Enc(const fuzz_utils::CropOrScaleParams& crop_or_scale_params,
35
         WebPConfig& config, WebPPicture& pic,
36
53.7k
         WebPMemoryWriter& memory_writer) {
37
  // Crop and scale.
38
53.7k
  if (!fuzz_utils::CropOrScale(&pic, crop_or_scale_params)) {
39
0
    const WebPEncodingError error_code = pic.error_code;
40
0
    if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return;
41
0
    fprintf(stderr, "ExtractAndCropOrScale failed. Error code: %d\n",
42
0
            error_code);
43
0
    std::abort();
44
0
  }
45
46
  // Skip slow settings on big images, it's likely to timeout.
47
53.7k
  if (pic.width * pic.height > 32 * 32) {
48
9.94k
    if (config.lossless) {
49
4.25k
      if (config.quality > 99.0f && config.method >= 5) {
50
161
        config.quality = 99.0f;
51
161
        config.method = 5;
52
161
      }
53
5.68k
    } else {
54
5.68k
      if (config.quality > 99.0f && config.method == 6) {
55
106
        config.quality = 99.0f;
56
106
      }
57
5.68k
    }
58
9.94k
    if (config.alpha_quality == 100 && config.method == 6) {
59
97
      config.alpha_quality = 99;
60
97
    }
61
9.94k
  }
62
63
  // Encode.
64
53.7k
  pic.writer = WebPMemoryWrite;
65
53.7k
  pic.custom_ptr = &memory_writer;
66
53.7k
  if (!WebPEncode(&config, &pic)) {
67
0
    const WebPEncodingError error_code = pic.error_code;
68
0
    if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY ||
69
0
        error_code == VP8_ENC_ERROR_BAD_WRITE) {
70
0
      return;
71
0
    }
72
0
    fprintf(stderr, "WebPEncode failed. Error code: %d\n", error_code);
73
0
    std::abort();
74
0
  }
75
53.7k
}
76
77
void EncDecValidTest(bool use_argb, fuzz_utils::WebPPictureCpp pic_cpp,
78
                     WebPConfig config, int optimization_index,
79
                     const fuzz_utils::CropOrScaleParams& crop_or_scale_params,
80
                     int colorspace,
81
26.0k
                     const fuzz_utils::WebPDecoderOptionsCpp& decoder_options) {
82
26.0k
  fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index);
83
84
  // Init the source picture.
85
26.0k
  WebPPicture& pic = pic_cpp.ref();
86
87
26.0k
  WebPMemoryWriter memory_writer;
88
26.0k
  WebPMemoryWriterInit(&memory_writer);
89
26.0k
  std::unique_ptr<WebPMemoryWriter, fuzz_utils::UniquePtrDeleter>
90
26.0k
      memory_writer_owner(&memory_writer);
91
92
26.0k
  Enc(crop_or_scale_params, config, pic, memory_writer);
93
94
  // Try decoding the result.
95
26.0k
  const uint8_t* const out_data = memory_writer.mem;
96
26.0k
  const size_t out_size = memory_writer.size;
97
26.0k
  WebPDecoderConfig dec_config;
98
26.0k
  std::unique_ptr<WebPDecoderConfig, fuzz_utils::UniquePtrDeleter>
99
26.0k
      dec_config_owner(&dec_config);
100
26.0k
  if (!WebPInitDecoderConfig(&dec_config)) {
101
0
    fprintf(stderr, "WebPInitDecoderConfig failed.\n");
102
0
    std::abort();
103
0
  }
104
105
26.0k
  dec_config.output.colorspace = MODE_BGRA;
106
26.0k
  VP8StatusCode status = WebPDecode(out_data, out_size, &dec_config);
107
26.0k
  if ((status != VP8_STATUS_OK && status != VP8_STATUS_OUT_OF_MEMORY &&
108
0
       status != VP8_STATUS_USER_ABORT) ||
109
26.0k
      (status == VP8_STATUS_OK && (dec_config.output.width != pic.width ||
110
26.0k
                                   dec_config.output.height != pic.height))) {
111
0
    fprintf(stderr, "WebPDecode failed. status: %d.\n", status);
112
0
    std::abort();
113
0
  }
114
115
  // Compare the results if exact encoding.
116
26.0k
  if (status == VP8_STATUS_OK && pic.use_argb && config.lossless &&
117
8.83k
      config.near_lossless == 100) {
118
1.31k
    const uint8_t* const rgba = dec_config.output.u.RGBA.rgba;
119
1.31k
    const int w = dec_config.output.width;
120
1.31k
    const int h = dec_config.output.height;
121
122
1.31k
    const uint32_t* src1 = (const uint32_t*)rgba;
123
1.31k
    const uint32_t* src2 = pic.argb;
124
47.2k
    for (int y = 0; y < h; ++y, src1 += w, src2 += pic.argb_stride) {
125
2.36M
      for (int x = 0; x < w; ++x) {
126
2.32M
        uint32_t v1 = src1[x], v2 = src2[x];
127
2.32M
        if (!config.exact) {
128
1.02M
          if ((v1 & 0xff000000u) == 0 || (v2 & 0xff000000u) == 0) {
129
            // Only keep alpha for comparison of fully transparent area.
130
4.72k
            v1 &= 0xff000000u;
131
4.72k
            v2 &= 0xff000000u;
132
4.72k
          }
133
1.02M
        }
134
2.32M
        if (v1 != v2) {
135
0
          fprintf(stderr, "Lossless compression failed pixel-exactness.\n");
136
0
          std::abort();
137
0
        }
138
2.32M
      }
139
45.9k
    }
140
1.31k
  }
141
142
  // Use given decoding options.
143
26.0k
  if (static_cast<int64_t>(decoder_options.crop_left) +
144
26.0k
              decoder_options.crop_width >
145
26.0k
          static_cast<int64_t>(pic.width) ||
146
20.3k
      static_cast<int64_t>(decoder_options.crop_top) +
147
20.3k
              decoder_options.crop_height >
148
20.3k
          static_cast<int64_t>(pic.height)) {
149
8.19k
    return;
150
8.19k
  }
151
17.8k
  WebPFreeDecBuffer(&dec_config.output);
152
17.8k
  if (!WebPInitDecoderConfig(&dec_config)) {
153
0
    fprintf(stderr, "WebPInitDecoderConfig failed.\n");
154
0
    abort();
155
0
  }
156
157
17.8k
  dec_config.output.colorspace = static_cast<WEBP_CSP_MODE>(colorspace);
158
17.8k
  std::memcpy(&dec_config.options, &decoder_options, sizeof(decoder_options));
159
17.8k
  status = WebPDecode(out_data, out_size, &dec_config);
160
17.8k
  if (status != VP8_STATUS_OK && status != VP8_STATUS_OUT_OF_MEMORY &&
161
0
      status != VP8_STATUS_USER_ABORT) {
162
0
    fprintf(stderr, "WebPDecode failed. status: %d.\n", status);
163
0
    abort();
164
0
  }
165
17.8k
}
166
167
////////////////////////////////////////////////////////////////////////////////
168
169
void EncDecTest(bool use_argb, fuzz_utils::WebPPictureCpp pic_cpp,
170
                WebPConfig config, int optimization_index,
171
                const fuzz_utils::CropOrScaleParams& crop_or_scale_params,
172
                int colorspace,
173
27.7k
                const fuzz_utils::WebPDecoderOptionsCpp& decoder_options) {
174
27.7k
  fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index);
175
176
  // Init the source picture.
177
27.7k
  WebPPicture& pic = pic_cpp.ref();
178
27.7k
  WebPMemoryWriter memory_writer;
179
27.7k
  WebPMemoryWriterInit(&memory_writer);
180
27.7k
  std::unique_ptr<WebPMemoryWriter, fuzz_utils::UniquePtrDeleter>
181
27.7k
      memory_writer_owner(&memory_writer);
182
183
27.7k
  Enc(crop_or_scale_params, config, pic, memory_writer);
184
185
  // Try decoding the result.
186
27.7k
  const uint8_t* const out_data = memory_writer.mem;
187
27.7k
  const size_t out_size = memory_writer.size;
188
27.7k
  WebPDecoderConfig dec_config;
189
27.7k
  std::unique_ptr<WebPDecoderConfig, fuzz_utils::UniquePtrDeleter>
190
27.7k
      dec_config_owner(&dec_config);
191
27.7k
  if (!WebPInitDecoderConfig(&dec_config)) {
192
0
    fprintf(stderr, "WebPInitDecoderConfig failed.\n");
193
0
    abort();
194
0
  }
195
27.7k
  if (decoder_options.use_scaling) {
196
16.3k
    int scaled_width = decoder_options.scaled_width;
197
16.3k
    int scaled_height = decoder_options.scaled_height;
198
16.3k
    if (!WebPRescalerGetScaledDimensions(pic.width, pic.height, &scaled_width,
199
16.3k
                                         &scaled_height)) {
200
      // Rescaled dimensions do not make sense.
201
9.90k
      return;
202
9.90k
    }
203
6.46k
    if (static_cast<uint64_t>(scaled_width) * scaled_height > 1000u * 1000u) {
204
      // Skip huge scaling.
205
929
      return;
206
929
    }
207
6.46k
  }
208
209
16.8k
  dec_config.output.colorspace = static_cast<WEBP_CSP_MODE>(colorspace);
210
16.8k
  std::memcpy(&dec_config.options, &decoder_options, sizeof(decoder_options));
211
16.8k
  const VP8StatusCode status = WebPDecode(out_data, out_size, &dec_config);
212
16.8k
  if (status != VP8_STATUS_OK && status != VP8_STATUS_OUT_OF_MEMORY &&
213
1.35k
      status != VP8_STATUS_USER_ABORT && status != VP8_STATUS_INVALID_PARAM) {
214
    fprintf(stderr, "WebPDecode failed. status: %d.\n", status);
215
0
    abort();
216
0
  }
217
16.8k
}
218
219
}  // namespace
220
221
FUZZ_TEST(EncIndexDec, EncDecValidTest)
222
    .WithDomains(/*use_argb=*/fuzztest::Arbitrary<bool>(),
223
                 fuzz_utils::ArbitraryWebPPictureFromIndex(),
224
                 fuzz_utils::ArbitraryWebPConfig(),
225
                 /*optimization_index=*/
226
                 fuzztest::InRange<uint32_t>(0,
227
                                             fuzz_utils::kMaxOptimizationIndex),
228
                 fuzz_utils::ArbitraryCropOrScaleParams(),
229
                 /*colorspace=*/fuzztest::InRange<int>(0, MODE_LAST - 1),
230
                 fuzz_utils::ArbitraryValidWebPDecoderOptions());
231
232
FUZZ_TEST(EncArbitraryDec, EncDecValidTest)
233
    .WithDomains(/*use_argb=*/fuzztest::Arbitrary<bool>(),
234
                 fuzz_utils::ArbitraryWebPPicture(),
235
                 fuzz_utils::ArbitraryWebPConfig(),
236
                 /*optimization_index=*/
237
                 fuzztest::InRange<uint32_t>(0,
238
                                             fuzz_utils::kMaxOptimizationIndex),
239
                 fuzz_utils::ArbitraryCropOrScaleParams(),
240
                 /*colorspace=*/fuzztest::InRange<int>(0, MODE_LAST - 1),
241
                 fuzz_utils::ArbitraryValidWebPDecoderOptions());
242
243
FUZZ_TEST(EncIndexDec, EncDecTest)
244
    .WithDomains(/*use_argb=*/fuzztest::Arbitrary<bool>(),
245
                 fuzz_utils::ArbitraryWebPPictureFromIndex(),
246
                 fuzz_utils::ArbitraryWebPConfig(),
247
                 /*optimization_index=*/
248
                 fuzztest::InRange<uint32_t>(0,
249
                                             fuzz_utils::kMaxOptimizationIndex),
250
                 fuzz_utils::ArbitraryCropOrScaleParams(),
251
                 /*colorspace=*/fuzztest::Arbitrary<int>(),
252
                 fuzz_utils::ArbitraryWebPDecoderOptions());
253
254
FUZZ_TEST(EncArbitraryDec, EncDecTest)
255
    .WithDomains(/*use_argb=*/fuzztest::Arbitrary<bool>(),
256
                 fuzz_utils::ArbitraryWebPPicture(),
257
                 fuzz_utils::ArbitraryWebPConfig(),
258
                 /*optimization_index=*/
259
                 fuzztest::InRange<uint32_t>(0,
260
                                             fuzz_utils::kMaxOptimizationIndex),
261
                 fuzz_utils::ArbitraryCropOrScaleParams(),
262
                 /*colorspace=*/fuzztest::Arbitrary<int>(),
263
                 fuzz_utils::ArbitraryWebPDecoderOptions());