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