Convert stream filtering errors to warnings

This commit is contained in:
Jay Berkenbilt 2017-07-27 18:18:18 -04:00
parent 40f00122b8
commit a4fd4b91c6
13 changed files with 58 additions and 32 deletions

View File

@ -1,5 +1,9 @@
2017-07-27 Jay Berkenbilt <ejb@ql.org>
* Recover gracefully from streams that aren't filterable because
the filter parameters are invalid in the stream dictionary or the
dictionary itself is invalid.
* Significantly improve recoverability from invalid qpdf objects.
Most conditions in basic object parsing that used to cause qpdf to
exit are now warnings. There are still many more opportunities for

View File

@ -526,6 +526,7 @@ class QPDF
class Warner
{
friend class QPDFObjectHandle;
friend class QPDF_Stream;
private:
static void warn(QPDF* qpdf, QPDFExc const& e)
{

View File

@ -235,9 +235,10 @@ QPDF_Stream::filterable(std::vector<std::string>& filters,
if (! filters_okay)
{
QTC::TC("qpdf", "QPDF_Stream invalid filter");
throw QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(),
"", this->offset,
"stream filter type is not name or array");
warn(QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(),
"", this->offset,
"stream filter type is not name or array"));
return false;
}
bool filterable = true;
@ -300,12 +301,16 @@ QPDF_Stream::filterable(std::vector<std::string>& filters,
// /Filters was empty has been seen in the wild.
if ((filters.size() != 0) && (decode_parms.size() != filters.size()))
{
// We should just issue a warning and treat this as not
// filterable.
throw QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(),
"", this->offset,
"stream /DecodeParms length is"
" inconsistent with filters");
warn(QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(),
"", this->offset,
"stream /DecodeParms length is"
" inconsistent with filters"));
filterable = false;
}
if (! filterable)
{
return false;
}
for (unsigned int i = 0; i < filters.size(); ++i)
@ -454,7 +459,9 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline, bool filter,
else
{
QTC::TC("qpdf", "QPDF_Stream provider length mismatch");
throw std::logic_error(
// This would be caused by programmer error on the
// part of a library user, not by invalid input data.
throw std::runtime_error(
"stream data provider for " +
QUtil::int_to_string(this->objid) + " " +
QUtil::int_to_string(this->generation) +
@ -542,3 +549,9 @@ QPDF_Stream::replaceDict(QPDFObjectHandle new_dict)
this->length = 0;
}
}
void
QPDF_Stream::warn(QPDFExc const& e)
{
QPDF::Warner::warn(this->qpdf, e);
}

View File

@ -646,6 +646,6 @@ QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf)
{
QPDF_ERROR_CODE status = QPDF_SUCCESS;
status = trap_errors(qpdf, &call_write);
QTC::TC("qpdf", "qpdf-c called qpdf_write", status);
QTC::TC("qpdf", "qpdf-c called qpdf_write", (status == 0) ? 0 : 1);
return status;
}

View File

@ -52,6 +52,7 @@ class QPDF_Stream: public QPDFObject
int& predictor, int& columns, bool& early_code_change);
bool filterable(std::vector<std::string>& filters,
int& predictor, int& columns, bool& early_code_change);
void warn(QPDFExc const& e);
QPDF* qpdf;
int objid;

View File

@ -139,7 +139,7 @@ qpdf-c called qpdf_set_preserve_encryption 0
qpdf-c called qpdf_set_r2_encryption_parameters 0
qpdf-c called qpdf_set_r3_encryption_parameters 0
qpdf-c called qpdf_set_linearization 0
qpdf-c called qpdf_write 3
qpdf-c called qpdf_write 1
qpdf-c called qpdf_allow_accessibility 0
qpdf-c called qpdf_allow_extract_all 0
qpdf-c called qpdf_allow_print_low_res 0

View File

@ -787,7 +787,7 @@ my @badfiles = ("not a PDF file", # 1
"bad dictionary key", # 36
);
$n_tests += @badfiles + 4;
$n_tests += @badfiles + 3;
# Test 6 contains errors in the free table consistency, but we no
# longer have any consistency check for this since it is not important
@ -796,7 +796,7 @@ $n_tests += @badfiles + 4;
# non-fatal.
my %badtest_overrides = (6 => 0, 12 => 0, 13 => 0,
14 => 0, 15 => 0, 17 => 0,
28 => 0, 31 => 0, 36 => 0);
28 => 0, 30 => 0, 31 => 0, 36 => 0);
for (my $i = 1; $i <= scalar(@badfiles); ++$i)
{
my $status = $badtest_overrides{$i};
@ -813,14 +813,9 @@ $td->runtest("C API: errors",
{$td->FILE => "c-read-errors.out",
$td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("C API: errors writing",
{$td->COMMAND => "qpdf-ctest 2 bad30.pdf '' a.pdf"},
{$td->FILE => "c-write-errors.out",
$td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("C API: errors and warnings writing",
$td->runtest("C API: warnings writing",
{$td->COMMAND => "qpdf-ctest 2 bad33.pdf '' a.pdf"},
{$td->FILE => "c-write-warnings-and-errors.out",
{$td->FILE => "c-write-warnings.out",
$td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("C API: no recovery",
@ -840,7 +835,7 @@ $n_tests += @badfiles + 8;
# though in some cases it may. Acrobat Reader would not be able to
# recover any of these files any better.
my %recover_failures = ();
for (1, 7, 16, 18..21, 24, 29..30, 33, 35)
for (1, 7, 16, 18..21, 24, 29, 35)
{
$recover_failures{$_} = 1;
}

View File

@ -3,4 +3,8 @@
Raw stream data:
xœ%<25>11 û¼b;t<>à4| wXIDì Øå÷8G·«Í>rQ¨uŠ êEŒ:©IWìÃPlíµII)Ãr´p4~;§ÎAs/òÒ…jcúú¾÷Žs§åözû»ž<C2BB>T.?®uŽæ§<Ž¼…Ð*6ä
Uncompressed stream data:
bad30.pdf (file position 629): stream filter type is not name or array
WARNING: bad30.pdf (file position 629): stream filter type is not name or array
Stream data is not filterable.
unparse: 7 0 R
unparseResolved: 7 0 R
test 1 done

View File

@ -3,4 +3,8 @@
Raw stream data:
xœ%<25>11 û¼b;t<>à4| wXIDì Øå÷8G·«Í>rQ¨uŠ êEŒ:©IWìÃPlíµII)Ãr´p4~;§ÎAs/òÒ…jcúú¾÷Žs§åözû»ž<C2BB>T.?®uŽæ§<Ž¼…Ð*6ä
Uncompressed stream data:
bad30.pdf (file position 629): stream filter type is not name or array
WARNING: bad30.pdf (file position 629): stream filter type is not name or array
Stream data is not filterable.
unparse: 7 0 R
unparseResolved: 7 0 R
test 0 done

View File

@ -6,4 +6,8 @@ WARNING: bad33.pdf: Attempting to reconstruct cross-reference table
Raw stream data:
xœ%<25>11 û¼b;t<>à4| wXIDì Øå÷8G·«Í>rQ¨uŠ êEŒ:©IWìÃPlíµII)Ãr´p4~;§ÎAs/òÒ…jcúú¾÷Žs§åözû»ž<C2BB>T.?®uŽæ§<Ž¼…Ð*6ä
Uncompressed stream data:
bad33.pdf (file position 629): stream filter type is not name or array
WARNING: bad33.pdf (file position 629): stream filter type is not name or array
Stream data is not filterable.
unparse: 7 0 R
unparseResolved: 7 0 R
test 1 done

View File

@ -1,5 +0,0 @@
error: bad30.pdf (file position 629): stream filter type is not name or array
code: 5
file: bad30.pdf
pos : 629
text: stream filter type is not name or array

View File

@ -13,7 +13,12 @@ warning: bad33.pdf: Attempting to reconstruct cross-reference table
file: bad33.pdf
pos : 0
text: Attempting to reconstruct cross-reference table
error: bad33.pdf (file position 629): stream filter type is not name or array
warning: bad33.pdf (file position 629): stream filter type is not name or array
code: 5
file: bad33.pdf
pos : 629
text: stream filter type is not name or array
warning: bad33.pdf (file position 629): stream filter type is not name or array
code: 5
file: bad33.pdf
pos : 629

View File

@ -567,7 +567,7 @@ void runtest(int n, char const* filename1, char const* arg2)
qstream.getStreamData();
std::cout << "oops -- getStreamData didn't throw" << std::endl;
}
catch (std::logic_error const& e)
catch (std::exception const& e)
{
std::cout << "exception: " << e.what() << std::endl;
}