/* * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "ultrahdr_api.h" #include "ultrahdr/ultrahdrcommon.h" #include "ultrahdr/jpegr.h" using namespace ultrahdr; // Color gamuts for image data, sync with ultrahdr_api.h constexpr int kCgMin = UHDR_CG_UNSPECIFIED; constexpr int kCgMax = UHDR_CG_BT_2100; // Color ranges for image data, sync with ultrahdr_api.h constexpr int kCrMin = UHDR_CR_UNSPECIFIED; constexpr int kCrMax = UHDR_CR_FULL_RANGE; // Transfer functions for image data, sync with ultrahdr_api.h constexpr int kTfMin = UHDR_CT_UNSPECIFIED; constexpr int kTfMax = UHDR_CT_SRGB; class UltraHdrEncFuzzer { public: UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {}; void process(); template void fillBuffer(T* data, int width, int height, int stride); private: FuzzedDataProvider mFdp; }; template void UltraHdrEncFuzzer::fillBuffer(T* data, int width, int height, int stride) { if (!mFdp.remaining_bytes()) return; T* tmp = data; std::vector buffer(width); for (int i = 0; i < buffer.size(); i++) { buffer[i] = mFdp.ConsumeIntegral(); } for (int j = 0; j < height; j++) { for (int i = 0; i < width; i += buffer.size()) { memcpy(tmp + i, buffer.data(), std::min((int)buffer.size(), (width - i)) * sizeof(*data)); std::shuffle(buffer.begin(), buffer.end(), std::default_random_engine(std::random_device{}())); } tmp += stride; } } void UltraHdrEncFuzzer::process() { if (mFdp.remaining_bytes()) { struct uhdr_raw_image hdrImg{}; struct uhdr_raw_image sdrImg{}; struct uhdr_raw_image gainmapImg{}; // which encode api to select int muxSwitch = mFdp.ConsumeIntegralInRange(0, 4); // hdr_img_fmt uhdr_img_fmt_t hdr_img_fmt = mFdp.PickValueInArray({UHDR_IMG_FMT_24bppYCbCrP010, UHDR_IMG_FMT_32bppRGBA1010102, UHDR_IMG_FMT_64bppRGBAHalfFloat}); // sdr_img_fmt uhdr_img_fmt_t sdr_img_fmt = mFdp.ConsumeBool() ? UHDR_IMG_FMT_12bppYCbCr420 : UHDR_IMG_FMT_32bppRGBA8888; if (muxSwitch > 1) sdr_img_fmt = UHDR_IMG_FMT_12bppYCbCr420; // width int width = mFdp.ConsumeIntegralInRange(kMinWidth, kMaxWidth); if (hdr_img_fmt == UHDR_IMG_FMT_24bppYCbCrP010 || sdr_img_fmt == UHDR_IMG_FMT_12bppYCbCr420) { width = (width >> 1) << 1; } // height int height = mFdp.ConsumeIntegralInRange(kMinHeight, kMaxHeight); if (hdr_img_fmt == UHDR_IMG_FMT_24bppYCbCrP010 || sdr_img_fmt == UHDR_IMG_FMT_12bppYCbCr420) { height = (height >> 1) << 1; } // hdr Ct auto hdr_ct = static_cast(mFdp.ConsumeIntegralInRange(kTfMin, kTfMax)); // hdr Cg auto hdr_cg = static_cast(mFdp.ConsumeIntegralInRange(kCgMin, kCgMax)); // sdr Cg auto sdr_cg = static_cast(mFdp.ConsumeIntegralInRange(kCgMin, kCgMax)); // color range auto hdr_cr = static_cast(mFdp.ConsumeIntegralInRange(kCrMin, kCrMax)); // base quality factor auto base_quality = mFdp.ConsumeIntegral(); // gain_map quality factor auto gainmap_quality = mFdp.ConsumeIntegral(); // multi channel gainmap auto multi_channel_gainmap = mFdp.ConsumeIntegral(); // gainmap scale factor auto gm_scale_factor = mFdp.ConsumeIntegralInRange(-32, 192); // encoding speed preset auto enc_preset = mFdp.ConsumeBool() ? UHDR_USAGE_REALTIME : UHDR_USAGE_BEST_QUALITY; // gainmap metadata auto minBoost = mFdp.ConsumeFloatingPointInRange(-4.0f, 64.0f); auto maxBoost = mFdp.ConsumeFloatingPointInRange(-4.0f, 64.0f); auto gamma = mFdp.ConsumeFloatingPointInRange(-1.0f, 5); auto offsetSdr = mFdp.ConsumeFloatingPointInRange(-1.0f, 1.0f); auto offsetHdr = mFdp.ConsumeFloatingPointInRange(-1.0f, 1.0f); auto minCapacity = mFdp.ConsumeFloatingPointInRange(-4.0f, 48.0f); auto maxCapacity = mFdp.ConsumeFloatingPointInRange(-4.0f, 48.0f); // target display peak brightness auto targetDispPeakBrightness = mFdp.ConsumeFloatingPointInRange(100.0f, 10500.0f); // raw buffer config bool hasHdrStride = mFdp.ConsumeBool(); size_t yHdrStride = mFdp.ConsumeIntegralInRange(width, width + 128); if (!hasHdrStride) yHdrStride = width; bool isHdrUVContiguous = mFdp.ConsumeBool(); bool hasHdrUVStride = mFdp.ConsumeBool(); size_t uvHdrStride = mFdp.ConsumeIntegralInRange(width, width + 128); if (!hasHdrUVStride) uvHdrStride = width; bool hasSdrStride = mFdp.ConsumeBool(); size_t ySdrStride = mFdp.ConsumeIntegralInRange(width, width + 128); if (!hasSdrStride) ySdrStride = width; bool isSdrUVContiguous = mFdp.ConsumeBool(); bool hasSdrUVStride = mFdp.ConsumeBool(); size_t uvSdrStride = mFdp.ConsumeIntegralInRange(width / 2, width / 2 + 128); if (!hasSdrUVStride) uvSdrStride = width / 2; // editing effects auto applyMirror = mFdp.ConsumeBool(); uhdr_mirror_direction_t direction = mFdp.ConsumeBool() ? UHDR_MIRROR_VERTICAL : UHDR_MIRROR_HORIZONTAL; auto applyRotate = mFdp.ConsumeBool(); int degrees = degrees = mFdp.PickValueInArray({-90, 0, 90, 180, 270}); auto applyCrop = mFdp.ConsumeBool(); int left = mFdp.ConsumeIntegral(); int right = mFdp.ConsumeIntegral(); int top = mFdp.ConsumeIntegral(); int bottom = mFdp.ConsumeIntegral(); auto applyResize = mFdp.ConsumeBool(); int resizeWidth = mFdp.ConsumeIntegralInRange(-32, kMaxWidth + 128); int resizeHeight = mFdp.ConsumeIntegralInRange(-32, kMaxHeight + 128); // exif char greeting[] = "Exif says hello world"; uhdr_mem_block_t exif{greeting, mFdp.ConsumeIntegralInRange(0, sizeof greeting * 2), sizeof greeting}; ALOGV("encoding configuration options : "); ALOGV("encoding api - %d ", (int)muxSwitch); ALOGV("image dimensions %d x %d ", (int)width, (int)height); ALOGV("hdr intent color aspects: gamut %d, transfer %d, range %d, format %d ", (int)hdr_cg, (int)hdr_ct, (int)hdr_cr, (int)hdr_img_fmt); ALOGV("sdr intent color aspects: gamut %d, format %d ", (int)sdr_cg, (int)sdr_img_fmt); ALOGV( "gainmap img config: scale factor %d, enabled multichannel gainmap %s, gainmap quality %d ", (int)gm_scale_factor, (int)multi_channel_gainmap ? "Yes" : "No", (int)gainmap_quality); ALOGV("base image quality %d ", (int)base_quality); ALOGV("encoding preset %d ", (int)enc_preset); ALOGV( "gainmap metadata: min content boost %f, max content boost %f, gamma %f, offset sdr %f, " "offset hdr %f, hdr min capacity %f, hdr max capacity %f", (float)minBoost, (float)maxBoost, (float)gamma, (float)offsetSdr, (float)offsetHdr, (float)minCapacity, (float)maxCapacity); ALOGV("hdr intent luma stride %d, chroma stride %d", yHdrStride, uvHdrStride); ALOGV("sdr intent luma stride %d, chroma stride %d", ySdrStride, uvSdrStride); if (applyMirror) ALOGV("added mirror effect, direction %d", (int)direction); if (applyRotate) ALOGV("added rotate effect, degrees %d", (int)degrees); if (applyCrop) ALOGV("added crop effect, crop-left %d, crop-right %d, crop-top %d, crop-bottom %d", left, right, top, bottom); if (applyResize) ALOGV("added resize effect, resize wd %d, resize ht %d", resizeWidth, resizeHeight); std::unique_ptr bufferFpHdr = nullptr; std::unique_ptr bufferHdr = nullptr; std::unique_ptr bufferYHdr = nullptr; std::unique_ptr bufferUVHdr = nullptr; std::unique_ptr bufferYSdr = nullptr; std::unique_ptr bufferUVSdr = nullptr; std::unique_ptr gainMapImageRaw = nullptr; uhdr_codec_private_t* enc_handle = uhdr_create_encoder(); if (!enc_handle) { ALOGE("Failed to create encoder"); return; } #define ON_ERR(x) \ { \ uhdr_error_info_t status_ = (x); \ if (status_.error_code != UHDR_CODEC_OK) { \ if (status_.has_detail) { \ ALOGE("%s", status_.detail); \ } \ } \ } if (muxSwitch != 4) { // init p010/rgba1010102 image hdrImg.w = width; hdrImg.h = height; hdrImg.cg = hdr_cg; hdrImg.fmt = hdr_img_fmt; hdrImg.ct = hdr_ct; hdrImg.range = hdr_cr; hdrImg.stride[UHDR_PLANE_Y] = yHdrStride; if (hdr_img_fmt == UHDR_IMG_FMT_24bppYCbCrP010) { if (isHdrUVContiguous) { size_t p010Size = yHdrStride * height * 3 / 2; bufferYHdr = std::make_unique(p010Size); hdrImg.planes[UHDR_PLANE_Y] = bufferYHdr.get(); fillBuffer(bufferYHdr.get(), width, height, yHdrStride); fillBuffer(bufferYHdr.get() + yHdrStride * height, width, height / 2, yHdrStride); hdrImg.planes[UHDR_PLANE_UV] = bufferYHdr.get() + yHdrStride * height; hdrImg.stride[UHDR_PLANE_UV] = yHdrStride; } else { size_t p010Size = yHdrStride * height; bufferYHdr = std::make_unique(p010Size); hdrImg.planes[UHDR_PLANE_Y] = bufferYHdr.get(); fillBuffer(bufferYHdr.get(), width, height, yHdrStride); size_t p010UVSize = uvHdrStride * hdrImg.h / 2; bufferUVHdr = std::make_unique(p010UVSize); hdrImg.planes[UHDR_PLANE_UV] = bufferUVHdr.get(); hdrImg.stride[UHDR_PLANE_UV] = uvHdrStride; fillBuffer(bufferUVHdr.get(), width, height / 2, uvHdrStride); } } else if (hdr_img_fmt == UHDR_IMG_FMT_32bppRGBA1010102) { size_t rgba1010102Size = yHdrStride * height; bufferHdr = std::make_unique(rgba1010102Size); hdrImg.planes[UHDR_PLANE_PACKED] = bufferHdr.get(); fillBuffer(bufferHdr.get(), width, height, yHdrStride); hdrImg.planes[UHDR_PLANE_U] = nullptr; hdrImg.stride[UHDR_PLANE_U] = 0; } else if (hdr_img_fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) { size_t rgbafp16Size = yHdrStride * height; bufferFpHdr = std::make_unique(rgbafp16Size); hdrImg.planes[UHDR_PLANE_PACKED] = bufferFpHdr.get(); fillBuffer(bufferFpHdr.get(), width, height, yHdrStride); hdrImg.planes[UHDR_PLANE_U] = nullptr; hdrImg.stride[UHDR_PLANE_U] = 0; } hdrImg.planes[UHDR_PLANE_V] = nullptr; hdrImg.stride[UHDR_PLANE_V] = 0; ON_ERR(uhdr_enc_set_raw_image(enc_handle, &hdrImg, UHDR_HDR_IMG)) } else { size_t map_width = width / ((gm_scale_factor <= 0) ? 1 : gm_scale_factor); size_t map_height = height / ((gm_scale_factor <= 0) ? 1 : gm_scale_factor); gainmapImg.fmt = UHDR_IMG_FMT_8bppYCbCr400; gainmapImg.w = map_width; gainmapImg.h = map_height; gainmapImg.cg = UHDR_CG_UNSPECIFIED; gainmapImg.ct = UHDR_CT_UNSPECIFIED; gainmapImg.range = UHDR_CR_FULL_RANGE; const size_t graySize = map_width * map_height; gainMapImageRaw = std::make_unique(graySize); gainmapImg.planes[UHDR_PLANE_Y] = gainMapImageRaw.get(); gainmapImg.stride[UHDR_PLANE_Y] = map_width; gainmapImg.planes[UHDR_PLANE_U] = nullptr; gainmapImg.planes[UHDR_PLANE_V] = nullptr; gainmapImg.stride[UHDR_PLANE_U] = 0; gainmapImg.stride[UHDR_PLANE_V] = 0; fillBuffer(gainMapImageRaw.get(), map_width, map_height, map_width); } if (muxSwitch > 0) { // init yuv420 Image if (sdr_img_fmt == UHDR_IMG_FMT_12bppYCbCr420) { sdrImg.w = width; sdrImg.h = height; sdrImg.cg = sdr_cg; sdrImg.fmt = UHDR_IMG_FMT_12bppYCbCr420; sdrImg.ct = UHDR_CT_SRGB; sdrImg.range = UHDR_CR_FULL_RANGE; sdrImg.stride[UHDR_PLANE_Y] = ySdrStride; if (isSdrUVContiguous) { size_t yuv420Size = ySdrStride * height * 3 / 2; bufferYSdr = std::make_unique(yuv420Size); sdrImg.planes[UHDR_PLANE_Y] = bufferYSdr.get(); sdrImg.planes[UHDR_PLANE_U] = bufferYSdr.get() + ySdrStride * height; sdrImg.planes[UHDR_PLANE_V] = bufferYSdr.get() + ySdrStride * height * 5 / 4; sdrImg.stride[UHDR_PLANE_U] = ySdrStride / 2; sdrImg.stride[UHDR_PLANE_V] = ySdrStride / 2; fillBuffer(bufferYSdr.get(), width, height, ySdrStride); fillBuffer(bufferYSdr.get() + ySdrStride * height, width / 2, height / 2, ySdrStride / 2); fillBuffer(bufferYSdr.get() + ySdrStride * height * 5 / 4, width / 2, height / 2, ySdrStride / 2); } else { size_t yuv420YSize = ySdrStride * height; bufferYSdr = std::make_unique(yuv420YSize); sdrImg.planes[UHDR_PLANE_Y] = bufferYSdr.get(); fillBuffer(bufferYSdr.get(), width, height, ySdrStride); size_t yuv420UVSize = uvSdrStride * sdrImg.h / 2 * 2; bufferUVSdr = std::make_unique(yuv420UVSize); sdrImg.planes[UHDR_PLANE_U] = bufferUVSdr.get(); sdrImg.stride[UHDR_PLANE_U] = uvSdrStride; fillBuffer(bufferUVSdr.get(), width / 2, height / 2, uvSdrStride); fillBuffer(bufferUVSdr.get() + uvSdrStride * height / 2, width / 2, height / 2, uvSdrStride); sdrImg.planes[UHDR_PLANE_V] = bufferUVSdr.get() + uvSdrStride * height / 2; sdrImg.stride[UHDR_PLANE_V] = uvSdrStride; } } else if (sdr_img_fmt == UHDR_IMG_FMT_32bppRGBA8888) { sdrImg.w = width; sdrImg.h = height; sdrImg.cg = sdr_cg; sdrImg.fmt = UHDR_IMG_FMT_32bppRGBA8888; sdrImg.ct = UHDR_CT_SRGB; sdrImg.range = UHDR_CR_FULL_RANGE; sdrImg.stride[UHDR_PLANE_PACKED] = ySdrStride; size_t rgba8888Size = ySdrStride * height; bufferHdr = std::make_unique(rgba8888Size); sdrImg.planes[UHDR_PLANE_PACKED] = bufferHdr.get(); fillBuffer(bufferHdr.get(), width, height, ySdrStride); sdrImg.planes[UHDR_PLANE_U] = nullptr; sdrImg.planes[UHDR_PLANE_V] = nullptr; sdrImg.stride[UHDR_PLANE_U] = 0; sdrImg.stride[UHDR_PLANE_V] = 0; } } if (muxSwitch == 1 || muxSwitch == 2) { ON_ERR(uhdr_enc_set_raw_image(enc_handle, &sdrImg, UHDR_SDR_IMG)) } ON_ERR(uhdr_enc_set_quality(enc_handle, base_quality, UHDR_BASE_IMG)) ON_ERR(uhdr_enc_set_quality(enc_handle, gainmap_quality, UHDR_GAIN_MAP_IMG)) ON_ERR(uhdr_enc_set_exif_data(enc_handle, &exif)) ON_ERR(uhdr_enc_set_using_multi_channel_gainmap(enc_handle, multi_channel_gainmap)) ON_ERR(uhdr_enc_set_gainmap_scale_factor(enc_handle, gm_scale_factor)) ON_ERR(uhdr_enc_set_gainmap_gamma(enc_handle, gamma)) ON_ERR(uhdr_enc_set_min_max_content_boost(enc_handle, minBoost, maxBoost)) ON_ERR(uhdr_enc_set_target_display_peak_brightness(enc_handle, targetDispPeakBrightness)) ON_ERR(uhdr_enc_set_preset(enc_handle, enc_preset)) ON_ERR(uhdr_enable_gpu_acceleration(enc_handle, 1)) if (applyMirror) ON_ERR(uhdr_add_effect_mirror(enc_handle, direction)) if (applyRotate) ON_ERR(uhdr_add_effect_rotate(enc_handle, degrees)) if (applyCrop) ON_ERR(uhdr_add_effect_crop(enc_handle, left, right, top, bottom)) if (applyResize) ON_ERR(uhdr_add_effect_resize(enc_handle, resizeWidth, resizeHeight)) uhdr_error_info_t status = {UHDR_CODEC_OK, 0, ""}; if (muxSwitch == 0 || muxSwitch == 1) { // api 0 or api 1 status = uhdr_encode(enc_handle); } else { // compressed img JpegEncoderHelper encoder; if (encoder.compressImage(&sdrImg, base_quality, nullptr, 0).error_code == UHDR_CODEC_OK) { struct uhdr_compressed_image jpegImg = encoder.getCompressedImage(); jpegImg.cg = sdr_cg; if (muxSwitch != 4) { // for api 4 compressed image will be set with UHDR_BASE_IMG intent uhdr_enc_set_compressed_image(enc_handle, &jpegImg, UHDR_SDR_IMG); } if (muxSwitch == 2 || muxSwitch == 3) { // api 2 or api 3 status = uhdr_encode(enc_handle); } else if (muxSwitch == 4) { // api 4 JpegEncoderHelper gainMapEncoder; if (gainMapEncoder.compressImage(&gainmapImg, gainmap_quality, nullptr, 0).error_code == UHDR_CODEC_OK) { struct uhdr_compressed_image jpegGainMap = gainMapEncoder.getCompressedImage(); uhdr_gainmap_metadata metadata; metadata.max_content_boost = maxBoost; metadata.min_content_boost = minBoost; metadata.gamma = gamma; metadata.offset_sdr = offsetSdr; metadata.offset_hdr = offsetHdr; metadata.hdr_capacity_min = minCapacity; metadata.hdr_capacity_max = maxCapacity; ON_ERR(uhdr_enc_set_compressed_image(enc_handle, &jpegImg, UHDR_BASE_IMG)) ON_ERR(uhdr_enc_set_gainmap_image(enc_handle, &jpegGainMap, &metadata)) status = uhdr_encode(enc_handle); } } } } if (status.error_code == UHDR_CODEC_OK) { auto output = uhdr_get_encoded_stream(enc_handle); if (output != nullptr) { uhdr_codec_private_t* dec_handle = uhdr_create_decoder(); if (dec_handle) { ON_ERR(uhdr_dec_set_image(dec_handle, output)) ON_ERR(uhdr_dec_set_out_color_transfer(dec_handle, hdr_ct)) if (hdr_ct == UHDR_CT_LINEAR) ON_ERR(uhdr_dec_set_out_img_format(dec_handle, UHDR_IMG_FMT_64bppRGBAHalfFloat)) else if (hdr_ct == UHDR_CT_SRGB) ON_ERR(uhdr_dec_set_out_img_format(dec_handle, UHDR_IMG_FMT_32bppRGBA8888)) else ON_ERR(uhdr_dec_set_out_img_format(dec_handle, UHDR_IMG_FMT_32bppRGBA1010102)) ON_ERR(uhdr_decode(dec_handle)) uhdr_release_decoder(dec_handle); } } } uhdr_reset_encoder(enc_handle); uhdr_release_encoder(enc_handle); ON_ERR(status); } } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { UltraHdrEncFuzzer fuzzHandle(data, size); fuzzHandle.process(); return 0; }