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,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& xref_obj) | @@ -1017,9 +998,9 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& 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& xref_obj) | @@ -1039,20 +1020,50 @@ QPDF::processXRefStream(qpdf_offset_t xref_offset, QPDFObjectHandle& 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 { |