Commit ba5fb6916446d8bdf79cba25f08d759bc5595aec

Authored by Jay Berkenbilt
1 parent dadf8307

Make popping pipeline stack safer

Use destructors to pop the pipeline stack, and ensure that code that
pops the stack is actually popping the intended thing.
include/qpdf/Pipeline.hh
@@ -70,6 +70,8 @@ class QPDF_DLL_CLASS Pipeline @@ -70,6 +70,8 @@ class QPDF_DLL_CLASS Pipeline
70 virtual void write(unsigned char* data, size_t len) = 0; 70 virtual void write(unsigned char* data, size_t len) = 0;
71 QPDF_DLL 71 QPDF_DLL
72 virtual void finish() = 0; 72 virtual void finish() = 0;
  73 + QPDF_DLL
  74 + std::string getIdentifier() const;
73 75
74 protected: 76 protected:
75 Pipeline* getNext(bool allow_null = false); 77 Pipeline* getNext(bool allow_null = false);
include/qpdf/QPDFWriter.hh
@@ -473,6 +473,34 @@ class QPDFWriter @@ -473,6 +473,34 @@ class QPDFWriter
473 473
474 enum trailer_e { t_normal, t_lin_first, t_lin_second }; 474 enum trailer_e { t_normal, t_lin_first, t_lin_second };
475 475
  476 + // An reference to a PipelinePopper instance is passed into
  477 + // activatePipelineStack. When the PipelinePopper goes out of
  478 + // scope, the pipeline stack is popped. PipelinePopper's
  479 + // destructor calls finish on the current pipeline and pops the
  480 + // pipeline stack until the top of stack is a previous active top
  481 + // of stack, and restores the pipeline to that point. It deletes
  482 + // any pipelines that it pops. If the bp argument is non-null and
  483 + // any of the stack items are of type Pl_Buffer, the buffer is
  484 + // retrieved.
  485 + class PipelinePopper
  486 + {
  487 + friend class QPDFWriter;
  488 + public:
  489 + PipelinePopper(QPDFWriter* qw,
  490 + PointerHolder<Buffer>* bp = 0) :
  491 + qw(qw),
  492 + bp(bp)
  493 + {
  494 + }
  495 + ~PipelinePopper();
  496 +
  497 + private:
  498 + QPDFWriter* qw;
  499 + PointerHolder<Buffer>* bp;
  500 + std::string stack_id;
  501 + };
  502 + friend class PipelinePopper;
  503 +
476 unsigned int bytesNeeded(long long n); 504 unsigned int bytesNeeded(long long n);
477 void writeBinary(unsigned long long val, unsigned int bytes); 505 void writeBinary(unsigned long long val, unsigned int bytes);
478 void writeString(std::string const& str); 506 void writeString(std::string const& str);
@@ -560,24 +588,17 @@ class QPDFWriter @@ -560,24 +588,17 @@ class QPDFWriter
560 int calculateXrefStreamPadding(qpdf_offset_t xref_bytes); 588 int calculateXrefStreamPadding(qpdf_offset_t xref_bytes);
561 589
562 // When filtering subsections, push additional pipelines to the 590 // When filtering subsections, push additional pipelines to the
563 - // stack. When ready to switch, activate the pipeline stack.  
564 - // Pipelines passed to pushPipeline are deleted when  
565 - // clearPipelineStack is called. 591 + // stack. When ready to switch, activate the pipeline stack. When
  592 + // the passed in PipelinePopper goes out of scope, the stack is
  593 + // popped.
566 Pipeline* pushPipeline(Pipeline*); 594 Pipeline* pushPipeline(Pipeline*);
567 - void activatePipelineStack(); 595 + void activatePipelineStack(PipelinePopper&);
568 void initializePipelineStack(Pipeline *); 596 void initializePipelineStack(Pipeline *);
569 597
570 - // Calls finish on the current pipeline and pops the pipeline  
571 - // stack until the top of stack is a previous active top of stack,  
572 - // and restores the pipeline to that point. Deletes any pipelines  
573 - // that it pops. If the bp argument is non-null and any of the  
574 - // stack items are of type Pl_Buffer, the buffer is retrieved.  
575 - void popPipelineStack(PointerHolder<Buffer>* bp = 0);  
576 -  
577 void adjustAESStreamLength(size_t& length); 598 void adjustAESStreamLength(size_t& length);
578 - void pushEncryptionFilter();  
579 - void pushDiscardFilter();  
580 - void pushMD5Pipeline(); 599 + void pushEncryptionFilter(PipelinePopper&);
  600 + void pushDiscardFilter(PipelinePopper&);
  601 + void pushMD5Pipeline(PipelinePopper&);
581 void computeDeterministicIDData(); 602 void computeDeterministicIDData();
582 603
583 void discardGeneration(std::map<QPDFObjGen, int> const& in, 604 void discardGeneration(std::map<QPDFObjGen, int> const& in,
@@ -654,6 +675,7 @@ class QPDFWriter @@ -654,6 +675,7 @@ class QPDFWriter
654 std::map<QPDFObjGen, int> object_to_object_stream; 675 std::map<QPDFObjGen, int> object_to_object_stream;
655 std::map<int, std::set<QPDFObjGen> > object_stream_to_objects; 676 std::map<int, std::set<QPDFObjGen> > object_stream_to_objects;
656 std::list<Pipeline*> pipeline_stack; 677 std::list<Pipeline*> pipeline_stack;
  678 + unsigned long long next_stack_id;
657 bool deterministic_id; 679 bool deterministic_id;
658 Pl_MD5* md5_pipeline; 680 Pl_MD5* md5_pipeline;
659 std::string deterministic_id_data; 681 std::string deterministic_id_data;
libqpdf/Pipeline.cc
@@ -31,3 +31,9 @@ Pipeline::getNext(bool allow_null) @@ -31,3 +31,9 @@ Pipeline::getNext(bool allow_null)
31 } 31 }
32 return this->m->next; 32 return this->m->next;
33 } 33 }
  34 +
  35 +std::string
  36 +Pipeline::getIdentifier() const
  37 +{
  38 + return this->identifier;
  39 +}
libqpdf/QPDFWriter.cc
@@ -63,6 +63,7 @@ QPDFWriter::Members::Members(QPDF&amp; pdf) : @@ -63,6 +63,7 @@ QPDFWriter::Members::Members(QPDF&amp; pdf) :
63 cur_stream_length(0), 63 cur_stream_length(0),
64 added_newline(false), 64 added_newline(false),
65 max_ostream_index(0), 65 max_ostream_index(0),
  66 + next_stack_id(0),
66 deterministic_id(false), 67 deterministic_id(false),
67 md5_pipeline(0), 68 md5_pipeline(0),
68 did_write_setup(false), 69 did_write_setup(false),
@@ -1049,36 +1050,51 @@ QPDFWriter::pushPipeline(Pipeline* p) @@ -1049,36 +1050,51 @@ QPDFWriter::pushPipeline(Pipeline* p)
1049 void 1050 void
1050 QPDFWriter::initializePipelineStack(Pipeline *p) 1051 QPDFWriter::initializePipelineStack(Pipeline *p)
1051 { 1052 {
1052 - this->m->pipeline = new Pl_Count("qpdf count", p); 1053 + this->m->pipeline = new Pl_Count("pipeline stack base", p);
1053 this->m->to_delete.push_back(this->m->pipeline); 1054 this->m->to_delete.push_back(this->m->pipeline);
1054 this->m->pipeline_stack.push_back(this->m->pipeline); 1055 this->m->pipeline_stack.push_back(this->m->pipeline);
1055 } 1056 }
1056 1057
1057 void 1058 void
1058 -QPDFWriter::activatePipelineStack() 1059 +QPDFWriter::activatePipelineStack(PipelinePopper& pp)
1059 { 1060 {
1060 - Pl_Count* c = new Pl_Count("count", this->m->pipeline_stack.back()); 1061 + std::string stack_id(
  1062 + "stack " + QUtil::uint_to_string(this->m->next_stack_id));
  1063 + Pl_Count* c = new Pl_Count(stack_id.c_str(),
  1064 + this->m->pipeline_stack.back());
  1065 + ++this->m->next_stack_id;
1061 this->m->pipeline_stack.push_back(c); 1066 this->m->pipeline_stack.push_back(c);
1062 this->m->pipeline = c; 1067 this->m->pipeline = c;
  1068 + pp.stack_id = stack_id;
1063 } 1069 }
1064 1070
1065 -void  
1066 -QPDFWriter::popPipelineStack(PointerHolder<Buffer>* bp) 1071 +QPDFWriter::PipelinePopper::~PipelinePopper()
1067 { 1072 {
1068 - assert(this->m->pipeline_stack.size() >= 2);  
1069 - this->m->pipeline->finish();  
1070 - assert(dynamic_cast<Pl_Count*>(this->m->pipeline_stack.back()) ==  
1071 - this->m->pipeline);  
1072 - delete this->m->pipeline_stack.back();  
1073 - this->m->pipeline_stack.pop_back();  
1074 - while (dynamic_cast<Pl_Count*>(this->m->pipeline_stack.back()) == 0)  
1075 - {  
1076 - Pipeline* p = this->m->pipeline_stack.back();  
1077 - if (dynamic_cast<Pl_MD5*>(p) == this->m->md5_pipeline) 1073 + if (stack_id.empty())
  1074 + {
  1075 + return;
  1076 + }
  1077 + assert(qw->m->pipeline_stack.size() >= 2);
  1078 + qw->m->pipeline->finish();
  1079 + assert(dynamic_cast<Pl_Count*>(qw->m->pipeline_stack.back()) ==
  1080 + qw->m->pipeline);
  1081 + // It might be possible for this assertion to fail if
  1082 + // writeLinearized exits by exception when deterministic ID, but I
  1083 + // don't think so. As of this writing, this is the only case in
  1084 + // which two dynamically allocated PipelinePopper objects ever
  1085 + // exist at the same time, so the assertion will fail if they get
  1086 + // popped out of order from automatic destruction.
  1087 + assert(qw->m->pipeline->getIdentifier() == stack_id);
  1088 + delete qw->m->pipeline_stack.back();
  1089 + qw->m->pipeline_stack.pop_back();
  1090 + while (dynamic_cast<Pl_Count*>(qw->m->pipeline_stack.back()) == 0)
  1091 + {
  1092 + Pipeline* p = qw->m->pipeline_stack.back();
  1093 + if (dynamic_cast<Pl_MD5*>(p) == qw->m->md5_pipeline)
1078 { 1094 {
1079 - this->m->md5_pipeline = 0; 1095 + qw->m->md5_pipeline = 0;
1080 } 1096 }
1081 - this->m->pipeline_stack.pop_back(); 1097 + qw->m->pipeline_stack.pop_back();
1082 Pl_Buffer* buf = dynamic_cast<Pl_Buffer*>(p); 1098 Pl_Buffer* buf = dynamic_cast<Pl_Buffer*>(p);
1083 if (bp && buf) 1099 if (bp && buf)
1084 { 1100 {
@@ -1086,7 +1102,7 @@ QPDFWriter::popPipelineStack(PointerHolder&lt;Buffer&gt;* bp) @@ -1086,7 +1102,7 @@ QPDFWriter::popPipelineStack(PointerHolder&lt;Buffer&gt;* bp)
1086 } 1102 }
1087 delete p; 1103 delete p;
1088 } 1104 }
1089 - this->m->pipeline = dynamic_cast<Pl_Count*>(this->m->pipeline_stack.back()); 1105 + qw->m->pipeline = dynamic_cast<Pl_Count*>(qw->m->pipeline_stack.back());
1090 } 1106 }
1091 1107
1092 void 1108 void
@@ -1103,7 +1119,7 @@ QPDFWriter::adjustAESStreamLength(size_t&amp; length) @@ -1103,7 +1119,7 @@ QPDFWriter::adjustAESStreamLength(size_t&amp; length)
1103 } 1119 }
1104 1120
1105 void 1121 void
1106 -QPDFWriter::pushEncryptionFilter() 1122 +QPDFWriter::pushEncryptionFilter(PipelinePopper& pp)
1107 { 1123 {
1108 if (this->m->encrypted && (! this->m->cur_data_key.empty())) 1124 if (this->m->encrypted && (! this->m->cur_data_key.empty()))
1109 { 1125 {
@@ -1125,18 +1141,18 @@ QPDFWriter::pushEncryptionFilter() @@ -1125,18 +1141,18 @@ QPDFWriter::pushEncryptionFilter()
1125 } 1141 }
1126 // Must call this unconditionally so we can call popPipelineStack 1142 // Must call this unconditionally so we can call popPipelineStack
1127 // to balance pushEncryptionFilter(). 1143 // to balance pushEncryptionFilter().
1128 - activatePipelineStack(); 1144 + activatePipelineStack(pp);
1129 } 1145 }
1130 1146
1131 void 1147 void
1132 -QPDFWriter::pushDiscardFilter() 1148 +QPDFWriter::pushDiscardFilter(PipelinePopper& pp)
1133 { 1149 {
1134 pushPipeline(new Pl_Discard()); 1150 pushPipeline(new Pl_Discard());
1135 - activatePipelineStack(); 1151 + activatePipelineStack(pp);
1136 } 1152 }
1137 1153
1138 void 1154 void
1139 -QPDFWriter::pushMD5Pipeline() 1155 +QPDFWriter::pushMD5Pipeline(PipelinePopper& pp)
1140 { 1156 {
1141 if (! this->m->id2.empty()) 1157 if (! this->m->id2.empty())
1142 { 1158 {
@@ -1153,7 +1169,7 @@ QPDFWriter::pushMD5Pipeline() @@ -1153,7 +1169,7 @@ QPDFWriter::pushMD5Pipeline()
1153 // Special case code in popPipelineStack clears this->m->md5_pipeline 1169 // Special case code in popPipelineStack clears this->m->md5_pipeline
1154 // upon deletion. 1170 // upon deletion.
1155 pushPipeline(this->m->md5_pipeline); 1171 pushPipeline(this->m->md5_pipeline);
1156 - activatePipelineStack(); 1172 + activatePipelineStack(pp);
1157 } 1173 }
1158 1174
1159 void 1175 void
@@ -1769,8 +1785,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, @@ -1769,8 +1785,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1769 for (int attempt = 1; attempt <= 2; ++attempt) 1785 for (int attempt = 1; attempt <= 2; ++attempt)
1770 { 1786 {
1771 pushPipeline(new Pl_Buffer("stream data")); 1787 pushPipeline(new Pl_Buffer("stream data"));
1772 - activatePipelineStack();  
1773 - 1788 + PipelinePopper pp_stream_data(this, &stream_data);
  1789 + activatePipelineStack(pp_stream_data);
1774 filtered = 1790 filtered =
1775 object.pipeStreamData( 1791 object.pipeStreamData(
1776 this->m->pipeline, 1792 this->m->pipeline,
@@ -1779,7 +1795,6 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, @@ -1779,7 +1795,6 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1779 (filter 1795 (filter
1780 ? (uncompress ? qpdf_dl_all : this->m->stream_decode_level) 1796 ? (uncompress ? qpdf_dl_all : this->m->stream_decode_level)
1781 : qpdf_dl_none), false, (attempt == 1)); 1797 : qpdf_dl_none), false, (attempt == 1));
1782 - popPipelineStack(&stream_data);  
1783 if (filter && (! filtered)) 1798 if (filter && (! filtered))
1784 { 1799 {
1785 // Try again 1800 // Try again
@@ -1808,11 +1823,14 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, @@ -1808,11 +1823,14 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1808 adjustAESStreamLength(this->m->cur_stream_length); 1823 adjustAESStreamLength(this->m->cur_stream_length);
1809 unparseObject(stream_dict, 0, flags, 1824 unparseObject(stream_dict, 0, flags,
1810 this->m->cur_stream_length, compress); 1825 this->m->cur_stream_length, compress);
  1826 + unsigned char last_char = '\0';
1811 writeString("\nstream\n"); 1827 writeString("\nstream\n");
1812 - pushEncryptionFilter();  
1813 - writeBuffer(stream_data);  
1814 - unsigned char last_char = this->m->pipeline->getLastChar();  
1815 - popPipelineStack(); 1828 + {
  1829 + PipelinePopper pp_enc(this);
  1830 + pushEncryptionFilter(pp_enc);
  1831 + writeBuffer(stream_data);
  1832 + last_char = this->m->pipeline->getLastChar();
  1833 + }
1816 1834
1817 if (this->m->newline_before_endstream || 1835 if (this->m->newline_before_endstream ||
1818 (this->m->qdf_mode && (last_char != '\n'))) 1836 (this->m->qdf_mode && (last_char != '\n')))
@@ -1911,9 +1929,11 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1911,9 +1929,11 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1911 bool compressed = false; 1929 bool compressed = false;
1912 for (int pass = 1; pass <= 2; ++pass) 1930 for (int pass = 1; pass <= 2; ++pass)
1913 { 1931 {
  1932 + // stream_buffer will be initialized only for pass 2
  1933 + PipelinePopper pp_ostream(this, &stream_buffer);
1914 if (pass == 1) 1934 if (pass == 1)
1915 { 1935 {
1916 - pushDiscardFilter(); 1936 + pushDiscardFilter(pp_ostream);
1917 } 1937 }
1918 else 1938 else
1919 { 1939 {
@@ -1928,10 +1948,12 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1928,10 +1948,12 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1928 1948
1929 // Take one pass at writing pairs of numbers so we can get 1949 // Take one pass at writing pairs of numbers so we can get
1930 // their size information 1950 // their size information
1931 - pushDiscardFilter();  
1932 - writeObjectStreamOffsets(offsets, first_obj);  
1933 - first += this->m->pipeline->getCount();  
1934 - popPipelineStack(); 1951 + {
  1952 + PipelinePopper pp_discard(this);
  1953 + pushDiscardFilter(pp_discard);
  1954 + writeObjectStreamOffsets(offsets, first_obj);
  1955 + first += this->m->pipeline->getCount();
  1956 + }
1935 1957
1936 // Set up a stream to write the stream data into a buffer. 1958 // Set up a stream to write the stream data into a buffer.
1937 Pipeline* next = pushPipeline(new Pl_Buffer("object stream")); 1959 Pipeline* next = pushPipeline(new Pl_Buffer("object stream"));
@@ -1944,7 +1966,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1944,7 +1966,7 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1944 new Pl_Flate("compress object stream", next, 1966 new Pl_Flate("compress object stream", next,
1945 Pl_Flate::a_deflate)); 1967 Pl_Flate::a_deflate));
1946 } 1968 }
1947 - activatePipelineStack(); 1969 + activatePipelineStack(pp_ostream);
1948 writeObjectStreamOffsets(offsets, first_obj); 1970 writeObjectStreamOffsets(offsets, first_obj);
1949 } 1971 }
1950 1972
@@ -1994,9 +2016,6 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -1994,9 +2016,6 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1994 2016
1995 this->m->xref[new_obj] = QPDFXRefEntry(2, new_id, count); 2017 this->m->xref[new_obj] = QPDFXRefEntry(2, new_id, count);
1996 } 2018 }
1997 -  
1998 - // stream_buffer will be initialized only for pass 2  
1999 - popPipelineStack(&stream_buffer);  
2000 } 2019 }
2001 2020
2002 // Write the object 2021 // Write the object
@@ -2037,9 +2056,11 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object) @@ -2037,9 +2056,11 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
2037 { 2056 {
2038 QTC::TC("qpdf", "QPDFWriter encrypt object stream"); 2057 QTC::TC("qpdf", "QPDFWriter encrypt object stream");
2039 } 2058 }
2040 - pushEncryptionFilter();  
2041 - writeBuffer(stream_buffer);  
2042 - popPipelineStack(); 2059 + {
  2060 + PipelinePopper pp_enc(this);
  2061 + pushEncryptionFilter(pp_enc);
  2062 + writeBuffer(stream_buffer);
  2063 + }
2043 if (this->m->newline_before_endstream) 2064 if (this->m->newline_before_endstream)
2044 { 2065 {
2045 writeString("\n"); 2066 writeString("\n");
@@ -2779,10 +2800,13 @@ QPDFWriter::writeHintStream(int hint_id) @@ -2779,10 +2800,13 @@ QPDFWriter::writeHintStream(int hint_id)
2779 { 2800 {
2780 QTC::TC("qpdf", "QPDFWriter encrypted hint stream"); 2801 QTC::TC("qpdf", "QPDFWriter encrypted hint stream");
2781 } 2802 }
2782 - pushEncryptionFilter();  
2783 - writeBuffer(hint_buffer);  
2784 - unsigned char last_char = this->m->pipeline->getLastChar();  
2785 - popPipelineStack(); 2803 + unsigned char last_char = '\0';
  2804 + {
  2805 + PipelinePopper pp_enc(this);
  2806 + pushEncryptionFilter(pp_enc);
  2807 + writeBuffer(hint_buffer);
  2808 + last_char = this->m->pipeline->getLastChar();
  2809 + }
2786 2810
2787 if (last_char != '\n') 2811 if (last_char != '\n')
2788 { 2812 {
@@ -2896,46 +2920,48 @@ QPDFWriter::writeXRefStream(int xref_id, int max_id, qpdf_offset_t max_offset, @@ -2896,46 +2920,48 @@ QPDFWriter::writeXRefStream(int xref_id, int max_id, qpdf_offset_t max_offset,
2896 new Pl_PNGFilter( 2920 new Pl_PNGFilter(
2897 "pngify xref", p, Pl_PNGFilter::a_encode, esize)); 2921 "pngify xref", p, Pl_PNGFilter::a_encode, esize));
2898 } 2922 }
2899 - activatePipelineStack();  
2900 - for (int i = first; i <= last; ++i) 2923 + PointerHolder<Buffer> xref_data;
2901 { 2924 {
2902 - QPDFXRefEntry& e = this->m->xref[i];  
2903 - switch (e.getType())  
2904 - {  
2905 - case 0:  
2906 - writeBinary(0, 1);  
2907 - writeBinary(0, f1_size);  
2908 - writeBinary(0, f2_size);  
2909 - break; 2925 + PipelinePopper pp_xref(this, &xref_data);
  2926 + activatePipelineStack(pp_xref);
  2927 + for (int i = first; i <= last; ++i)
  2928 + {
  2929 + QPDFXRefEntry& e = this->m->xref[i];
  2930 + switch (e.getType())
  2931 + {
  2932 + case 0:
  2933 + writeBinary(0, 1);
  2934 + writeBinary(0, f1_size);
  2935 + writeBinary(0, f2_size);
  2936 + break;
2910 2937
2911 - case 1:  
2912 - {  
2913 - qpdf_offset_t offset = e.getOffset();  
2914 - if ((hint_id != 0) &&  
2915 - (i != hint_id) &&  
2916 - (offset >= hint_offset))  
2917 - {  
2918 - offset += hint_length;  
2919 - }  
2920 - writeBinary(1, 1);  
2921 - writeBinary(QIntC::to_ulonglong(offset), f1_size);  
2922 - writeBinary(0, f2_size);  
2923 - }  
2924 - break; 2938 + case 1:
  2939 + {
  2940 + qpdf_offset_t offset = e.getOffset();
  2941 + if ((hint_id != 0) &&
  2942 + (i != hint_id) &&
  2943 + (offset >= hint_offset))
  2944 + {
  2945 + offset += hint_length;
  2946 + }
  2947 + writeBinary(1, 1);
  2948 + writeBinary(QIntC::to_ulonglong(offset), f1_size);
  2949 + writeBinary(0, f2_size);
  2950 + }
  2951 + break;
2925 2952
2926 - case 2:  
2927 - writeBinary(2, 1);  
2928 - writeBinary(QIntC::to_ulonglong(e.getObjStreamNumber()), f1_size);  
2929 - writeBinary(QIntC::to_ulonglong(e.getObjStreamIndex()), f2_size);  
2930 - break; 2953 + case 2:
  2954 + writeBinary(2, 1);
  2955 + writeBinary(QIntC::to_ulonglong(e.getObjStreamNumber()), f1_size);
  2956 + writeBinary(QIntC::to_ulonglong(e.getObjStreamIndex()), f2_size);
  2957 + break;
2931 2958
2932 - default:  
2933 - throw std::logic_error("invalid type writing xref stream");  
2934 - break;  
2935 - } 2959 + default:
  2960 + throw std::logic_error("invalid type writing xref stream");
  2961 + break;
  2962 + }
  2963 + }
2936 } 2964 }
2937 - PointerHolder<Buffer> xref_data;  
2938 - popPipelineStack(&xref_data);  
2939 2965
2940 openObject(xref_id); 2966 openObject(xref_id);
2941 writeString("<<"); 2967 writeString("<<");
@@ -3156,6 +3182,8 @@ QPDFWriter::writeLinearized() @@ -3156,6 +3182,8 @@ QPDFWriter::writeLinearized()
3156 // Write file in two passes. Part numbers refer to PDF spec 1.4. 3182 // Write file in two passes. Part numbers refer to PDF spec 1.4.
3157 3183
3158 FILE* lin_pass1_file = 0; 3184 FILE* lin_pass1_file = 0;
  3185 + PointerHolder<PipelinePopper> pp_pass1 = new PipelinePopper(this);
  3186 + PointerHolder<PipelinePopper> pp_md5 = new PipelinePopper(this);
3159 for (int pass = 1; pass <= 2; ++pass) 3187 for (int pass = 1; pass <= 2; ++pass)
3160 { 3188 {
3161 if (pass == 1) 3189 if (pass == 1)
@@ -3167,15 +3195,15 @@ QPDFWriter::writeLinearized() @@ -3167,15 +3195,15 @@ QPDFWriter::writeLinearized()
3167 this->m->lin_pass1_filename.c_str(), "wb"); 3195 this->m->lin_pass1_filename.c_str(), "wb");
3168 pushPipeline( 3196 pushPipeline(
3169 new Pl_StdioFile("linearization pass1", lin_pass1_file)); 3197 new Pl_StdioFile("linearization pass1", lin_pass1_file));
3170 - activatePipelineStack(); 3198 + activatePipelineStack(*pp_pass1);
3171 } 3199 }
3172 else 3200 else
3173 { 3201 {
3174 - pushDiscardFilter(); 3202 + pushDiscardFilter(*pp_pass1);
3175 } 3203 }
3176 if (this->m->deterministic_id) 3204 if (this->m->deterministic_id)
3177 { 3205 {
3178 - pushMD5Pipeline(); 3206 + pushMD5Pipeline(*pp_md5);
3179 } 3207 }
3180 } 3208 }
3181 3209
@@ -3392,23 +3420,25 @@ QPDFWriter::writeLinearized() @@ -3392,23 +3420,25 @@ QPDFWriter::writeLinearized()
3392 QTC::TC("qpdf", "QPDFWriter linearized deterministic ID", 3420 QTC::TC("qpdf", "QPDFWriter linearized deterministic ID",
3393 need_xref_stream ? 0 : 1); 3421 need_xref_stream ? 0 : 1);
3394 computeDeterministicIDData(); 3422 computeDeterministicIDData();
3395 - popPipelineStack(); 3423 + pp_md5 = 0;
3396 assert(this->m->md5_pipeline == 0); 3424 assert(this->m->md5_pipeline == 0);
3397 } 3425 }
3398 3426
3399 // Close first pass pipeline 3427 // Close first pass pipeline
3400 file_size = this->m->pipeline->getCount(); 3428 file_size = this->m->pipeline->getCount();
3401 - popPipelineStack(); 3429 + pp_pass1 = 0;
3402 3430
3403 // Save hint offset since it will be set to zero by 3431 // Save hint offset since it will be set to zero by
3404 // calling openObject. 3432 // calling openObject.
3405 qpdf_offset_t hint_offset = this->m->xref[hint_id].getOffset(); 3433 qpdf_offset_t hint_offset = this->m->xref[hint_id].getOffset();
3406 3434
3407 // Write hint stream to a buffer 3435 // Write hint stream to a buffer
3408 - pushPipeline(new Pl_Buffer("hint buffer"));  
3409 - activatePipelineStack();  
3410 - writeHintStream(hint_id);  
3411 - popPipelineStack(&hint_buffer); 3436 + {
  3437 + pushPipeline(new Pl_Buffer("hint buffer"));
  3438 + PipelinePopper pp_hint(this, &hint_buffer);
  3439 + activatePipelineStack(pp_hint);
  3440 + writeHintStream(hint_id);
  3441 + }
3412 hint_length = QIntC::to_offset(hint_buffer->getSize()); 3442 hint_length = QIntC::to_offset(hint_buffer->getSize());
3413 3443
3414 // Restore hint offset 3444 // Restore hint offset
@@ -3541,9 +3571,10 @@ QPDFWriter::registerProgressReporter(PointerHolder&lt;ProgressReporter&gt; pr) @@ -3541,9 +3571,10 @@ QPDFWriter::registerProgressReporter(PointerHolder&lt;ProgressReporter&gt; pr)
3541 void 3571 void
3542 QPDFWriter::writeStandard() 3572 QPDFWriter::writeStandard()
3543 { 3573 {
  3574 + PointerHolder<PipelinePopper> pp_md5 = new PipelinePopper(this);
3544 if (this->m->deterministic_id) 3575 if (this->m->deterministic_id)
3545 { 3576 {
3546 - pushMD5Pipeline(); 3577 + pushMD5Pipeline(*pp_md5);
3547 } 3578 }
3548 3579
3549 // Start writing 3580 // Start writing
@@ -3597,7 +3628,7 @@ QPDFWriter::writeStandard() @@ -3597,7 +3628,7 @@ QPDFWriter::writeStandard()
3597 { 3628 {
3598 QTC::TC("qpdf", "QPDFWriter standard deterministic ID", 3629 QTC::TC("qpdf", "QPDFWriter standard deterministic ID",
3599 this->m->object_stream_to_objects.empty() ? 0 : 1); 3630 this->m->object_stream_to_objects.empty() ? 0 : 1);
3600 - popPipelineStack(); 3631 + pp_md5 = 0;
3601 assert(this->m->md5_pipeline == 0); 3632 assert(this->m->md5_pipeline == 0);
3602 } 3633 }
3603 } 3634 }