Commit 8f455ffa6a177a61e12ad087c2cf916b6209c5da
Committed by
GitHub
Merge pull request #1590 from m-holger/i1475
Add a limit on the maximum number of filters allowed when filtering streams.
Showing
19 changed files
with
157 additions
and
17 deletions
include/qpdf/Constants.h
| ... | ... | @@ -287,6 +287,9 @@ enum qpdf_param_e { |
| 287 | 287 | qpdf_p_parser_max_container_size, |
| 288 | 288 | qpdf_p_parser_max_container_size_damaged, |
| 289 | 289 | |
| 290 | + /* stream and filter limits */ | |
| 291 | + qpdf_p_max_stream_filters = 0x14000, | |
| 292 | + | |
| 290 | 293 | /* next section = 0x20000 */ |
| 291 | 294 | qpdf_enum_max = 0x7fffffff, |
| 292 | 295 | }; | ... | ... |
include/qpdf/auto_job_c_global.hh
| ... | ... | @@ -10,3 +10,4 @@ QPDF_DLL GlobalConfig* parserMaxContainerSize(std::string const& parameter); |
| 10 | 10 | QPDF_DLL GlobalConfig* parserMaxContainerSizeDamaged(std::string const& parameter); |
| 11 | 11 | QPDF_DLL GlobalConfig* parserMaxErrors(std::string const& parameter); |
| 12 | 12 | QPDF_DLL GlobalConfig* parserMaxNesting(std::string const& parameter); |
| 13 | +QPDF_DLL GlobalConfig* maxStreamFilters(std::string const& parameter); | ... | ... |
include/qpdf/global.hh
| ... | ... | @@ -56,7 +56,7 @@ namespace qpdf::global |
| 56 | 56 | |
| 57 | 57 | /// @brief Retrieves the number of limit errors. |
| 58 | 58 | /// |
| 59 | - /// Returns the number a global limit was exceeded. This item is read only. | |
| 59 | + /// Returns the number of times a global limit was exceeded. This item is read only. | |
| 60 | 60 | /// |
| 61 | 61 | /// @return The number of limit errors. |
| 62 | 62 | /// |
| ... | ... | @@ -229,6 +229,34 @@ namespace qpdf::global |
| 229 | 229 | { |
| 230 | 230 | set_uint32(qpdf_p_parser_max_container_size_damaged, value); |
| 231 | 231 | } |
| 232 | + | |
| 233 | + /// @brief Retrieves the maximum number of filters allowed when filtering streams. | |
| 234 | + /// | |
| 235 | + /// An excessive number of stream filters is usually a sign that a file is damaged or | |
| 236 | + /// specially constructed. If the maximum is exceeded for a stream the stream is treated as | |
| 237 | + /// unfilterable. The default maximum is 25. | |
| 238 | + /// | |
| 239 | + /// @return The maximum number of filters allowed when filtering streams. | |
| 240 | + /// | |
| 241 | + /// @since 12.3 | |
| 242 | + uint32_t inline max_stream_filters() | |
| 243 | + { | |
| 244 | + return get_uint32(qpdf_p_max_stream_filters); | |
| 245 | + } | |
| 246 | + | |
| 247 | + /// @brief Sets the maximum number of filters allowed when filtering streams. | |
| 248 | + /// | |
| 249 | + /// An excessive number of stream filters is usually a sign that a file is damaged or | |
| 250 | + /// specially constructed. If the maximum is exceeded for a stream the stream is treated as | |
| 251 | + /// unfilterable. The default maximum is 25. | |
| 252 | + /// | |
| 253 | + /// @param value The maximum number of filters allowed when filtering streams to set. | |
| 254 | + /// | |
| 255 | + /// @since 12.3 | |
| 256 | + void inline max_stream_filters(uint32_t value) | |
| 257 | + { | |
| 258 | + set_uint32(qpdf_p_max_stream_filters, value); | |
| 259 | + } | |
| 232 | 260 | } // namespace limits |
| 233 | 261 | |
| 234 | 262 | } // namespace qpdf::global | ... | ... |
job.sums
| ... | ... | @@ -4,18 +4,18 @@ generate_auto_job 8e3175a515aa8837d8a01bba0346b04b3d777d70330ba5b7d52f691316054a |
| 4 | 4 | include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4 |
| 5 | 5 | include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42 |
| 6 | 6 | include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5 |
| 7 | -include/qpdf/auto_job_c_global.hh f1dc365206d033a0d6b19b6e561cc244fbd5b49a8d9604b5b646a5fd92895a5a | |
| 7 | +include/qpdf/auto_job_c_global.hh 7df0ff87d18d7fa6d57437960377509420b6b6eb9527b534996f86d3bd7a0ddc | |
| 8 | 8 | include/qpdf/auto_job_c_main.hh b865eb827356554763bb8349eadfcbc5cb260f80e025a5e229467c525007356d |
| 9 | 9 | include/qpdf/auto_job_c_pages.hh 09ca15649cc94fdaf6d9bdae28a20723f2a66616bf15aa86d83df31051d82506 |
| 10 | 10 | include/qpdf/auto_job_c_uo.hh 9c2f98a355858dd54d0bba444b73177a59c9e56833e02fa6406f429c07f39e62 |
| 11 | -job.yml 131922d22086d9f4710743e18229cc1e956268197bcae8e1aae30f3be42877be | |
| 11 | +job.yml fa98c8444c8a22a89aeeb76670aa5919aa7a86ebfac2eb45018602fbc7e45b79 | |
| 12 | 12 | libqpdf/qpdf/auto_job_decl.hh d612a02839e4f20a80e1c6a3ba09c17187fccddc3581ec7ebb1e3919ffd6801d |
| 13 | -libqpdf/qpdf/auto_job_help.hh 00ac90c621b6c0529d7bad9ea596f57595517901c8d33f49d2812fbea52dfb41 | |
| 14 | -libqpdf/qpdf/auto_job_init.hh 889dde948e0ab53616584976d9520ab7ab3773c787d241f8a107f5e2f9f2112f | |
| 13 | +libqpdf/qpdf/auto_job_help.hh 7503b1083c952ace12976857c8ece4e1af67788af9f827fb4248bd22329f93cd | |
| 14 | +libqpdf/qpdf/auto_job_init.hh 10a697528d4cae1ac566ee7612f62e611190b3c10c0021862a77fa7e4f330570 | |
| 15 | 15 | libqpdf/qpdf/auto_job_json_decl.hh 7dbb83ddadcea39bfd1faa4ca061e1e3c3134d693b8ae634b463e7e19dc8bd0a |
| 16 | -libqpdf/qpdf/auto_job_json_init.hh 3c5f3d07a85e89dd7ecd79342c18e1f0ad580fc57758abb434aa9c9ae277c01e | |
| 17 | -libqpdf/qpdf/auto_job_schema.hh eb21c99d3a4dc40b333fd1b19d5de52f8813c74a1d4ca830ea4c3311c120d63e | |
| 16 | +libqpdf/qpdf/auto_job_json_init.hh e9cacbcb78ca250a962c226a935067ef9b76f5485bae7e5302eea0a1a8e2ff65 | |
| 17 | +libqpdf/qpdf/auto_job_schema.hh 2b974a436c5b4d03fb38258d6213f993cfa9f673834cebe754b4c7ad657481c9 | |
| 18 | 18 | manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580 |
| 19 | -manual/cli.rst 08e9e7a18d2b0d05102a072f82eabf9ede6bfb1fb797be307ea680eed93ea60f | |
| 20 | -manual/qpdf.1 19a45f8de6b7c0584fe4395c4ae98b92147a2875e45dbdf729c70e644ccca295 | |
| 19 | +manual/cli.rst 0b0f6a1d8ec523751d91999586bca1356abd8f17e207bc0139ce5d7dfd64fdb4 | |
| 20 | +manual/qpdf.1 c1d6e58e37aed1b8d434b37edd1837b7261c9933b09d64bf3915dc3f35d6cccb | |
| 21 | 21 | manual/qpdf.1.in 436ecc85d45c4c9e2dbd1725fb7f0177fb627179469f114561adf3cb6cbb677b | ... | ... |
job.yml
| ... | ... | @@ -95,6 +95,7 @@ options: |
| 95 | 95 | parser-max-container-size-damaged: level |
| 96 | 96 | parser-max-errors: level |
| 97 | 97 | parser-max-nesting: level |
| 98 | + max-stream-filters: level | |
| 98 | 99 | - table: main |
| 99 | 100 | config: c_main |
| 100 | 101 | manual: |
| ... | ... | @@ -419,6 +420,7 @@ json: |
| 419 | 420 | parser-max-container-size-damaged: |
| 420 | 421 | parser-max-errors: |
| 421 | 422 | parser-max-nesting: |
| 423 | + max-stream-filters: | |
| 422 | 424 | # other options |
| 423 | 425 | update-from-json: |
| 424 | 426 | allow-weak-crypto: | ... | ... |
libqpdf/QPDFJob_config.cc
| ... | ... | @@ -1216,6 +1216,13 @@ QPDFJob::GlobalConfig::parserMaxNesting(const std::string& parameter) |
| 1216 | 1216 | return this; |
| 1217 | 1217 | } |
| 1218 | 1218 | |
| 1219 | +QPDFJob::GlobalConfig* | |
| 1220 | +QPDFJob::GlobalConfig::maxStreamFilters(const std::string& parameter) | |
| 1221 | +{ | |
| 1222 | + global::Limits::max_stream_filters(to_uint32("max-stream-filters", parameter)); | |
| 1223 | + return this; | |
| 1224 | +} | |
| 1225 | + | |
| 1219 | 1226 | QPDFJob::Config* |
| 1220 | 1227 | QPDFJob::Config::setPageLabels(const std::vector<std::string>& specs) |
| 1221 | 1228 | { | ... | ... |
libqpdf/QPDF_Stream.cc
| ... | ... | @@ -513,6 +513,13 @@ Stream::filterable( |
| 513 | 513 | // No filters |
| 514 | 514 | return true; |
| 515 | 515 | } |
| 516 | + if (filter_obj.size() > global::Limits::max_stream_filters()) { | |
| 517 | + global::Limits::error(); | |
| 518 | + warn( | |
| 519 | + "limits error(max-stream-filters): too many filters for stream; treating stream as " | |
| 520 | + "not filterable"); | |
| 521 | + return false; | |
| 522 | + } | |
| 516 | 523 | if (filter_obj.isName()) { |
| 517 | 524 | // One filter |
| 518 | 525 | auto ff = s->filter_factory(filter_obj.getName()); | ... | ... |
libqpdf/global.cc
| ... | ... | @@ -28,6 +28,9 @@ Limits::disable_defaults() |
| 28 | 28 | if (!l.parser_max_container_size_damaged_set_) { |
| 29 | 29 | l.parser_max_container_size_damaged_ = std::numeric_limits<uint32_t>::max(); |
| 30 | 30 | } |
| 31 | + if (!l.max_stream_filters_set_) { | |
| 32 | + l.max_stream_filters_ = std::numeric_limits<uint32_t>::max(); | |
| 33 | + } | |
| 31 | 34 | } |
| 32 | 35 | |
| 33 | 36 | qpdf_result_e |
| ... | ... | @@ -56,6 +59,9 @@ qpdf_global_get_uint32(qpdf_param_e param, uint32_t* value) |
| 56 | 59 | case qpdf_p_parser_max_container_size_damaged: |
| 57 | 60 | *value = Limits::parser_max_container_size(true); |
| 58 | 61 | return qpdf_r_ok; |
| 62 | + case qpdf_p_max_stream_filters: | |
| 63 | + *value = Limits::max_stream_filters(); | |
| 64 | + return qpdf_r_ok; | |
| 59 | 65 | default: |
| 60 | 66 | return qpdf_r_bad_parameter; |
| 61 | 67 | } |
| ... | ... | @@ -83,6 +89,9 @@ qpdf_global_set_uint32(qpdf_param_e param, uint32_t value) |
| 83 | 89 | case qpdf_p_parser_max_container_size_damaged: |
| 84 | 90 | Limits::parser_max_container_size(true, value); |
| 85 | 91 | return qpdf_r_ok; |
| 92 | + case qpdf_p_max_stream_filters: | |
| 93 | + Limits::max_stream_filters(value); | |
| 94 | + return qpdf_r_ok; | |
| 86 | 95 | default: |
| 87 | 96 | return qpdf_r_bad_parameter; |
| 88 | 97 | } | ... | ... |
libqpdf/qpdf/auto_job_help.hh
| ... | ... | @@ -1044,6 +1044,13 @@ See also --parser-max-container-size. |
| 1044 | 1044 | } |
| 1045 | 1045 | static void add_help_9(QPDFArgParser& ap) |
| 1046 | 1046 | { |
| 1047 | +ap.addOptionHelp("--max-stream-filters", "global", "set the maximum number of filters allowed when filtering streams", R"(--max-stream-filters=n | |
| 1048 | + | |
| 1049 | +An excessive number of stream filters is usually a sign that a file | |
| 1050 | +is damaged or specially constructed. If the maximum is exceeded for | |
| 1051 | +a stream the stream is treated as unfilterable. | |
| 1052 | +The default limit is 25. | |
| 1053 | +)"); | |
| 1047 | 1054 | ap.addHelpTopic("testing", "options for testing or debugging", R"(The options below are useful when writing automated test code that |
| 1048 | 1055 | includes files created by qpdf or when testing qpdf itself. |
| 1049 | 1056 | )"); | ... | ... |
libqpdf/qpdf/auto_job_init.hh
| ... | ... | @@ -40,6 +40,7 @@ this->ap.addRequiredParameter("parser-max-container-size", [this](std::string co |
| 40 | 40 | this->ap.addRequiredParameter("parser-max-container-size-damaged", [this](std::string const& x){c_global->parserMaxContainerSizeDamaged(x);}, "level"); |
| 41 | 41 | this->ap.addRequiredParameter("parser-max-errors", [this](std::string const& x){c_global->parserMaxErrors(x);}, "level"); |
| 42 | 42 | this->ap.addRequiredParameter("parser-max-nesting", [this](std::string const& x){c_global->parserMaxNesting(x);}, "level"); |
| 43 | +this->ap.addRequiredParameter("max-stream-filters", [this](std::string const& x){c_global->maxStreamFilters(x);}, "level"); | |
| 43 | 44 | this->ap.selectMainOptionTable(); |
| 44 | 45 | this->ap.addPositional(p(&ArgParser::argPositional)); |
| 45 | 46 | this->ap.addBare("add-attachment", b(&ArgParser::argAddAttachment)); | ... | ... |
libqpdf/qpdf/auto_job_json_init.hh
| ... | ... | @@ -289,6 +289,9 @@ popHandler(); // key: parserMaxErrors |
| 289 | 289 | pushKey("parserMaxNesting"); |
| 290 | 290 | addParameter([this](std::string const& p) { c_global->parserMaxNesting(p); }); |
| 291 | 291 | popHandler(); // key: parserMaxNesting |
| 292 | +pushKey("maxStreamFilters"); | |
| 293 | +addParameter([this](std::string const& p) { c_global->maxStreamFilters(p); }); | |
| 294 | +popHandler(); // key: maxStreamFilters | |
| 292 | 295 | popHandler(); // key: global |
| 293 | 296 | pushKey("updateFromJson"); |
| 294 | 297 | addParameter([this](std::string const& p) { c_main->updateFromJson(p); }); | ... | ... |
libqpdf/qpdf/auto_job_schema.hh
| ... | ... | @@ -95,7 +95,8 @@ static constexpr char const* JOB_SCHEMA_DATA = R"({ |
| 95 | 95 | "parserMaxContainerSize": "set the maximum container size while parsing", |
| 96 | 96 | "parserMaxContainerSizeDamaged": "set the maximum container size while parsing damaged files", |
| 97 | 97 | "parserMaxErrors": "set the maximum number of errors while parsing", |
| 98 | - "parserMaxNesting": "set the maximum nesting level while parsing objects" | |
| 98 | + "parserMaxNesting": "set the maximum nesting level while parsing objects", | |
| 99 | + "maxStreamFilters": "set the maximum number of filters allowed when filtering streams" | |
| 99 | 100 | }, |
| 100 | 101 | "updateFromJson": "update a PDF from qpdf JSON", |
| 101 | 102 | "allowWeakCrypto": "allow insecure cryptographic algorithms", | ... | ... |
libqpdf/qpdf/global_private.hh
| ... | ... | @@ -48,6 +48,19 @@ namespace qpdf::global |
| 48 | 48 | |
| 49 | 49 | static void parser_max_container_size(bool damaged, uint32_t value); |
| 50 | 50 | |
| 51 | + static uint32_t const& | |
| 52 | + max_stream_filters() | |
| 53 | + { | |
| 54 | + return l.max_stream_filters_; | |
| 55 | + } | |
| 56 | + | |
| 57 | + static void | |
| 58 | + max_stream_filters(uint32_t value) | |
| 59 | + { | |
| 60 | + l.max_stream_filters_set_ = true; | |
| 61 | + l.max_stream_filters_ = value; | |
| 62 | + } | |
| 63 | + | |
| 51 | 64 | /// Record a limit error. |
| 52 | 65 | static void |
| 53 | 66 | error() |
| ... | ... | @@ -79,6 +92,8 @@ namespace qpdf::global |
| 79 | 92 | uint32_t parser_max_container_size_{std::numeric_limits<uint32_t>::max()}; |
| 80 | 93 | uint32_t parser_max_container_size_damaged_{5'000}; |
| 81 | 94 | bool parser_max_container_size_damaged_set_{false}; |
| 95 | + uint32_t max_stream_filters_{25}; | |
| 96 | + bool max_stream_filters_set_{false}; | |
| 82 | 97 | }; |
| 83 | 98 | |
| 84 | 99 | class Options | ... | ... |
libtests/objects.cc
| ... | ... | @@ -4,6 +4,7 @@ |
| 4 | 4 | |
| 5 | 5 | #include <qpdf/QPDF.hh> |
| 6 | 6 | |
| 7 | +#include <qpdf/Pl_Discard.hh> | |
| 7 | 8 | #include <qpdf/QIntC.hh> |
| 8 | 9 | #include <qpdf/QPDFJob.hh> |
| 9 | 10 | #include <qpdf/QPDFObjectHandle_private.hh> |
| ... | ... | @@ -167,6 +168,7 @@ test_2(QPDF& pdf, char const* arg2) |
| 167 | 168 | assert(parser_max_errors() == 15); |
| 168 | 169 | assert(parser_max_container_size() == std::numeric_limits<uint32_t>::max()); |
| 169 | 170 | assert(parser_max_container_size_damaged() == 5'000); |
| 171 | + assert(max_stream_filters() == 25); | |
| 170 | 172 | assert(default_limits()); |
| 171 | 173 | |
| 172 | 174 | // Test disabling optional default limits |
| ... | ... | @@ -175,6 +177,7 @@ test_2(QPDF& pdf, char const* arg2) |
| 175 | 177 | assert(parser_max_errors() == 0); |
| 176 | 178 | assert(parser_max_container_size() == std::numeric_limits<uint32_t>::max()); |
| 177 | 179 | assert(parser_max_container_size_damaged() == std::numeric_limits<uint32_t>::max()); |
| 180 | + assert(max_stream_filters() == std::numeric_limits<uint32_t>::max()); | |
| 178 | 181 | assert(!default_limits()); |
| 179 | 182 | |
| 180 | 183 | // Check disabling default limits is irreversible |
| ... | ... | @@ -186,11 +189,13 @@ test_2(QPDF& pdf, char const* arg2) |
| 186 | 189 | parser_max_errors(12); |
| 187 | 190 | parser_max_container_size(13); |
| 188 | 191 | parser_max_container_size_damaged(14); |
| 192 | + max_stream_filters(15); | |
| 189 | 193 | |
| 190 | 194 | assert(parser_max_nesting() == 11); |
| 191 | 195 | assert(parser_max_errors() == 12); |
| 192 | 196 | assert(parser_max_container_size() == 13); |
| 193 | 197 | assert(parser_max_container_size_damaged() == 14); |
| 198 | + assert(max_stream_filters() == 15); | |
| 194 | 199 | |
| 195 | 200 | // Check disabling default limits does not override explicit limits |
| 196 | 201 | default_limits(false); |
| ... | ... | @@ -198,6 +203,7 @@ test_2(QPDF& pdf, char const* arg2) |
| 198 | 203 | assert(parser_max_errors() == 12); |
| 199 | 204 | assert(parser_max_container_size() == 13); |
| 200 | 205 | assert(parser_max_container_size_damaged() == 14); |
| 206 | + assert(max_stream_filters() == 15); | |
| 201 | 207 | |
| 202 | 208 | // Test parameter checking |
| 203 | 209 | QUtil::handle_result_code(qpdf_r_ok, ""); |
| ... | ... | @@ -239,6 +245,19 @@ test_2(QPDF& pdf, char const* arg2) |
| 239 | 245 | } |
| 240 | 246 | assert(qpdf::global::limit_errors() == 2); |
| 241 | 247 | |
| 248 | + // Test max_stream_filters | |
| 249 | + QPDF qpdf; | |
| 250 | + qpdf.emptyPDF(); | |
| 251 | + auto s = qpdf.newStream("\x01\x01\x01A"); | |
| 252 | + s.getDict().replace("/Filter", Array({Name("/RL"), Name("/RL"), Name("/RL")})); | |
| 253 | + Pl_Discard p; | |
| 254 | + auto x = s.pipeStreamData(&p, 0, qpdf_dl_all, true); | |
| 255 | + assert(x); | |
| 256 | + max_stream_filters(2); | |
| 257 | + assert(!s.pipeStreamData(&p, 0, qpdf_dl_all, true)); | |
| 258 | + max_stream_filters(3); | |
| 259 | + assert(s.pipeStreamData(&p, 0, qpdf_dl_all, true)); | |
| 260 | + | |
| 242 | 261 | // Test global settings using the QPDFJob interface |
| 243 | 262 | QPDFJob j; |
| 244 | 263 | j.config() |
| ... | ... | @@ -248,14 +267,16 @@ test_2(QPDF& pdf, char const* arg2) |
| 248 | 267 | ->parserMaxErrors("112") |
| 249 | 268 | ->parserMaxContainerSize("113") |
| 250 | 269 | ->parserMaxContainerSizeDamaged("114") |
| 270 | + ->maxStreamFilters("115") | |
| 251 | 271 | ->noDefaultLimits() |
| 252 | 272 | ->endGlobal() |
| 253 | 273 | ->outputFile("a.pdf"); |
| 254 | - auto qpdf = j.createQPDF(); | |
| 274 | + auto qpdf_uptr = j.createQPDF(); | |
| 255 | 275 | assert(parser_max_nesting() == 111); |
| 256 | 276 | assert(parser_max_errors() == 112); |
| 257 | 277 | assert(parser_max_container_size() == 113); |
| 258 | 278 | assert(parser_max_container_size_damaged() == 114); |
| 279 | + assert(max_stream_filters() == 115); | |
| 259 | 280 | assert(!default_limits()); |
| 260 | 281 | |
| 261 | 282 | // Test global settings using the JobJSON |
| ... | ... | @@ -268,16 +289,18 @@ test_2(QPDF& pdf, char const* arg2) |
| 268 | 289 | "parserMaxErrors": "212", |
| 269 | 290 | "parserMaxContainerSize": "213", |
| 270 | 291 | "parserMaxContainerSizeDamaged": "214", |
| 292 | + "maxStreamFilters": "215", | |
| 271 | 293 | "noDefaultLimits": "" |
| 272 | 294 | }, |
| 273 | 295 | "outputFile": "a.pdf" |
| 274 | 296 | } |
| 275 | 297 | )"); |
| 276 | - qpdf = j.createQPDF(); | |
| 298 | + qpdf_uptr = jj.createQPDF(); | |
| 277 | 299 | assert(parser_max_nesting() == 211); |
| 278 | 300 | assert(parser_max_errors() == 212); |
| 279 | 301 | assert(parser_max_container_size() == 213); |
| 280 | 302 | assert(parser_max_container_size_damaged() == 214); |
| 303 | + assert(max_stream_filters() == 215); | |
| 281 | 304 | assert(!default_limits()); |
| 282 | 305 | } |
| 283 | 306 | ... | ... |
libtests/qtest/objects.test
| ... | ... | @@ -20,12 +20,12 @@ $td->runtest("integer type checks", |
| 20 | 20 | |
| 21 | 21 | $td->runtest("dictionary checks", |
| 22 | 22 | {$td->COMMAND => "objects 1 -"}, |
| 23 | - {$td->STRING => => "test 1 done\n", $td->EXIT_STATUS => 0}, | |
| 23 | + {$td->STRING => "test 1 done\n", $td->EXIT_STATUS => 0}, | |
| 24 | 24 | $td->NORMALIZE_NEWLINES); |
| 25 | 25 | |
| 26 | -$td->runtest("global object limits", | |
| 26 | +$td->runtest("global limits", | |
| 27 | 27 | {$td->COMMAND => "objects 2 -"}, |
| 28 | - {$td->STRING => => "test 2 done\n", $td->EXIT_STATUS => 0}, | |
| 28 | + {$td->FILE => "test2.out", $td->EXIT_STATUS => 0}, | |
| 29 | 29 | $td->NORMALIZE_NEWLINES); |
| 30 | 30 | |
| 31 | 31 | $td->report($n_tests); | ... | ... |
libtests/qtest/objects/test2.out
0 → 100644
manual/cli.rst
| ... | ... | @@ -3822,7 +3822,6 @@ Parser Limits |
| 3822 | 3822 | Set the maximum nesting level while parsing objects. The maximum nesting level is not |
| 3823 | 3823 | disabled by :qpdf:ref:`--no-default-limits`. Defaults to 499. |
| 3824 | 3824 | |
| 3825 | - | |
| 3826 | 3825 | .. qpdf:option:: --parser-max-errors=n |
| 3827 | 3826 | |
| 3828 | 3827 | .. help: set the maximum number of errors while parsing |
| ... | ... | @@ -3848,7 +3847,6 @@ parsing. The limit applies when the PDF document's xref table is undamaged |
| 3848 | 3847 | and the object itself can be parsed without errors. The default limit |
| 3849 | 3848 | is 4,294,967,295. See also :qpdf:ref:`--parser-max-container-size-damaged`. |
| 3850 | 3849 | |
| 3851 | - | |
| 3852 | 3850 | .. qpdf:option:: --parser-max-container-size-damaged=n |
| 3853 | 3851 | |
| 3854 | 3852 | .. help: set the maximum container size while parsing damaged files |
| ... | ... | @@ -3866,6 +3864,25 @@ or the object itself is damaged. The limit also applies when parsing |
| 3866 | 3864 | xref streams. The default limit is 5,000. |
| 3867 | 3865 | See also :qpdf:ref:`--parser-max-container-size`. |
| 3868 | 3866 | |
| 3867 | + | |
| 3868 | +Stream and Filter Limits | |
| 3869 | +......................... | |
| 3870 | + | |
| 3871 | +.. qpdf:option:: --max-stream-filters=n | |
| 3872 | + | |
| 3873 | + .. help: set the maximum number of filters allowed when filtering streams | |
| 3874 | + | |
| 3875 | + An excessive number of stream filters is usually a sign that a file | |
| 3876 | + is damaged or specially constructed. If the maximum is exceeded for | |
| 3877 | + a stream the stream is treated as unfilterable. | |
| 3878 | + The default limit is 25. | |
| 3879 | + | |
| 3880 | +Set the maximum number of filters allowed when filtering streams. An excessive | |
| 3881 | +number of stream filters is usually a sign that a file is damaged or specially | |
| 3882 | +constructed. If the maximum is exceeded for a stream the stream is treated as | |
| 3883 | +unfilterable. The default limit is 25. | |
| 3884 | + | |
| 3885 | + | |
| 3869 | 3886 | .. _test-options: |
| 3870 | 3887 | |
| 3871 | 3888 | Options for Testing or Debugging | ... | ... |
manual/qpdf.1
| ... | ... | @@ -1236,6 +1236,14 @@ parsing. The limit applies when the PDF document's xref table is damaged |
| 1236 | 1236 | or the object itself is damaged. The limit also applies when parsing |
| 1237 | 1237 | xref streams. The default limit is 5,000. |
| 1238 | 1238 | See also --parser-max-container-size. |
| 1239 | +.TP | |
| 1240 | +.B --max-stream-filters \-\- set the maximum number of filters allowed when filtering streams | |
| 1241 | +--max-stream-filters=n | |
| 1242 | + | |
| 1243 | +An excessive number of stream filters is usually a sign that a file | |
| 1244 | +is damaged or specially constructed. If the maximum is exceeded for | |
| 1245 | +a stream the stream is treated as unfilterable. | |
| 1246 | +The default limit is 25. | |
| 1239 | 1247 | .SH TESTING (options for testing or debugging) |
| 1240 | 1248 | The options below are useful when writing automated test code that |
| 1241 | 1249 | includes files created by qpdf or when testing qpdf itself. | ... | ... |
manual/release-notes.rst
| ... | ... | @@ -106,6 +106,12 @@ more detail. |
| 106 | 106 | |
| 107 | 107 | - Other changes |
| 108 | 108 | |
| 109 | + - By default, streams with more than 25 filters are now treated as unfilterable. | |
| 110 | + A large number of filters typically occur in damaged or specially constructed | |
| 111 | + files and can cause excessive use of resources and/or stack overflows. The | |
| 112 | + limit can be changed if necessary with the new :qpdf:ref:`--max-stream-filters` | |
| 113 | + CLI option or the new ``qpdf::global::max_stream_filters`` function. | |
| 114 | + | |
| 109 | 115 | - When running in a FIPS environment using the GnuTLS crypto provider, |
| 110 | 116 | calls to GnuTLS now use 'LAX' mode as the use of weak algorithms is |
| 111 | 117 | required to decrypt existing files and is specified by the PDF standards | ... | ... |