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 2013-10-05 Jay Berkenbilt <ejb@ql.org> 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 * Security fix: In places where std::vector<T>(size_t) was used, 8 * Security fix: In places where std::vector<T>(size_t) was used,
4 either validate that the size parameter is sane or refactor code 9 either validate that the size parameter is sane or refactor code
5 to avoid the need to pre-allocate the vector. This reduces the 10 to avoid the need to pre-allocate the vector. This reduces the
libqpdf/Pl_LZWDecoder.cc
1 #include <qpdf/Pl_LZWDecoder.hh> 1 #include <qpdf/Pl_LZWDecoder.hh>
2 2
3 #include <qpdf/QTC.hh> 3 #include <qpdf/QTC.hh>
  4 +#include <qpdf/QUtil.hh>
4 #include <stdexcept> 5 #include <stdexcept>
5 #include <string.h> 6 #include <string.h>
6 #include <assert.h> 7 #include <assert.h>
@@ -100,14 +101,23 @@ Pl_LZWDecoder::getFirstChar(int code) @@ -100,14 +101,23 @@ Pl_LZWDecoder::getFirstChar(int code)
100 { 101 {
101 result = static_cast<unsigned char>(code); 102 result = static_cast<unsigned char>(code);
102 } 103 }
103 - else 104 + else if (code > 257)
104 { 105 {
105 - assert(code > 257);  
106 unsigned int idx = code - 258; 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 Buffer& b = table[idx]; 112 Buffer& b = table[idx];
109 result = b.getBuffer()[0]; 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 return result; 121 return result;
112 } 122 }
113 123
@@ -124,15 +134,24 @@ Pl_LZWDecoder::addToTable(unsigned char next) @@ -124,15 +134,24 @@ Pl_LZWDecoder::addToTable(unsigned char next)
124 last_data = tmp; 134 last_data = tmp;
125 last_size = 1; 135 last_size = 1;
126 } 136 }
127 - else 137 + else if (this->last_code > 257)
128 { 138 {
129 - assert(this->last_code > 257);  
130 unsigned int idx = this->last_code - 258; 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 Buffer& b = table[idx]; 145 Buffer& b = table[idx];
133 last_data = b.getBuffer(); 146 last_data = b.getBuffer();
134 last_size = b.getSize(); 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 Buffer entry(1 + last_size); 156 Buffer entry(1 + last_size);
138 unsigned char* new_data = entry.getBuffer(); 157 unsigned char* new_data = entry.getBuffer();
libqpdf/QPDFWriter.cc
@@ -772,7 +772,11 @@ QPDFWriter::bytesNeeded(unsigned long long n) @@ -772,7 +772,11 @@ QPDFWriter::bytesNeeded(unsigned long long n)
772 void 772 void
773 QPDFWriter::writeBinary(unsigned long long val, unsigned int bytes) 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 unsigned char data[sizeof(unsigned long long)]; 780 unsigned char data[sizeof(unsigned long long)];
777 for (unsigned int i = 0; i < bytes; ++i) 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,7 +1101,11 @@ QPDFWriter::writeTrailer(trailer_e which, int size, bool xref_stream,
1097 qpdf_offset_t pos = this->pipeline->getCount(); 1101 qpdf_offset_t pos = this->pipeline->getCount();
1098 writeString(QUtil::int_to_string(prev)); 1102 writeString(QUtil::int_to_string(prev));
1099 int nspaces = pos - this->pipeline->getCount() + 21; 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 writePad(nspaces); 1109 writePad(nspaces);
1102 } 1110 }
1103 } 1111 }
@@ -2814,9 +2822,11 @@ QPDFWriter::writeLinearized() @@ -2814,9 +2822,11 @@ QPDFWriter::writeLinearized()
2814 // place as in pass 1. 2822 // place as in pass 1.
2815 writePad(first_xref_end - endpos); 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 writeString("\n"); 2831 writeString("\n");
2822 } 2832 }
@@ -2897,8 +2907,13 @@ QPDFWriter::writeLinearized() @@ -2897,8 +2907,13 @@ QPDFWriter::writeLinearized()
2897 2907
2898 // If this assertion fails, maybe we didn't have 2908 // If this assertion fails, maybe we didn't have
2899 // enough padding above. 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 else 2919 else
libqpdf/QPDF_linearization.cc
@@ -606,15 +606,21 @@ QPDF::checkLinearizationInternal() @@ -606,15 +606,21 @@ QPDF::checkLinearizationInternal()
606 // agree with pdlin. As of this writing, the test suite doesn't 606 // agree with pdlin. As of this writing, the test suite doesn't
607 // contain any files with threads. 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 qpdf_offset_t min_E = -1; 613 qpdf_offset_t min_E = -1;
611 qpdf_offset_t max_E = -1; 614 qpdf_offset_t max_E = -1;
612 for (std::vector<QPDFObjectHandle>::iterator iter = this->part6.begin(); 615 for (std::vector<QPDFObjectHandle>::iterator iter = this->part6.begin();
613 iter != this->part6.end(); ++iter) 616 iter != this->part6.end(); ++iter)
614 { 617 {
615 QPDFObjGen og((*iter).getObjGen()); 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 ObjCache const& oc = this->obj_cache[og]; 624 ObjCache const& oc = this->obj_cache[og];
619 min_E = std::max(min_E, oc.end_before_space); 625 min_E = std::max(min_E, oc.end_before_space);
620 max_E = std::max(max_E, oc.end_after_space); 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,14 +838,23 @@ QPDF::checkHPageOffset(std::list&lt;std::string&gt;&amp; errors,
832 for (int i = 0; i < he.nshared_objects; ++i) 838 for (int i = 0; i < he.nshared_objects; ++i)
833 { 839 {
834 int idx = he.shared_identifiers[i]; 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 hint_shared.insert(shared_idx_to_obj[idx]); 847 hint_shared.insert(shared_idx_to_obj[idx]);
837 } 848 }
838 849
839 for (int i = 0; i < ce.nshared_objects; ++i) 850 for (int i = 0; i < ce.nshared_objects; ++i)
840 { 851 {
841 int idx = ce.shared_identifiers[i]; 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 int obj = this->c_shared_object_data.entries[idx].object; 858 int obj = this->c_shared_object_data.entries[idx].object;
844 computed_shared.insert(obj); 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,8 +1769,12 @@ QPDF::calculateLinearizationData(std::map&lt;int, int&gt; const&amp; object_stream_data)
1754 shared.push_back(CHSharedObjectEntry(obj)); 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 // Now compute the list of shared objects for each page after the 1779 // Now compute the list of shared objects for each page after the
1761 // first page. 1780 // first page.
libqpdf/QPDF_pages.cc
@@ -120,7 +120,10 @@ QPDF::flattenPagesTree() @@ -120,7 +120,10 @@ QPDF::flattenPagesTree()
120 120
121 pages.replaceKey("/Kids", QPDFObjectHandle::newArray(this->all_pages)); 121 pages.replaceKey("/Kids", QPDFObjectHandle::newArray(this->all_pages));
122 // /Count has not changed 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 void 129 void