diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 99ee2da..f76beb1 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -1028,6 +1028,8 @@ class QPDF qpdf_offset_t read_xrefTable(qpdf_offset_t offset); qpdf_offset_t read_xrefStream(qpdf_offset_t offset); qpdf_offset_t processXRefStream(qpdf_offset_t offset, QPDFObjectHandle& xref_stream); + std::pair> + processXRefW(QPDFObjectHandle& dict, std::function damaged); std::pair> processXRefIndex( QPDFObjectHandle& dict, size_t entry_size, diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 6d60df9..18bf373 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -968,6 +968,36 @@ QPDF::read_xrefStream(qpdf_offset_t xref_offset) return 0; // unreachable } +// Return the entry size of the xref stream and the processed W array. +std::pair> +QPDF::processXRefW(QPDFObjectHandle& dict, std::function damaged) +{ + auto W_obj = dict.getKey("/W"); + if (!(W_obj.isArray() && (W_obj.getArrayNItems() >= 3) && W_obj.getArrayItem(0).isInteger() && + W_obj.getArrayItem(1).isInteger() && W_obj.getArrayItem(2).isInteger())) { + throw damaged("Cross-reference stream does not have a proper /W key"); + } + + std::array W; + int entry_size = 0; + auto w_vector = W_obj.getArrayAsVector(); + int max_bytes = sizeof(qpdf_offset_t); + for (size_t i = 0; i < 3; ++i) { + W[i] = w_vector[i].getIntValueAsInt(); + if (W[i] > max_bytes) { + throw damaged("Cross-reference stream's /W contains impossibly large values"); + } + if (W[i] < 0) { + throw damaged("Cross-reference stream's /W contains negative values"); + } + entry_size += W[i]; + } + if (entry_size == 0) { + throw damaged("Cross-reference stream's /W indicates entry size of 0"); + } + return {toS(entry_size), W}; +} + // Return the expected size of the xref stream and the processed Index array. std::pair> QPDF::processXRefIndex( @@ -1033,28 +1063,12 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj) }; auto dict = xref_obj.getDict(); - auto W_obj = dict.getKey("/W"); auto Size_obj = dict.getKey("/Size"); - if (!(W_obj.isArray() && (W_obj.getArrayNItems() >= 3) && W_obj.getArrayItem(0).isInteger() && - W_obj.getArrayItem(1).isInteger() && W_obj.getArrayItem(2).isInteger() && - Size_obj.isInteger())) { - throw damaged("Cross-reference stream does not have a proper /W key"); - } - - int W[3]; - size_t entry_size = 0; - int max_bytes = sizeof(qpdf_offset_t); - for (int i = 0; i < 3; ++i) { - W[i] = W_obj.getArrayItem(i).getIntValueAsInt(); - if (W[i] > max_bytes) { - throw damaged("Cross-reference stream's /W contains impossibly large values"); - } - entry_size += toS(W[i]); - } - if (entry_size == 0) { - throw damaged("Cross-reference stream's /W indicates entry size of 0"); + if (!Size_obj.isInteger()) { + throw damaged("Cross-reference stream does not have a proper /Size key"); } + auto [entry_size, W] = processXRefW(dict, damaged); auto [expected_size, indx] = processXRefIndex(dict, entry_size, damaged); std::shared_ptr bp = xref_obj.getStreamData(qpdf_dl_specialized); @@ -1090,8 +1104,8 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj) throw std::range_error(msg.str()); } // Read this entry - qpdf_offset_t fields[3]; - for (int j = 0; j < 3; ++j) { + std::array fields; + for (size_t j = 0; j < 3; ++j) { fields[j] = 0; if ((j == 0) && (W[0] == 0)) { QTC::TC("qpdf", "QPDF default for xref stream field 0");