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,6 +1028,10 @@ class QPDF
1028 qpdf_offset_t read_xrefTable(qpdf_offset_t offset); 1028 qpdf_offset_t read_xrefTable(qpdf_offset_t offset);
1029 qpdf_offset_t read_xrefStream(qpdf_offset_t offset); 1029 qpdf_offset_t read_xrefStream(qpdf_offset_t offset);
1030 qpdf_offset_t processXRefStream(qpdf_offset_t offset, QPDFObjectHandle& xref_stream); 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 void insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2); 1035 void insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2);
1032 void insertFreeXrefEntry(QPDFObjGen); 1036 void insertFreeXrefEntry(QPDFObjGen);
1033 void insertReconstructedXrefEntry(int obj, qpdf_offset_t f1, int f2); 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,38 +968,19 @@ QPDF::read_xrefStream(qpdf_offset_t xref_offset)
968 return 0; // unreachable 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 auto Index_obj = dict.getKey("/Index"); 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 std::vector<long long> indx; 984 std::vector<long long> indx;
1004 if (Index_obj.isArray()) { 985 if (Index_obj.isArray()) {
1005 int n_index = Index_obj.getArrayNItems(); 986 int n_index = Index_obj.getArrayNItems();
@@ -1017,9 +998,9 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -1017,9 +998,9 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj)
1017 } 998 }
1018 QTC::TC("qpdf", "QPDF xref /Index is array", n_index == 2 ? 0 : 1); 999 QTC::TC("qpdf", "QPDF xref /Index is array", n_index == 2 ? 0 : 1);
1019 } else { 1000 } else {
1020 - // We have already validayed the /Index key. 1001 + // We have already validated the /Index key.
1021 QTC::TC("qpdf", "QPDF xref /Index is null"); 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 indx.push_back(0); 1004 indx.push_back(0);
1024 indx.push_back(size); 1005 indx.push_back(size);
1025 } 1006 }
@@ -1039,20 +1020,50 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -1039,20 +1020,50 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj)
1039 } 1020 }
1040 num_entries += toS(indx.at(i)); 1021 num_entries += toS(indx.at(i));
1041 } 1022 }
1042 -  
1043 // entry_size and num_entries have both been validated to ensure that this multiplication does 1023 // entry_size and num_entries have both been validated to ensure that this multiplication does
1044 // not cause an overflow. 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 std::shared_ptr<Buffer> bp = xref_obj.getStreamData(qpdf_dl_specialized); 1060 std::shared_ptr<Buffer> bp = xref_obj.getStreamData(qpdf_dl_specialized);
1048 size_t actual_size = bp->getSize(); 1061 size_t actual_size = bp->getSize();
1049 1062
1050 if (expected_size != actual_size) { 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 if (expected_size > actual_size) { 1067 if (expected_size > actual_size) {
1057 throw x; 1068 throw x;
1058 } else { 1069 } else {