transforms_fuzzer.cc (5069B)
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 <stdint.h> 7 8 #include "lib/jxl/base/random.h" 9 #include "lib/jxl/base/status.h" 10 #include "lib/jxl/dec_bit_reader.h" 11 #include "lib/jxl/modular/encoding/encoding.h" 12 #include "lib/jxl/modular/transform/transform.h" 13 14 namespace jpegxl { 15 namespace tools { 16 17 using ::jxl::BitReader; 18 using ::jxl::BitReaderScopedCloser; 19 using ::jxl::Bytes; 20 using ::jxl::Channel; 21 using ::jxl::GroupHeader; 22 using ::jxl::Image; 23 using ::jxl::ModularOptions; 24 using ::jxl::pixel_type; 25 using ::jxl::Rng; 26 using ::jxl::Status; 27 using ::jxl::Transform; 28 using ::jxl::weighted::Header; 29 30 namespace { 31 void FillChannel(Channel& ch, Rng& rng) { 32 auto* p = &ch.plane; 33 const size_t w = ch.w; 34 const size_t h = ch.h; 35 for (size_t y = 0; y < h; ++y) { 36 pixel_type* row = p->Row(y); 37 for (size_t x = 0; x < w; ++x) { 38 row[x] = rng.UniformU(0, 0x80000000); 39 } 40 } 41 } 42 template <typename T> 43 void AssertEq(T a, T b) { 44 if (a != b) __builtin_trap(); 45 } 46 } // namespace 47 48 int TestOneInput(const uint8_t* data, size_t size) { 49 static Status nevermind = true; 50 BitReader reader(Bytes(data, size)); 51 BitReaderScopedCloser reader_closer(&reader, &nevermind); 52 53 Rng rng(reader.ReadFixedBits<56>()); 54 55 // One of {0, 1, _2_, 3}; "2" will be filtered out soon. 56 size_t nb_chans = static_cast<size_t>(reader.ReadFixedBits<8>()) & 0x3; 57 size_t nb_extra = static_cast<size_t>(reader.ReadFixedBits<8>()) & 0x7; 58 // 1..32 59 size_t bit_depth = 60 (static_cast<size_t>(reader.ReadFixedBits<8>()) & 0x1F) + 1; 61 // {0, 1, 2, 3} 62 size_t log_upsampling = 63 (static_cast<size_t>(reader.ReadFixedBits<8>()) & 0x3); 64 size_t upsampling = 1 << log_upsampling; 65 66 size_t w_orig = static_cast<size_t>(reader.ReadFixedBits<16>()); 67 size_t h_orig = static_cast<size_t>(reader.ReadFixedBits<16>()); 68 size_t w = jxl::DivCeil(w_orig, upsampling); 69 size_t h = jxl::DivCeil(h_orig, upsampling); 70 71 if ((nb_chans == 2) || ((nb_chans + nb_extra) == 0) || (w * h == 0) || 72 ((w_orig * h_orig * (nb_chans + nb_extra)) > (1 << 23))) { 73 return 0; 74 } 75 76 std::vector<int> hshift; 77 std::vector<int> vshift; 78 std::vector<size_t> ec_upsampling; 79 80 for (size_t c = 0; c < nb_chans; c++) { 81 hshift.push_back(static_cast<int>(reader.ReadFixedBits<8>()) & 1); 82 vshift.push_back(static_cast<int>(reader.ReadFixedBits<8>()) & 1); 83 } 84 85 for (size_t ec = 0; ec < nb_extra; ec++) { 86 size_t log_ec_upsampling = 87 (static_cast<size_t>(reader.ReadFixedBits<8>()) & 0x3); 88 log_ec_upsampling = std::max(log_ec_upsampling, log_upsampling); 89 ec_upsampling.push_back(1 << log_ec_upsampling); 90 } 91 92 JXL_ASSIGN_OR_DIE(Image image, 93 Image::Create(w, h, bit_depth, nb_chans + nb_extra)); 94 95 for (size_t c = 0; c < nb_chans; c++) { 96 Channel& ch = image.channel[c]; 97 ch.hshift = hshift[c]; 98 ch.vshift = vshift[c]; 99 JXL_CHECK(ch.shrink(jxl::DivCeil(w, 1 << hshift[c]), 100 jxl::DivCeil(h, 1 << vshift[c]))); 101 } 102 103 for (size_t ec = 0; ec < nb_extra; ec++) { 104 Channel& ch = image.channel[ec + nb_chans]; 105 size_t ch_up = ec_upsampling[ec]; 106 int up_level = 107 jxl::CeilLog2Nonzero(ch_up) - jxl::CeilLog2Nonzero(upsampling); 108 JXL_CHECK( 109 ch.shrink(jxl::DivCeil(w_orig, ch_up), jxl::DivCeil(h_orig, ch_up))); 110 ch.hshift = ch.vshift = up_level; 111 } 112 113 GroupHeader header; 114 if (!jxl::Bundle::Read(&reader, &header)) return 0; 115 Header w_header; 116 if (!jxl::Bundle::Read(&reader, &w_header)) return 0; 117 118 // TODO(eustas): give it a try? 119 if (!reader.AllReadsWithinBounds()) return 0; 120 121 image.transform = header.transforms; 122 for (Transform& transform : image.transform) { 123 if (!transform.MetaApply(image)) return 0; 124 } 125 if (image.error) return 0; 126 127 ModularOptions options; 128 if (!ValidateChannelDimensions(image, options)) return 0; 129 130 for (size_t i = 0; i < image.channel.size(); ++i) { 131 FillChannel(image.channel[i], rng); 132 } 133 134 image.undo_transforms(w_header); 135 136 AssertEq(image.error, false); 137 AssertEq<size_t>(image.nb_meta_channels, 0); 138 AssertEq(image.channel.size(), nb_chans + nb_extra); 139 140 for (size_t c = 0; c < nb_chans; c++) { 141 const Channel& ch = image.channel[c]; 142 AssertEq(ch.hshift, hshift[c]); 143 AssertEq(ch.vshift, vshift[c]); 144 AssertEq(ch.w, jxl::DivCeil(w, 1 << hshift[c])); 145 AssertEq(ch.h, jxl::DivCeil(h, 1 << vshift[c])); 146 } 147 148 for (size_t ec = 0; ec < nb_extra; ec++) { 149 const Channel& ch = image.channel[ec + nb_chans]; 150 size_t ch_up = ec_upsampling[ec]; 151 int up_level = 152 jxl::CeilLog2Nonzero(ch_up) - jxl::CeilLog2Nonzero(upsampling); 153 AssertEq(ch.w, jxl::DivCeil(w_orig, ch_up)); 154 AssertEq(ch.h, jxl::DivCeil(h_orig, ch_up)); 155 AssertEq(ch.hshift, up_level); 156 AssertEq(ch.vshift, up_level); 157 } 158 159 return 0; 160 } 161 162 } // namespace tools 163 } // namespace jpegxl 164 165 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { 166 return jpegxl::tools::TestOneInput(data, size); 167 }