Coverage Report

Created: 2024-09-08 06:06

/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
512k
{
23
512k
    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
512k
    this->zdata = new z_stream;
28
29
512k
    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
512k
    z_stream& zstream = *(static_cast<z_stream*>(this->zdata));
35
512k
    zstream.zalloc = nullptr;
36
512k
    zstream.zfree = nullptr;
37
512k
    zstream.opaque = nullptr;
38
512k
    zstream.next_in = nullptr;
39
512k
    zstream.avail_in = 0;
40
512k
    zstream.next_out = this->outbuf.get();
41
512k
    zstream.avail_out = QIntC::to_uint(out_bufsize);
42
512k
}
43
44
Pl_Flate::Members::~Members()
45
512k
{
46
512k
    if (this->initialized) {
47
14.4k
        z_stream& zstream = *(static_cast<z_stream*>(this->zdata));
48
14.4k
        if (action == a_deflate) {
49
2.27k
            deflateEnd(&zstream);
50
12.1k
        } else {
51
12.1k
            inflateEnd(&zstream);
52
12.1k
        }
53
14.4k
    }
54
55
512k
    delete static_cast<z_stream*>(this->zdata);
56
512k
    this->zdata = nullptr;
57
512k
}
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
512k
{
64
512k
}
65
66
Pl_Flate::~Pl_Flate() // NOLINT (modernize-use-equals-default)
67
512k
{
68
    // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
69
512k
}
70
71
void
72
Pl_Flate::setMemoryLimit(unsigned long long limit)
73
80.5k
{
74
80.5k
    memory_limit = limit;
75
80.5k
}
76
77
void
78
Pl_Flate::setWarnCallback(std::function<void(char const*, int)> callback)
79
180k
{
80
180k
    m->callback = callback;
81
180k
}
82
83
void
84
Pl_Flate::warn(char const* msg, int code)
85
10.1k
{
86
10.1k
    if (m->callback != nullptr) {
87
10.1k
        m->callback(msg, code);
88
10.1k
    }
89
10.1k
}
90
91
void
92
Pl_Flate::write(unsigned char const* data, size_t len)
93
24.4M
{
94
24.4M
    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
24.4M
    static size_t const max_bytes = 1 << 30;
101
24.4M
    size_t bytes_left = len;
102
24.4M
    unsigned char const* buf = data;
103
48.9M
    while (bytes_left > 0) {
104
24.4M
        size_t bytes = (bytes_left >= max_bytes ? max_bytes : bytes_left);
105
24.4M
        handleData(buf, bytes, (m->action == a_inflate ? Z_SYNC_FLUSH : Z_NO_FLUSH));
106
24.4M
        bytes_left -= bytes;
107
24.4M
        buf += bytes;
108
24.4M
    }
109
24.4M
}
110
111
void
112
Pl_Flate::handleData(unsigned char const* data, size_t len, int flush)
113
24.9M
{
114
24.9M
    if (len > UINT_MAX) {
115
0
        throw std::runtime_error("Pl_Flate: zlib doesn't support data blocks larger than int");
116
0
    }
117
24.9M
    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
24.9M
    zstream.next_in = const_cast<unsigned char*>(data);
121
24.9M
    zstream.avail_in = QIntC::to_uint(len);
122
123
24.9M
    if (!m->initialized) {
124
498k
        int err = Z_OK;
125
126
        // deflateInit and inflateInit are macros that use old-style casts.
127
498k
#if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || defined(__clang__))
128
498k
# pragma GCC diagnostic push
129
498k
# pragma GCC diagnostic ignored "-Wold-style-cast"
130
498k
#endif
131
498k
        if (m->action == a_deflate) {
132
318k
            err = deflateInit(&zstream, compression_level);
133
318k
        } else {
134
180k
            err = inflateInit(&zstream);
135
180k
        }
136
498k
#if ((defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || defined(__clang__))
137
498k
# pragma GCC diagnostic pop
138
498k
#endif
139
140
498k
        checkError("Init", err);
141
498k
        m->initialized = true;
142
498k
    }
143
144
24.9M
    int err = Z_OK;
145
146
24.9M
    bool done = false;
147
49.9M
    while (!done) {
148
24.9M
        if (m->action == a_deflate) {
149
21.9M
            err = deflate(&zstream, flush);
150
21.9M
        } else {
151
2.98M
            err = inflate(&zstream, flush);
152
2.98M
        }
153
24.9M
        if ((m->action == a_inflate) && (err != Z_OK) && zstream.msg &&
154
24.9M
            (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
65.7k
            err = Z_STREAM_END;
158
65.7k
        }
159
24.9M
        switch (err) {
160
10.1k
        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
10.1k
            this->warn("input stream is complete but output may still be valid", err);
168
10.1k
            done = true;
169
10.1k
            break;
170
171
677k
        case Z_STREAM_END:
172
677k
            done = true;
173
            // fall through
174
175
24.9M
        case Z_OK:
176
24.9M
            {
177
24.9M
                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
24.8M
                    done = true;
181
24.8M
                }
182
24.9M
                uLong ready = QIntC::to_ulong(m->out_bufsize - zstream.avail_out);
183
24.9M
                if (ready > 0) {
184
2.58M
                    if (memory_limit) {
185
2.57M
                        m->written += ready;
186
2.57M
                        if (m->written > memory_limit) {
187
460
                            throw std::runtime_error("PL_Flate memory limit exceeded");
188
460
                        }
189
2.57M
                    }
190
2.58M
                    this->getNext()->write(m->outbuf.get(), ready);
191
2.58M
                    zstream.next_out = m->outbuf.get();
192
2.58M
                    zstream.avail_out = QIntC::to_uint(m->out_bufsize);
193
2.58M
                }
194
24.9M
            }
195
24.9M
            break;
196
197
24.9M
        default:
198
22.5k
            this->checkError("data", err);
199
22.5k
            break;
200
24.9M
        }
201
24.9M
    }
202
24.9M
}
203
204
void
205
Pl_Flate::finish()
206
501k
{
207
501k
    if (m->written > memory_limit) {
208
406
        return;
209
406
    }
210
501k
    try {
211
501k
        if (m->outbuf.get()) {
212
501k
            if (m->initialized) {
213
494k
                z_stream& zstream = *(static_cast<z_stream*>(m->zdata));
214
494k
                unsigned char buf[1];
215
494k
                buf[0] = '\0';
216
494k
                handleData(buf, 0, Z_FINISH);
217
494k
                int err = Z_OK;
218
494k
                if (m->action == a_deflate) {
219
316k
                    err = deflateEnd(&zstream);
220
316k
                } else {
221
178k
                    err = inflateEnd(&zstream);
222
178k
                }
223
494k
                m->initialized = false;
224
494k
                checkError("End", err);
225
494k
            }
226
227
501k
            m->outbuf = nullptr;
228
501k
        }
229
501k
    } catch (std::exception& e) {
230
10.4k
        try {
231
10.4k
            this->getNext()->finish();
232
10.4k
        } catch (...) {
233
            // ignore secondary exception
234
9
        }
235
10.4k
        throw std::runtime_error(e.what());
236
10.4k
    }
237
490k
    this->getNext()->finish();
238
490k
}
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
1.00M
{
249
1.00M
    z_stream& zstream = *(static_cast<z_stream*>(m->zdata));
250
1.00M
    if (error_code != Z_OK) {
251
22.5k
        char const* action_str = (m->action == a_deflate ? "deflate" : "inflate");
252
22.5k
        std::string msg = this->identifier + ": " + action_str + ": " + prefix + ": ";
253
254
22.5k
        if (zstream.msg) {
255
22.2k
            msg += zstream.msg;
256
22.2k
        } else {
257
300
            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
300
            default:
283
300
                msg += std::string("zlib unknown error (") + std::to_string(error_code) + ")";
284
300
                break;
285
300
            }
286
300
        }
287
288
22.5k
        throw std::runtime_error(msg);
289
22.5k
    }
290
1.00M
}