Commit e19eb579b221ade503d7d1ff0a6511d289863785

Authored by Jay Berkenbilt
1 parent 0bfe9024

Replace some assertions with std::logic_error

Ideally, the library should never call assert outside of test code,
but it does in several places.  For some cases where the assertion
might conceivably fail because of a problem with the input data,
replace assertions with exceptions so that they can be trapped by the
calling application.  This commit surely misses some cases and
replaced some cases unnecessarily, but it should still be an
improvement.
ChangeLog
1 1 2013-10-05 Jay Berkenbilt <ejb@ql.org>
2 2  
  3 + * Replace some assert() calls with std::logic_error exceptions.
  4 + Ideally there shouldn't be assert() calls outside of testing.
  5 + This change may make a few more potential code errors in handling
  6 + invalid data recoverable.
  7 +
3 8 * Security fix: In places where std::vector<T>(size_t) was used,
4 9 either validate that the size parameter is sane or refactor code
5 10 to avoid the need to pre-allocate the vector. This reduces the
... ...
libqpdf/Pl_LZWDecoder.cc
1 1 #include <qpdf/Pl_LZWDecoder.hh>
2 2  
3 3 #include <qpdf/QTC.hh>
  4 +#include <qpdf/QUtil.hh>
4 5 #include <stdexcept>
5 6 #include <string.h>
6 7 #include <assert.h>
... ... @@ -100,14 +101,23 @@ Pl_LZWDecoder::getFirstChar(int code)
100 101 {
101 102 result = static_cast<unsigned char>(code);
102 103 }
103   - else
  104 + else if (code > 257)
104 105 {
105   - assert(code > 257);
106 106 unsigned int idx = code - 258;
107   - assert(idx < table.size());
  107 + if (idx >= table.size())
  108 + {
  109 + throw std::logic_error(
  110 + "Pl_LZWDecoder::getFirstChar: table overflow");
  111 + }
108 112 Buffer& b = table[idx];
109 113 result = b.getBuffer()[0];
110 114 }
  115 + else
  116 + {
  117 + throw std::logic_error(
  118 + "Pl_LZWDecoder::getFirstChar called with invalid code (" +
  119 + QUtil::int_to_string(code) + ")");
  120 + }
111 121 return result;
112 122 }
113 123  
... ... @@ -124,15 +134,24 @@ Pl_LZWDecoder::addToTable(unsigned char next)
124 134 last_data = tmp;
125 135 last_size = 1;
126 136 }
127   - else
  137 + else if (this->last_code > 257)
128 138 {
129   - assert(this->last_code > 257);
130 139 unsigned int idx = this->last_code - 258;
131   - assert(idx < table.size());
  140 + if (idx >= table.size())
  141 + {
  142 + throw std::logic_error(
  143 + "Pl_LZWDecoder::addToTable: table overflow");
  144 + }
132 145 Buffer& b = table[idx];
133 146 last_data = b.getBuffer();
134 147 last_size = b.getSize();
135 148 }
  149 + else
  150 + {
  151 + throw std::logic_error(
  152 + "Pl_LZWDecoder::addToTable called with invalid code (" +
  153 + QUtil::int_to_string(this->last_code) + ")");
  154 + }
136 155  
137 156 Buffer entry(1 + last_size);
138 157 unsigned char* new_data = entry.getBuffer();
... ...
libqpdf/QPDFWriter.cc
... ... @@ -772,7 +772,11 @@ QPDFWriter::bytesNeeded(unsigned long long n)
772 772 void
773 773 QPDFWriter::writeBinary(unsigned long long val, unsigned int bytes)
774 774 {
775   - assert(bytes <= sizeof(unsigned long long));
  775 + if (bytes > sizeof(unsigned long long))
  776 + {
  777 + throw std::logic_error(
  778 + "QPDFWriter::writeBinary called with too many bytes");
  779 + }
776 780 unsigned char data[sizeof(unsigned long long)];
777 781 for (unsigned int i = 0; i < bytes; ++i)
778 782 {
... ... @@ -1097,7 +1101,11 @@ QPDFWriter::writeTrailer(trailer_e which, int size, bool xref_stream,
1097 1101 qpdf_offset_t pos = this->pipeline->getCount();
1098 1102 writeString(QUtil::int_to_string(prev));
1099 1103 int nspaces = pos - this->pipeline->getCount() + 21;
1100   - assert(nspaces >= 0);
  1104 + if (nspaces < 0)
  1105 + {
  1106 + throw std::logic_error(
  1107 + "QPDFWriter: no padding required in trailer");
  1108 + }
1101 1109 writePad(nspaces);
1102 1110 }
1103 1111 }
... ... @@ -2814,9 +2822,11 @@ QPDFWriter::writeLinearized()
2814 2822 // place as in pass 1.
2815 2823 writePad(first_xref_end - endpos);
2816 2824  
2817   - // A failure of this insertion means we didn't allow
2818   - // enough padding for the first pass xref stream.
2819   - assert(this->pipeline->getCount() == first_xref_end);
  2825 + if (this->pipeline->getCount() != first_xref_end)
  2826 + {
  2827 + throw std::logic_error(
  2828 + "insufficient padding for first pass xref stream");
  2829 + }
2820 2830 }
2821 2831 writeString("\n");
2822 2832 }
... ... @@ -2897,8 +2907,13 @@ QPDFWriter::writeLinearized()
2897 2907  
2898 2908 // If this assertion fails, maybe we didn't have
2899 2909 // enough padding above.
2900   - assert(this->pipeline->getCount() ==
2901   - second_xref_end + hint_length);
  2910 + if (this->pipeline->getCount() !=
  2911 + second_xref_end + hint_length)
  2912 + {
  2913 + throw std::logic_error(
  2914 + "count mismatch after xref stream;"
  2915 + " possible insufficient padding?");
  2916 + }
2902 2917 }
2903 2918 }
2904 2919 else
... ...
libqpdf/QPDF_linearization.cc
... ... @@ -606,15 +606,21 @@ QPDF::checkLinearizationInternal()
606 606 // agree with pdlin. As of this writing, the test suite doesn't
607 607 // contain any files with threads.
608 608  
609   - assert(! this->part6.empty());
  609 + if (this->part6.empty())
  610 + {
  611 + throw std::logic_error("linearization part 6 unexpectedly empty");
  612 + }
610 613 qpdf_offset_t min_E = -1;
611 614 qpdf_offset_t max_E = -1;
612 615 for (std::vector<QPDFObjectHandle>::iterator iter = this->part6.begin();
613 616 iter != this->part6.end(); ++iter)
614 617 {
615 618 QPDFObjGen og((*iter).getObjGen());
616   - // All objects have to have been dereferenced to be classified.
617   - assert(this->obj_cache.count(og) > 0);
  619 + if (this->obj_cache.count(og) == 0)
  620 + {
  621 + // All objects have to have been dereferenced to be classified.
  622 + throw std::logic_error("linearization part6 object not in cache");
  623 + }
618 624 ObjCache const& oc = this->obj_cache[og];
619 625 min_E = std::max(min_E, oc.end_before_space);
620 626 max_E = std::max(max_E, oc.end_after_space);
... ... @@ -832,14 +838,23 @@ QPDF::checkHPageOffset(std::list&lt;std::string&gt;&amp; errors,
832 838 for (int i = 0; i < he.nshared_objects; ++i)
833 839 {
834 840 int idx = he.shared_identifiers[i];
835   - assert(shared_idx_to_obj.count(idx) > 0);
  841 + if (shared_idx_to_obj.count(idx) == 0)
  842 + {
  843 + throw std::logic_error(
  844 + "unable to get object for item in"
  845 + " shared objects hint table");
  846 + }
836 847 hint_shared.insert(shared_idx_to_obj[idx]);
837 848 }
838 849  
839 850 for (int i = 0; i < ce.nshared_objects; ++i)
840 851 {
841 852 int idx = ce.shared_identifiers[i];
842   - assert(idx < this->c_shared_object_data.nshared_total);
  853 + if (idx >= this->c_shared_object_data.nshared_total)
  854 + {
  855 + throw std::logic_error(
  856 + "index out of bounds for shared object hint table");
  857 + }
843 858 int obj = this->c_shared_object_data.entries[idx].object;
844 859 computed_shared.insert(obj);
845 860 }
... ... @@ -1754,8 +1769,12 @@ QPDF::calculateLinearizationData(std::map&lt;int, int&gt; const&amp; object_stream_data)
1754 1769 shared.push_back(CHSharedObjectEntry(obj));
1755 1770 }
1756 1771 }
1757   - assert(static_cast<size_t>(this->c_shared_object_data.nshared_total) ==
1758   - this->c_shared_object_data.entries.size());
  1772 + if (static_cast<size_t>(this->c_shared_object_data.nshared_total) !=
  1773 + this->c_shared_object_data.entries.size())
  1774 + {
  1775 + throw std::logic_error(
  1776 + "shared object hint table has wrong number of entries");
  1777 + }
1759 1778  
1760 1779 // Now compute the list of shared objects for each page after the
1761 1780 // first page.
... ...
libqpdf/QPDF_pages.cc
... ... @@ -120,7 +120,10 @@ QPDF::flattenPagesTree()
120 120  
121 121 pages.replaceKey("/Kids", QPDFObjectHandle::newArray(this->all_pages));
122 122 // /Count has not changed
123   - assert(pages.getKey("/Count").getIntValue() == len);
  123 + if (pages.getKey("/Count").getIntValue() != len)
  124 + {
  125 + throw std::logic_error("/Count is wrong after flattening pages tree");
  126 + }
124 127 }
125 128  
126 129 void
... ...