cjxl_fuzzer.cc (8162B)
1 // Copyright (c) the JPEG XL Project Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 #include <jxl/encode.h> 7 #include <jxl/encode_cxx.h> 8 #include <jxl/thread_parallel_runner.h> 9 #include <jxl/thread_parallel_runner_cxx.h> 10 #include <jxl/types.h> 11 #include <limits.h> 12 #include <stdint.h> 13 #include <stdlib.h> 14 #include <string.h> 15 16 #include <algorithm> 17 #include <functional> 18 #include <hwy/targets.h> 19 #include <random> 20 #include <vector> 21 22 #include "lib/jxl/base/status.h" 23 #include "lib/jxl/test_image.h" 24 25 namespace { 26 27 #define TRY(expr) \ 28 do { \ 29 if (JXL_ENC_SUCCESS != (expr)) return false; \ 30 } while (0) 31 32 struct FuzzSpec { 33 size_t xsize; 34 size_t ysize; 35 struct OptionSpec { 36 JxlEncoderFrameSettingId id; 37 int32_t value; 38 }; 39 std::vector<OptionSpec> options; 40 bool is_jpeg = false; 41 bool lossless = false; 42 bool have_alpha = false; 43 bool premultiply = false; 44 bool orig_profile = true; 45 uint16_t pixels_seed = 0; 46 uint16_t alpha_seed = 0; 47 size_t bit_depth = 8; 48 size_t alpha_bit_depth = 8; 49 int32_t codestream_level = -1; 50 std::vector<uint8_t> icc; 51 JxlColorEncoding color_encoding; 52 size_t num_frames = 1; 53 size_t output_buffer_size = 1; 54 }; 55 56 bool EncodeJpegXl(const FuzzSpec& spec) { 57 // Multi-threaded parallel runner. Limit to max 2 threads since the fuzzer 58 // itself is already multithreaded. 59 size_t num_threads = 60 std::min<size_t>(2, JxlThreadParallelRunnerDefaultNumWorkerThreads()); 61 auto runner = JxlThreadParallelRunnerMake(nullptr, num_threads); 62 JxlEncoderPtr enc_ptr = JxlEncoderMake(/*memory_manager=*/nullptr); 63 JxlEncoder* enc = enc_ptr.get(); 64 for (size_t num_rep = 0; num_rep < 2; ++num_rep) { 65 JxlEncoderReset(enc); 66 TRY(JxlEncoderSetParallelRunner(enc, JxlThreadParallelRunner, 67 runner.get())); 68 JxlEncoderFrameSettings* frame_settings = 69 JxlEncoderFrameSettingsCreate(enc, nullptr); 70 71 for (auto option : spec.options) { 72 TRY(JxlEncoderFrameSettingsSetOption(frame_settings, option.id, 73 option.value)); 74 } 75 76 TRY(JxlEncoderSetCodestreamLevel(enc, spec.codestream_level)); 77 JxlBasicInfo basic_info; 78 JxlEncoderInitBasicInfo(&basic_info); 79 basic_info.xsize = spec.xsize; 80 basic_info.ysize = spec.ysize; 81 basic_info.bits_per_sample = spec.bit_depth; 82 basic_info.uses_original_profile = TO_JXL_BOOL(spec.orig_profile); 83 if (spec.have_alpha) { 84 basic_info.alpha_bits = spec.alpha_bit_depth; 85 basic_info.num_extra_channels = 1; 86 } 87 TRY(JxlEncoderSetBasicInfo(enc, &basic_info)); 88 if (spec.lossless) { 89 TRY(JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE)); 90 } 91 92 // TODO(szabadka) Add icc color profiles. 93 TRY(JxlEncoderSetColorEncoding(enc, &spec.color_encoding)); 94 95 // TODO(szabadka) Add jpeg frames. 96 for (size_t i = 0; i < spec.num_frames; ++i) { 97 JxlFrameHeader frame_header; 98 JxlEncoderInitFrameHeader(&frame_header); 99 // TODO(szabadka) Add more frame header options. 100 TRY(JxlEncoderSetFrameHeader(frame_settings, &frame_header)); 101 if (spec.have_alpha) { 102 JxlExtraChannelInfo extra_channel_info; 103 JxlEncoderInitExtraChannelInfo(JXL_CHANNEL_ALPHA, &extra_channel_info); 104 TRY(JxlEncoderSetExtraChannelInfo(enc, 0, &extra_channel_info)); 105 extra_channel_info.alpha_premultiplied = TO_JXL_BOOL(spec.premultiply); 106 } 107 JxlPixelFormat pixelformat = {3, JXL_TYPE_UINT16, JXL_LITTLE_ENDIAN, 0}; 108 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage( 109 spec.xsize, spec.ysize, 3, spec.pixels_seed); 110 TRY(JxlEncoderAddImageFrame(frame_settings, &pixelformat, pixels.data(), 111 pixels.size())); 112 if (spec.have_alpha) { 113 std::vector<uint8_t> alpha_pixels = jxl::test::GetSomeTestImage( 114 spec.xsize, spec.ysize, 1, spec.alpha_seed); 115 TRY(JxlEncoderSetExtraChannelBuffer(frame_settings, &pixelformat, 116 alpha_pixels.data(), 117 alpha_pixels.size(), 0)); 118 } 119 } 120 // Reading compressed output 121 JxlEncoderStatus process_result = JXL_ENC_NEED_MORE_OUTPUT; 122 while (process_result == JXL_ENC_NEED_MORE_OUTPUT) { 123 std::vector<uint8_t> buf(spec.output_buffer_size + 32); 124 uint8_t* next_out = buf.data(); 125 size_t avail_out = buf.size(); 126 process_result = JxlEncoderProcessOutput(enc, &next_out, &avail_out); 127 } 128 if (JXL_ENC_SUCCESS != process_result) { 129 return false; 130 } 131 } 132 return true; 133 } 134 135 template <typename T> 136 T Select(const std::vector<T>& vec, 137 const std::function<uint32_t(size_t)>& get_index) { 138 return vec[get_index(vec.size() - 1)]; 139 } 140 141 int TestOneInput(const uint8_t* data, size_t size) { 142 uint64_t flags = 0; 143 size_t flag_bits = 0; 144 145 const auto consume_data = [&]() { 146 if (size < 4) abort(); 147 uint32_t buf = 0; 148 memcpy(&buf, data, 4); 149 data += 4; 150 size -= 4; 151 flags = (flags << 32) | buf; 152 flag_bits += 32; 153 }; 154 155 const auto get_flag = [&](size_t max_value) { 156 size_t limit = 1; 157 while (limit <= max_value) { 158 limit <<= 1; 159 --flag_bits; 160 if (flag_bits <= 16) { 161 consume_data(); 162 } 163 } 164 uint32_t result = flags % limit; 165 flags /= limit; 166 return result % (max_value + 1); 167 }; 168 const auto get_bool_flag = [&]() -> bool { 169 return get_flag(1) ? true : false; 170 }; 171 172 std::vector<JxlColorSpace> colorspaces = { 173 JXL_COLOR_SPACE_RGB, JXL_COLOR_SPACE_GRAY, JXL_COLOR_SPACE_XYB, 174 JXL_COLOR_SPACE_UNKNOWN}; 175 std::vector<JxlWhitePoint> whitepoints = { 176 JXL_WHITE_POINT_D65, JXL_WHITE_POINT_CUSTOM, JXL_WHITE_POINT_E, 177 JXL_WHITE_POINT_DCI}; 178 std::vector<JxlPrimaries> primaries = {JXL_PRIMARIES_SRGB, 179 JXL_PRIMARIES_CUSTOM, 180 JXL_PRIMARIES_2100, JXL_PRIMARIES_P3}; 181 std::vector<JxlTransferFunction> transfer_functions = { 182 JXL_TRANSFER_FUNCTION_709, JXL_TRANSFER_FUNCTION_UNKNOWN, 183 JXL_TRANSFER_FUNCTION_LINEAR, JXL_TRANSFER_FUNCTION_SRGB, 184 JXL_TRANSFER_FUNCTION_PQ, JXL_TRANSFER_FUNCTION_DCI, 185 JXL_TRANSFER_FUNCTION_HLG, JXL_TRANSFER_FUNCTION_GAMMA}; 186 std::vector<JxlRenderingIntent> rendering_intents = { 187 JXL_RENDERING_INTENT_PERCEPTUAL, 188 JXL_RENDERING_INTENT_RELATIVE, 189 JXL_RENDERING_INTENT_SATURATION, 190 JXL_RENDERING_INTENT_ABSOLUTE, 191 }; 192 193 FuzzSpec spec; 194 // Randomly set some options. 195 // TODO(szabadka) Make value bounds option specific. 196 size_t num_options = get_flag(32); 197 for (size_t i = 0; i < num_options; ++i) { 198 FuzzSpec::OptionSpec option; 199 option.id = static_cast<JxlEncoderFrameSettingId>(get_flag(32)); 200 option.value = static_cast<int32_t>(get_flag(16)) - 1; 201 spec.options.push_back(option); 202 } 203 204 spec.xsize = get_flag(4095) + 1; 205 spec.ysize = get_flag(4095) + 1; 206 spec.lossless = get_bool_flag(); 207 if (!spec.lossless) { 208 spec.orig_profile = get_bool_flag(); 209 } 210 spec.have_alpha = get_bool_flag(); 211 spec.premultiply = get_bool_flag(); 212 spec.pixels_seed = get_flag((1 << 16) - 1); 213 spec.alpha_seed = get_flag((1 << 16) - 1); 214 spec.bit_depth = get_flag(15) + 1; 215 spec.alpha_bit_depth = get_flag(15) + 1; 216 spec.color_encoding.color_space = Select(colorspaces, get_flag); 217 spec.color_encoding.white_point = Select(whitepoints, get_flag); 218 spec.color_encoding.primaries = Select(primaries, get_flag); 219 spec.color_encoding.transfer_function = Select(transfer_functions, get_flag); 220 spec.color_encoding.rendering_intent = Select(rendering_intents, get_flag); 221 spec.output_buffer_size = get_flag(4095) + 1; 222 223 const auto targets = hwy::SupportedAndGeneratedTargets(); 224 hwy::SetSupportedTargetsForTest(Select(targets, get_flag)); 225 EncodeJpegXl(spec); 226 hwy::SetSupportedTargetsForTest(0); 227 228 return 0; 229 } 230 231 } // namespace 232 233 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { 234 return TestOneInput(data, size); 235 }