libjxl

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

jpegli_dec_fuzzer.cc (6528B)


      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 <setjmp.h>
      7 #include <stdint.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 
     11 #include <hwy/targets.h>
     12 #include <vector>
     13 
     14 #include "lib/jpegli/decode.h"
     15 
     16 namespace {
     17 
     18 // Externally visible value to ensure pixels are used in the fuzzer.
     19 int external_code = 0;
     20 
     21 template <typename It>
     22 void Consume(const It& begin, const It& end) {
     23   for (auto it = begin; it < end; ++it) {
     24     if (*it == 0) {
     25       external_code ^= ~0;
     26     } else {
     27       external_code ^= *it;
     28     }
     29   }
     30 }
     31 
     32 // Options for the fuzzing
     33 struct FuzzSpec {
     34   size_t chunk_size;
     35   JpegliDataType output_type;
     36   JpegliEndianness output_endianness;
     37   int crop_output;
     38 };
     39 
     40 constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9};
     41 constexpr size_t kNumSourceBuffers = 4;
     42 
     43 class SourceManager {
     44  public:
     45   SourceManager(const uint8_t* data, size_t len, size_t max_chunk_size)
     46       : data_(data), len_(len), max_chunk_size_(max_chunk_size) {
     47     pub_.skip_input_data = skip_input_data;
     48     pub_.resync_to_restart = jpegli_resync_to_restart;
     49     pub_.term_source = term_source;
     50     pub_.init_source = init_source;
     51     pub_.fill_input_buffer = fill_input_buffer;
     52     if (max_chunk_size_ == 0) max_chunk_size_ = len;
     53     buffers_.resize(kNumSourceBuffers, std::vector<uint8_t>(max_chunk_size_));
     54     Reset();
     55   }
     56 
     57   void Reset() {
     58     pub_.next_input_byte = nullptr;
     59     pub_.bytes_in_buffer = 0;
     60     pos_ = 0;
     61     chunk_idx_ = 0;
     62   }
     63 
     64  private:
     65   jpeg_source_mgr pub_;
     66   const uint8_t* data_;
     67   size_t len_;
     68   size_t chunk_idx_;
     69   size_t pos_;
     70   size_t max_chunk_size_;
     71   std::vector<std::vector<uint8_t>> buffers_;
     72 
     73   static void init_source(j_decompress_ptr cinfo) {}
     74 
     75   static boolean fill_input_buffer(j_decompress_ptr cinfo) {
     76     auto* src = reinterpret_cast<SourceManager*>(cinfo->src);
     77     if (src->pos_ < src->len_) {
     78       size_t remaining = src->len_ - src->pos_;
     79       size_t chunk_size = std::min(remaining, src->max_chunk_size_);
     80       size_t next_idx = ++src->chunk_idx_ % kNumSourceBuffers;
     81       // Larger number of chunks causes fuzzer timuout.
     82       if (src->chunk_idx_ >= (1u << 15)) {
     83         chunk_size = remaining;
     84         next_idx = src->buffers_.size();
     85         src->buffers_.emplace_back(chunk_size);
     86       }
     87       uint8_t* next_buffer = src->buffers_[next_idx].data();
     88       memcpy(next_buffer, src->data_ + src->pos_, chunk_size);
     89       src->pub_.next_input_byte = next_buffer;
     90       src->pub_.bytes_in_buffer = chunk_size;
     91     } else {
     92       src->pub_.next_input_byte = kFakeEoiMarker;
     93       src->pub_.bytes_in_buffer = 2;
     94       src->len_ += 2;
     95     }
     96     src->pos_ += src->pub_.bytes_in_buffer;
     97     return TRUE;
     98   }
     99 
    100   static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
    101     auto* src = reinterpret_cast<SourceManager*>(cinfo->src);
    102     if (num_bytes <= 0) {
    103       return;
    104     }
    105     if (src->pub_.bytes_in_buffer >= static_cast<size_t>(num_bytes)) {
    106       src->pub_.bytes_in_buffer -= num_bytes;
    107       src->pub_.next_input_byte += num_bytes;
    108     } else {
    109       src->pos_ += num_bytes - src->pub_.bytes_in_buffer;
    110       src->pub_.bytes_in_buffer = 0;
    111     }
    112   }
    113 
    114   static void term_source(j_decompress_ptr cinfo) {}
    115 };
    116 
    117 bool DecodeJpeg(const uint8_t* data, size_t size, size_t max_pixels,
    118                 const FuzzSpec& spec, std::vector<uint8_t>* pixels,
    119                 size_t* xsize, size_t* ysize) {
    120   SourceManager src(data, size, spec.chunk_size);
    121   jpeg_decompress_struct cinfo;
    122   const auto try_catch_block = [&]() -> bool {
    123     jpeg_error_mgr jerr;
    124     jmp_buf env;
    125     cinfo.err = jpegli_std_error(&jerr);
    126     if (setjmp(env)) {
    127       return false;
    128     }
    129     cinfo.client_data = reinterpret_cast<void*>(&env);
    130     cinfo.err->error_exit = [](j_common_ptr cinfo) {
    131       jmp_buf* env = reinterpret_cast<jmp_buf*>(cinfo->client_data);
    132       jpegli_destroy(cinfo);
    133       longjmp(*env, 1);
    134     };
    135     cinfo.err->emit_message = [](j_common_ptr cinfo, int msg_level) {};
    136     jpegli_create_decompress(&cinfo);
    137     cinfo.src = reinterpret_cast<jpeg_source_mgr*>(&src);
    138     jpegli_read_header(&cinfo, TRUE);
    139     *xsize = cinfo.image_width;
    140     *ysize = cinfo.image_height;
    141     size_t num_pixels = *xsize * *ysize;
    142     if (num_pixels > max_pixels) return false;
    143     jpegli_set_output_format(&cinfo, spec.output_type, spec.output_endianness);
    144     jpegli_start_decompress(&cinfo);
    145     if (spec.crop_output) {
    146       JDIMENSION xoffset = cinfo.output_width / 3;
    147       JDIMENSION xsize_cropped = cinfo.output_width / 3;
    148       jpegli_crop_scanline(&cinfo, &xoffset, &xsize_cropped);
    149     }
    150 
    151     size_t bytes_per_sample = jpegli_bytes_per_sample(spec.output_type);
    152     size_t stride =
    153         bytes_per_sample * cinfo.output_components * cinfo.output_width;
    154     size_t buffer_size = *ysize * stride;
    155     pixels->resize(buffer_size);
    156     for (size_t y = 0; y < *ysize; ++y) {
    157       JSAMPROW rows[] = {pixels->data() + y * stride};
    158       jpegli_read_scanlines(&cinfo, rows, 1);
    159     }
    160     Consume(pixels->cbegin(), pixels->cend());
    161     jpegli_finish_decompress(&cinfo);
    162     return true;
    163   };
    164   bool success = try_catch_block();
    165   jpegli_destroy_decompress(&cinfo);
    166   return success;
    167 }
    168 
    169 int TestOneInput(const uint8_t* data, size_t size) {
    170   if (size < 4) return 0;
    171   uint32_t flags = 0;
    172   size_t used_flag_bits = 0;
    173   memcpy(&flags, data + size - 4, 4);
    174   size -= 4;
    175 
    176   const auto getFlag = [&flags, &used_flag_bits](size_t max_value) {
    177     size_t limit = 1;
    178     while (limit <= max_value) {
    179       limit <<= 1;
    180       used_flag_bits++;
    181       if (used_flag_bits > 32) abort();
    182     }
    183     uint32_t result = flags % limit;
    184     flags /= limit;
    185     return result % (max_value + 1);
    186   };
    187 
    188   FuzzSpec spec;
    189   spec.output_type = static_cast<JpegliDataType>(getFlag(JPEGLI_TYPE_UINT16));
    190   spec.output_endianness =
    191       static_cast<JpegliEndianness>(getFlag(JPEGLI_BIG_ENDIAN));
    192   uint32_t chunks = getFlag(15);
    193   spec.chunk_size = chunks ? 1u << (chunks - 1) : 0;
    194   spec.crop_output = getFlag(1);
    195 
    196   std::vector<uint8_t> pixels;
    197   size_t xsize;
    198   size_t ysize;
    199   size_t max_pixels = 1 << 21;
    200 
    201   const auto targets = hwy::SupportedAndGeneratedTargets();
    202   hwy::SetSupportedTargetsForTest(targets[getFlag(targets.size() - 1)]);
    203   DecodeJpeg(data, size, max_pixels, spec, &pixels, &xsize, &ysize);
    204   hwy::SetSupportedTargetsForTest(0);
    205 
    206   return 0;
    207 }
    208 
    209 }  // namespace
    210 
    211 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
    212   return TestOneInput(data, size);
    213 }