Commit 44a13951940def9699b2c169e8a8eabd35973934
1 parent
06a2d955
Refactor QPDF::Xref_table::read_entry and read_bad_entry
Return results rather than using reference parameters. Fixes bug in #1272 where parameters were not reinitialized when calling read_bad_entry from read_entry.
Showing
2 changed files
with
30 additions
and
28 deletions
libqpdf/QPDF.cc
| @@ -832,10 +832,6 @@ std::vector<QPDF::Xref_table::Subsection> | @@ -832,10 +832,6 @@ std::vector<QPDF::Xref_table::Subsection> | ||
| 832 | QPDF::Xref_table::bad_subsections(std::string& line, qpdf_offset_t start) | 832 | QPDF::Xref_table::bad_subsections(std::string& line, qpdf_offset_t start) |
| 833 | { | 833 | { |
| 834 | std::vector<QPDF::Xref_table::Subsection> result; | 834 | std::vector<QPDF::Xref_table::Subsection> result; |
| 835 | - qpdf_offset_t f1 = 0; | ||
| 836 | - int f2 = 0; | ||
| 837 | - char type = '\0'; | ||
| 838 | - | ||
| 839 | file->seek(start, SEEK_SET); | 835 | file->seek(start, SEEK_SET); |
| 840 | 836 | ||
| 841 | while (true) { | 837 | while (true) { |
| @@ -844,7 +840,7 @@ QPDF::Xref_table::bad_subsections(std::string& line, qpdf_offset_t start) | @@ -844,7 +840,7 @@ QPDF::Xref_table::bad_subsections(std::string& line, qpdf_offset_t start) | ||
| 844 | auto [obj, num, offset] = result.emplace_back(subsection(line)); | 840 | auto [obj, num, offset] = result.emplace_back(subsection(line)); |
| 845 | file->seek(offset, SEEK_SET); | 841 | file->seek(offset, SEEK_SET); |
| 846 | for (qpdf_offset_t i = obj; i - num < obj; ++i) { | 842 | for (qpdf_offset_t i = obj; i - num < obj; ++i) { |
| 847 | - if (!read_entry(f1, f2, type)) { | 843 | + if (!std::get<0>(read_entry())) { |
| 848 | QTC::TC("qpdf", "QPDF invalid xref entry"); | 844 | QTC::TC("qpdf", "QPDF invalid xref entry"); |
| 849 | throw damaged_table("invalid xref entry (obj=" + std::to_string(i) + ")"); | 845 | throw damaged_table("invalid xref entry (obj=" + std::to_string(i) + ")"); |
| 850 | } | 846 | } |
| @@ -890,9 +886,13 @@ QPDF::Xref_table::subsections(std::string& line) | @@ -890,9 +886,13 @@ QPDF::Xref_table::subsections(std::string& line) | ||
| 890 | } | 886 | } |
| 891 | } | 887 | } |
| 892 | 888 | ||
| 893 | -bool | ||
| 894 | -QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | 889 | +// Returns (success, f1, f2, type). |
| 890 | +std::tuple<bool, qpdf_offset_t, int, char> | ||
| 891 | +QPDF::Xref_table::read_bad_entry() | ||
| 895 | { | 892 | { |
| 893 | + qpdf_offset_t f1{0}; | ||
| 894 | + int f2{0}; | ||
| 895 | + char type{'\0'}; | ||
| 896 | // Reposition after initial read attempt and reread. | 896 | // Reposition after initial read attempt and reread. |
| 897 | file->seek(file->getLastOffset(), SEEK_SET); | 897 | file->seek(file->getLastOffset(), SEEK_SET); |
| 898 | auto line = file->readLine(30); | 898 | auto line = file->readLine(30); |
| @@ -910,7 +910,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | @@ -910,7 +910,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | ||
| 910 | } | 910 | } |
| 911 | // Require digit | 911 | // Require digit |
| 912 | if (!QUtil::is_digit(*p)) { | 912 | if (!QUtil::is_digit(*p)) { |
| 913 | - return false; | 913 | + return {false, 0, 0, '\0'}; |
| 914 | } | 914 | } |
| 915 | // Gather digits | 915 | // Gather digits |
| 916 | std::string f1_str; | 916 | std::string f1_str; |
| @@ -919,7 +919,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | @@ -919,7 +919,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | ||
| 919 | } | 919 | } |
| 920 | // Require space | 920 | // Require space |
| 921 | if (!QUtil::is_space(*p)) { | 921 | if (!QUtil::is_space(*p)) { |
| 922 | - return false; | 922 | + return {false, 0, 0, '\0'}; |
| 923 | } | 923 | } |
| 924 | if (QUtil::is_space(*(p + 1))) { | 924 | if (QUtil::is_space(*(p + 1))) { |
| 925 | QTC::TC("qpdf", "QPDF ignore first extra space in xref entry"); | 925 | QTC::TC("qpdf", "QPDF ignore first extra space in xref entry"); |
| @@ -931,7 +931,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | @@ -931,7 +931,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | ||
| 931 | } | 931 | } |
| 932 | // Require digit | 932 | // Require digit |
| 933 | if (!QUtil::is_digit(*p)) { | 933 | if (!QUtil::is_digit(*p)) { |
| 934 | - return false; | 934 | + return {false, 0, 0, '\0'}; |
| 935 | } | 935 | } |
| 936 | // Gather digits | 936 | // Gather digits |
| 937 | std::string f2_str; | 937 | std::string f2_str; |
| @@ -940,7 +940,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | @@ -940,7 +940,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | ||
| 940 | } | 940 | } |
| 941 | // Require space | 941 | // Require space |
| 942 | if (!QUtil::is_space(*p)) { | 942 | if (!QUtil::is_space(*p)) { |
| 943 | - return false; | 943 | + return {false, 0, 0, '\0'}; |
| 944 | } | 944 | } |
| 945 | if (QUtil::is_space(*(p + 1))) { | 945 | if (QUtil::is_space(*(p + 1))) { |
| 946 | QTC::TC("qpdf", "QPDF ignore second extra space in xref entry"); | 946 | QTC::TC("qpdf", "QPDF ignore second extra space in xref entry"); |
| @@ -953,7 +953,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | @@ -953,7 +953,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | ||
| 953 | if ((*p == 'f') || (*p == 'n')) { | 953 | if ((*p == 'f') || (*p == 'n')) { |
| 954 | type = *p; | 954 | type = *p; |
| 955 | } else { | 955 | } else { |
| 956 | - return false; | 956 | + return {false, 0, 0, '\0'}; |
| 957 | } | 957 | } |
| 958 | if ((f1_str.length() != 10) || (f2_str.length() != 5)) { | 958 | if ((f1_str.length() != 10) || (f2_str.length() != 5)) { |
| 959 | QTC::TC("qpdf", "QPDF ignore length error xref entry"); | 959 | QTC::TC("qpdf", "QPDF ignore length error xref entry"); |
| @@ -967,18 +967,23 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | @@ -967,18 +967,23 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) | ||
| 967 | f1 = QUtil::string_to_ll(f1_str.c_str()); | 967 | f1 = QUtil::string_to_ll(f1_str.c_str()); |
| 968 | f2 = QUtil::string_to_int(f2_str.c_str()); | 968 | f2 = QUtil::string_to_int(f2_str.c_str()); |
| 969 | 969 | ||
| 970 | - return true; | 970 | + return {true, f1, f2, type}; |
| 971 | } | 971 | } |
| 972 | 972 | ||
| 973 | // Optimistically read and parse xref entry. If entry is bad, call read_bad_xrefEntry and return | 973 | // Optimistically read and parse xref entry. If entry is bad, call read_bad_xrefEntry and return |
| 974 | -// result. | ||
| 975 | -bool | ||
| 976 | -QPDF::Xref_table::read_entry(qpdf_offset_t& f1, int& f2, char& type) | 974 | +// result. Returns (success, f1, f2, type). |
| 975 | +std::tuple<bool, qpdf_offset_t, int, char> | ||
| 976 | +QPDF::Xref_table::read_entry() | ||
| 977 | { | 977 | { |
| 978 | + qpdf_offset_t f1{0}; | ||
| 979 | + int f2{0}; | ||
| 980 | + char type{'\0'}; | ||
| 978 | std::array<char, 21> line; | 981 | std::array<char, 21> line; |
| 982 | + f1 = 0; | ||
| 983 | + f2 = 0; | ||
| 979 | if (file->read(line.data(), 20) != 20) { | 984 | if (file->read(line.data(), 20) != 20) { |
| 980 | // C++20: [[unlikely]] | 985 | // C++20: [[unlikely]] |
| 981 | - return false; | 986 | + return {false, 0, 0, '\0'}; |
| 982 | } | 987 | } |
| 983 | line[20] = '\0'; | 988 | line[20] = '\0'; |
| 984 | char const* p = line.data(); | 989 | char const* p = line.data(); |
| @@ -1002,7 +1007,7 @@ QPDF::Xref_table::read_entry(qpdf_offset_t& f1, int& f2, char& type) | @@ -1002,7 +1007,7 @@ QPDF::Xref_table::read_entry(qpdf_offset_t& f1, int& f2, char& type) | ||
| 1002 | if (!QUtil::is_space(*p++)) { | 1007 | if (!QUtil::is_space(*p++)) { |
| 1003 | // Entry doesn't start with space or digit. | 1008 | // Entry doesn't start with space or digit. |
| 1004 | // C++20: [[unlikely]] | 1009 | // C++20: [[unlikely]] |
| 1005 | - return false; | 1010 | + return {false, 0, 0, '\0'}; |
| 1006 | } | 1011 | } |
| 1007 | // Gather digits. NB No risk of overflow as 99'999 < max int. | 1012 | // Gather digits. NB No risk of overflow as 99'999 < max int. |
| 1008 | while (*p == '0') { | 1013 | while (*p == '0') { |
| @@ -1019,10 +1024,10 @@ QPDF::Xref_table::read_entry(qpdf_offset_t& f1, int& f2, char& type) | @@ -1019,10 +1024,10 @@ QPDF::Xref_table::read_entry(qpdf_offset_t& f1, int& f2, char& type) | ||
| 1019 | // No test for valid line[19]. | 1024 | // No test for valid line[19]. |
| 1020 | if (*(++p) && *(++p) && (*p == '\n' || *p == '\r') && f1_len == 10 && f2_len == 5) { | 1025 | if (*(++p) && *(++p) && (*p == '\n' || *p == '\r') && f1_len == 10 && f2_len == 5) { |
| 1021 | // C++20: [[likely]] | 1026 | // C++20: [[likely]] |
| 1022 | - return true; | 1027 | + return {true, f1, f2, type}; |
| 1023 | } | 1028 | } |
| 1024 | } | 1029 | } |
| 1025 | - return read_bad_entry(f1, f2, type); | 1030 | + return read_bad_entry(); |
| 1026 | } | 1031 | } |
| 1027 | 1032 | ||
| 1028 | // Read a single cross-reference table section and associated trailer. | 1033 | // Read a single cross-reference table section and associated trailer. |
| @@ -1064,10 +1069,8 @@ QPDF::Xref_table::process_section(qpdf_offset_t xref_offset) | @@ -1064,10 +1069,8 @@ QPDF::Xref_table::process_section(qpdf_offset_t xref_offset) | ||
| 1064 | first_item_offset_ = file->tell(); | 1069 | first_item_offset_ = file->tell(); |
| 1065 | } | 1070 | } |
| 1066 | // For xref_table, these will always be small enough to be ints | 1071 | // For xref_table, these will always be small enough to be ints |
| 1067 | - qpdf_offset_t f1 = 0; | ||
| 1068 | - int f2 = 0; | ||
| 1069 | - char type = '\0'; | ||
| 1070 | - if (!read_entry(f1, f2, type)) { | 1072 | + auto [success, f1, f2, type] = read_entry(); |
| 1073 | + if (!success) { | ||
| 1071 | throw damaged_table("invalid xref entry (obj=" + std::to_string(i) + ")"); | 1074 | throw damaged_table("invalid xref entry (obj=" + std::to_string(i) + ")"); |
| 1072 | } | 1075 | } |
| 1073 | if (type == 'f') { | 1076 | if (type == 'f') { |
| @@ -1585,8 +1588,7 @@ QPDF::Xref_table::read_trailer() | @@ -1585,8 +1588,7 @@ QPDF::Xref_table::read_trailer() | ||
| 1585 | { | 1588 | { |
| 1586 | qpdf_offset_t offset = file->tell(); | 1589 | qpdf_offset_t offset = file->tell(); |
| 1587 | bool empty = false; | 1590 | bool empty = false; |
| 1588 | - auto object = | ||
| 1589 | - QPDFParser(*file, "trailer", tokenizer, nullptr, &qpdf, true).parse(empty, false); | 1591 | + auto object = QPDFParser(*file, "trailer", tokenizer, nullptr, &qpdf, true).parse(empty, false); |
| 1590 | if (empty) { | 1592 | if (empty) { |
| 1591 | // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in | 1593 | // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in |
| 1592 | // actual PDF files and Adobe Reader appears to ignore them. | 1594 | // actual PDF files and Adobe Reader appears to ignore them. |
libqpdf/qpdf/QPDF_private.hh
| @@ -292,8 +292,8 @@ class QPDF::Xref_table | @@ -292,8 +292,8 @@ class QPDF::Xref_table | ||
| 292 | std::vector<Subsection> subsections(std::string& line); | 292 | std::vector<Subsection> subsections(std::string& line); |
| 293 | std::vector<Subsection> bad_subsections(std::string& line, qpdf_offset_t offset); | 293 | std::vector<Subsection> bad_subsections(std::string& line, qpdf_offset_t offset); |
| 294 | Subsection subsection(std::string const& line); | 294 | Subsection subsection(std::string const& line); |
| 295 | - bool read_entry(qpdf_offset_t& f1, int& f2, char& type); | ||
| 296 | - bool read_bad_entry(qpdf_offset_t& f1, int& f2, char& type); | 295 | + std::tuple<bool, qpdf_offset_t, int, char> read_entry(); |
| 296 | + std::tuple<bool, qpdf_offset_t, int, char> read_bad_entry(); | ||
| 297 | 297 | ||
| 298 | // Methods to parse streams | 298 | // Methods to parse streams |
| 299 | qpdf_offset_t read_stream(qpdf_offset_t offset); | 299 | qpdf_offset_t read_stream(qpdf_offset_t offset); |