Commit 0186d60dcf8679f2133ffbe1b34816731837f136

Authored by m-holger
1 parent 7aa5027b

Add new private method QPDF::processXRefIndex

include/qpdf/QPDF.hh
... ... @@ -1028,6 +1028,10 @@ class QPDF
1028 1028 qpdf_offset_t read_xrefTable(qpdf_offset_t offset);
1029 1029 qpdf_offset_t read_xrefStream(qpdf_offset_t offset);
1030 1030 qpdf_offset_t processXRefStream(qpdf_offset_t offset, QPDFObjectHandle& xref_stream);
  1031 + std::pair<size_t, std::vector<long long>> processXRefIndex(
  1032 + QPDFObjectHandle& dict,
  1033 + size_t entry_size,
  1034 + std::function<QPDFExc(std::string_view)> damaged);
1031 1035 void insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2);
1032 1036 void insertFreeXrefEntry(QPDFObjGen);
1033 1037 void insertReconstructedXrefEntry(int obj, qpdf_offset_t f1, int f2);
... ...
libqpdf/QPDF.cc
... ... @@ -968,38 +968,19 @@ QPDF::read_xrefStream(qpdf_offset_t xref_offset)
968 968 return 0; // unreachable
969 969 }
970 970  
971   -qpdf_offset_t
972   -QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj)
  971 +// Return the expected size of the xref stream and the processed Index array.
  972 +std::pair<size_t, std::vector<long long>>
  973 +QPDF::processXRefIndex(
  974 + QPDFObjectHandle& dict, size_t entry_size, std::function<QPDFExc(std::string_view)> damaged)
973 975 {
974   - auto damaged = [this, xref_offset](std::string_view msg) -> QPDFExc {
975   - return damagedPDF("xref stream", xref_offset, msg.data());
976   - };
  976 + auto max_num_entries = static_cast<unsigned long long>(-1) / entry_size;
977 977  
978   - auto dict = xref_obj.getDict();
979   - auto W_obj = dict.getKey("/W");
  978 + auto Size_obj = dict.getKey("/Size");
980 979 auto Index_obj = dict.getKey("/Index");
981   - if (!(W_obj.isArray() && (W_obj.getArrayNItems() >= 3) && W_obj.getArrayItem(0).isInteger() &&
982   - W_obj.getArrayItem(1).isInteger() && W_obj.getArrayItem(2).isInteger() &&
983   - dict.getKey("/Size").isInteger() && (Index_obj.isArray() || Index_obj.isNull()))) {
984   - throw damaged("Cross-reference stream does not have proper /W and /Index keys");
  980 + if (!(Index_obj.isArray() || Index_obj.isNull())) {
  981 + throw damaged("Cross-reference stream does not have a proper /Index key");
985 982 }
986 983  
987   - int W[3];
988   - size_t entry_size = 0;
989   - int max_bytes = sizeof(qpdf_offset_t);
990   - for (int i = 0; i < 3; ++i) {
991   - W[i] = W_obj.getArrayItem(i).getIntValueAsInt();
992   - if (W[i] > max_bytes) {
993   - throw damaged("Cross-reference stream's /W contains impossibly large values");
994   - }
995   - entry_size += toS(W[i]);
996   - }
997   - if (entry_size == 0) {
998   - throw damaged("Cross-reference stream's /W indicates entry size of 0");
999   - }
1000   - unsigned long long max_num_entries = static_cast<unsigned long long>(-1) / entry_size;
1001   -
1002   - // Process /Index entry
1003 984 std::vector<long long> indx;
1004 985 if (Index_obj.isArray()) {
1005 986 int n_index = Index_obj.getArrayNItems();
... ... @@ -1017,9 +998,9 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj)
1017 998 }
1018 999 QTC::TC("qpdf", "QPDF xref /Index is array", n_index == 2 ? 0 : 1);
1019 1000 } else {
1020   - // We have already validayed the /Index key.
  1001 + // We have already validated the /Index key.
1021 1002 QTC::TC("qpdf", "QPDF xref /Index is null");
1022   - long long size = dict.getKey("/Size").getIntValue();
  1003 + long long size = Size_obj.getIntValue();
1023 1004 indx.push_back(0);
1024 1005 indx.push_back(size);
1025 1006 }
... ... @@ -1039,20 +1020,50 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj)
1039 1020 }
1040 1021 num_entries += toS(indx.at(i));
1041 1022 }
1042   -
1043 1023 // entry_size and num_entries have both been validated to ensure that this multiplication does
1044 1024 // not cause an overflow.
1045   - size_t expected_size = entry_size * num_entries;
  1025 + return {entry_size * num_entries, indx};
  1026 +}
  1027 +
  1028 +qpdf_offset_t
  1029 +QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& xref_obj)
  1030 +{
  1031 + auto damaged = [this, xref_offset](std::string_view msg) -> QPDFExc {
  1032 + return damagedPDF("xref stream", xref_offset, msg.data());
  1033 + };
  1034 +
  1035 + auto dict = xref_obj.getDict();
  1036 + auto W_obj = dict.getKey("/W");
  1037 + auto Size_obj = dict.getKey("/Size");
  1038 + if (!(W_obj.isArray() && (W_obj.getArrayNItems() >= 3) && W_obj.getArrayItem(0).isInteger() &&
  1039 + W_obj.getArrayItem(1).isInteger() && W_obj.getArrayItem(2).isInteger() &&
  1040 + Size_obj.isInteger())) {
  1041 + throw damaged("Cross-reference stream does not have a proper /W key");
  1042 + }
  1043 +
  1044 + int W[3];
  1045 + size_t entry_size = 0;
  1046 + int max_bytes = sizeof(qpdf_offset_t);
  1047 + for (int i = 0; i < 3; ++i) {
  1048 + W[i] = W_obj.getArrayItem(i).getIntValueAsInt();
  1049 + if (W[i] > max_bytes) {
  1050 + throw damaged("Cross-reference stream's /W contains impossibly large values");
  1051 + }
  1052 + entry_size += toS(W[i]);
  1053 + }
  1054 + if (entry_size == 0) {
  1055 + throw damaged("Cross-reference stream's /W indicates entry size of 0");
  1056 + }
  1057 +
  1058 + auto [expected_size, indx] = processXRefIndex(dict, entry_size, damaged);
1046 1059  
1047 1060 std::shared_ptr<Buffer> bp = xref_obj.getStreamData(qpdf_dl_specialized);
1048 1061 size_t actual_size = bp->getSize();
1049 1062  
1050 1063 if (expected_size != actual_size) {
1051   - QPDFExc x = damagedPDF(
1052   - "xref stream",
1053   - xref_offset,
1054   - ("Cross-reference stream data has the wrong size; expected = " +
1055   - std::to_string(expected_size) + "; actual = " + std::to_string(actual_size)));
  1064 + QPDFExc x = damaged(
  1065 + "Cross-reference stream data has the wrong size; expected = " +
  1066 + std::to_string(expected_size) + "; actual = " + std::to_string(actual_size));
1056 1067 if (expected_size > actual_size) {
1057 1068 throw x;
1058 1069 } else {
... ...