Skip to content

Add RecursionGuard - #9456

Open
kevinbackhouse wants to merge 1 commit into
Exiv2:mainfrom
kevinbackhouse:recursion_guard
Open

Add RecursionGuard#9456
kevinbackhouse wants to merge 1 commit into
Exiv2:mainfrom
kevinbackhouse:recursion_guard

Conversation

@kevinbackhouse

Copy link
Copy Markdown
Collaborator

This is my proposed solution to #9414. The idea is that we add this statement at the start of every recursive parsing function:

RECURSION_GUARD(recursion_limit_);

In this PR, I haven't added it to all the parsing functions yet. I have only added it to the parsers that already had recursion depth tracking, for example bmffimage.

It works by decrementing Image::recursion_limit_ at the beginning of the function and incrementing it back to it's original value on function exit. This is done by the RecursionGuard class. Function exit is handled automatically by RecursionGuard's destructor. An exception is thrown if the limit is decremented all the way down to zero.

The default limit is 1000. I've also added a command-line parameter so that you can change it. For example:

exiv2 pic.jpg  # limit is 1000
exiv2 -R 20000 pic.jpg  # limit is 20000
exiv2 -R 0 pic.jpg  # no limit

@kevinbackhouse kevinbackhouse added this to the v0.29.0 milestone Aug 28, 2026
@kevinbackhouse
kevinbackhouse requested a balanced review from Copilot August 29, 2026 13:36
@codecov

codecov Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.96610% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.46%. Comparing base (53ad1a1) to head (371f9e2).
⚠️ Report is 64 commits behind head on main.

Files with missing lines Patch % Lines
app/exiv2.cpp 15.78% 16 Missing ⚠️
src/tiffvisitor_int.cpp 53.84% 5 Missing and 1 partial ⚠️
src/preview.cpp 33.33% 4 Missing ⚠️
app/actions.cpp 92.68% 3 Missing ⚠️
src/bmffimage.cpp 66.66% 1 Missing and 1 partial ⚠️
src/pgfimage.cpp 60.00% 2 Missing ⚠️
src/rw2image.cpp 33.33% 2 Missing ⚠️
include/exiv2/recursion_guard.hpp 91.66% 0 Missing and 1 partial ⚠️
src/cr2image.cpp 0.00% 1 Missing ⚠️
src/mrwimage.cpp 0.00% 1 Missing ⚠️
... and 1 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9456      +/-   ##
==========================================
- Coverage   65.61%   65.46%   -0.16%     
==========================================
  Files         117      118       +1     
  Lines       21596    21679      +83     
  Branches    10740    10788      +48     
==========================================
+ Hits        14171    14192      +21     
- Misses       5133     5191      +58     
- Partials     2292     2296       +4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

Review tier: Lite
Findings: 7 High severity · 4 Medium severity · 1 Low severity

New issues introduced by this change (12)
Severity Finding
High severity include/​exiv2/​image.hpp — These signature changes are an API break for downstream users (e.g., `ImageFactory::open(path, bool…
High severity include/​exiv2/​image.hpp — These signature changes are an API break for downstream users (e.g., `ImageFactory::open(path, bool…
High severity include/​exiv2/​image.hpp — These signature changes are an API break for downstream users (e.g., `ImageFactory::open(path, bool…
High severity include/​exiv2/​image.hpp — These signature changes are an API break for downstream users (e.g., `ImageFactory::open(path, bool…
High severity include/​exiv2/​image.hpp — These signature changes are an API break for downstream users (e.g., `ImageFactory::open(path, bool…
High severity include/​exiv2/​image.hpp — These signature changes are an API break for downstream users (e.g., `ImageFactory::open(path, bool…
Medium severity include/​exiv2/​recursion_guard.hpp — The macro expands to a fixed local variable name (_recursion_guard), which can cause a compile…
Low severity include/​exiv2/​recursion_guard.hpp — Fix grammar in this new documentation comment: use 'Its constructor' (possessive) instead of 'It's…
High severity app/​exiv2.cpp — If the user passes a positive value that exceeds size_t::max, the code silently falls back to 'no…
Medium severity app/​exiv2.cpp — The PR introduces a new CLI surface (-R) and new parsing/validation paths in setRecursionLimit,…
Medium severity app/​exiv2.cpp — The PR introduces a new CLI surface (-R) and new parsing/validation paths in setRecursionLimit,…
Medium severity app/​exiv2.cpp — This new error message is vague and doesn’t indicate which option failed or what format is…
What changed in this PR

[!WARNING]
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

This PR introduces a reusable recursion-limiting mechanism (RecursionLimit + RecursionGuard) and wires it through image decoding/metadata parsing to mitigate excessively deep nesting (solution direction for #9414). It also adds a CLI flag (-R) to configure the maximum recursion depth and updates tests/fixtures accordingly.

Changes:

  • Add RecursionLimit / RecursionGuard and a RECURSION_GUARD(...) helper macro; adopt it in selected recursive parsers.
  • Replace several max_recursion_depth_ usages with the new RecursionLimit plumbing via ImageCtorParams / DecodeParams.
  • Add -R CLI option (and update golden outputs + a regression test expectation) to configure recursion depth / disable the limit.
File Description
unitTests/​unittest_utils.cpp Updates helper constructors to pass new ImageCtorParams / RecursionLimit.
unitTests/​test_ImageFactory.cpp Updates ImageFactory tests to use new ImageCtorParams signatures.
tests/​suite.conf Adds expected error string key for max recursion depth.
tests/​bugfixes/​github/​test_issue_ghsa_crmj_qh74_2r36.py Updates expected stderr to new recursion-depth error.
test/​data/​test_reference_files/​exiv2-test.out Updates CLI help reference output to include -R.
src/​xmpsidecar.cpp Switches decode params to use recursion_limit().
src/​xmp.cpp Refactors validator construction to accept RecursionLimit.
src/​webpimage.cpp Switches decode params to use recursion_limit().
src/​tiffvisitor_int.hpp Changes decoder member from size_t to RecursionLimit.
src/​tiffvisitor_int.cpp Formatting-only changes to warnings/errors (line wrapping).
src/​tiffimage.cpp Switches decode params to use recursion_limit().
src/​rw2image.cpp Switches decode params to use recursion_limit(); threads params through preview open.
src/​rafimage.cpp Threads ImageCtorParams into embedded JPEG parsing; switches decode params.
src/​quicktimevideo.cpp Replaces manual depth tracking with RECURSION_GUARD in recursive traversal.
src/​psdimage.cpp Switches decode params to use recursion_limit().
src/​preview.cpp Threads recursion limit into preview image opens.
src/​pngimage.cpp Switches decode params to use recursion_limit().
src/​pgfimage.cpp Adds RECURSION_GUARD and threads params into internal opens/creates.
src/​orfimage.cpp Switches decode params to use recursion_limit().
src/​mrwimage.cpp Switches decode params to use recursion_limit().
src/​jpgimage.cpp Switches decode params to use recursion_limit().
src/​jp2image.cpp Switches decode params to use recursion_limit().
src/​image.cpp Introduces ImageCtorParams defaults + withCreate; updates ImageFactory open/create APIs.
src/​exif.cpp Updates DecodeParams ctor to accept RecursionLimit.
src/​error.cpp Adds localized error message for kerMaxRecursionDepth.
src/​epsimage.cpp Switches decode params to use recursion_limit().
src/​cr2image.cpp Switches decode params to use recursion_limit().
src/​bmffimage.cpp Adds RECURSION_GUARD to recursion entry point; removes redundant depth check.
src/​asfvideo.cpp Migrates recursion limiting from manual depth to RECURSION_GUARD.
src/​CMakeLists.txt Exposes new public header recursion_guard.hpp.
samples/​xmpparser-test.cpp Updates sample to construct DecodeParams with RecursionLimit.
samples/​xmpparse.cpp Updates sample to construct DecodeParams with RecursionLimit.
samples/​tiff-test.cpp Updates sample to use RecursionLimit / default ctor params.
samples/​remotetest.cpp Updates sample to use new ImageFactory::open signature with ImageCtorParams.
include/​meson.build Exposes new public header recursion_guard.hpp for Meson builds.
include/​exiv2/​recursion_guard.hpp Adds new RecursionLimit / RecursionGuard API + macro.
include/​exiv2/​quicktimevideo.hpp Updates method signatures after removing explicit depth parameters.
include/​exiv2/​params.hpp Changes DecodeParams to store RecursionLimit.
include/​exiv2/​image.hpp Changes ImageFactory API to accept ImageCtorParams; adds recursion accessor.
include/​exiv2/​error.hpp Adds new ErrorCode::kerMaxRecursionDepth.
include/​exiv2/​asfvideo.hpp Updates method signatures after removing explicit depth parameters.
app/​exiv2app.hpp Adds max_recursion_depth_ storage + setter declaration.
app/​exiv2.cpp Adds -R option parsing and help text.
app/​actions.hpp Makes Task non-copyable; stores ImageCtorParams for consistent recursion configuration.
app/​actions.cpp Threads ImageCtorParams through app open/create paths; updates helper signatures.
Suppressed comments (1)

src/tiffvisitor_int.hpp:1

  • This member is now a RecursionLimit but is also const and still named max_recursion_depth_. If this is intended as a numeric limit used for comparisons, storing a size_t (or renaming to something like recursion_limit_ and storing a plain 'limit' value) would better communicate intent. If it’s intended to participate in RECURSION_GUARD, it can’t be const because the guard mutates the counter.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread include/exiv2/image.hpp
Comment thread include/exiv2/image.hpp
Comment thread include/exiv2/image.hpp
Comment thread include/exiv2/image.hpp
Comment thread include/exiv2/image.hpp
Comment thread include/exiv2/recursion_guard.hpp Outdated
Comment thread app/exiv2.cpp
Comment thread app/exiv2.cpp
Comment thread app/exiv2.cpp
Comment thread app/exiv2.cpp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants