Coverage Report

Created: 2025-11-16 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/tools/transforms_fuzzer.cc
Line
Count
Source
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/memory_manager.h>
7
8
#include <algorithm>
9
#include <cstddef>
10
#include <cstdint>
11
#include <cstdio>
12
#include <vector>
13
14
#include "lib/jxl/base/bits.h"
15
#include "lib/jxl/base/common.h"
16
#include "lib/jxl/base/compiler_specific.h"
17
#include "lib/jxl/base/random.h"
18
#include "lib/jxl/base/span.h"
19
#include "lib/jxl/base/status.h"
20
#include "lib/jxl/dec_bit_reader.h"
21
#include "lib/jxl/fields.h"
22
#include "lib/jxl/fuzztest.h"
23
#include "lib/jxl/modular/encoding/encoding.h"
24
#include "lib/jxl/modular/modular_image.h"
25
#include "lib/jxl/modular/options.h"
26
#include "lib/jxl/modular/transform/transform.h"
27
#include "tools/tracking_memory_manager.h"
28
29
namespace {
30
31
using ::jpegxl::tools::kGiB;
32
using ::jpegxl::tools::TrackingMemoryManager;
33
using ::jxl::BitReader;
34
using ::jxl::BitReaderScopedCloser;
35
using ::jxl::Bytes;
36
using ::jxl::Channel;
37
using ::jxl::GroupHeader;
38
using ::jxl::Image;
39
using ::jxl::ModularOptions;
40
using ::jxl::pixel_type;
41
using ::jxl::Rng;
42
using ::jxl::Status;
43
using ::jxl::Transform;
44
using ::jxl::weighted::Header;
45
46
121k
void FillChannel(Channel& ch, Rng& rng) {
47
121k
  auto* p = &ch.plane;
48
121k
  const size_t w = ch.w;
49
121k
  const size_t h = ch.h;
50
196M
  for (size_t y = 0; y < h; ++y) {
51
196M
    pixel_type* row = p->Row(y);
52
3.46G
    for (size_t x = 0; x < w; ++x) {
53
3.26G
      row[x] = rng.UniformU(0, 0x80000000);
54
3.26G
    }
55
196M
  }
56
121k
}
57
58
28.7k
void CheckImpl(bool ok, const char* conndition, const char* file, int line) {
59
28.7k
  if (!ok) {
60
0
    fprintf(stderr, "Check(%s) failed at %s:%d\n", conndition, file, line);
61
0
    JXL_CRASH();
62
0
  }
63
28.7k
}
64
28.7k
#define Check(OK) CheckImpl((OK), #OK, __FILE__, __LINE__)
65
66
2.29k
void Run(BitReader& reader, JxlMemoryManager* memory_manager) {
67
2.29k
  Rng rng(reader.ReadFixedBits<56>());
68
69
  // One of {0, 1, _2_, 3}; "2" will be filtered out soon.
70
2.29k
  size_t nb_chans = static_cast<size_t>(reader.ReadFixedBits<8>()) & 0x3;
71
2.29k
  size_t nb_extra = static_cast<size_t>(reader.ReadFixedBits<8>()) & 0x7;
72
  // 1..32
73
2.29k
  size_t bit_depth =
74
2.29k
      (static_cast<size_t>(reader.ReadFixedBits<8>()) & 0x1F) + 1;
75
  // {0, 1, 2, 3}
76
2.29k
  size_t log_upsampling =
77
2.29k
      (static_cast<size_t>(reader.ReadFixedBits<8>()) & 0x3);
78
2.29k
  size_t upsampling = 1 << log_upsampling;
79
80
2.29k
  size_t w_orig = static_cast<size_t>(reader.ReadFixedBits<16>());
81
2.29k
  size_t h_orig = static_cast<size_t>(reader.ReadFixedBits<16>());
82
2.29k
  size_t w = jxl::DivCeil(w_orig, upsampling);
83
2.29k
  size_t h = jxl::DivCeil(h_orig, upsampling);
84
85
2.29k
  if ((nb_chans == 2) || ((nb_chans + nb_extra) == 0) || (w * h == 0) ||
86
2.28k
      ((w_orig * h_orig * (nb_chans + nb_extra)) > (1 << 23))) {
87
35
    return;
88
35
  }
89
90
2.26k
  std::vector<int> hshift;
91
2.26k
  std::vector<int> vshift;
92
2.26k
  std::vector<size_t> ec_upsampling;
93
94
2.61k
  for (size_t c = 0; c < nb_chans; c++) {
95
350
    hshift.push_back(static_cast<int>(reader.ReadFixedBits<8>()) & 1);
96
350
    vshift.push_back(static_cast<int>(reader.ReadFixedBits<8>()) & 1);
97
350
  }
98
99
7.48k
  for (size_t ec = 0; ec < nb_extra; ec++) {
100
5.22k
    size_t log_ec_upsampling =
101
5.22k
        (static_cast<size_t>(reader.ReadFixedBits<8>()) & 0x3);
102
5.22k
    log_ec_upsampling = std::max(log_ec_upsampling, log_upsampling);
103
5.22k
    ec_upsampling.push_back(1 << log_ec_upsampling);
104
5.22k
  }
105
106
2.26k
  Image image{memory_manager};
107
2.26k
  bool ok = [&]() -> Status {
108
2.26k
    JXL_ASSIGN_OR_RETURN(image, Image::Create(memory_manager, w, h, bit_depth,
109
2.26k
                                              nb_chans + nb_extra));
110
2.26k
    return true;
111
2.26k
  }();
112
  // OOM is ok here.
113
2.26k
  if (!ok) return;
114
115
2.61k
  for (size_t c = 0; c < nb_chans; c++) {
116
350
    Channel& ch = image.channel[c];
117
350
    ch.hshift = hshift[c];
118
350
    ch.vshift = vshift[c];
119
350
    Check(ch.shrink(jxl::DivCeil(w, 1 << hshift[c]),
120
350
                    jxl::DivCeil(h, 1 << vshift[c])));
121
350
  }
122
123
7.48k
  for (size_t ec = 0; ec < nb_extra; ec++) {
124
5.22k
    Channel& ch = image.channel[ec + nb_chans];
125
5.22k
    size_t ch_up = ec_upsampling[ec];
126
5.22k
    int up_level =
127
5.22k
        jxl::CeilLog2Nonzero(ch_up) - jxl::CeilLog2Nonzero(upsampling);
128
5.22k
    Check(ch.shrink(jxl::DivCeil(w_orig, ch_up), jxl::DivCeil(h_orig, ch_up)));
129
5.22k
    ch.hshift = ch.vshift = up_level;
130
5.22k
  }
131
132
2.26k
  GroupHeader header;
133
2.26k
  if (!jxl::Bundle::Read(&reader, &header)) return;
134
1.98k
  Header w_header;
135
1.98k
  if (!jxl::Bundle::Read(&reader, &w_header)) return;
136
137
  // TODO(eustas): give it a try?
138
1.97k
  if (!reader.AllReadsWithinBounds()) return;
139
140
1.97k
  image.transform = header.transforms;
141
6.09k
  for (Transform& transform : image.transform) {
142
6.09k
    if (!transform.MetaApply(image)) return;
143
6.09k
  }
144
1.72k
  if (image.error) return;
145
146
1.72k
  ModularOptions options;
147
1.72k
  if (!ValidateChannelDimensions(image, options)) return;
148
149
121k
  for (Channel& ch : image.channel) {
150
121k
    FillChannel(ch, rng);
151
121k
  }
152
153
1.72k
  image.undo_transforms(w_header);
154
155
1.72k
  Check(!image.error);
156
1.72k
  Check(image.nb_meta_channels == 0);
157
1.72k
  Check(image.channel.size() == nb_chans + nb_extra);
158
159
1.81k
  for (size_t c = 0; c < nb_chans; c++) {
160
95
    const Channel& ch = image.channel[c];
161
95
    Check(ch.hshift == hshift[c]);
162
95
    Check(ch.vshift == vshift[c]);
163
95
    Check(ch.w == jxl::DivCeil(w, 1 << hshift[c]));
164
95
    Check(ch.h == jxl::DivCeil(h, 1 << vshift[c]));
165
95
  }
166
167
5.56k
  for (size_t ec = 0; ec < nb_extra; ec++) {
168
3.83k
    const Channel& ch = image.channel[ec + nb_chans];
169
3.83k
    size_t ch_up = ec_upsampling[ec];
170
3.83k
    int up_level =
171
3.83k
        jxl::CeilLog2Nonzero(ch_up) - jxl::CeilLog2Nonzero(upsampling);
172
3.83k
    Check(ch.w == jxl::DivCeil(w_orig, ch_up));
173
3.83k
    Check(ch.h == jxl::DivCeil(h_orig, ch_up));
174
3.83k
    Check(ch.hshift == up_level);
175
3.83k
    Check(ch.vshift == up_level);
176
3.83k
  }
177
1.72k
}
178
179
2.30k
int DoTestOneInput(const uint8_t* data, size_t size) {
180
2.30k
  if (size < 15) return 0;
181
182
2.29k
  TrackingMemoryManager memory_manager{/* cap */ 1 * kGiB,
183
2.29k
                                       /* total_cap */ 5 * kGiB};
184
2.29k
  {
185
2.29k
    static Status nevermind = true;
186
2.29k
    BitReader reader(Bytes(data, size));
187
2.29k
    BitReaderScopedCloser reader_closer(reader, nevermind);
188
2.29k
    Run(reader, memory_manager.get());
189
2.29k
  }
190
2.29k
  Check(memory_manager.Reset());
191
192
2.29k
  return 0;
193
2.30k
}
194
195
}  // namespace
196
197
67.9k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
198
67.9k
  return DoTestOneInput(data, size);
199
67.9k
}
200
201
0
void TestOneInput(const std::vector<uint8_t>& data) {
202
0
  DoTestOneInput(data.data(), data.size());
203
0
}
204
205
FUZZ_TEST(TransformsFuzzTest, TestOneInput);