diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6bd673d1..e12b0e2c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -175,7 +175,7 @@ jobs: - name: Cleanup old nightly if: env.TAG_NAME == 'nightly' - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | try{ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4d86f679..4c92f3df 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -310,17 +310,17 @@ install(TARGETS exiv2lib EXPORT exiv2Export) include(CMakePackageConfigHelpers) configure_package_config_file( - ../cmake/exiv2Config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/exiv2Config.cmake INSTALL_DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/exiv2" + ../cmake/exiv2Config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/exiv2Config.cmake INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/exiv2" ) install(FILES ${PUBLIC_HEADERS} ${CMAKE_BINARY_DIR}/exv_conf.h ${CMAKE_BINARY_DIR}/exiv2lib_export.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/exiv2) install( EXPORT exiv2Export - DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/exiv2" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/exiv2" NAMESPACE Exiv2:: ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/exiv2ConfigVersion.cmake ${CMAKE_CURRENT_BINARY_DIR}/exiv2Config.cmake - DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/exiv2" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/exiv2" ) diff --git a/src/bmffimage.cpp b/src/bmffimage.cpp index 4eb2b123..ad084ea2 100644 --- a/src/bmffimage.cpp +++ b/src/bmffimage.cpp @@ -216,7 +216,7 @@ void BmffImage::brotliUncompress(const byte* compressedBuf, size_t compressedBuf uncompressedLen *= 2; // DoS protection - can't be bigger than 128k if (uncompressedLen > 131072) { - if (++dos > 1) + if (++dos > 1 || total_out > 131072) break; uncompressedLen = 131072; } diff --git a/src/nikonmn_int.cpp b/src/nikonmn_int.cpp index f28731c1..bb6bbf9b 100644 --- a/src/nikonmn_int.cpp +++ b/src/nikonmn_int.cpp @@ -3954,6 +3954,9 @@ std::ostream& Nikon3MakerNote::printLensId4ZMount(std::ostream& os, const Value& {42, "Nikon", "Nikkor Z 180-600mm f/5.6-6.3 VR"}, {43, "Nikon", "Nikkor Z DX 24mm f/1.7"}, {44, "Nikon", "Nikkor Z 70-180mm f/2.8"}, + {45, "Nikon", "Nikkor Z 600mm f/6.3 VR S"}, + {46, "Nikon", "Nikkor Z 135mm f/1.8 S Plena"}, + {53251, "Sigma", "56mm F1.4 DC DN | C"}, }; auto lid = static_cast(value.toInt64()); diff --git a/src/rafimage.cpp b/src/rafimage.cpp index 495e9692..3ad906a6 100644 --- a/src/rafimage.cpp +++ b/src/rafimage.cpp @@ -14,6 +14,7 @@ #include "safe_op.hpp" #include "tiffimage.hpp" +#include #include // ***************************************************************************** @@ -71,19 +72,19 @@ void RafImage::printStructure(std::ostream& out, PrintStructureOption option, si if (bPrint) { io_->seek(0, BasicIo::beg); // rewind size_t address = io_->tell(); - constexpr auto format = " %8zu | %8ld | "; + constexpr auto format = " %9zu | %9" PRIu32 " | "; { out << Internal::indent(depth) << "STRUCTURE OF RAF FILE: " << io().path() << std::endl; - out << Internal::indent(depth) << " Address | Length | Payload" << std::endl; + out << Internal::indent(depth) << " Address | Length | Payload" << std::endl; } byte magicdata[17]; - io_->read(magicdata, 16); + io_->readOrThrow(magicdata, 16); magicdata[16] = 0; { - out << Internal::indent(depth) << Internal::stringFormat(format, address, 16L) // 0 - << " magic : " << reinterpret_cast(magicdata) << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address, 16U) // 0 + << " magic : " << reinterpret_cast(magicdata) << std::endl; } address = io_->tell(); @@ -91,8 +92,8 @@ void RafImage::printStructure(std::ostream& out, PrintStructureOption option, si io_->read(data1, 4); data1[4] = 0; { - out << Internal::indent(depth) << Internal::stringFormat(format, address, 4L) // 16 - << " data1 : " << std::string(reinterpret_cast(&data1)) << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address, 4U) // 16 + << " data1 : " << std::string(reinterpret_cast(&data1)) << std::endl; } address = io_->tell(); @@ -100,8 +101,8 @@ void RafImage::printStructure(std::ostream& out, PrintStructureOption option, si io_->read(data2, 8); data2[8] = 0; { - out << Internal::indent(depth) << Internal::stringFormat(format, address, 8L) // 20 - << " data2 : " << std::string(reinterpret_cast(&data2)) << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address, 8U) // 20 + << " data2 : " << std::string(reinterpret_cast(&data2)) << std::endl; } address = io_->tell(); @@ -109,8 +110,8 @@ void RafImage::printStructure(std::ostream& out, PrintStructureOption option, si io_->read(camdata, 32); camdata[32] = 0; { - out << Internal::indent(depth) << Internal::stringFormat(format, address, 32L) // 28 - << " camera : " << std::string(reinterpret_cast(&camdata)) << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address, 32U) // 28 + << " camera : " << std::string(reinterpret_cast(&camdata)) << std::endl; } address = io_->tell(); @@ -118,16 +119,16 @@ void RafImage::printStructure(std::ostream& out, PrintStructureOption option, si io_->read(dir_version, 4); dir_version[4] = 0; { - out << Internal::indent(depth) << Internal::stringFormat(format, address, 4L) // 60 - << " version : " << std::string(reinterpret_cast(&dir_version)) << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address, 4U) // 60 + << " version : " << std::string(reinterpret_cast(&dir_version)) << std::endl; } address = io_->tell(); DataBuf unknown(20); - io_->read(unknown.data(), unknown.size()); + io_->readOrThrow(unknown.data(), unknown.size()); { - out << Internal::indent(depth) << Internal::stringFormat(format, address, 20L) - << " unknown : " << Internal::binaryToString(makeSlice(unknown, 0, unknown.size())) << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address, 20U) + << " unknown : " << Internal::binaryToString(makeSlice(unknown, 0, unknown.size())) << std::endl; } address = io_->tell(); @@ -137,80 +138,123 @@ void RafImage::printStructure(std::ostream& out, PrintStructureOption option, si size_t address2 = io_->tell(); io_->read(jpg_img_length, 4); - long jpg_img_off = Exiv2::getULong(jpg_img_offset, bigEndian); - long jpg_img_len = Exiv2::getULong(jpg_img_length, bigEndian); - std::stringstream j_off; - std::stringstream j_len; - j_off << jpg_img_off; - j_len << jpg_img_len; + uint32_t jpg_img_off = Exiv2::getULong(jpg_img_offset, bigEndian); + uint32_t jpg_img_len = Exiv2::getULong(jpg_img_length, bigEndian); { - out << Internal::indent(depth) << Internal::stringFormat(format, address, 4L) << "JPEG Offset : " << j_off.str() + std::stringstream j_off; + std::stringstream j_len; + j_off << jpg_img_off; + j_len << jpg_img_len; + out << Internal::indent(depth) << Internal::stringFormat(format, address, 4U) << " JPEG offset : " << j_off.str() << std::endl; - out << Internal::indent(depth) << Internal::stringFormat(format, address2, 4L) << "JPEG Length : " << j_len.str() + out << Internal::indent(depth) << Internal::stringFormat(format, address2, 4U) << " JPEG length : " << j_len.str() << std::endl; } - address = io_->tell(); - byte cfa_header_offset[4]; - io_->read(cfa_header_offset, 4); - byte cfa_header_length[4]; - address2 = io_->tell(); - io_->read(cfa_header_length, 4); - long cfa_hdr_off = Exiv2::getULong(cfa_header_offset, bigEndian); - long cfa_hdr_len = Exiv2::getULong(cfa_header_length, bigEndian); - std::stringstream ch_off; - std::stringstream ch_len; - ch_off << cfa_hdr_off; - ch_len << cfa_hdr_len; - { - out << Internal::indent(depth) << Internal::stringFormat(format, address, 4L) << " CFA Offset : " << ch_off.str() - << std::endl; - out << Internal::indent(depth) << Internal::stringFormat(format, address2, 4L) << " CFA Length : " << ch_len.str() - << std::endl; - } - - byte cfa_offset[4]; - address = io_->tell(); - io_->read(cfa_offset, 4); - byte cfa_length[4]; - address2 = io_->tell(); - io_->read(cfa_length, 4); - long cfa_off = Exiv2::getULong(cfa_offset, bigEndian); - long cfa_len = Exiv2::getULong(cfa_length, bigEndian); - std::stringstream c_off; - std::stringstream c_len; - c_off << cfa_off; - c_len << cfa_len; - { - out << Internal::indent(depth) << Internal::stringFormat(format, address, 4L) << "TIFF Offset : " << c_off.str() - << std::endl; - out << Internal::indent(depth) << Internal::stringFormat(format, address2, 4L) << "TIFF Length : " << c_len.str() - << std::endl; + // RAFs can carry the payload in one or two parts + uint32_t meta_off[2], meta_len[2]; + uint32_t cfa_off[2], cfa_len[2], cfa_skip[2], cfa_size[2], cfa_stride[2]; + for (size_t i = 0; i < 2; i++) { + address = io_->tell(); + byte data[4]; + io_->readOrThrow(data, 4); + meta_off[i] = Exiv2::getULong(data, bigEndian); + address2 = io_->tell(); + io_->readOrThrow(data, 4); + meta_len[i] = Exiv2::getULong(data, bigEndian); + { + std::stringstream c_off; + std::stringstream c_len; + c_off << meta_off[i]; + c_len << meta_len[i]; + out << Internal::indent(depth) << Internal::stringFormat(format, address, 4U) << "meta offset" << i + 1 << " : " + << c_off.str() << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address2, 4U) << "meta length" << i + 1 + << " : " << c_len.str() << std::endl; + } + + address = io_->tell(); + io_->readOrThrow(data, 4); + cfa_off[i] = Exiv2::getULong(data, bigEndian); + address2 = io_->tell(); + io_->readOrThrow(data, 4); + cfa_len[i] = Exiv2::getULong(data, bigEndian); + size_t address3 = io_->tell(); + io_->readOrThrow(data, 4); + cfa_skip[i] = Exiv2::getULong(data, bigEndian); + size_t address4 = io_->tell(); + io_->readOrThrow(data, 4); + cfa_size[i] = Exiv2::getULong(data, bigEndian); + size_t address5 = io_->tell(); + io_->readOrThrow(data, 4); + cfa_stride[i] = Exiv2::getULong(data, bigEndian); + { + std::stringstream c_off; + std::stringstream c_len; + std::stringstream c_skip; + std::stringstream c_size; + std::stringstream c_stride; + c_off << cfa_off[i]; + c_len << cfa_len[i]; + c_skip << cfa_skip[i]; + c_size << cfa_size[i]; + c_stride << cfa_stride[i]; + out << Internal::indent(depth) << Internal::stringFormat(format, address, 4U) << " CFA offset" << i + 1 << " : " + << c_off.str() << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address2, 4U) << " CFA length" << i + 1 + << " : " << c_len.str() << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address3, 4U) << " CFA skip" << i + 1 + << " : " << c_skip.str() << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address4, 4U) << " CFA chunk" << i + 1 + << " : " << c_size.str() << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address5, 4U) << " CFA stride" << i + 1 + << " : " << c_stride.str() << std::endl; + } } io_->seek(jpg_img_off, BasicIo::beg); // rewind address = io_->tell(); DataBuf payload(16); // header is different from chunks - io_->read(payload.data(), payload.size()); + io_->readOrThrow(payload.data(), payload.size()); { - out << Internal::indent(depth) << Internal::stringFormat(format, address, jpg_img_len) // , jpg_img_off) - << " JPEG : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address, jpg_img_len) + << " JPEG data : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << std::endl; } - io_->seek(cfa_hdr_off, BasicIo::beg); // rewind + io_->seek(meta_off[0], BasicIo::beg); // rewind address = io_->tell(); - io_->read(payload.data(), payload.size()); + io_->readOrThrow(payload.data(), payload.size()); { - out << Internal::indent(depth) << Internal::stringFormat(format, address, cfa_hdr_len) // cfa_hdr_off - << " CFA : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address, meta_len[0]) + << " meta data1 : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << std::endl; } - io_->seek(cfa_off, BasicIo::beg); // rewind + if (meta_off[1] && meta_len[1]) { + io_->seek(meta_off[1], BasicIo::beg); // rewind + address = io_->tell(); + io_->readOrThrow(payload.data(), payload.size()); + { + out << Internal::indent(depth) << Internal::stringFormat(format, address, meta_len[1]) + << " meta data2 : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << std::endl; + } + } + + io_->seek(cfa_off[0], BasicIo::beg); // rewind address = io_->tell(); - io_->read(payload.data(), payload.size()); + io_->readOrThrow(payload.data(), payload.size()); { - out << Internal::indent(depth) << Internal::stringFormat(format, address, cfa_len) // cfa_off - << " TIFF : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << std::endl; + out << Internal::indent(depth) << Internal::stringFormat(format, address, cfa_len[0]) + << " CFA data1 : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << std::endl; + } + + if (cfa_off[1] && cfa_len[1]) { + io_->seek(cfa_off[1], BasicIo::beg); // rewind + address = io_->tell(); + io_->readOrThrow(payload.data(), payload.size()); + { + out << Internal::indent(depth) << Internal::stringFormat(format, address, cfa_len[1]) // cfa_off + << " CFA data2 : " << Internal::binaryToString(makeSlice(payload, 0, payload.size())) << std::endl; + } } } } // RafImage::printStructure diff --git a/src/sonymn_int.cpp b/src/sonymn_int.cpp index 30c4e756..0d732ea4 100644 --- a/src/sonymn_int.cpp +++ b/src/sonymn_int.cpp @@ -512,6 +512,7 @@ constexpr TagDetails sonyModelId[] = { {389, "ZV-1F"}, {390, "ILCE-7RM5"}, {391, "ILME-FX30"}, + {392, "ILCE-9M3"}, {393, "ZV-E1"}, {394, "ILCE-6700"}, {395, "ZV-1M2"}, @@ -547,7 +548,7 @@ constexpr StringTagDetails sonyFileFormat[] = { {"0 0 0 2", "JPEG"}, {"1 0 0 0", "SR2 1.0"}, {"2 0 0 0", "ARW 1.0"}, {"3 0 0 0", "ARW 2.0"}, {"3 1 0 0", "ARW 2.1"}, {"3 2 0 0", "ARW 2.2"}, {"3 3 0 0", "ARW 2.3"}, {"3 3 1 0", "ARW 2.3.1"}, {"3 3 2 0", "ARW 2.3.2"}, {"3 3 3 0", "ARW 2.3.3"}, {"3 3 5 0", "ARW 2.3.5"}, {"4 0 0 0", "ARW 4.0"}, - {"4 0 1 0", "ARW 4.0.1"}, + {"4 0 1 0", "ARW 4.0.1"}, {"5 0 0 0", "ARW 5.0.0"}, }; //! Lookup table to translate Sony dynamic range optimizer values to readable labels diff --git a/test/data/issue_ghsa_hrw9_ggg3_3r4r_poc.jpg b/test/data/issue_ghsa_hrw9_ggg3_3r4r_poc.jpg new file mode 100644 index 00000000..2fd8e675 Binary files /dev/null and b/test/data/issue_ghsa_hrw9_ggg3_3r4r_poc.jpg differ diff --git a/tests/bugfixes/github/test_issue_2427.py b/tests/bugfixes/github/test_issue_2427.py index 113aa0c5..ca7b2f11 100644 --- a/tests/bugfixes/github/test_issue_2427.py +++ b/tests/bugfixes/github/test_issue_2427.py @@ -7,7 +7,5 @@ class issue_2427_BmffImage_brotliUncompress_memleak(metaclass=CaseMeta): filename = "$data_path/issue_2427_poc.jpg" commands = ["$exiv2 $filename"] retval = [1] - stderr = ["""$exiv2_exception_message $filename: -CL_SPACE -"""] stdout = [""] + compare_stderr = check_no_ASAN_UBSAN_errors diff --git a/tests/bugfixes/github/test_issue_ghsa_hrw9_ggg3_3r4r.py b/tests/bugfixes/github/test_issue_ghsa_hrw9_ggg3_3r4r.py new file mode 100644 index 00000000..a054ba28 --- /dev/null +++ b/tests/bugfixes/github/test_issue_ghsa_hrw9_ggg3_3r4r.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +from system_tests import CaseMeta, path + +class BrotliUncompressOutOfBoundsWrite(metaclass=CaseMeta): + """ + Regression test for the bug described in: + https://github.com/Exiv2/exiv2/security/advisories/GHSA-hrw9-ggg3-3r4r + """ + url = "https://github.com/Exiv2/exiv2/security/advisories/GHSA-hrw9-ggg3-3r4r" + + filename = path("$data_path/issue_ghsa_hrw9_ggg3_3r4r_poc.jpg") + commands = ["$exiv2 $filename"] + stdout = [""] + stderr = [ +"""Exiv2 exception in print action for file $filename: +$kerFailedToReadImageData +"""] + retval = [1] diff --git a/tests/regression_tests/test_regression_allfiles.py b/tests/regression_tests/test_regression_allfiles.py index 8ffce39c..426a0c36 100644 --- a/tests/regression_tests/test_regression_allfiles.py +++ b/tests/regression_tests/test_regression_allfiles.py @@ -116,6 +116,7 @@ def get_valid_files(data_dir): "issue_ghsa_583f_w9pm_99r2_poc.jp2", "issue_ghsa_7569_phvm_vwc2_poc.jp2", "issue_ghsa_mxw9_qx4c_6m8v_poc.jp2", + "issue_ghsa_hrw9_ggg3_3r4r_poc.jpg", "pocIssue283.jpg", "poc_1522.jp2", "xmpsdk.xmp", diff --git a/tests/tiff_test/test_tag_compare.py b/tests/tiff_test/test_tag_compare.py index 974d1a9a..4bf44431 100644 --- a/tests/tiff_test/test_tag_compare.py +++ b/tests/tiff_test/test_tag_compare.py @@ -98,9 +98,9 @@ class OutputTagExtract(metaclass=system_tests.CaseMeta): def compare_stdout(self, i, command, got_stdout, expected_stdout): super().compare_stdout(i, command, got_stdout, expected_stdout) - if '-pa' in command: + if ' -pa ' in command: self.pa_data = self.parse_pa(got_stdout.splitlines()) - if '-pS' in command: + if ' -pS ' in command: self.pS_data = self.parse_pS(got_stdout.splitlines()) if i == 1: