Coverage Report

Created: 2023-09-25 06:28

/src/libwebp/tests/fuzzer/animencoder_fuzzer.cc
Line
Count
Source (jump to first uncovered line)
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 <stdio.h>
18
#include <stdlib.h>
19
20
#include "./fuzz_utils.h"
21
#include "src/webp/encode.h"
22
#include "src/webp/mux.h"
23
24
namespace {
25
26
const VP8CPUInfo default_VP8GetCPUInfo = VP8GetCPUInfo;
27
28
int AddFrame(WebPAnimEncoder** const enc,
29
             const WebPAnimEncoderOptions& anim_config, int* const width,
30
             int* const height, int timestamp_ms, const uint8_t data[],
31
47.3k
             size_t size, uint32_t* const bit_pos) {
32
47.3k
  if (enc == nullptr || width == nullptr || height == nullptr) {
33
0
    fprintf(stderr, "NULL parameters.\n");
34
0
    if (enc != nullptr) WebPAnimEncoderDelete(*enc);
35
0
    abort();
36
0
  }
37
38
  // Init the source picture.
39
47.3k
  WebPPicture pic;
40
47.3k
  if (!WebPPictureInit(&pic)) {
41
0
    fprintf(stderr, "WebPPictureInit failed.\n");
42
0
    WebPAnimEncoderDelete(*enc);
43
0
    abort();
44
0
  }
45
47.3k
  pic.use_argb = Extract(1, data, size, bit_pos);
46
47
  // Read the source picture.
48
47.3k
  if (!ExtractSourcePicture(&pic, data, size, bit_pos)) {
49
0
    const WebPEncodingError error_code = pic.error_code;
50
0
    WebPAnimEncoderDelete(*enc);
51
0
    WebPPictureFree(&pic);
52
0
    if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
53
0
    fprintf(stderr, "Can't read input image. Error code: %d\n", error_code);
54
0
    abort();
55
0
  }
56
57
  // Crop and scale.
58
47.3k
  if (*enc == nullptr) {  // First frame will set canvas width and height.
59
4.73k
    if (!ExtractAndCropOrScale(&pic, data, size, bit_pos)) {
60
0
      const WebPEncodingError error_code = pic.error_code;
61
0
      WebPPictureFree(&pic);
62
0
      if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
63
0
      fprintf(stderr, "ExtractAndCropOrScale failed. Error code: %d\n",
64
0
              error_code);
65
0
      abort();
66
0
    }
67
42.6k
  } else {  // Other frames will be resized to the first frame's dimensions.
68
42.6k
    if (!WebPPictureRescale(&pic, *width, *height)) {
69
0
      const WebPEncodingError error_code = pic.error_code;
70
0
      WebPAnimEncoderDelete(*enc);
71
0
      WebPPictureFree(&pic);
72
0
      if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
73
0
      fprintf(stderr,
74
0
              "WebPPictureRescale failed. Size: %d,%d. Error code: %d\n",
75
0
              *width, *height, error_code);
76
0
      abort();
77
0
    }
78
42.6k
  }
79
80
  // Create encoder if it doesn't exist.
81
47.3k
  if (*enc == nullptr) {
82
4.73k
    *width = pic.width;
83
4.73k
    *height = pic.height;
84
4.73k
    *enc = WebPAnimEncoderNew(*width, *height, &anim_config);
85
4.73k
    if (*enc == nullptr) {
86
0
      WebPPictureFree(&pic);
87
0
      return 0;
88
0
    }
89
4.73k
  }
90
91
  // Create frame encoding config.
92
47.3k
  WebPConfig config;
93
47.3k
  if (!ExtractWebPConfig(&config, data, size, bit_pos)) {
94
0
    fprintf(stderr, "ExtractWebPConfig failed.\n");
95
0
    WebPAnimEncoderDelete(*enc);
96
0
    WebPPictureFree(&pic);
97
0
    abort();
98
0
  }
99
  // Skip slow settings on big images, it's likely to timeout.
100
47.3k
  if (pic.width * pic.height > 32 * 32) {
101
22.5k
    config.method = (config.method > 4) ? 4 : config.method;
102
22.5k
    config.quality = (config.quality > 99.0f) ? 99.0f : config.quality;
103
22.5k
    config.alpha_quality =
104
22.5k
        (config.alpha_quality > 99) ? 99 : config.alpha_quality;
105
22.5k
  }
106
107
  // Encode.
108
47.3k
  if (!WebPAnimEncoderAdd(*enc, &pic, timestamp_ms, &config)) {
109
0
    const WebPEncodingError error_code = pic.error_code;
110
0
    WebPAnimEncoderDelete(*enc);
111
0
    WebPPictureFree(&pic);
112
    // Tolerate failures when running under the nallocfuzz engine as
113
    // WebPAnimEncoderAdd() may fail due to memory allocation errors outside of
114
    // the encoder; in muxer functions that return booleans for instance.
115
0
    if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY ||
116
0
        error_code == VP8_ENC_ERROR_BAD_WRITE ||
117
0
        getenv("NALLOC_FUZZ_VERSION") != nullptr) {
118
0
      return 0;
119
0
    }
120
0
    fprintf(stderr, "WebPEncode failed. Error code: %d\n", error_code);
121
0
    abort();
122
0
  }
123
124
47.3k
  WebPPictureFree(&pic);
125
47.3k
  return 1;
126
47.3k
}
127
128
}  // namespace
129
130
4.73k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
131
4.73k
  WebPAnimEncoder* enc = nullptr;
132
4.73k
  int width = 0, height = 0, timestamp_ms = 0;
133
4.73k
  uint32_t bit_pos = 0;
134
135
4.73k
  ExtractAndDisableOptimizations(default_VP8GetCPUInfo, data, size, &bit_pos);
136
137
  // Extract a configuration from the packed bits.
138
4.73k
  WebPAnimEncoderOptions anim_config;
139
4.73k
  if (!WebPAnimEncoderOptionsInit(&anim_config)) {
140
0
    fprintf(stderr, "WebPAnimEncoderOptionsInit failed.\n");
141
0
    abort();
142
0
  }
143
4.73k
  anim_config.minimize_size = Extract(1, data, size, &bit_pos);
144
4.73k
  anim_config.kmax = Extract(15, data, size, &bit_pos);
145
4.73k
  const int min_kmin = (anim_config.kmax > 1) ? (anim_config.kmax / 2) : 0;
146
4.73k
  const int max_kmin = (anim_config.kmax > 1) ? (anim_config.kmax - 1) : 0;
147
4.73k
  anim_config.kmin =
148
4.73k
      min_kmin + Extract((uint32_t)(max_kmin - min_kmin), data, size, &bit_pos);
149
4.73k
  anim_config.allow_mixed = Extract(1, data, size, &bit_pos);
150
4.73k
  anim_config.verbose = 0;
151
152
4.73k
  const int nb_frames = 1 + Extract(15, data, size, &bit_pos);
153
154
  // For each frame.
155
52.0k
  for (int i = 0; i < nb_frames; ++i) {
156
47.3k
    if (!AddFrame(&enc, anim_config, &width, &height, timestamp_ms, data, size,
157
47.3k
                  &bit_pos)) {
158
0
      return 0;
159
0
    }
160
161
47.3k
    timestamp_ms += (1 << (2 + Extract(15, data, size, &bit_pos))) +
162
47.3k
                    Extract(1, data, size, &bit_pos);  // [1..131073], arbitrary
163
47.3k
  }
164
165
  // Assemble.
166
4.73k
  if (!WebPAnimEncoderAdd(enc, nullptr, timestamp_ms, nullptr)) {
167
0
    fprintf(stderr, "Last WebPAnimEncoderAdd failed: %s.\n",
168
0
            WebPAnimEncoderGetError(enc));
169
0
    WebPAnimEncoderDelete(enc);
170
0
    abort();
171
0
  }
172
4.73k
  WebPData webp_data;
173
4.73k
  WebPDataInit(&webp_data);
174
  // Tolerate failures when running under the nallocfuzz engine as allocations
175
  // during assembly may fail.
176
4.73k
  if (!WebPAnimEncoderAssemble(enc, &webp_data) &&
177
4.73k
      getenv("NALLOC_FUZZ_VERSION") == nullptr) {
178
0
    fprintf(stderr, "WebPAnimEncoderAssemble failed: %s.\n",
179
0
            WebPAnimEncoderGetError(enc));
180
0
    WebPAnimEncoderDelete(enc);
181
0
    WebPDataClear(&webp_data);
182
0
    abort();
183
0
  }
184
185
4.73k
  WebPAnimEncoderDelete(enc);
186
4.73k
  WebPDataClear(&webp_data);
187
4.73k
  return 0;
188
4.73k
}