/src/qpdf/libqpdf/Pl_Flate.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include <qpdf/Pl_Flate.hh> |
2 | | |
3 | | #include <climits> |
4 | | #include <cstring> |
5 | | #include <zlib.h> |
6 | | |
7 | | #include <qpdf/QIntC.hh> |
8 | | #include <qpdf/QUtil.hh> |
9 | | |
10 | | namespace |
11 | | { |
12 | | unsigned long long memory_limit{0}; |
13 | | } // namespace |
14 | | |
15 | | int Pl_Flate::compression_level = Z_DEFAULT_COMPRESSION; |
16 | | |
17 | | Pl_Flate::Members::Members(size_t out_bufsize, action_e action) : |
18 | | out_bufsize(out_bufsize), |
19 | | action(action), |
20 | | initialized(false), |
21 | | zdata(nullptr) |
22 | 2.13k | { |
23 | 2.13k | this->outbuf = QUtil::make_shared_array<unsigned char>(out_bufsize); |
24 | | // Indirect through zdata to reach the z_stream so we don't have to include zlib.h in |
25 | | // Pl_Flate.hh. This means people using shared library versions of qpdf don't have to have zlib |
26 | | // development files available, which particularly helps in a Windows environment. |
27 | 2.13k | this->zdata = new z_stream; |
28 | | |
29 | 2.13k | if (out_bufsize > UINT_MAX) { |
30 | 0 | throw std::runtime_error( |
31 | 0 | "Pl_Flate: zlib doesn't support buffer sizes larger than unsigned int"); |
32 | 0 | } |
33 | | |
34 | 2.13k | z_stream& zstream = *(static_cast<z_stream*>(this->zdata)); |
35 | 2.13k | zstream.zalloc = nullptr; |
36 | 2.13k | zstream.zfree = nullptr; |
37 | 2.13k | zstream.opaque = nullptr; |
38 | 2.13k | zstream.next_in = nullptr; |
39 | 2.13k | zstream.avail_in = 0; |
40 | 2.13k | zstream.next_out = this->outbuf.get(); |
41 | 2.13k | zstream.avail_out = QIntC::to_uint(out_bufsize); |
42 | 2.13k | } |
43 | | |
44 | | Pl_Flate::Members::~Members() |
45 | 2.13k | { |
46 | 2.13k | if (this->initialized) { |
47 | 0 | z_stream& zstream = *(static_cast<z_stream*>(this->zdata)); |
48 | 0 | if (action == a_deflate) { |
49 | 0 | deflateEnd(&zstream); |
50 | 0 | } else { |
51 | 0 | inflateEnd(&zstream); |
52 | 0 | } |
53 | 0 | } |
54 | | |
55 | 2.13k | delete static_cast<z_stream*>(this->zdata); |
56 | 2.13k | this->zdata = nullptr; |
57 | 2.13k | } |
58 | | |
59 | | Pl_Flate::Pl_Flate( |
60 | | char const* identifier, Pipeline* next, action_e action, unsigned int out_bufsize_int) : |
61 | | Pipeline(identifier, next), |
62 | | m(new Members(QIntC::to_size(out_bufsize_int), action)) |
63 | 2.13k | { |
64 | 2.13k | } |
65 | | |
66 | | Pl_Flate::~Pl_Flate() // NOLINT (modernize-use-equals-default) |
67 | 2.13k | { |
68 | | // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer |
69 | 2.13k | } |
70 | | |
71 | | void |
72 | | Pl_Flate::setMemoryLimit(unsigned long long limit) |
73 | 0 | { |
74 | 0 | memory_limit = limit; |
75 | 0 | } |
76 | | |
77 | | void |
78 | | Pl_Flate::setWarnCallback(std::function<void(char const*, int)> callback) |
79 | 0 | { |
80 | 0 | m->callback = callback; |
81 | 0 | } |
82 | | |
83 | | void |
84 | | Pl_Flate::warn(char const* msg, int code) |
85 | 0 | { |
86 | 0 | if (m->callback != nullptr) { |
87 | 0 | m->callback(msg, code); |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | | void |
92 | | Pl_Flate::write(unsigned char const* data, size_t len) |
93 | 2.13k | { |
94 | 2.13k | if (m->outbuf == nullptr) { |
95 | 0 | throw std::logic_error( |
96 | 0 | this->identifier + ": Pl_Flate: write() called after finish() called"); |
97 | 0 | } |
98 | | |
99 | | // Write in chunks in case len is too big to fit in an int. Assume int is at least 32 bits. |
100 | 2.13k | static size_t const max_bytes = 1 << 30; |
101 | 2.13k | size_t bytes_left = len; |
102 | 2.13k | unsigned char const* buf = data; |
103 | 4.27k | while (bytes_left > 0) { |
104 | 2.13k | size_t bytes = (bytes_left >= max_bytes ? max_bytes : bytes_left); |
105 | 2.13k | handleData(buf, bytes, (m->action == a_inflate ? Z_SYNC_FLUSH : Z_NO_FLUSH)); |
106 | 2.13k | bytes_left -= bytes; |
107 | 2.13k | buf += bytes; |
108 | 2.13k | } |
109 | 2.13k | } |
110 | | |
111 | | void |
112 | | Pl_Flate::handleData(unsigned char const* data, size_t len, int flush) |
113 | 4.27k | { |
114 | 4.27k | if (len > UINT_MAX) { |
115 | 0 | throw std::runtime_error("Pl_Flate: zlib doesn't support data blocks larger than int"); |
116 | 0 | } |
117 | 4.27k | z_stream& zstream = *(static_cast<z_stream*>(m->zdata)); |
118 | | // zlib is known not to modify the data pointed to by next_in but doesn't declare the field |
119 | | // value const unless compiled to do so. |
120 | 4.27k | zstream.next_in = const_cast<unsigned char*>(data); |
121 | 4.27k | zstream.avail_in = QIntC::to_uint(len); |
122 | | |
123 | 4.27k | if (!m->initialized) { |
124 | 2.13k | int err = Z_OK; |
125 | | |
126 | | // deflateInit and inflateInit are macros that use old-style casts. |
127 | 2.13k | #if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || defined(__clang__)) |
128 | 2.13k | # pragma GCC diagnostic push |
129 | 2.13k | # pragma GCC diagnostic ignored "-Wold-style-cast" |
130 | 2.13k | #endif |
131 | 2.13k | if (m->action == a_deflate) { |
132 | 2.13k | err = deflateInit(&zstream, compression_level); |
133 | 2.13k | } else { |
134 | 0 | err = inflateInit(&zstream); |
135 | 0 | } |
136 | 2.13k | #if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || defined(__clang__)) |
137 | 2.13k | # pragma GCC diagnostic pop |
138 | 2.13k | #endif |
139 | | |
140 | 2.13k | checkError("Init", err); |
141 | 2.13k | m->initialized = true; |
142 | 2.13k | } |
143 | | |
144 | 4.27k | int err = Z_OK; |
145 | | |
146 | 4.27k | bool done = false; |
147 | 9.52k | while (!done) { |
148 | 5.24k | if (m->action == a_deflate) { |
149 | 5.24k | err = deflate(&zstream, flush); |
150 | 5.24k | } else { |
151 | 0 | err = inflate(&zstream, flush); |
152 | 0 | } |
153 | 5.24k | if ((m->action == a_inflate) && (err != Z_OK) && zstream.msg && |
154 | 5.24k | (strcmp(zstream.msg, "incorrect data check") == 0)) { |
155 | | // Other PDF readers ignore this specific error. Combining this with Z_SYNC_FLUSH |
156 | | // enables qpdf to handle some broken zlib streams without losing data. |
157 | 0 | err = Z_STREAM_END; |
158 | 0 | } |
159 | 5.24k | switch (err) { |
160 | 0 | case Z_BUF_ERROR: |
161 | | // Probably shouldn't be able to happen, but possible as a boundary condition: if the |
162 | | // last call to inflate exactly filled the output buffer, it's possible that the next |
163 | | // call to inflate could have nothing to do. There are PDF files in the wild that have |
164 | | // this error (including at least one in qpdf's test suite). In some cases, we want to |
165 | | // know about this, because it indicates incorrect compression, so call a callback if |
166 | | // provided. |
167 | 0 | this->warn("input stream is complete but output may still be valid", err); |
168 | 0 | done = true; |
169 | 0 | break; |
170 | | |
171 | 2.13k | case Z_STREAM_END: |
172 | 2.13k | done = true; |
173 | | // fall through |
174 | | |
175 | 5.24k | case Z_OK: |
176 | 5.24k | { |
177 | 5.24k | if ((zstream.avail_in == 0) && (zstream.avail_out > 0)) { |
178 | | // There is nothing left to read, and there was sufficient buffer space to write |
179 | | // everything we needed, so we're done for now. |
180 | 4.27k | done = true; |
181 | 4.27k | } |
182 | 5.24k | uLong ready = QIntC::to_ulong(m->out_bufsize - zstream.avail_out); |
183 | 5.24k | if (ready > 0) { |
184 | 5.24k | if (memory_limit) { |
185 | 0 | m->written += ready; |
186 | 0 | if (m->written > memory_limit) { |
187 | 0 | throw std::runtime_error("PL_Flate memory limit exceeded"); |
188 | 0 | } |
189 | 0 | } |
190 | 5.24k | this->getNext()->write(m->outbuf.get(), ready); |
191 | 5.24k | zstream.next_out = m->outbuf.get(); |
192 | 5.24k | zstream.avail_out = QIntC::to_uint(m->out_bufsize); |
193 | 5.24k | } |
194 | 5.24k | } |
195 | 5.24k | break; |
196 | | |
197 | 5.24k | default: |
198 | 0 | this->checkError("data", err); |
199 | 0 | break; |
200 | 5.24k | } |
201 | 5.24k | } |
202 | 4.27k | } |
203 | | |
204 | | void |
205 | | Pl_Flate::finish() |
206 | 2.13k | { |
207 | 2.13k | if (m->written > memory_limit) { |
208 | 0 | return; |
209 | 0 | } |
210 | 2.13k | try { |
211 | 2.13k | if (m->outbuf.get()) { |
212 | 2.13k | if (m->initialized) { |
213 | 2.13k | z_stream& zstream = *(static_cast<z_stream*>(m->zdata)); |
214 | 2.13k | unsigned char buf[1]; |
215 | 2.13k | buf[0] = '\0'; |
216 | 2.13k | handleData(buf, 0, Z_FINISH); |
217 | 2.13k | int err = Z_OK; |
218 | 2.13k | if (m->action == a_deflate) { |
219 | 2.13k | err = deflateEnd(&zstream); |
220 | 2.13k | } else { |
221 | 0 | err = inflateEnd(&zstream); |
222 | 0 | } |
223 | 2.13k | m->initialized = false; |
224 | 2.13k | checkError("End", err); |
225 | 2.13k | } |
226 | | |
227 | 2.13k | m->outbuf = nullptr; |
228 | 2.13k | } |
229 | 2.13k | } catch (std::exception& e) { |
230 | 0 | try { |
231 | 0 | this->getNext()->finish(); |
232 | 0 | } catch (...) { |
233 | | // ignore secondary exception |
234 | 0 | } |
235 | 0 | throw std::runtime_error(e.what()); |
236 | 0 | } |
237 | 2.13k | this->getNext()->finish(); |
238 | 2.13k | } |
239 | | |
240 | | void |
241 | | Pl_Flate::setCompressionLevel(int level) |
242 | 0 | { |
243 | 0 | compression_level = level; |
244 | 0 | } |
245 | | |
246 | | void |
247 | | Pl_Flate::checkError(char const* prefix, int error_code) |
248 | 4.27k | { |
249 | 4.27k | z_stream& zstream = *(static_cast<z_stream*>(m->zdata)); |
250 | 4.27k | if (error_code != Z_OK) { |
251 | 0 | char const* action_str = (m->action == a_deflate ? "deflate" : "inflate"); |
252 | 0 | std::string msg = this->identifier + ": " + action_str + ": " + prefix + ": "; |
253 | |
|
254 | 0 | if (zstream.msg) { |
255 | 0 | msg += zstream.msg; |
256 | 0 | } else { |
257 | 0 | switch (error_code) { |
258 | 0 | case Z_ERRNO: |
259 | 0 | msg += "zlib system error"; |
260 | 0 | break; |
261 | | |
262 | 0 | case Z_STREAM_ERROR: |
263 | 0 | msg += "zlib stream error"; |
264 | 0 | break; |
265 | | |
266 | 0 | case Z_DATA_ERROR: |
267 | 0 | msg += "zlib data error"; |
268 | 0 | break; |
269 | | |
270 | 0 | case Z_MEM_ERROR: |
271 | 0 | msg += "zlib memory error"; |
272 | 0 | break; |
273 | | |
274 | 0 | case Z_BUF_ERROR: |
275 | 0 | msg += "zlib buffer error"; |
276 | 0 | break; |
277 | | |
278 | 0 | case Z_VERSION_ERROR: |
279 | 0 | msg += "zlib version error"; |
280 | 0 | break; |
281 | | |
282 | 0 | default: |
283 | 0 | msg += std::string("zlib unknown error (") + std::to_string(error_code) + ")"; |
284 | 0 | break; |
285 | 0 | } |
286 | 0 | } |
287 | | |
288 | 0 | throw std::runtime_error(msg); |
289 | 0 | } |
290 | 4.27k | } |