Coverage Report

Created: 2026-07-16 08:15

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