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 1009 QPDFObjectHandle readTrailer();
1010 1010 QPDFObjectHandle readObject(std::string const& description, QPDFObjGen og);
1011 1011 void readStream(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
  1012 + void validateStreamLineEnd(QPDFObjectHandle& object, QPDFObjGen og, qpdf_offset_t offset);
1012 1013 QPDFObjectHandle readObjectInStream(std::shared_ptr<InputSource>, QPDFObjGen og);
1013 1014 size_t recoverStreamLength(
1014 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 1324 void
1325 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 1368 // The PDF specification states that the word "stream" should be followed by either a carriage
1328 1369 // return and a newline or by a newline alone. It specifically disallowed following it by a
1329 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 1377 if (m->file->read(&ch, 1) == 0) {
1337 1378 // A premature EOF here will result in some other problem that will get reported at
1338 1379 // another time.
1339   - break;
  1380 + return;
1340 1381 }
1341 1382 if (ch == '\n') {
1342 1383 // ready to read stream data
1343 1384 QTC::TC("qpdf", "QPDF stream with NL only");
1344   - break;
  1385 + return;
1345 1386 }
1346 1387 if (ch == '\r') {
1347 1388 // Read another character
... ... @@ -1358,52 +1399,17 @@ QPDF::readStream(QPDFObjectHandle&amp; object, QPDFObjGen og, qpdf_offset_t offset)
1358 1399 m->file->tell(), "stream keyword followed by carriage return only"));
1359 1400 }
1360 1401 }
1361   - break;
  1402 + return;
1362 1403 }
1363 1404 if (!QUtil::is_space(ch)) {
1364 1405 QTC::TC("qpdf", "QPDF stream without newline");
1365 1406 m->file->unreadCh(ch);
1366 1407 warn(damagedPDF(
1367 1408 m->file->tell(), "stream keyword not followed by proper line terminator"));
1368   - break;
  1409 + return;
1369 1410 }
1370 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 1415 QPDFObjectHandle
... ...