OpenJPH
Open-source implementation of JPEG2000 Part-15
Loading...
Searching...
No Matches
ojph_expand_fuzz_target.cpp
Go to the documentation of this file.
1//***************************************************************************/
2// This software is released under the 2-Clause BSD license, included
3// below.
4//
5// Copyright (c) 2019, Aous Naman
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are
9// met:
10//
11// 1. Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13//
14// 2. Redistributions in binary form must reproduce the above copyright
15// notice, this list of conditions and the following disclaimer in the
16// documentation and/or other materials provided with the distribution.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
21// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29//***************************************************************************/
30// This file is part of the OpenJPH software implementation.
31// File: ojph_expand_fuzz_target.cpp
32// Author: Pierre-Anthony Lemieux
33// Date: 17 February 2026
34//***************************************************************************/
35
36#include <cstdint>
37#include <cstdio>
38#include <cstdlib>
39#include <ctime>
40#include <vector>
41
42#include <ojph_arch.h>
43#include <ojph_file.h>
44#include <ojph_params.h>
45#include <ojph_mem.h>
46#include <ojph_codestream.h>
47#include <ojph_message.h>
48#include <exception>
49
50extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
51{
52 // The first 2 bytes are used to control decoder options:
53 // byte 0 bit 1: force planar mode
54 // byte 0 bit 2: force interleaved mode
55 // byte 1: number of resolutions to skip (0-7)
56 if (Size < 3)
57 return 0;
58
59 uint8_t opts = Data[0];
60 uint8_t skip_res = Data[1] & 0x07;
61 Data += 2;
62 Size -= 2;
63
64 bool force_planar = (opts & 0x02) != 0;
65 bool force_interleaved = (opts & 0x04) != 0;
66
67 try
68 {
69 ojph::mem_infile infile;
70 infile.open(reinterpret_cast<const ojph::ui8 *>(Data), Size);
71
73
74 // Always enable resilience: all fuzzer inputs are untrusted/mutated,
75 // so the decoder must use its error-recovery path.
77
78 cs.read_headers(&infile);
79
80 // Guard against inputs that cause excessive decoding work.
81 {
82 ojph::param_siz siz = cs.access_siz();
83 ojph::point extent = siz.get_image_extent();
84 ojph::point offset = siz.get_image_offset();
85 ojph::ui64 w = extent.x - offset.x;
86 ojph::ui64 h = extent.y - offset.y;
87 if (w * h * siz.get_num_components() > 65536)
88 {
89 cs.close();
90 return 0;
91 }
92
93 ojph::param_cod cod = cs.access_cod();
94 if (cod.get_num_decompositions() > 5)
95 {
96 cs.close();
97 return 0;
98 }
99
100 // Large precincts cause huge internal buffers and very expensive
101 // per-row wavelet transforms even for small images.
102 for (ojph::ui32 lev = 0; lev <= cod.get_num_decompositions(); ++lev)
103 {
104 ojph::size psiz = cod.get_precinct_size(lev);
105 if (psiz.w > 256 || psiz.h > 256)
106 {
107 cs.close();
108 return 0;
109 }
110 }
111 }
112
113 if (skip_res > 0)
114 cs.restrict_input_resolution(skip_res, skip_res);
115
116 if (force_planar)
117 cs.set_planar(true);
118 else if (force_interleaved)
119 cs.set_planar(false);
120
121 cs.create();
122
123 ojph::param_siz siz = cs.access_siz();
124
125 // Second guard: cap reconstructed dimensions after create().
126 {
127 ojph::ui64 total_recon = 0;
128 for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c)
129 total_recon += (ojph::ui64)siz.get_recon_width(c)
131 if (total_recon > 65536)
132 {
133 cs.close();
134 return 0;
135 }
136 }
137
138 // Time budget: abort if decoding takes too long.
139 struct timespec start_ts;
140 clock_gettime(CLOCK_MONOTONIC, &start_ts);
141 ojph::ui32 pull_count = 0;
142 const ojph::ui32 MAX_SECONDS = 10;
143 bool timed_out = false;
144
145 if (cs.is_planar())
146 {
147 for (ojph::ui32 c = 0; c < siz.get_num_components() && !timed_out; ++c)
148 {
149 ojph::ui32 height = siz.get_recon_height(c);
150 for (ojph::ui32 i = height; i > 0 && !timed_out; --i)
151 {
152 ojph::ui32 comp_num;
153 cs.pull(comp_num);
154 if (++pull_count % 64 == 0)
155 {
156 struct timespec now;
157 clock_gettime(CLOCK_MONOTONIC, &now);
158 if ((ojph::ui32)(now.tv_sec - start_ts.tv_sec) >= MAX_SECONDS)
159 timed_out = true;
160 }
161 }
162 }
163 }
164 else
165 {
166 ojph::ui32 height = siz.get_recon_height(0);
167 for (ojph::ui32 i = 0; i < height && !timed_out; ++i)
168 {
169 for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c)
170 {
171 ojph::ui32 comp_num;
172 cs.pull(comp_num);
173 if (++pull_count % 64 == 0)
174 {
175 struct timespec now;
176 clock_gettime(CLOCK_MONOTONIC, &now);
177 if ((ojph::ui32)(now.tv_sec - start_ts.tv_sec) >= MAX_SECONDS)
178 timed_out = true;
179 }
180 }
181 }
182 }
183
184 cs.close();
185 }
186 catch (const std::exception &)
187 {
188 }
189
190 return 0;
191}
192
193#ifdef OJPH_FUZZ_TARGET_MAIN
194int main(int argc, char **argv) {
195 if (argc != 2) {
196 return -1;
197 }
198 FILE *f = fopen(argv[1], "rb");
199 if (!f) { return -1; }
200 fseek(f, 0, SEEK_END);
201 long len = ftell(f);
202 if (len < 0) {
203 return -1;
204 }
205 rewind(f);
206 // Prepend 2 control bytes (default: no skip)
207 std::vector<uint8_t> buf(len + 2);
208 buf[0] = 0;
209 buf[1] = 0;
210 size_t n = fread(buf.data() + 2, 1, len, f);
211 if(n != static_cast<size_t>(len)) {
212 return -1;
213 }
214 fclose(f);
215 LLVMFuzzerTestOneInput(buf.data(), buf.size());
216 return 0;
217}
218#endif
int main(int argc, char *argv[])
The object represent a codestream.
param_siz access_siz()
Returns the underlying SIZ marker segment object.
param_cod access_cod()
Returns the underlying COD marker segment object.
void restrict_input_resolution(ui32 skipped_res_for_data, ui32 skipped_res_for_recon)
This function restricts resolution decoding for a codestream. It is for a reading (decoding) codestre...
void close()
Call this function to close the underlying file; works for both encoding and decoding codestreams.
void set_planar(bool planar)
Sets the sequence of pushing or pull rows from the machinery.
void enable_resilience()
This enables codestream resilience; that is, the library tries its best to decode the codestream,...
void read_headers(infile_base *file)
This call reads the headers of a codestream. It is for a reading (or decoding) codestream,...
void create()
This call is for a decoding (or reading) codestream. Call this function after calling restrict_input_...
bool is_planar() const
Query if the codestream extraction is planar or not. See the documentation for ojph::codestream::set_...
line_buf * pull(ui32 &comp_num)
This call is to pull one row from the codestream, being decoded. The returned line_buf object holds o...
void open(const ui8 *data, size_t size)
ui32 get_num_decompositions() const
size get_precinct_size(ui32 level_num) const
point get_image_extent() const
point get_image_offset() const
ui32 get_recon_height(ui32 comp_num) const
ui32 get_recon_width(ui32 comp_num) const
ui32 get_num_components() const
uint64_t ui64
Definition ojph_defs.h:56
uint32_t ui32
Definition ojph_defs.h:54
uint8_t ui8
Definition ojph_defs.h:50
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)