• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 + 1;
31 const int kCgMax = ULTRAHDR_COLORGAMUT_MAX;
32 
33 // Transfer functions for image data, sync with ultrahdr.h
34 const int kTfMin = ULTRAHDR_TF_UNSPECIFIED + 1;
35 const int kTfMax = ULTRAHDR_TF_PQ;
36 
37 // Transfer functions for image data, sync with ultrahdr.h
38 const int kOfMin = ULTRAHDR_OUTPUT_UNSPECIFIED + 1;
39 const int kOfMax = ULTRAHDR_OUTPUT_MAX;
40 
41 // quality factor
42 const int kQfMin = 0;
43 const int kQfMax = 100;
44 
45 class UltraHdrEncFuzzer {
46  public:
UltraHdrEncFuzzer(const uint8_t * data,size_t size)47   UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
48   void process();
49   void fillP010Buffer(uint16_t* data, int width, int height, int stride);
50   void fill420Buffer(uint8_t* data, int width, int height, int stride);
51 
52  private:
53   FuzzedDataProvider mFdp;
54 };
55 
fillP010Buffer(uint16_t * data,int width,int height,int stride)56 void UltraHdrEncFuzzer::fillP010Buffer(uint16_t* data, int width, int height, int stride) {
57   uint16_t* tmp = data;
58   std::vector<uint16_t> buffer(16);
59   for (int i = 0; i < buffer.size(); i++) {
60     buffer[i] = (mFdp.ConsumeIntegralInRange<int>(0, (1 << 10) - 1)) << 6;
61   }
62   for (int j = 0; j < height; j++) {
63     for (int i = 0; i < width; i += buffer.size()) {
64       memcpy(tmp + i, buffer.data(), std::min((int)buffer.size(), (width - i)) * sizeof(*data));
65       std::shuffle(buffer.begin(), buffer.end(),
66                    std::default_random_engine(std::random_device{}()));
67     }
68     tmp += stride;
69   }
70 }
71 
fill420Buffer(uint8_t * data,int width,int height,int stride)72 void UltraHdrEncFuzzer::fill420Buffer(uint8_t* data, int width, int height, int stride) {
73   uint8_t* tmp = data;
74   std::vector<uint8_t> buffer(16);
75   mFdp.ConsumeData(buffer.data(), buffer.size());
76   for (int j = 0; j < height; j++) {
77     for (int i = 0; i < width; i += buffer.size()) {
78       memcpy(tmp + i, buffer.data(), std::min((int)buffer.size(), (width - i)) * sizeof(*data));
79       std::shuffle(buffer.begin(), buffer.end(),
80                    std::default_random_engine(std::random_device{}()));
81     }
82     tmp += stride;
83   }
84 }
85 
process()86 void UltraHdrEncFuzzer::process() {
87   while (mFdp.remaining_bytes()) {
88     struct jpegr_uncompressed_struct p010Img {};
89     struct jpegr_uncompressed_struct yuv420Img {};
90     struct jpegr_uncompressed_struct grayImg {};
91     struct jpegr_compressed_struct jpegImgR {};
92     struct jpegr_compressed_struct jpegImg {};
93     struct jpegr_compressed_struct jpegGainMap {};
94 
95     // which encode api to select
96     int muxSwitch = mFdp.ConsumeIntegralInRange<int>(0, 4);
97 
98     // quality factor
99     int quality = mFdp.ConsumeIntegralInRange<int>(kQfMin, kQfMax);
100 
101     // hdr_tf
102     auto tf =
103         static_cast<ultrahdr_transfer_function>(mFdp.ConsumeIntegralInRange<int>(kTfMin, kTfMax));
104 
105     // p010 Cg
106     auto p010Cg =
107         static_cast<ultrahdr_color_gamut>(mFdp.ConsumeIntegralInRange<int>(kCgMin, kCgMax));
108 
109     // 420 Cg
110     auto yuv420Cg =
111         static_cast<ultrahdr_color_gamut>(mFdp.ConsumeIntegralInRange<int>(kCgMin, kCgMax));
112 
113     // hdr_of
114     auto of = static_cast<ultrahdr_output_format>(mFdp.ConsumeIntegralInRange<int>(kOfMin, kOfMax));
115 
116     int width = mFdp.ConsumeIntegralInRange<int>(kMinWidth, kMaxWidth);
117     width = (width >> 1) << 1;
118 
119     int height = mFdp.ConsumeIntegralInRange<int>(kMinHeight, kMaxHeight);
120     height = (height >> 1) << 1;
121 
122     std::unique_ptr<uint16_t[]> bufferYHdr = nullptr;
123     std::unique_ptr<uint16_t[]> bufferUVHdr = nullptr;
124     std::unique_ptr<uint8_t[]> bufferYSdr = nullptr;
125     std::unique_ptr<uint8_t[]> bufferUVSdr = nullptr;
126     std::unique_ptr<uint8_t[]> grayImgRaw = nullptr;
127     if (muxSwitch != 4) {
128       // init p010 image
129       bool isUVContiguous = mFdp.ConsumeBool();
130       bool hasYStride = mFdp.ConsumeBool();
131       int yStride = hasYStride ? mFdp.ConsumeIntegralInRange<int>(width, width + 128) : width;
132       p010Img.width = width;
133       p010Img.height = height;
134       p010Img.colorGamut = p010Cg;
135       p010Img.luma_stride = hasYStride ? yStride : 0;
136       if (isUVContiguous) {
137         size_t p010Size = yStride * height * 3 / 2;
138         bufferYHdr = std::make_unique<uint16_t[]>(p010Size);
139         p010Img.data = bufferYHdr.get();
140         p010Img.chroma_data = nullptr;
141         p010Img.chroma_stride = 0;
142         fillP010Buffer(bufferYHdr.get(), width, height, yStride);
143         fillP010Buffer(bufferYHdr.get() + yStride * height, width, height / 2, yStride);
144       } else {
145         int uvStride = mFdp.ConsumeIntegralInRange<int>(width, width + 128);
146         size_t p010YSize = yStride * height;
147         bufferYHdr = std::make_unique<uint16_t[]>(p010YSize);
148         p010Img.data = bufferYHdr.get();
149         fillP010Buffer(bufferYHdr.get(), width, height, yStride);
150         size_t p010UVSize = uvStride * p010Img.height / 2;
151         bufferUVHdr = std::make_unique<uint16_t[]>(p010UVSize);
152         p010Img.chroma_data = bufferUVHdr.get();
153         p010Img.chroma_stride = uvStride;
154         fillP010Buffer(bufferUVHdr.get(), width, height / 2, uvStride);
155       }
156     } else {
157       size_t map_width = width / kMapDimensionScaleFactor;
158       size_t map_height = height / kMapDimensionScaleFactor;
159       // init 400 image
160       grayImg.width = map_width;
161       grayImg.height = map_height;
162       grayImg.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
163 
164       const size_t graySize = map_width * map_height;
165       grayImgRaw = std::make_unique<uint8_t[]>(graySize);
166       grayImg.data = grayImgRaw.get();
167       fill420Buffer(grayImgRaw.get(), map_width, map_height, map_width);
168       grayImg.chroma_data = nullptr;
169       grayImg.luma_stride = 0;
170       grayImg.chroma_stride = 0;
171     }
172 
173     if (muxSwitch > 0) {
174       // init 420 image
175       bool isUVContiguous = mFdp.ConsumeBool();
176       bool hasYStride = mFdp.ConsumeBool();
177       int yStride = hasYStride ? mFdp.ConsumeIntegralInRange<int>(width, width + 128) : width;
178       yuv420Img.width = width;
179       yuv420Img.height = height;
180       yuv420Img.colorGamut = yuv420Cg;
181       yuv420Img.luma_stride = hasYStride ? yStride : 0;
182       if (isUVContiguous) {
183         size_t yuv420Size = yStride * height * 3 / 2;
184         bufferYSdr = std::make_unique<uint8_t[]>(yuv420Size);
185         yuv420Img.data = bufferYSdr.get();
186         yuv420Img.chroma_data = nullptr;
187         yuv420Img.chroma_stride = 0;
188         fill420Buffer(bufferYSdr.get(), width, height, yStride);
189         fill420Buffer(bufferYSdr.get() + yStride * height, width / 2, height / 2, yStride / 2);
190         fill420Buffer(bufferYSdr.get() + yStride * height * 5 / 4, width / 2, height / 2,
191                       yStride / 2);
192       } else {
193         int uvStride = mFdp.ConsumeIntegralInRange<int>(width / 2, width / 2 + 128);
194         size_t yuv420YSize = yStride * height;
195         bufferYSdr = std::make_unique<uint8_t[]>(yuv420YSize);
196         yuv420Img.data = bufferYSdr.get();
197         fill420Buffer(bufferYSdr.get(), width, height, yStride);
198         size_t yuv420UVSize = uvStride * yuv420Img.height / 2 * 2;
199         bufferUVSdr = std::make_unique<uint8_t[]>(yuv420UVSize);
200         yuv420Img.chroma_data = bufferUVSdr.get();
201         yuv420Img.chroma_stride = uvStride;
202         fill420Buffer(bufferUVSdr.get(), width / 2, height / 2, uvStride);
203         fill420Buffer(bufferUVSdr.get() + uvStride * height / 2, width / 2, height / 2, uvStride);
204       }
205     }
206 
207     // dest
208     // 2 * p010 size as input data is random, DCT compression might not behave as expected
209     jpegImgR.maxLength = std::max(8 * 1024 /* min size 8kb */, width * height * 3 * 2);
210     auto jpegImgRaw = std::make_unique<uint8_t[]>(jpegImgR.maxLength);
211     jpegImgR.data = jpegImgRaw.get();
212 
213 //#define DUMP_PARAM
214 #ifdef DUMP_PARAM
215     std::cout << "Api Select " << muxSwitch << std::endl;
216     std::cout << "image dimensions " << width << " x " << height << std::endl;
217     std::cout << "p010 color gamut " << p010Img.colorGamut << std::endl;
218     std::cout << "p010 luma stride " << p010Img.luma_stride << std::endl;
219     std::cout << "p010 chroma stride " << p010Img.chroma_stride << std::endl;
220     std::cout << "420 color gamut " << yuv420Img.colorGamut << std::endl;
221     std::cout << "420 luma stride " << yuv420Img.luma_stride << std::endl;
222     std::cout << "420 chroma stride " << yuv420Img.chroma_stride << std::endl;
223     std::cout << "quality factor " << quality << std::endl;
224 #endif
225 
226     JpegR jpegHdr;
227     status_t status = JPEGR_UNKNOWN_ERROR;
228     if (muxSwitch == 0) {  // api 0
229       jpegImgR.length = 0;
230       status = jpegHdr.encodeJPEGR(&p010Img, tf, &jpegImgR, quality, nullptr);
231     } else if (muxSwitch == 1) {  // api 1
232       jpegImgR.length = 0;
233       status = jpegHdr.encodeJPEGR(&p010Img, &yuv420Img, tf, &jpegImgR, quality, nullptr);
234     } else {
235       // compressed img
236       JpegEncoderHelper encoder;
237       struct jpegr_uncompressed_struct yuv420ImgCopy = yuv420Img;
238       if (yuv420ImgCopy.luma_stride == 0) yuv420ImgCopy.luma_stride = yuv420Img.width;
239       if (!yuv420ImgCopy.chroma_data) {
240         uint8_t* data = reinterpret_cast<uint8_t*>(yuv420Img.data);
241         yuv420ImgCopy.chroma_data = data + yuv420Img.luma_stride * yuv420Img.height;
242         yuv420ImgCopy.chroma_stride = yuv420Img.luma_stride >> 1;
243       }
244 
245       const uint8_t* planes[3]{reinterpret_cast<uint8_t*>(yuv420ImgCopy.data),
246                                reinterpret_cast<uint8_t*>(yuv420ImgCopy.chroma_data),
247                                reinterpret_cast<uint8_t*>(yuv420ImgCopy.chroma_data) +
248                                    yuv420ImgCopy.chroma_stride * yuv420ImgCopy.height / 2};
249       const size_t strides[3]{yuv420ImgCopy.luma_stride, yuv420ImgCopy.chroma_stride,
250                               yuv420ImgCopy.chroma_stride};
251       if (encoder.compressImage(planes, strides, yuv420ImgCopy.width, yuv420ImgCopy.height,
252                                 UHDR_IMG_FMT_12bppYCbCr420, quality, nullptr, 0)) {
253         jpegImg.length = encoder.getCompressedImageSize();
254         jpegImg.maxLength = jpegImg.length;
255         jpegImg.data = encoder.getCompressedImagePtr();
256         jpegImg.colorGamut = yuv420Cg;
257 
258         if (muxSwitch == 2) {  // api 2
259           jpegImgR.length = 0;
260           status = jpegHdr.encodeJPEGR(&p010Img, &yuv420Img, &jpegImg, tf, &jpegImgR);
261         } else if (muxSwitch == 3) {  // api 3
262           jpegImgR.length = 0;
263           status = jpegHdr.encodeJPEGR(&p010Img, &jpegImg, tf, &jpegImgR);
264         } else if (muxSwitch == 4) {  // api 4
265           jpegImgR.length = 0;
266           JpegEncoderHelper gainMapEncoder;
267           const uint8_t* planeGm[1]{reinterpret_cast<uint8_t*>(grayImg.data)};
268           const size_t strideGm[1]{grayImg.width};
269           if (gainMapEncoder.compressImage(planeGm, strideGm, grayImg.width, grayImg.height,
270                                            UHDR_IMG_FMT_8bppYCbCr400, quality, nullptr, 0)) {
271             jpegGainMap.length = gainMapEncoder.getCompressedImageSize();
272             jpegGainMap.maxLength = jpegImg.length;
273             jpegGainMap.data = gainMapEncoder.getCompressedImagePtr();
274             jpegGainMap.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
275             ultrahdr_metadata_struct metadata;
276             metadata.version = kJpegrVersion;
277             if (tf == ULTRAHDR_TF_HLG) {
278               metadata.maxContentBoost = kHlgMaxNits / kSdrWhiteNits;
279             } else if (tf == ULTRAHDR_TF_PQ) {
280               metadata.maxContentBoost = kPqMaxNits / kSdrWhiteNits;
281             } else {
282               metadata.maxContentBoost = 1.0f;
283             }
284             metadata.minContentBoost = 1.0f;
285             metadata.gamma = 1.0f;
286             metadata.offsetSdr = 0.0f;
287             metadata.offsetHdr = 0.0f;
288             metadata.hdrCapacityMin = 1.0f;
289             metadata.hdrCapacityMax = metadata.maxContentBoost;
290             status = jpegHdr.encodeJPEGR(&jpegImg, &jpegGainMap, &metadata, &jpegImgR);
291           }
292         }
293       }
294     }
295     if (status == JPEGR_NO_ERROR) {
296       jpegr_info_struct info{};
297       status = jpegHdr.getJPEGRInfo(&jpegImgR, &info);
298       if (status == JPEGR_NO_ERROR) {
299         size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
300         jpegr_uncompressed_struct decodedJpegR;
301         auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
302         decodedJpegR.data = decodedRaw.get();
303         ultrahdr_metadata_struct metadata;
304         status = jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
305                                      mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX), nullptr,
306                                      of, nullptr, &metadata);
307         if (status != JPEGR_NO_ERROR) {
308           ALOGE("encountered error during decoding %d", status);
309         }
310       } else {
311         ALOGE("encountered error during get jpeg info %d", status);
312       }
313     } else {
314       ALOGE("encountered error during encoding %d", status);
315     }
316   }
317 }
318 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)319 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
320   UltraHdrEncFuzzer fuzzHandle(data, size);
321   fuzzHandle.process();
322   return 0;
323 }
324