Coverage Report

Created: 2025-10-28 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libultrahdr/fuzzer/ultrahdr_legacy_fuzzer.cpp
Line
Count
Source
1
/*
2
 * Copyright 2023 The Android Open Source Project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include <fuzzer/FuzzedDataProvider.h>
18
#include <algorithm>
19
#include <iostream>
20
#include <memory>
21
#include <random>
22
23
#include "ultrahdr/ultrahdrcommon.h"
24
#include "ultrahdr/gainmapmath.h"
25
#include "ultrahdr/jpegr.h"
26
27
using namespace ultrahdr;
28
29
// Color gamuts for image data, sync with ultrahdr.h
30
const int kCgMin = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
31
const int kCgMax = ULTRAHDR_COLORGAMUT_BT2100;
32
33
// Transfer functions for image data, sync with ultrahdr.h
34
const int kTfMin = ULTRAHDR_TF_UNSPECIFIED;
35
const int kTfMax = ULTRAHDR_TF_SRGB;
36
37
// Transfer functions for image data, sync with ultrahdr.h
38
const int kOfMin = ULTRAHDR_OUTPUT_UNSPECIFIED;
39
const int kOfMax = ULTRAHDR_OUTPUT_HDR_HLG;
40
41
// quality factor
42
const int kQfMin = -10;
43
const int kQfMax = 110;
44
45
class UltraHdrEncFuzzer {
46
 public:
47
3.02k
  UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {};
48
  void process();
49
  template <typename T>
50
  void fillBuffer(T* data, int width, int height, int stride);
51
52
 private:
53
  FuzzedDataProvider mFdp;
54
};
55
56
template <typename T>
57
11.1k
void UltraHdrEncFuzzer::fillBuffer(T* data, int width, int height, int stride) {
58
11.1k
  if (!mFdp.remaining_bytes()) return;
59
60
3.38k
  T* tmp = data;
61
3.38k
  std::vector<T> buffer(width);
62
1.68M
  for (int i = 0; i < buffer.size(); i++) {
63
1.68M
    buffer[i] = mFdp.ConsumeIntegral<T>();
64
1.68M
  }
65
2.60M
  for (int j = 0; j < height; j++) {
66
5.19M
    for (int i = 0; i < width; i += buffer.size()) {
67
2.59M
      memcpy(tmp + i, buffer.data(), std::min((int)buffer.size(), (width - i)) * sizeof(*data));
68
2.59M
      std::shuffle(buffer.begin(), buffer.end(),
69
2.59M
                   std::default_random_engine(std::random_device{}()));
70
2.59M
    }
71
2.59M
    tmp += stride;
72
2.59M
  }
73
3.38k
}
void UltraHdrEncFuzzer::fillBuffer<unsigned short>(unsigned short*, int, int, int)
Line
Count
Source
57
4.07k
void UltraHdrEncFuzzer::fillBuffer(T* data, int width, int height, int stride) {
58
4.07k
  if (!mFdp.remaining_bytes()) return;
59
60
1.48k
  T* tmp = data;
61
1.48k
  std::vector<T> buffer(width);
62
640k
  for (int i = 0; i < buffer.size(); i++) {
63
638k
    buffer[i] = mFdp.ConsumeIntegral<T>();
64
638k
  }
65
1.13M
  for (int j = 0; j < height; j++) {
66
2.26M
    for (int i = 0; i < width; i += buffer.size()) {
67
1.13M
      memcpy(tmp + i, buffer.data(), std::min((int)buffer.size(), (width - i)) * sizeof(*data));
68
1.13M
      std::shuffle(buffer.begin(), buffer.end(),
69
1.13M
                   std::default_random_engine(std::random_device{}()));
70
1.13M
    }
71
1.13M
    tmp += stride;
72
1.13M
  }
73
1.48k
}
void UltraHdrEncFuzzer::fillBuffer<unsigned char>(unsigned char*, int, int, int)
Line
Count
Source
57
7.11k
void UltraHdrEncFuzzer::fillBuffer(T* data, int width, int height, int stride) {
58
7.11k
  if (!mFdp.remaining_bytes()) return;
59
60
1.90k
  T* tmp = data;
61
1.90k
  std::vector<T> buffer(width);
62
1.04M
  for (int i = 0; i < buffer.size(); i++) {
63
1.04M
    buffer[i] = mFdp.ConsumeIntegral<T>();
64
1.04M
  }
65
1.46M
  for (int j = 0; j < height; j++) {
66
2.93M
    for (int i = 0; i < width; i += buffer.size()) {
67
1.46M
      memcpy(tmp + i, buffer.data(), std::min((int)buffer.size(), (width - i)) * sizeof(*data));
68
1.46M
      std::shuffle(buffer.begin(), buffer.end(),
69
1.46M
                   std::default_random_engine(std::random_device{}()));
70
1.46M
    }
71
1.46M
    tmp += stride;
72
1.46M
  }
73
1.90k
}
74
75
3.02k
void UltraHdrEncFuzzer::process() {
76
3.02k
  if (mFdp.remaining_bytes()) {
77
3.02k
    struct jpegr_uncompressed_struct p010Img{};
78
3.02k
    struct jpegr_uncompressed_struct yuv420Img{};
79
3.02k
    struct jpegr_uncompressed_struct grayImg{};
80
3.02k
    struct jpegr_compressed_struct jpegImgR{};
81
3.02k
    struct jpegr_compressed_struct jpegImg{};
82
3.02k
    struct jpegr_compressed_struct jpegGainMap{};
83
84
    // which encode api to select
85
3.02k
    int muxSwitch = mFdp.ConsumeIntegralInRange<int>(0, 4);
86
87
    // quality factor
88
3.02k
    int quality = mFdp.ConsumeIntegralInRange<int>(kQfMin, kQfMax);
89
90
    // hdr_tf
91
3.02k
    auto tf =
92
3.02k
        static_cast<ultrahdr_transfer_function>(mFdp.ConsumeIntegralInRange<int>(kTfMin, kTfMax));
93
94
    // p010 Cg
95
3.02k
    auto p010Cg =
96
3.02k
        static_cast<ultrahdr_color_gamut>(mFdp.ConsumeIntegralInRange<int>(kCgMin, kCgMax));
97
98
    // 420 Cg
99
3.02k
    auto yuv420Cg =
100
3.02k
        static_cast<ultrahdr_color_gamut>(mFdp.ConsumeIntegralInRange<int>(kCgMin, kCgMax));
101
102
    // hdr_of
103
3.02k
    auto of = static_cast<ultrahdr_output_format>(mFdp.ConsumeIntegralInRange<int>(kOfMin, kOfMax));
104
105
3.02k
    int width = mFdp.ConsumeIntegralInRange<int>(kMinWidth, kMaxWidth);
106
3.02k
    width = (width >> 1) << 1;
107
108
3.02k
    int height = mFdp.ConsumeIntegralInRange<int>(kMinHeight, kMaxHeight);
109
3.02k
    height = (height >> 1) << 1;
110
111
    // gain_map quality factor
112
3.02k
    auto gainmap_quality = mFdp.ConsumeIntegral<int8_t>();
113
114
    // multi channel gainmap
115
3.02k
    auto multi_channel_gainmap = mFdp.ConsumeIntegral<int8_t>();
116
117
    // gainmap scale factor
118
3.02k
    auto gm_scale_factor = mFdp.ConsumeIntegralInRange<int16_t>(-32, 192);
119
120
    // encoding speed preset
121
3.02k
    auto enc_preset = mFdp.ConsumeBool() ? UHDR_USAGE_REALTIME : UHDR_USAGE_BEST_QUALITY;
122
123
    // gainmap metadata
124
3.02k
    auto minBoost = mFdp.ConsumeFloatingPointInRange<float>(-4.0f, 64.0f);
125
3.02k
    auto maxBoost = mFdp.ConsumeFloatingPointInRange<float>(-4.0f, 64.0f);
126
3.02k
    auto gamma = mFdp.ConsumeFloatingPointInRange<float>(-1.0f, 5);
127
3.02k
    auto offsetSdr = mFdp.ConsumeFloatingPointInRange<float>(-1.0f, 1.0f);
128
3.02k
    auto offsetHdr = mFdp.ConsumeFloatingPointInRange<float>(-1.0f, 1.0f);
129
3.02k
    auto minCapacity = mFdp.ConsumeFloatingPointInRange<float>(-4.0f, 48.0f);
130
3.02k
    auto maxCapacity = mFdp.ConsumeFloatingPointInRange<float>(-4.0f, 48.0f);
131
132
    // target display peak brightness
133
3.02k
    auto targetDispPeakBrightness = mFdp.ConsumeFloatingPointInRange<float>(100.0f, 10500.0f);
134
135
    // raw buffer config
136
3.02k
    bool hasP010Stride = mFdp.ConsumeBool();
137
3.02k
    size_t yP010Stride = mFdp.ConsumeIntegralInRange<uint16_t>(width, width + 128);
138
3.02k
    if (!hasP010Stride) yP010Stride = width;
139
3.02k
    bool isP010UVContiguous = mFdp.ConsumeBool();
140
3.02k
    bool hasP010UVStride = mFdp.ConsumeBool();
141
3.02k
    size_t uvP010Stride = mFdp.ConsumeIntegralInRange<uint16_t>(width, width + 128);
142
3.02k
    if (!hasP010UVStride) uvP010Stride = width;
143
144
3.02k
    bool hasYuv420Stride = mFdp.ConsumeBool();
145
3.02k
    size_t yYuv420Stride = mFdp.ConsumeIntegralInRange<uint16_t>(width, width + 128);
146
3.02k
    if (!hasYuv420Stride) yYuv420Stride = width;
147
3.02k
    bool isYuv420UVContiguous = mFdp.ConsumeBool();
148
3.02k
    bool hasYuv420UVStride = mFdp.ConsumeBool();
149
3.02k
    size_t uvYuv420Stride = mFdp.ConsumeIntegralInRange<uint16_t>(width / 2, width / 2 + 128);
150
3.02k
    if (!hasYuv420UVStride) uvYuv420Stride = width / 2;
151
152
    // display boost
153
3.02k
    float displayBoost = mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX);
154
155
3.02k
    std::unique_ptr<uint16_t[]> bufferYHdr = nullptr;
156
3.02k
    std::unique_ptr<uint16_t[]> bufferUVHdr = nullptr;
157
3.02k
    std::unique_ptr<uint8_t[]> bufferYSdr = nullptr;
158
3.02k
    std::unique_ptr<uint8_t[]> bufferUVSdr = nullptr;
159
3.02k
    std::unique_ptr<uint8_t[]> grayImgRaw = nullptr;
160
3.02k
    if (muxSwitch != 4) {
161
      // init p010 image
162
2.03k
      p010Img.width = width;
163
2.03k
      p010Img.height = height;
164
2.03k
      p010Img.colorGamut = p010Cg;
165
2.03k
      p010Img.luma_stride = yP010Stride;
166
2.03k
      if (isP010UVContiguous) {
167
565
        size_t p010Size = yP010Stride * height * 3 / 2;
168
565
        bufferYHdr = std::make_unique<uint16_t[]>(p010Size);
169
565
        p010Img.data = bufferYHdr.get();
170
565
        p010Img.chroma_data = nullptr;
171
565
        p010Img.chroma_stride = 0;
172
565
        fillBuffer<uint16_t>(bufferYHdr.get(), width, height, yP010Stride);
173
565
        fillBuffer<uint16_t>(bufferYHdr.get() + yP010Stride * height, width, height / 2,
174
565
                             yP010Stride);
175
1.47k
      } else {
176
1.47k
        size_t p010YSize = yP010Stride * height;
177
1.47k
        bufferYHdr = std::make_unique<uint16_t[]>(p010YSize);
178
1.47k
        p010Img.data = bufferYHdr.get();
179
1.47k
        fillBuffer<uint16_t>(bufferYHdr.get(), width, height, yP010Stride);
180
1.47k
        size_t p010UVSize = uvP010Stride * p010Img.height / 2;
181
1.47k
        bufferUVHdr = std::make_unique<uint16_t[]>(p010UVSize);
182
1.47k
        p010Img.chroma_data = bufferUVHdr.get();
183
1.47k
        p010Img.chroma_stride = uvP010Stride;
184
1.47k
        fillBuffer<uint16_t>(bufferUVHdr.get(), width, height / 2, uvP010Stride);
185
1.47k
      }
186
2.03k
    } else {
187
991
      size_t map_width = width / kMapDimensionScaleFactorDefault;
188
991
      size_t map_height = height / kMapDimensionScaleFactorDefault;
189
      // init 400 image
190
991
      grayImg.width = map_width;
191
991
      grayImg.height = map_height;
192
991
      grayImg.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
193
991
      const size_t graySize = map_width * map_height;
194
991
      grayImgRaw = std::make_unique<uint8_t[]>(graySize);
195
991
      grayImg.data = grayImgRaw.get();
196
991
      fillBuffer<uint8_t>(grayImgRaw.get(), map_width, map_height, map_width);
197
991
      grayImg.chroma_data = nullptr;
198
991
      grayImg.luma_stride = 0;
199
991
      grayImg.chroma_stride = 0;
200
991
    }
201
202
3.02k
    if (muxSwitch > 0) {
203
      // init 420 image
204
2.04k
      yuv420Img.width = width;
205
2.04k
      yuv420Img.height = height;
206
2.04k
      yuv420Img.colorGamut = yuv420Cg;
207
2.04k
      yuv420Img.luma_stride = yYuv420Stride;
208
2.04k
      if (isYuv420UVContiguous) {
209
584
        size_t yuv420Size = yYuv420Stride * height * 3 / 2;
210
584
        bufferYSdr = std::make_unique<uint8_t[]>(yuv420Size);
211
584
        yuv420Img.data = bufferYSdr.get();
212
584
        yuv420Img.chroma_data = nullptr;
213
584
        yuv420Img.chroma_stride = 0;
214
584
        fillBuffer<uint8_t>(bufferYSdr.get(), width, height, yYuv420Stride);
215
584
        fillBuffer<uint8_t>(bufferYSdr.get() + yYuv420Stride * height, width / 2, height / 2,
216
584
                            yYuv420Stride / 2);
217
584
        fillBuffer<uint8_t>(bufferYSdr.get() + yYuv420Stride * height * 5 / 4, width / 2,
218
584
                            height / 2, yYuv420Stride / 2);
219
1.45k
      } else {
220
1.45k
        size_t yuv420YSize = yYuv420Stride * height;
221
1.45k
        bufferYSdr = std::make_unique<uint8_t[]>(yuv420YSize);
222
1.45k
        yuv420Img.data = bufferYSdr.get();
223
1.45k
        fillBuffer<uint8_t>(bufferYSdr.get(), width, height, yYuv420Stride);
224
1.45k
        size_t yuv420UVSize = uvYuv420Stride * yuv420Img.height / 2 * 2;
225
1.45k
        bufferUVSdr = std::make_unique<uint8_t[]>(yuv420UVSize);
226
1.45k
        yuv420Img.chroma_data = bufferUVSdr.get();
227
1.45k
        yuv420Img.chroma_stride = uvYuv420Stride;
228
1.45k
        fillBuffer<uint8_t>(bufferUVSdr.get(), width / 2, height / 2, uvYuv420Stride);
229
1.45k
        fillBuffer<uint8_t>(bufferUVSdr.get() + uvYuv420Stride * height / 2, width / 2, height / 2,
230
1.45k
                            uvYuv420Stride);
231
1.45k
      }
232
2.04k
    }
233
234
    // dest
235
    // 2 * p010 size as input data is random, DCT compression might not behave as expected
236
3.02k
    jpegImgR.maxLength = std::max(64 * 1024 /* min size 8kb */, width * height * 3 * 2);
237
3.02k
    auto jpegImgRaw = std::make_unique<uint8_t[]>(jpegImgR.maxLength);
238
3.02k
    jpegImgR.data = jpegImgRaw.get();
239
// #define DUMP_PARAM
240
#ifdef DUMP_PARAM
241
    std::cout << "Api Select " << muxSwitch << std::endl;
242
    std::cout << "image dimensions " << width << " x " << height << std::endl;
243
    std::cout << "p010 color gamut " << p010Img.colorGamut << std::endl;
244
    std::cout << "p010 luma stride " << p010Img.luma_stride << std::endl;
245
    std::cout << "p010 chroma stride " << p010Img.chroma_stride << std::endl;
246
    std::cout << "420 color gamut " << yuv420Img.colorGamut << std::endl;
247
    std::cout << "420 luma stride " << yuv420Img.luma_stride << std::endl;
248
    std::cout << "420 chroma stride " << yuv420Img.chroma_stride << std::endl;
249
    std::cout << "quality factor " << quality << std::endl;
250
#endif
251
3.02k
    JpegR jpegHdr(nullptr, gm_scale_factor, gainmap_quality, multi_channel_gainmap, gamma,
252
3.02k
                  enc_preset, minBoost, maxBoost, targetDispPeakBrightness);
253
3.02k
    status_t status = JPEGR_UNKNOWN_ERROR;
254
3.02k
    if (muxSwitch == 0) {  // api 0
255
985
      jpegImgR.length = 0;
256
985
      status = jpegHdr.encodeJPEGR(&p010Img, tf, &jpegImgR, quality, nullptr);
257
2.04k
    } else if (muxSwitch == 1) {  // api 1
258
338
      jpegImgR.length = 0;
259
338
      status = jpegHdr.encodeJPEGR(&p010Img, &yuv420Img, tf, &jpegImgR, quality, nullptr);
260
1.70k
    } else {
261
      // compressed img
262
1.70k
      JpegEncoderHelper encoder;
263
1.70k
      struct jpegr_uncompressed_struct yuv420ImgCopy = yuv420Img;
264
1.70k
      if (yuv420ImgCopy.luma_stride == 0) yuv420ImgCopy.luma_stride = yuv420Img.width;
265
1.70k
      if (!yuv420ImgCopy.chroma_data) {
266
437
        uint8_t* data = reinterpret_cast<uint8_t*>(yuv420Img.data);
267
437
        yuv420ImgCopy.chroma_data = data + yuv420Img.luma_stride * yuv420Img.height;
268
437
        yuv420ImgCopy.chroma_stride = yuv420Img.luma_stride >> 1;
269
437
      }
270
1.70k
      const uint8_t* planes[3]{reinterpret_cast<uint8_t*>(yuv420ImgCopy.data),
271
1.70k
                               reinterpret_cast<uint8_t*>(yuv420ImgCopy.chroma_data),
272
1.70k
                               reinterpret_cast<uint8_t*>(yuv420ImgCopy.chroma_data) +
273
1.70k
                                   yuv420ImgCopy.chroma_stride * yuv420ImgCopy.height / 2};
274
1.70k
      const unsigned int strides[3]{yuv420ImgCopy.luma_stride, yuv420ImgCopy.chroma_stride,
275
1.70k
                                    yuv420ImgCopy.chroma_stride};
276
1.70k
      if (encoder
277
1.70k
              .compressImage(planes, strides, yuv420ImgCopy.width, yuv420ImgCopy.height,
278
1.70k
                             UHDR_IMG_FMT_12bppYCbCr420, quality, nullptr, 0)
279
1.70k
              .error_code == UHDR_CODEC_OK) {
280
1.70k
        jpegImg.length = encoder.getCompressedImageSize();
281
1.70k
        jpegImg.maxLength = jpegImg.length;
282
1.70k
        jpegImg.data = encoder.getCompressedImagePtr();
283
1.70k
        jpegImg.colorGamut = yuv420Cg;
284
1.70k
        if (muxSwitch == 2) {  // api 2
285
182
          jpegImgR.length = 0;
286
182
          status = jpegHdr.encodeJPEGR(&p010Img, &yuv420Img, &jpegImg, tf, &jpegImgR);
287
1.52k
        } else if (muxSwitch == 3) {  // api 3
288
531
          jpegImgR.length = 0;
289
531
          status = jpegHdr.encodeJPEGR(&p010Img, &jpegImg, tf, &jpegImgR);
290
991
        } else if (muxSwitch == 4) {  // api 4
291
991
          jpegImgR.length = 0;
292
991
          JpegEncoderHelper gainMapEncoder;
293
991
          const uint8_t* planeGm[1]{reinterpret_cast<uint8_t*>(grayImg.data)};
294
991
          const unsigned int strideGm[1]{grayImg.width};
295
991
          if (gainMapEncoder
296
991
                  .compressImage(planeGm, strideGm, grayImg.width, grayImg.height,
297
991
                                 UHDR_IMG_FMT_8bppYCbCr400, quality, nullptr, 0)
298
991
                  .error_code == UHDR_CODEC_OK) {
299
991
            jpegGainMap.length = gainMapEncoder.getCompressedImageSize();
300
991
            jpegGainMap.maxLength = jpegImg.length;
301
991
            jpegGainMap.data = gainMapEncoder.getCompressedImagePtr();
302
991
            jpegGainMap.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
303
991
            ultrahdr_metadata_struct metadata;
304
991
            metadata.version = kJpegrVersion;
305
991
            metadata.maxContentBoost = maxBoost;
306
991
            metadata.minContentBoost = minBoost;
307
991
            metadata.gamma = gamma;
308
991
            metadata.offsetSdr = offsetSdr;
309
991
            metadata.offsetHdr = offsetHdr;
310
991
            metadata.hdrCapacityMin = minCapacity;
311
991
            metadata.hdrCapacityMax = maxCapacity;
312
991
            status = jpegHdr.encodeJPEGR(&jpegImg, &jpegGainMap, &metadata, &jpegImgR);
313
991
          }
314
991
        }
315
1.70k
      }
316
1.70k
    }
317
3.02k
    if (status == JPEGR_NO_ERROR) {
318
2.13k
      jpegr_info_struct info{};
319
2.13k
      status = jpegHdr.getJPEGRInfo(&jpegImgR, &info);
320
2.13k
      if (status == JPEGR_NO_ERROR) {
321
2.13k
        size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
322
2.13k
        jpegr_uncompressed_struct decodedJpegR;
323
2.13k
        auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
324
2.13k
        decodedJpegR.data = decodedRaw.get();
325
2.13k
        ultrahdr_metadata_struct metadata;
326
2.13k
        status = jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR, displayBoost, nullptr, of, nullptr,
327
2.13k
                                     &metadata);
328
2.13k
        if (status != JPEGR_NO_ERROR) {
329
700
          ALOGE("encountered error during decoding %d", status);
330
700
        }
331
2.13k
      } else {
332
0
        ALOGE("encountered error during get jpeg info %d", status);
333
0
      }
334
2.13k
    } else {
335
896
      ALOGE("encountered error during encoding %d", status);
336
896
    }
337
3.02k
  }
338
3.02k
}
339
340
13.6k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
341
13.6k
  UltraHdrEncFuzzer fuzzHandle(data, size);
342
13.6k
  fuzzHandle.process();
343
13.6k
  return 0;
344
13.6k
}