Coverage Report

Created: 2026-06-16 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qpdf/fuzz/qpdf_pages_fuzzer.cc
Line
Count
Source
1
#include <qpdf/Buffer.hh>
2
#include <qpdf/BufferInputSource.hh>
3
#include <qpdf/Pl_Discard.hh>
4
#include <qpdf/QPDF.hh>
5
#include <qpdf/QPDFAcroFormDocumentHelper.hh>
6
#include <qpdf/QPDFOutlineDocumentHelper.hh>
7
#include <qpdf/QPDFPageDocumentHelper.hh>
8
#include <qpdf/QPDFPageLabelDocumentHelper.hh>
9
#include <qpdf/QPDFPageObjectHelper.hh>
10
#include <qpdf/global.hh>
11
12
#include <chrono>
13
#include <iostream>
14
15
class FuzzHelper
16
{
17
  public:
18
    FuzzHelper(unsigned char const* data, size_t size) :
19
        // We do not modify data, so it is safe to remove the const for Buffer
20
273k
        input_buffer(const_cast<unsigned char*>(data), size)
21
273k
    {
22
273k
    }
23
24
    void
25
    run()
26
43.1k
    {
27
43.1k
        qpdf::global::options::fuzz_mode(true);
28
        // The goal here is that you should be able to throw anything at libqpdf and it will respond
29
        // without any memory errors and never do anything worse than throwing a QPDFExc or
30
        // std::runtime_error. Throwing any other kind of exception, segfaulting, or having a memory
31
        // error (when built with appropriate sanitizers) will all cause abnormal exit.
32
43.1k
        try {
33
43.1k
            info("");
34
            // Write in various ways to exercise QPDFWriter
35
36
43.1k
            auto is = std::make_shared<BufferInputSource>("fuzz input", &input_buffer);
37
43.1k
            QPDF qpdf;
38
43.1k
            qpdf.processInputSource(is);
39
43.1k
            info("processInputSource done");
40
43.1k
            QPDFPageDocumentHelper pdh(qpdf);
41
43.1k
            QPDFPageLabelDocumentHelper pldh(qpdf);
42
43.1k
            QPDFOutlineDocumentHelper odh(qpdf);
43
43.1k
            QPDFAcroFormDocumentHelper afdh(qpdf);
44
43.1k
            afdh.generateAppearancesIfNeeded();
45
43.1k
            info("generateAppearancesIfNeeded done");
46
43.1k
            pdh.flattenAnnotations();
47
43.1k
            info("flattenAnnotations done");
48
43.1k
            int pageno = 0;
49
43.1k
            for (auto& page: pdh.getAllPages()) {
50
37.3k
                ++pageno;
51
37.3k
                try {
52
37.3k
                    info("start page", pageno);
53
37.3k
                    page.coalesceContentStreams();
54
37.3k
                    info("coalesceContentStreams done");
55
37.3k
                    page.parseContents(nullptr);
56
37.3k
                    info("parseContents done");
57
37.3k
                    page.getImages();
58
37.3k
                    info("getImages done");
59
37.3k
                    pldh.getLabelForPage(pageno);
60
37.3k
                    info("getLabelForPage done");
61
37.3k
                    QPDFObjectHandle page_obj(page.getObjectHandle());
62
37.3k
                    page_obj.getJSON(JSON::LATEST, true).unparse();
63
37.3k
                    info("getJSON done");
64
37.3k
                    odh.getOutlinesForPage(page_obj);
65
37.3k
                    info("getOutlinesForPage done");
66
67
37.3k
                    for (auto& aoh: afdh.getWidgetAnnotationsForPage(page)) {
68
3.23k
                        afdh.getFieldForAnnotation(aoh);
69
3.23k
                    }
70
37.3k
                } catch (QPDFExc& e) {
71
5.64k
                    std::cerr << "page " << pageno << ": " << e.what() << '\n';
72
5.64k
                }
73
37.3k
            }
74
43.1k
        } catch (std::runtime_error const& e) {
75
28.2k
            std::cerr << "runtime_error: " << e.what() << '\n';
76
28.2k
        }
77
43.1k
    }
78
79
  private:
80
    void
81
    info(std::string const& msg, int pageno = 0) const
82
330k
    {
83
330k
        const std::chrono::duration<double> elapsed{std::chrono::steady_clock::now() - start};
84
85
330k
        std::cerr << elapsed.count() << " info - " << msg;
86
330k
        if (pageno > 0) {
87
37.3k
            std::cerr << " page " << pageno;
88
37.3k
        }
89
330k
        std::cerr << '\n';
90
330k
    }
91
92
    Buffer input_buffer;
93
    Pl_Discard discard;
94
    const std::chrono::time_point<std::chrono::steady_clock> start;
95
};
96
97
extern "C" int
98
LLVMFuzzerTestOneInput(unsigned char const* data, size_t size)
99
273k
{
100
273k
#ifndef _WIN32
101
    // Used by jpeg library to work around false positives in memory sanitizer.
102
273k
    setenv("JSIMD_FORCENONE", "1", 1);
103
273k
#endif
104
273k
    FuzzHelper f(data, size);
105
273k
    f.run();
106
273k
    return 0;
107
273k
}