Commit 0186d60dcf8679f2133ffbe1b34816731837f136
1 parent
7aa5027b
Add new private method QPDF::processXRefIndex
Showing
2 changed files
with
51 additions
and
36 deletions
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& 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& 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 { | ... | ... |