diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aecd621..cbbd859 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,7 @@ SET( LIBEXIV2_PRIVATE_HDR canonmn_int.hpp pngchunk_int.hpp rcsid_int.hpp rw2image_int.hpp + safe_op.hpp samsungmn_int.hpp sigmamn_int.hpp sonymn_int.hpp @@ -102,6 +103,7 @@ SET( LIBEXIV2_SRC asfvideo.cpp futils.cpp fujimn.cpp gifimage.cpp + helper_functions.cpp http.cpp image.cpp ini.cpp diff --git a/src/helper_functions.cpp b/src/helper_functions.cpp new file mode 100644 index 0000000..623fbc1 --- /dev/null +++ b/src/helper_functions.cpp @@ -0,0 +1,39 @@ +// ********************************************************* -*- C++ -*- +/* + * Copyright (C) 2004-2018 Exiv2 authors + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/*! + @file helper_functions.cpp + @brief A collection of helper functions + @author Dan Čermák (D4N) + dan.cermak@cgc-instruments.com + @date 25-May-18, D4N: created + */ + +#include "helper_functions.hpp" + +#include + + +std::string string_from_unterminated(const char* data, size_t data_length) +{ + const size_t StringLength = strnlen(data, data_length); + + return std::string(data, StringLength); +} diff --git a/src/helper_functions.hpp b/src/helper_functions.hpp new file mode 100644 index 0000000..d70cbc1 --- /dev/null +++ b/src/helper_functions.hpp @@ -0,0 +1,50 @@ +// ********************************************************* -*- C++ -*- +/* + * Copyright (C) 2004-2018 Exiv2 authors + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/*! + @file helper_functions.hpp + @brief A collection of helper functions + @author Dan Čermák (D4N) + dan.cermak@cgc-instruments.com + @date 25-May-18, D4N: created + */ +#ifndef HELPER_FUNCTIONS_HPP +#define HELPER_FUNCTIONS_HPP + +#include + +/*! + @brief Convert a (potentially not null terminated) array into a + std::string. + + Convert a C style string that may or may not be null terminated safely + into a std::string. The string's termination is either set at the first \0 + or after data_length characters. + + @param[in] data A c-string from which the std::string shall be + constructed. Does not need to be null terminated. + @param[in] data_length An upper bound for the string length (must be at most + the allocated length of `buffer`). If no null terminator is found in data, + then the resulting std::string will be null terminated at `data_length`. + + */ +std::string string_from_unterminated(const char* data, size_t data_length); + +#endif // HELPER_FUNCTIONS_HPP diff --git a/src/pngchunk.cpp b/src/pngchunk.cpp index 29ffcfa..e4e3274 100644 --- a/src/pngchunk.cpp +++ b/src/pngchunk.cpp @@ -38,6 +38,8 @@ EXIV2_RCSID("@(#) $Id$") #include "image.hpp" #include "error.hpp" #include "enforce.hpp" +#include "helper_functions.hpp" +#include "safe_op.hpp" // + standard includes #include @@ -137,6 +139,8 @@ namespace Exiv2 { if(type == zTXt_Chunk) { + enforce(data.size_ >= Safe::add(keysize, 2), Exiv2::kerCorruptedMetadata); + // Extract a deflate compressed Latin-1 text chunk // we get the compression method after the key @@ -153,11 +157,13 @@ namespace Exiv2 { // compressed string after the compression technique spec const byte* compressedText = data.pData_ + keysize + 2; unsigned int compressedTextSize = data.size_ - keysize - 2; + enforce(compressedTextSize < data.size_, kerCorruptedMetadata); zlibUncompress(compressedText, compressedTextSize, arr); } else if(type == tEXt_Chunk) { + enforce(data.size_ >= Safe::add(keysize, 1), Exiv2::kerCorruptedMetadata); // Extract a non-compressed Latin-1 text chunk // the text comes after the key, but isn't null terminated @@ -168,6 +174,7 @@ namespace Exiv2 { } else if(type == iTXt_Chunk) { + enforce(data.size_ >= Safe::add(keysize, 3), Exiv2::kerCorruptedMetadata); const int nullSeparators = std::count(&data.pData_[keysize+3], &data.pData_[data.size_], '\0'); enforce(nullSeparators >= 2, Exiv2::kerCorruptedMetadata); @@ -180,42 +187,46 @@ namespace Exiv2 { const byte compressionMethod = data.pData_[keysize + 2]; enforce(compressionFlag == 0x00 || compressionFlag == 0x01, Exiv2::kerCorruptedMetadata); enforce(compressionMethod == 0x00, Exiv2::kerCorruptedMetadata); + // language description string after the compression technique spec - std::string languageText((const char*)(data.pData_ + keysize + 3)); - unsigned int languageTextSize = static_cast(languageText.size()); + const size_t languageTextMaxSize = data.size_ - keysize - 3; + std::string languageText = + string_from_unterminated((const char*)(data.pData_ + Safe::add(keysize, 3)), languageTextMaxSize); + const unsigned int languageTextSize = static_cast(languageText.size()); + enforce(data.size_ >= Safe::add(static_cast(Safe::add(keysize, 4)), languageTextSize), + Exiv2::kerCorruptedMetadata); + // translated keyword string after the language description - std::string translatedKeyText((const char*)(data.pData_ + keysize + 3 + languageTextSize +1)); - unsigned int translatedKeyTextSize = static_cast(translatedKeyText.size()); + std::string translatedKeyText = + string_from_unterminated((const char*)(data.pData_ + keysize + 3 + languageTextSize + 1), + data.size_ - (keysize + 3 + languageTextSize + 1)); + const unsigned int translatedKeyTextSize = static_cast(translatedKeyText.size()); - if ( compressionFlag == 0x00 ) - { - // then it's an uncompressed iTXt chunk -#ifdef DEBUG - std::cout << "Exiv2::PngChunk::parseTXTChunk: We found an uncompressed iTXt field\n"; -#endif + if ((compressionFlag == 0x00) || (compressionFlag == 0x01 && compressionMethod == 0x00)) { + enforce(Safe::add(static_cast(keysize + 3 + languageTextSize + 1), + Safe::add(translatedKeyTextSize, 1u)) <= data.size_, + Exiv2::kerCorruptedMetadata); - // the text comes after the translated keyword, but isn't null terminated const byte* text = data.pData_ + keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1; - long textsize = data.size_ - (keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1); + const long textsize = data.size_ - (keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1); - arr.alloc(textsize); - arr = DataBuf(text, textsize); - } - else if ( compressionFlag == 0x01 && compressionMethod == 0x00 ) - { - // then it's a zlib compressed iTXt chunk + if (compressionFlag == 0x00) { + // then it's an uncompressed iTXt chunk #ifdef DEBUG - std::cout << "Exiv2::PngChunk::parseTXTChunk: We found a zlib compressed iTXt field\n"; + std::cout << "Exiv2::PngChunk::parseTXTChunk: We found an uncompressed iTXt field\n"; #endif - // the compressed text comes after the translated keyword, but isn't null terminated - const byte* compressedText = data.pData_ + keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1; - long compressedTextSize = data.size_ - (keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1); - - zlibUncompress(compressedText, compressedTextSize, arr); - } - else - { + arr.alloc(textsize); + arr = DataBuf(text, textsize); + } else if (compressionFlag == 0x01 && compressionMethod == 0x00) { + // then it's a zlib compressed iTXt chunk +#ifdef DEBUG + std::cout << "Exiv2::PngChunk::parseTXTChunk: We found a zlib compressed iTXt field\n"; +#endif + // the compressed text comes after the translated keyword, but isn't null terminated + zlibUncompress(text, textsize, arr); + } + } else { // then it isn't zlib compressed and we are sunk #ifdef DEBUG std::cerr << "Exiv2::PngChunk::parseTXTChunk: Non-standard iTXt compression method.\n";