Coverage Report

Created: 2024-09-08 06:06

/src/qpdf/fuzz/qpdf_crypt_insecure_fuzzer.cc
Line
Count
Source
1
#include <qpdf/Buffer.hh>
2
#include <qpdf/BufferInputSource.hh>
3
#include <qpdf/Pl_DCT.hh>
4
#include <qpdf/Pl_Discard.hh>
5
#include <qpdf/Pl_Flate.hh>
6
#include <qpdf/Pl_PNGFilter.hh>
7
#include <qpdf/Pl_TIFFPredictor.hh>
8
#include <qpdf/QPDF.hh>
9
#include <qpdf/QPDFPageObjectHelper.hh>
10
#include <qpdf/QPDFWriter.hh>
11
#include <qpdf/QUtil.hh>
12
#include <cstdlib>
13
14
class DiscardContents: public QPDFObjectHandle::ParserCallbacks
15
{
16
  public:
17
    ~DiscardContents() override = default;
18
    void
19
    handleObject(QPDFObjectHandle) override
20
    {
21
    }
22
    void
23
    handleEOF() override
24
    {
25
    }
26
};
27
28
class FuzzHelper
29
{
30
  public:
31
    FuzzHelper(unsigned char const* data, size_t size);
32
    void run();
33
34
  private:
35
    std::shared_ptr<QPDF> getQpdf();
36
    std::shared_ptr<QPDFWriter> getWriter(std::shared_ptr<QPDF>);
37
    void doWrite(std::shared_ptr<QPDFWriter> w);
38
    void testWrite();
39
    void doChecks();
40
41
    Buffer input_buffer;
42
    Pl_Discard discard;
43
};
44
45
FuzzHelper::FuzzHelper(unsigned char const* data, size_t size) :
46
    // We do not modify data, so it is safe to remove the const for Buffer
47
    input_buffer(const_cast<unsigned char*>(data), size)
48
114k
{
49
114k
}
50
51
std::shared_ptr<QPDF>
52
FuzzHelper::getQpdf()
53
89.1k
{
54
89.1k
    auto is =
55
89.1k
        std::shared_ptr<InputSource>(new BufferInputSource("fuzz input", &this->input_buffer));
56
89.1k
    auto qpdf = QPDF::create();
57
89.1k
    qpdf->setMaxWarnings(200);
58
89.1k
    qpdf->processInputSource(is);
59
89.1k
    return qpdf;
60
89.1k
}
61
62
std::shared_ptr<QPDFWriter>
63
FuzzHelper::getWriter(std::shared_ptr<QPDF> qpdf)
64
34.6k
{
65
34.6k
    auto w = std::make_shared<QPDFWriter>(*qpdf);
66
34.6k
    w->setOutputPipeline(&this->discard);
67
34.6k
    w->setDecodeLevel(qpdf_dl_all);
68
34.6k
    return w;
69
34.6k
}
70
71
void
72
FuzzHelper::doWrite(std::shared_ptr<QPDFWriter> w)
73
33.4k
{
74
33.4k
    try {
75
33.4k
        w->write();
76
33.4k
    } catch (QPDFExc const& e) {
77
4.07k
        std::cerr << e.what() << std::endl;
78
4.07k
    } catch (std::runtime_error const& e) {
79
1.49k
        std::cerr << e.what() << std::endl;
80
1.49k
    }
81
33.4k
}
82
83
void
84
FuzzHelper::testWrite()
85
53.6k
{
86
    // Write in various ways to exercise QPDFWriter
87
88
53.6k
    std::shared_ptr<QPDF> q;
89
53.6k
    std::shared_ptr<QPDFWriter> w;
90
91
53.6k
    q = getQpdf();
92
53.6k
    w = getWriter(q);
93
53.6k
    w->setStaticID(true);
94
53.6k
    w->setObjectStreamMode(qpdf_o_disable);
95
53.6k
    w->setR3EncryptionParametersInsecure(
96
53.6k
        "u", "o", true, true, true, true, true, true, qpdf_r3p_full);
97
53.6k
    doWrite(w);
98
53.6k
}
99
100
void
101
FuzzHelper::doChecks()
102
101k
{
103
    // Limit the memory used to decompress JPEG files during fuzzing. Excessive memory use during
104
    // fuzzing is due to corrupt JPEG data which sometimes cannot be detected before
105
    // jpeg_start_decompress is called. During normal use of qpdf very large JPEGs can occasionally
106
    // occur legitimately and therefore must be allowed during normal operations.
107
101k
    Pl_DCT::setMemoryLimit(100'000'000);
108
101k
    Pl_DCT::setScanLimit(50);
109
110
101k
    Pl_PNGFilter::setMemoryLimit(1'000'000);
111
101k
    Pl_TIFFPredictor::setMemoryLimit(1'000'000);
112
101k
    Pl_Flate::setMemoryLimit(1'000'000);
113
114
    // Do not decompress corrupt data. This may cause extended runtime within jpeglib without
115
    // exercising additional code paths in qpdf, and potentially causing counterproductive timeouts.
116
101k
    Pl_DCT::setThrowOnCorruptData(true);
117
118
    // Get as much coverage as possible in parts of the library that
119
    // might benefit from fuzzing.
120
101k
    std::cerr << "\ninfo: starting testWrite\n";
121
101k
    testWrite();
122
101k
}
123
124
void
125
FuzzHelper::run()
126
80.5k
{
127
    // The goal here is that you should be able to throw anything at
128
    // libqpdf and it will respond without any memory errors and never
129
    // do anything worse than throwing a QPDFExc or
130
    // std::runtime_error. Throwing any other kind of exception,
131
    // segfaulting, or having a memory error (when built with
132
    // appropriate sanitizers) will all cause abnormal exit.
133
80.5k
    try {
134
80.5k
        doChecks();
135
80.5k
    } catch (QPDFExc const& e) {
136
47.9k
        std::cerr << "QPDFExc: " << e.what() << std::endl;
137
47.9k
    } catch (std::runtime_error const& e) {
138
822
        std::cerr << "runtime_error: " << e.what() << std::endl;
139
822
    }
140
80.5k
}
141
142
extern "C" int
143
LLVMFuzzerTestOneInput(unsigned char const* data, size_t size)
144
114k
{
145
114k
#ifndef _WIN32
146
    // Used by jpeg library to work around false positives in memory
147
    // sanitizer.
148
114k
    setenv("JSIMD_FORCENONE", "1", 1);
149
114k
#endif
150
114k
    FuzzHelper f(data, size);
151
114k
    f.run();
152
114k
    return 0;
153
114k
}