From 9a133856ba94c4f5a52ec73c10207399a767fca8 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Sun, 27 Nov 2022 15:28:00 +0000 Subject: [PATCH 1/2] Regression test for https://github.com/Exiv2/exiv2/issues/2427 --- test/data/issue_2427_poc.jpg | Bin 0 -> 80 bytes tests/bugfixes/github/test_issue_2427.py | 13 +++++++++++++ tests/regression_tests/test_regression_allfiles.py | 1 + 3 files changed, 14 insertions(+) create mode 100644 test/data/issue_2427_poc.jpg create mode 100644 tests/bugfixes/github/test_issue_2427.py diff --git a/test/data/issue_2427_poc.jpg b/test/data/issue_2427_poc.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e42d8e107d2a1cf427934c2018604573993e756b GIT binary patch literal 80 zcmZQzU=U0zsVrb&$V`65009z7Mfpj|`8h>Q3@ooWfnu5<@faspABBjJOrQpCCI(gp ShW`-N3JeT48J;sjcpLzCJ`RWg literal 0 HcmV?d00001 diff --git a/tests/bugfixes/github/test_issue_2427.py b/tests/bugfixes/github/test_issue_2427.py new file mode 100644 index 00000000..113aa0c5 --- /dev/null +++ b/tests/bugfixes/github/test_issue_2427.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + +from system_tests import CaseMeta, check_no_ASAN_UBSAN_errors + +class issue_2427_BmffImage_brotliUncompress_memleak(metaclass=CaseMeta): + url = "https://github.com/Exiv2/exiv2/issues/2427" + filename = "$data_path/issue_2427_poc.jpg" + commands = ["$exiv2 $filename"] + retval = [1] + stderr = ["""$exiv2_exception_message $filename: +CL_SPACE +"""] + stdout = [""] diff --git a/tests/regression_tests/test_regression_allfiles.py b/tests/regression_tests/test_regression_allfiles.py index bf2d04fb..4ce711c2 100644 --- a/tests/regression_tests/test_regression_allfiles.py +++ b/tests/regression_tests/test_regression_allfiles.py @@ -66,6 +66,7 @@ def get_valid_files(data_dir): "issue_2383_poc.mp4", "issue_2393_poc.mp4", "issue_2423_poc.mp4", + "issue_2427_poc.jpg", "2018-01-09-exiv2-crash-001.tiff", "cve_2017_1000126_stack-oob-read.webp", "exiv2-bug1247.jpg", From 2739d90073fd4a920fd0a8f4431d19b90514229f Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Sun, 27 Nov 2022 15:29:59 +0000 Subject: [PATCH 2/2] Credit to OSS-Fuzz: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52767 Add wrapper class to automatically call BrotliDecoderDestroyInstance() on exit. --- src/bmffimage.cpp | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/bmffimage.cpp b/src/bmffimage.cpp index 8aa38e8a..47bfe311 100644 --- a/src/bmffimage.cpp +++ b/src/bmffimage.cpp @@ -164,13 +164,30 @@ std::string BmffImage::uuidName(const Exiv2::DataBuf& uuid) { } #ifdef EXV_HAVE_BROTLI -void BmffImage::brotliUncompress(const byte* compressedBuf, size_t compressedBufSize, DataBuf& arr) { - BrotliDecoderState* decoder = NULL; - decoder = BrotliDecoderCreateInstance(NULL, NULL, NULL); - if (!decoder) { - throw Error(ErrorCode::kerMallocFailed); + +// Wrapper class for BrotliDecoderState that automatically calls +// BrotliDecoderDestroyInstance in its destructor. +class BrotliDecoderWrapper { + BrotliDecoderState* decoder_; + + public: + BrotliDecoderWrapper() : decoder_(BrotliDecoderCreateInstance(NULL, NULL, NULL)) { + if (!decoder_) { + throw Error(ErrorCode::kerMallocFailed); + } } + ~BrotliDecoderWrapper() { + BrotliDecoderDestroyInstance(decoder_); + } + + BrotliDecoderState* get() const { + return decoder_; + } +}; + +void BmffImage::brotliUncompress(const byte* compressedBuf, size_t compressedBufSize, DataBuf& arr) { + BrotliDecoderWrapper decoder; size_t uncompressedLen = compressedBufSize * 2; // just a starting point BrotliDecoderResult result; int dos = 0; @@ -184,7 +201,8 @@ void BmffImage::brotliUncompress(const byte* compressedBuf, size_t compressedBuf arr.alloc(uncompressedLen); available_out = uncompressedLen - total_out; next_out = arr.data() + total_out; - result = BrotliDecoderDecompressStream(decoder, &available_in, &next_in, &available_out, &next_out, &total_out); + result = + BrotliDecoderDecompressStream(decoder.get(), &available_in, &next_in, &available_out, &next_out, &total_out); if (result == BROTLI_DECODER_RESULT_SUCCESS) { arr.resize(total_out); } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { @@ -200,12 +218,10 @@ void BmffImage::brotliUncompress(const byte* compressedBuf, size_t compressedBuf throw Error(ErrorCode::kerFailedToReadImageData); } else { // something bad happened - throw Error(ErrorCode::kerErrorMessage, BrotliDecoderErrorString(BrotliDecoderGetErrorCode(decoder))); + throw Error(ErrorCode::kerErrorMessage, BrotliDecoderErrorString(BrotliDecoderGetErrorCode(decoder.get()))); } } while (result != BROTLI_DECODER_RESULT_SUCCESS); - BrotliDecoderDestroyInstance(decoder); - if (result != BROTLI_DECODER_RESULT_SUCCESS) { throw Error(ErrorCode::kerFailedToReadImageData); }