From 5030a55f1d1f92866d0dd34ef69a7cbf6a213ba2 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Mon, 25 May 2026 16:58:42 +0100 Subject: [PATCH 1/2] Regression test for https://github.com/Exiv2/exiv2/security/advisories/GHSA-fgw8-p7pr-37cp --- test/data/issue_ghsa_fgw8_p7pr_37cp_poc.mov | Bin 0 -> 5938 bytes .../github/test_issue_ghsa_fgw8_p7pr_37cp.py | 19 ++++++++++++++++++ .../test_regression_allfiles.py | 1 + 3 files changed, 20 insertions(+) create mode 100644 test/data/issue_ghsa_fgw8_p7pr_37cp_poc.mov create mode 100644 tests/bugfixes/github/test_issue_ghsa_fgw8_p7pr_37cp.py diff --git a/test/data/issue_ghsa_fgw8_p7pr_37cp_poc.mov b/test/data/issue_ghsa_fgw8_p7pr_37cp_poc.mov new file mode 100644 index 0000000000000000000000000000000000000000..b27037e2ff13a3463b7dcec52d7a17ba6568bcd6 GIT binary patch literal 5938 zcmeHK%}T>S5S|1C5%CFHFo=kU3Z+_$C#|;Ls)&bPtKF6yYD!Iuq6fYC2EA77)td+L z1w_GveFC3A5Fa4UrrkA}HjN@`tqCNX&17fi|CHFZpOb*Ks#}Vnfr?il!w)N*bcE*g`x~Y(>tzuhw;V zeX4Qua`9BZOw1fXGNb{VRI7SEc6;4yxrw195|uXZs5gpwspc?Ax8&_FHX>5X5hr{) z`-Z3%50NS)AG%VH(7&_w5-A)6o@|V%DTp_Q(OamZ2n3?7qj0s4D1P(+Rt5`J1l;^M zV%l&j5Hr5G7%6T=YOJ!a8+#R_T4pr{BLKVMx&+;(MC7AH{poV1jmRAq2-v@m&uuWjjicLwr}>`pYnIi(TB2mbQCHP@?PWbrUD4Depiej zBYWeSR~me^6UVR>tQ`^Dv+4Jv2N~JxP$$9TBgZ)xIK4xG^Y|vA0#ByAH~f>uW>B(x oOy%ao3*mSfZ|k*jdc{Y};)sonfJE<(xjW|HOl7uLvXj8x0MWGIcmMzZ literal 0 HcmV?d00001 diff --git a/tests/bugfixes/github/test_issue_ghsa_fgw8_p7pr_37cp.py b/tests/bugfixes/github/test_issue_ghsa_fgw8_p7pr_37cp.py new file mode 100644 index 0000000000..4b570e232d --- /dev/null +++ b/tests/bugfixes/github/test_issue_ghsa_fgw8_p7pr_37cp.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from system_tests import CaseMeta, path, check_no_ASAN_UBSAN_errors + +class QuickTimeVideo_userDataDecoder_infinite_loop(metaclass=CaseMeta): + """ + Regression test for the bug described in: + https://github.com/Exiv2/exiv2/security/advisories/GHSA-fgw8-p7pr-37cp + """ + url = "https://github.com/Exiv2/exiv2/security/advisories/GHSA-fgw8-p7pr-37cp" + + filename = path("$data_path/issue_ghsa_fgw8_p7pr_37cp_poc.mov") + commands = ["$exiv2 $filename"] + stdout = [""] + stderr = [ + """$exiv2_exception_message """ + filename + """: +$kerCorruptedMetadata +"""] + retval = [1] diff --git a/tests/regression_tests/test_regression_allfiles.py b/tests/regression_tests/test_regression_allfiles.py index aa603264a5..8eb358a461 100644 --- a/tests/regression_tests/test_regression_allfiles.py +++ b/tests/regression_tests/test_regression_allfiles.py @@ -123,6 +123,7 @@ def get_valid_files(data_dir): "issue_ghsa_38h4_fx85_qcx7_poc.tiff", "issue_ghsa_496f_x7cq_cq39_poc.jpg", "issue_ghsa_9mxq_4j5g_5wrp.crw", + "issue_ghsa_fgw8_p7pr_37cp_poc.mov", "pocIssue283.jpg", "poc_1522.jp2", "xmpsdk.xmp", From 5a562569998a1a198cebfd0f5f296f931bf376a7 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Mon, 25 May 2026 16:59:52 +0100 Subject: [PATCH 2/2] Stop the parser from backtracking, which could cause an infinite loop. --- src/quicktimevideo.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/quicktimevideo.cpp b/src/quicktimevideo.cpp index 5cd34bb058..2d6cab1058 100644 --- a/src/quicktimevideo.cpp +++ b/src/quicktimevideo.cpp @@ -848,7 +848,7 @@ void QuickTimeVideo::CameraTagsDecoder(size_t size) { void QuickTimeVideo::userDataDecoder(size_t outer_size, size_t recursion_depth) { enforce(recursion_depth < max_recursion_depth_, Exiv2::ErrorCode::kerCorruptedMetadata); - size_t cur_pos = io_->tell(); + const size_t start_pos = io_->tell(); const TagVocabulary* td; const TagVocabulary* tv; const TagVocabulary* tv_internal; @@ -859,6 +859,7 @@ void QuickTimeVideo::userDataDecoder(size_t outer_size, size_t recursion_depth) std::memset(buf.data(), 0x0, buf.size()); while ((size_internal / 4 != 0) && (size_internal > 0)) { + const size_t loop_start_pos = io_->tell(); buf.data()[4] = '\0'; io_->readOrThrow(buf.data(), 4); const size_t size = buf.read_uint32(0, bigEndian); @@ -910,9 +911,11 @@ void QuickTimeVideo::userDataDecoder(size_t outer_size, size_t recursion_depth) else if (td) tagDecoder(buf, size - 8, recursion_depth + 1); + + enforce(io_->tell() <= loop_start_pos + size, Exiv2::ErrorCode::kerCorruptedMetadata); } - io_->seek(cur_pos + outer_size, BasicIo::beg); + io_->seek(start_pos + outer_size, BasicIo::beg); } // QuickTimeVideo::userDataDecoder void QuickTimeVideo::NikonTagsDecoder(size_t size) {