Commit 7477ea7828e57904213b2717f5d8ede0a8697ded

Authored by m-holger
1 parent f74b28f0

Add new private method QPDF::processXRefSize

include/qpdf/QPDF.hh
@@ -1028,11 +1028,13 @@ class QPDF @@ -1028,11 +1028,13 @@ 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::array<int, 3>> 1031 + std::pair<int, std::array<int, 3>>
1032 processXRefW(QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged); 1032 processXRefW(QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged);
1033 - std::pair<size_t, std::vector<long long>> processXRefIndex( 1033 + int processXRefSize(
  1034 + QPDFObjectHandle& dict, int entry_size, std::function<QPDFExc(std::string_view)> damaged);
  1035 + std::pair<int, std::vector<long long>> processXRefIndex(
1034 QPDFObjectHandle& dict, 1036 QPDFObjectHandle& dict,
1035 - size_t entry_size, 1037 + int max_num_entries,
1036 std::function<QPDFExc(std::string_view)> damaged); 1038 std::function<QPDFExc(std::string_view)> damaged);
1037 void insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2); 1039 void insertXrefEntry(int obj, int f0, qpdf_offset_t f1, int f2);
1038 void insertFreeXrefEntry(QPDFObjGen); 1040 void insertFreeXrefEntry(QPDFObjGen);
libqpdf/QPDF.cc
@@ -969,7 +969,7 @@ QPDF::read_xrefStream(qpdf_offset_t xref_offset) @@ -969,7 +969,7 @@ QPDF::read_xrefStream(qpdf_offset_t xref_offset)
969 } 969 }
970 970
971 // Return the entry size of the xref stream and the processed W array. 971 // Return the entry size of the xref stream and the processed W array.
972 -std::pair<size_t, std::array<int, 3>> 972 +std::pair<int, std::array<int, 3>>
973 QPDF::processXRefW(QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged) 973 QPDF::processXRefW(QPDFObjectHandle& dict, std::function<QPDFExc(std::string_view)> damaged)
974 { 974 {
975 auto W_obj = dict.getKey("/W"); 975 auto W_obj = dict.getKey("/W");
@@ -995,17 +995,39 @@ QPDF::processXRefW(QPDFObjectHandle&amp; dict, std::function&lt;QPDFExc(std::string_vie @@ -995,17 +995,39 @@ QPDF::processXRefW(QPDFObjectHandle&amp; dict, std::function&lt;QPDFExc(std::string_vie
995 if (entry_size == 0) { 995 if (entry_size == 0) {
996 throw damaged("Cross-reference stream's /W indicates entry size of 0"); 996 throw damaged("Cross-reference stream's /W indicates entry size of 0");
997 } 997 }
998 - return {toS(entry_size), W}; 998 + return {entry_size, W};
999 } 999 }
1000 1000
1001 -// Return the expected size of the xref stream and the processed Index array.  
1002 -std::pair<size_t, std::vector<long long>>  
1003 -QPDF::processXRefIndex(  
1004 - QPDFObjectHandle& dict, size_t entry_size, std::function<QPDFExc(std::string_view)> damaged) 1001 +// Validate Size key and return the maximum number of entries that the xref stream can contain.
  1002 +int
  1003 +QPDF::processXRefSize(
  1004 + QPDFObjectHandle& dict, int entry_size, std::function<QPDFExc(std::string_view)> damaged)
1005 { 1005 {
1006 - auto max_num_entries = static_cast<unsigned long long>(-1) / entry_size; 1006 + // Number of entries is limited by the highest possible object id and stream size.
  1007 + auto max_num_entries = std::numeric_limits<int>::max();
  1008 + if (max_num_entries > (std::numeric_limits<qpdf_offset_t>::max() / entry_size)) {
  1009 + max_num_entries = toI(std::numeric_limits<qpdf_offset_t>::max() / entry_size);
  1010 + }
1007 1011
1008 auto Size_obj = dict.getKey("/Size"); 1012 auto Size_obj = dict.getKey("/Size");
  1013 + long long size;
  1014 + if (!dict.getKey("/Size").getValueAsInt(size)) {
  1015 + throw damaged("Cross-reference stream does not have a proper /Size key");
  1016 + } else if (size < 0) {
  1017 + throw damaged("Cross-reference stream has a negative /Size key");
  1018 + } else if (size >= max_num_entries) {
  1019 + throw damaged("Cross-reference stream has an impossibly large /Size key");
  1020 + }
  1021 + // We are not validating that Size <= (Size key of parent xref / trailer).
  1022 + return max_num_entries;
  1023 +}
  1024 +
  1025 +// Return the number of entries of the xref stream and the processed Index array.
  1026 +std::pair<int, std::vector<long long>>
  1027 +QPDF::processXRefIndex(
  1028 + QPDFObjectHandle& dict, int max_num_entries, std::function<QPDFExc(std::string_view)> damaged)
  1029 +{
  1030 + auto size = dict.getKey("/Size").getIntValueAsInt();
1009 auto Index_obj = dict.getKey("/Index"); 1031 auto Index_obj = dict.getKey("/Index");
1010 if (!(Index_obj.isArray() || Index_obj.isNull())) { 1032 if (!(Index_obj.isArray() || Index_obj.isNull())) {
1011 throw damaged("Cross-reference stream does not have a proper /Index key"); 1033 throw damaged("Cross-reference stream does not have a proper /Index key");
@@ -1030,29 +1052,28 @@ QPDF::processXRefIndex( @@ -1030,29 +1052,28 @@ QPDF::processXRefIndex(
1030 } else { 1052 } else {
1031 // We have already validated the /Index key. 1053 // We have already validated the /Index key.
1032 QTC::TC("qpdf", "QPDF xref /Index is null"); 1054 QTC::TC("qpdf", "QPDF xref /Index is null");
1033 - long long size = Size_obj.getIntValue();  
1034 indx.push_back(0); 1055 indx.push_back(0);
1035 indx.push_back(size); 1056 indx.push_back(size);
1036 } 1057 }
1037 1058
1038 - size_t num_entries = 0; 1059 + int num_entries = 0;
1039 for (size_t i = 1; i < indx.size(); i += 2) { 1060 for (size_t i = 1; i < indx.size(); i += 2) {
1040 // We are guarding against the possibility of num_entries * entry_size overflowing. 1061 // We are guarding against the possibility of num_entries * entry_size overflowing.
1041 // We are not checking that entries are in ascending order as required by the spec, which 1062 // We are not checking that entries are in ascending order as required by the spec, which
1042 // probably should generate a warning. We are also not checking that for each subsection 1063 // probably should generate a warning. We are also not checking that for each subsection
1043 // first object number + number of entries <= /Size. The spec requires us to ignore object 1064 // first object number + number of entries <= /Size. The spec requires us to ignore object
1044 // number > /Size. 1065 // number > /Size.
1045 - if (indx.at(i) > QIntC::to_longlong(max_num_entries - num_entries)) { 1066 + if (indx.at(i) > (max_num_entries - num_entries)) {
1046 throw damaged( 1067 throw damaged(
1047 "Cross-reference stream claims to contain too many entries: " + 1068 "Cross-reference stream claims to contain too many entries: " +
1048 std::to_string(indx.at(i)) + " " + std::to_string(max_num_entries) + " " + 1069 std::to_string(indx.at(i)) + " " + std::to_string(max_num_entries) + " " +
1049 std::to_string(num_entries)); 1070 std::to_string(num_entries));
1050 } 1071 }
1051 - num_entries += toS(indx.at(i)); 1072 + num_entries += indx.at(i);
1052 } 1073 }
1053 // entry_size and num_entries have both been validated to ensure that this multiplication does 1074 // entry_size and num_entries have both been validated to ensure that this multiplication does
1054 // not cause an overflow. 1075 // not cause an overflow.
1055 - return {entry_size * num_entries, indx}; 1076 + return {num_entries, indx};
1056 } 1077 }
1057 1078
1058 qpdf_offset_t 1079 qpdf_offset_t
@@ -1063,16 +1084,14 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj) @@ -1063,16 +1084,14 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle&amp; xref_obj)
1063 }; 1084 };
1064 1085
1065 auto dict = xref_obj.getDict(); 1086 auto dict = xref_obj.getDict();
1066 - auto Size_obj = dict.getKey("/Size");  
1067 - if (!Size_obj.isInteger()) {  
1068 - throw damaged("Cross-reference stream does not have a proper /Size key");  
1069 - }  
1070 1087
1071 auto [entry_size, W] = processXRefW(dict, damaged); 1088 auto [entry_size, W] = processXRefW(dict, damaged);
1072 - auto [expected_size, indx] = processXRefIndex(dict, entry_size, damaged); 1089 + int max_num_entries = processXRefSize(dict, entry_size, damaged);
  1090 + auto [num_entries, indx] = processXRefIndex(dict, max_num_entries, damaged);
1073 1091
1074 std::shared_ptr<Buffer> bp = xref_obj.getStreamData(qpdf_dl_specialized); 1092 std::shared_ptr<Buffer> bp = xref_obj.getStreamData(qpdf_dl_specialized);
1075 size_t actual_size = bp->getSize(); 1093 size_t actual_size = bp->getSize();
  1094 + auto expected_size = toS(entry_size) * toS(num_entries);
1076 1095
1077 if (expected_size != actual_size) { 1096 if (expected_size != actual_size) {
1078 QPDFExc x = damaged( 1097 QPDFExc x = damaged(