Commit aabd3f6f9b09e844958fd4ee07bde5e8df546fc4

Authored by m-holger
1 parent ef866d68

Add private method QPDF::validateStreamLineEnd

include/qpdf/QPDF.hh
@@ -1009,6 +1009,7 @@ class QPDF @@ -1009,6 +1009,7 @@ class QPDF
1009 QPDFObjectHandle readTrailer(); 1009 QPDFObjectHandle readTrailer();
1010 QPDFObjectHandle readObject(std::string const& description, QPDFObjGen og); 1010 QPDFObjectHandle readObject(std::string const& description, QPDFObjGen og);
1011 void readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset); 1011 void readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
  1012 + void validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
1012 QPDFObjectHandle readObjectInStream(std::shared_ptr<InputSource>, QPDFObjGen og); 1013 QPDFObjectHandle readObjectInStream(std::shared_ptr<InputSource>, QPDFObjGen og);
1013 size_t recoverStreamLength( 1014 size_t recoverStreamLength(
1014 std::shared_ptr<InputSource> input, QPDFObjGen const& og, qpdf_offset_t stream_offset); 1015 std::shared_ptr<InputSource> input, QPDFObjGen const& og, qpdf_offset_t stream_offset);
libqpdf/QPDF.cc
@@ -1324,6 +1324,47 @@ QPDF::readObject(std::string const&amp; description, QPDFObjGen og) @@ -1324,6 +1324,47 @@ QPDF::readObject(std::string const&amp; description, QPDFObjGen og)
1324 void 1324 void
1325 QPDF::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset) 1325 QPDF::readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset)
1326 { 1326 {
  1327 + validateStreamLineEnd(object, og, offset);
  1328 +
  1329 + // Must get offset before accessing any additional objects since resolving a previously
  1330 + // unresolved indirect object will change file position.
  1331 + qpdf_offset_t stream_offset = m->file->tell();
  1332 + size_t length = 0;
  1333 +
  1334 + try {
  1335 + auto length_obj = object.getKey("/Length");
  1336 +
  1337 + if (!length_obj.isInteger()) {
  1338 + if (length_obj.isNull()) {
  1339 + QTC::TC("qpdf", "QPDF stream without length");
  1340 + throw damagedPDF(offset, "stream dictionary lacks /Length key");
  1341 + }
  1342 + QTC::TC("qpdf", "QPDF stream length not integer");
  1343 + throw damagedPDF(offset, "/Length key in stream dictionary is not an integer");
  1344 + }
  1345 +
  1346 + length = toS(length_obj.getUIntValue());
  1347 + // Seek in two steps to avoid potential integer overflow
  1348 + m->file->seek(stream_offset, SEEK_SET);
  1349 + m->file->seek(toO(length), SEEK_CUR);
  1350 + if (!readToken(m->file).isWord("endstream")) {
  1351 + QTC::TC("qpdf", "QPDF missing endstream");
  1352 + throw damagedPDF("expected endstream");
  1353 + }
  1354 + } catch (QPDFExc& e) {
  1355 + if (m->attempt_recovery) {
  1356 + warn(e);
  1357 + length = recoverStreamLength(m->file, og, stream_offset);
  1358 + } else {
  1359 + throw;
  1360 + }
  1361 + }
  1362 + object = newIndirect(og, QPDF_Stream::create(this, og, object, stream_offset, length));
  1363 +}
  1364 +
  1365 +void
  1366 +QPDF::validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset)
  1367 +{
1327 // The PDF specification states that the word "stream" should be followed by either a carriage 1368 // The PDF specification states that the word "stream" should be followed by either a carriage
1328 // return and a newline or by a newline alone. It specifically disallowed following it by a 1369 // return and a newline or by a newline alone. It specifically disallowed following it by a
1329 // carriage return alone since, in that case, there would be no way to tell whether the NL in a 1370 // carriage return alone since, in that case, there would be no way to tell whether the NL in a
@@ -1336,12 +1377,12 @@ QPDF::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offset) @@ -1336,12 +1377,12 @@ QPDF::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offset)
1336 if (m->file->read(&ch, 1) == 0) { 1377 if (m->file->read(&ch, 1) == 0) {
1337 // A premature EOF here will result in some other problem that will get reported at 1378 // A premature EOF here will result in some other problem that will get reported at
1338 // another time. 1379 // another time.
1339 - break; 1380 + return;
1340 } 1381 }
1341 if (ch == '\n') { 1382 if (ch == '\n') {
1342 // ready to read stream data 1383 // ready to read stream data
1343 QTC::TC("qpdf", "QPDF stream with NL only"); 1384 QTC::TC("qpdf", "QPDF stream with NL only");
1344 - break; 1385 + return;
1345 } 1386 }
1346 if (ch == '\r') { 1387 if (ch == '\r') {
1347 // Read another character 1388 // Read another character
@@ -1358,52 +1399,17 @@ QPDF::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offset) @@ -1358,52 +1399,17 @@ QPDF::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offset)
1358 m->file->tell(), "stream keyword followed by carriage return only")); 1399 m->file->tell(), "stream keyword followed by carriage return only"));
1359 } 1400 }
1360 } 1401 }
1361 - break; 1402 + return;
1362 } 1403 }
1363 if (!QUtil::is_space(ch)) { 1404 if (!QUtil::is_space(ch)) {
1364 QTC::TC("qpdf", "QPDF stream without newline"); 1405 QTC::TC("qpdf", "QPDF stream without newline");
1365 m->file->unreadCh(ch); 1406 m->file->unreadCh(ch);
1366 warn(damagedPDF( 1407 warn(damagedPDF(
1367 m->file->tell(), "stream keyword not followed by proper line terminator")); 1408 m->file->tell(), "stream keyword not followed by proper line terminator"));
1368 - break; 1409 + return;
1369 } 1410 }
1370 warn(damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace")); 1411 warn(damagedPDF(m->file->tell(), "stream keyword followed by extraneous whitespace"));
1371 } 1412 }
1372 -  
1373 - // Must get offset before accessing any additional objects since resolving a previously  
1374 - // unresolved indirect object will change file position.  
1375 - qpdf_offset_t stream_offset = m->file->tell();  
1376 - size_t length = 0;  
1377 -  
1378 - try {  
1379 - auto length_obj = object.getKey("/Length");  
1380 -  
1381 - if (!length_obj.isInteger()) {  
1382 - if (length_obj.isNull()) {  
1383 - QTC::TC("qpdf", "QPDF stream without length");  
1384 - throw damagedPDF(offset, "stream dictionary lacks /Length key");  
1385 - }  
1386 - QTC::TC("qpdf", "QPDF stream length not integer");  
1387 - throw damagedPDF(offset, "/Length key in stream dictionary is not an integer");  
1388 - }  
1389 -  
1390 - length = toS(length_obj.getUIntValue());  
1391 - // Seek in two steps to avoid potential integer overflow  
1392 - m->file->seek(stream_offset, SEEK_SET);  
1393 - m->file->seek(toO(length), SEEK_CUR);  
1394 - if (!readToken(m->file).isWord("endstream")) {  
1395 - QTC::TC("qpdf", "QPDF missing endstream");  
1396 - throw damagedPDF("expected endstream");  
1397 - }  
1398 - } catch (QPDFExc& e) {  
1399 - if (m->attempt_recovery) {  
1400 - warn(e);  
1401 - length = recoverStreamLength(m->file, og, stream_offset);  
1402 - } else {  
1403 - throw;  
1404 - }  
1405 - }  
1406 - object = newIndirect(og, QPDF_Stream::create(this, og, object, stream_offset, length));  
1407 } 1413 }
1408 1414
1409 QPDFObjectHandle 1415 QPDFObjectHandle