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#include <chrono>
42
43#include <ojph_arch.h>
44#include <ojph_file.h>
45#include <ojph_params.h>
46#include <ojph_mem.h>
47#include <ojph_codestream.h>
48#include <ojph_message.h>
49#include <exception>
50
51extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
52{
53 // The first 2 bytes are used to control decoder options:
54 // byte 0 bit 1: force planar mode
55 // byte 0 bit 2: force interleaved mode
56 // byte 1: number of resolutions to skip (0-7)
57 if (Size < 3)
58 return 0;
59
60 uint8_t opts = Data[0];
61 uint8_t skip_res = Data[1] & 0x07;
62 Data += 2;
63 Size -= 2;
64
65 bool force_planar = (opts & 0x02) != 0;
66 bool force_interleaved = (opts & 0x04) != 0;
67
68 try
69 {
70 ojph::mem_infile infile;
71 infile.open(reinterpret_cast<const ojph::ui8 *>(Data), Size);
72
74
75 // Always enable resilience: all fuzzer inputs are untrusted/mutated,
76 // so the decoder must use its error-recovery path.
78
79 cs.read_headers(&infile);
80
81 // Guard against inputs that cause excessive decoding work.
82 {
83 ojph::param_siz siz = cs.access_siz();
84 ojph::point extent = siz.get_image_extent();
85 ojph::point offset = siz.get_image_offset();
86 ojph::ui64 w = extent.x - offset.x;
87 ojph::ui64 h = extent.y - offset.y;
88 if (w * h * siz.get_num_components() > 65536)
89 {
90 cs.close();
91 return 0;
92 }
93
94 ojph::param_cod cod = cs.access_cod();
95 if (cod.get_num_decompositions() > 5)
96 {
97 cs.close();
98 return 0;
99 }
100
101 // Large precincts cause huge internal buffers and very expensive
102 // per-row wavelet transforms even for small images.
103 for (ojph::ui32 lev = 0; lev <= cod.get_num_decompositions(); ++lev)
104 {
105 ojph::size psiz = cod.get_precinct_size(lev);
106 if (psiz.w > 256 || psiz.h > 256)
107 {
108 cs.close();
109 return 0;
110 }
111 }
112 }
113
114 if (skip_res > 0)
115 cs.restrict_input_resolution(skip_res, skip_res);
116
117 if (force_planar)
118 cs.set_planar(true);
119 else if (force_interleaved)
120 cs.set_planar(false);
121
122 cs.create();
123
124 ojph::param_siz siz = cs.access_siz();
125
126 // Second guard: cap reconstructed dimensions after create().
127 {
128 ojph::ui64 total_recon = 0;
129 for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c)
130 total_recon += (ojph::ui64)siz.get_recon_width(c)
132 if (total_recon > 65536)
133 {
134 cs.close();
135 return 0;
136 }
137 }
138
139 // Time budget: abort if decoding takes too long.
140 auto start_ts = std::chrono::steady_clock::now();
141 ojph::ui32 pull_count = 0;
142 const auto MAX_SECONDS = std::chrono::seconds(10u);
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 auto now = std::chrono::steady_clock::now();
157 if (now - start_ts >= MAX_SECONDS)
158 timed_out = true;
159 }
160 }
161 }
162 }
163 else
164 {
165 ojph::ui32 height = siz.get_recon_height(0);
166 for (ojph::ui32 i = 0; i < height && !timed_out; ++i)
167 {
168 for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c)
169 {
170 ojph::ui32 comp_num;
171 cs.pull(comp_num);
172 if (++pull_count % 64 == 0)
173 {
174 auto now = std::chrono::steady_clock::now();
175 if (now - start_ts >= MAX_SECONDS)
176 timed_out = true;
177 }
178 }
179 }
180 }
181
182 cs.close();
183 }
184 catch (const std::exception &)
185 {
186 }
187
188 return 0;
189}
190
191#ifdef OJPH_FUZZ_TARGET_MAIN
192int main(int argc, char **argv) {
193 if (argc != 2) {
194 return -1;
195 }
196 FILE *f = fopen(argv[1], "rb");
197 if (!f) { return -1; }
198 fseek(f, 0, SEEK_END);
199 long len = ftell(f);
200 if (len < 0) {
201 return -1;
202 }
203 rewind(f);
204 // Prepend 2 control bytes (default: no skip)
205 std::vector<uint8_t> buf(len + 2);
206 buf[0] = 0;
207 buf[1] = 0;
208 size_t n = fread(buf.data() + 2, 1, len, f);
209 if(n != static_cast<size_t>(len)) {
210 return -1;
211 }
212 fclose(f);
213 LLVMFuzzerTestOneInput(buf.data(), buf.size());
214 return 0;
215}
216#endif
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 main(int argc, char *argv[])
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)