Coverage Report

Created: 2026-02-14 07:08

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
56.5k
         WebPMemoryWriter& memory_writer) {
37
  // Crop and scale.
38
56.5k
  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
56.5k
  if (pic.width * pic.height > 32 * 32) {
48
10.5k
    if (config.lossless) {
49
4.49k
      if (config.quality > 99.0f && config.method >= 5) {
50
178
        config.quality = 99.0f;
51
178
        config.method = 5;
52
178
      }
53
6.09k
    } else {
54
6.09k
      if (config.quality > 99.0f && config.method == 6) {
55
122
        config.quality = 99.0f;
56
122
      }
57
6.09k
    }
58
10.5k
    if (config.alpha_quality == 100 && config.method == 6) {
59
114
      config.alpha_quality = 99;
60
114
    }
61
10.5k
  }
62
63
  // Encode.
64
56.5k
  pic.writer = WebPMemoryWrite;
65
56.5k
  pic.custom_ptr = &memory_writer;
66
56.5k
  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
56.5k
}
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
27.9k
                     const fuzz_utils::WebPDecoderOptionsCpp& decoder_options) {
82
27.9k
  fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index);
83
84
  // Init the source picture.
85
27.9k
  WebPPicture& pic = pic_cpp.ref();
86
87
27.9k
  WebPMemoryWriter memory_writer;
88
27.9k
  WebPMemoryWriterInit(&memory_writer);
89
27.9k
  std::unique_ptr<WebPMemoryWriter, fuzz_utils::UniquePtrDeleter>
90
27.9k
      memory_writer_owner(&memory_writer);
91
92
27.9k
  Enc(crop_or_scale_params, config, pic, memory_writer);
93
94
  // Try decoding the result.
95
27.9k
  const uint8_t* const out_data = memory_writer.mem;
96
27.9k
  const size_t out_size = memory_writer.size;
97
27.9k
  WebPDecoderConfig dec_config;
98
27.9k
  std::unique_ptr<WebPDecoderConfig, fuzz_utils::UniquePtrDeleter>
99
27.9k
      dec_config_owner(&dec_config);
100
27.9k
  if (!WebPInitDecoderConfig(&dec_config)) {
101
0
    fprintf(stderr, "WebPInitDecoderConfig failed.\n");
102
0
    std::abort();
103
0
  }
104
105
27.9k
  dec_config.output.colorspace = MODE_BGRA;
106
27.9k
  VP8StatusCode status = WebPDecode(out_data, out_size, &dec_config);
107
27.9k
  if ((status != VP8_STATUS_OK && status != VP8_STATUS_OUT_OF_MEMORY &&
108
0
       status != VP8_STATUS_USER_ABORT) ||
109
27.9k
      (status == VP8_STATUS_OK && (dec_config.output.width != pic.width ||
110
27.9k
                                   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
27.9k
  if (status == VP8_STATUS_OK && pic.use_argb && config.lossless &&
117
9.41k
      config.near_lossless == 100) {
118
1.46k
    const uint8_t* const rgba = dec_config.output.u.RGBA.rgba;
119
1.46k
    const int w = dec_config.output.width;
120
1.46k
    const int h = dec_config.output.height;
121
122
1.46k
    const uint32_t* src1 = (const uint32_t*)rgba;
123
1.46k
    const uint32_t* src2 = pic.argb;
124
49.0k
    for (int y = 0; y < h; ++y, src1 += w, src2 += pic.argb_stride) {
125
2.53M
      for (int x = 0; x < w; ++x) {
126
2.48M
        uint32_t v1 = src1[x], v2 = src2[x];
127
2.48M
        if (!config.exact) {
128
1.30M
          if ((v1 & 0xff000000u) == 0 || (v2 & 0xff000000u) == 0) {
129
            // Only keep alpha for comparison of fully transparent area.
130
2.09k
            v1 &= 0xff000000u;
131
2.09k
            v2 &= 0xff000000u;
132
2.09k
          }
133
1.30M
        }
134
2.48M
        if (v1 != v2) {
135
0
          fprintf(stderr, "Lossless compression failed pixel-exactness.\n");
136
0
          std::abort();
137
0
        }
138
2.48M
      }
139
47.5k
    }
140
1.46k
  }
141
142
  // Use given decoding options.
143
27.9k
  if (static_cast<int64_t>(decoder_options.crop_left) +
144
27.9k
              decoder_options.crop_width >
145
27.9k
          static_cast<int64_t>(pic.width) ||
146
22.1k
      static_cast<int64_t>(decoder_options.crop_top) +
147
22.1k
              decoder_options.crop_height >
148
22.1k
          static_cast<int64_t>(pic.height)) {
149
8.31k
    return;
150
8.31k
  }
151
19.6k
  WebPFreeDecBuffer(&dec_config.output);
152
19.6k
  if (!WebPInitDecoderConfig(&dec_config)) {
153
0
    fprintf(stderr, "WebPInitDecoderConfig failed.\n");
154
0
    abort();
155
0
  }
156
157
19.6k
  dec_config.output.colorspace = static_cast<WEBP_CSP_MODE>(colorspace);
158
19.6k
  std::memcpy(&dec_config.options, &decoder_options, sizeof(decoder_options));
159
19.6k
  status = WebPDecode(out_data, out_size, &dec_config);
160
19.6k
  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
19.6k
}
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
28.5k
                const fuzz_utils::WebPDecoderOptionsCpp& decoder_options) {
174
28.5k
  fuzz_utils::SetOptimization(default_VP8GetCPUInfo, optimization_index);
175
176
  // Init the source picture.
177
28.5k
  WebPPicture& pic = pic_cpp.ref();
178
28.5k
  WebPMemoryWriter memory_writer;
179
28.5k
  WebPMemoryWriterInit(&memory_writer);
180
28.5k
  std::unique_ptr<WebPMemoryWriter, fuzz_utils::UniquePtrDeleter>
181
28.5k
      memory_writer_owner(&memory_writer);
182
183
28.5k
  Enc(crop_or_scale_params, config, pic, memory_writer);
184
185
  // Try decoding the result.
186
28.5k
  const uint8_t* const out_data = memory_writer.mem;
187
28.5k
  const size_t out_size = memory_writer.size;
188
28.5k
  WebPDecoderConfig dec_config;
189
28.5k
  std::unique_ptr<WebPDecoderConfig, fuzz_utils::UniquePtrDeleter>
190
28.5k
      dec_config_owner(&dec_config);
191
28.5k
  if (!WebPInitDecoderConfig(&dec_config)) {
192
0
    fprintf(stderr, "WebPInitDecoderConfig failed.\n");
193
0
    abort();
194
0
  }
195
28.5k
  if (decoder_options.use_scaling) {
196
16.7k
    int scaled_width = decoder_options.scaled_width;
197
16.7k
    int scaled_height = decoder_options.scaled_height;
198
16.7k
    if (!WebPRescalerGetScaledDimensions(pic.width, pic.height, &scaled_width,
199
16.7k
                                         &scaled_height)) {
200
      // Rescaled dimensions do not make sense.
201
10.0k
      return;
202
10.0k
    }
203
6.73k
    if (static_cast<uint64_t>(scaled_width) * scaled_height > 1000u * 1000u) {
204
      // Skip huge scaling.
205
928
      return;
206
928
    }
207
6.73k
  }
208
209
17.6k
  dec_config.output.colorspace = static_cast<WEBP_CSP_MODE>(colorspace);
210
17.6k
  std::memcpy(&dec_config.options, &decoder_options, sizeof(decoder_options));
211
17.6k
  const VP8StatusCode status = WebPDecode(out_data, out_size, &dec_config);
212
17.6k
  if (status != VP8_STATUS_OK && status != VP8_STATUS_OUT_OF_MEMORY &&
213
1.41k
      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
17.6k
}
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());