libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

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 }