Add overflow & overread checks to PngChunk::parseTXTChunk()

This function was creating a lot of new pointers and strings without
properly checking the array bounds. This commit adds several calls
to enforce(), making sure that the pointers stay within bounds.
Strings are now created using the helper function
string_from_unterminated() to prevent overreads in the constructor of
std::string.

This fixes #400
v0.27.3
Dan Čermák 7 years ago
parent 6da77e2c64
commit 35b3e596ed

@ -34,6 +34,8 @@
#include "image.hpp" #include "image.hpp"
#include "error.hpp" #include "error.hpp"
#include "enforce.hpp" #include "enforce.hpp"
#include "helper_functions.hpp"
#include "safe_op.hpp"
// + standard includes // + standard includes
#include <sstream> #include <sstream>
@ -133,6 +135,8 @@ namespace Exiv2 {
if(type == zTXt_Chunk) if(type == zTXt_Chunk)
{ {
enforce(data.size_ >= Safe::add(keysize, 2), Exiv2::kerCorruptedMetadata);
// Extract a deflate compressed Latin-1 text chunk // Extract a deflate compressed Latin-1 text chunk
// we get the compression method after the key // we get the compression method after the key
@ -149,11 +153,13 @@ namespace Exiv2 {
// compressed string after the compression technique spec // compressed string after the compression technique spec
const byte* compressedText = data.pData_ + keysize + 2; const byte* compressedText = data.pData_ + keysize + 2;
unsigned int compressedTextSize = data.size_ - keysize - 2; unsigned int compressedTextSize = data.size_ - keysize - 2;
enforce(compressedTextSize < data.size_, kerCorruptedMetadata);
zlibUncompress(compressedText, compressedTextSize, arr); zlibUncompress(compressedText, compressedTextSize, arr);
} }
else if(type == tEXt_Chunk) else if(type == tEXt_Chunk)
{ {
enforce(data.size_ >= Safe::add(keysize, 1), Exiv2::kerCorruptedMetadata);
// Extract a non-compressed Latin-1 text chunk // Extract a non-compressed Latin-1 text chunk
// the text comes after the key, but isn't null terminated // the text comes after the key, but isn't null terminated
@ -164,6 +170,7 @@ namespace Exiv2 {
} }
else if(type == iTXt_Chunk) 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'); const int nullSeparators = std::count(&data.pData_[keysize+3], &data.pData_[data.size_], '\0');
enforce(nullSeparators >= 2, Exiv2::kerCorruptedMetadata); enforce(nullSeparators >= 2, Exiv2::kerCorruptedMetadata);
@ -178,41 +185,45 @@ namespace Exiv2 {
enforce(compressionMethod == 0x00, Exiv2::kerCorruptedMetadata); enforce(compressionMethod == 0x00, Exiv2::kerCorruptedMetadata);
// language description string after the compression technique spec // language description string after the compression technique spec
std::string languageText((const char*)(data.pData_ + keysize + 3)); const size_t languageTextMaxSize = data.size_ - keysize - 3;
unsigned int languageTextSize = static_cast<unsigned int>(languageText.size()); std::string languageText =
string_from_unterminated((const char*)(data.pData_ + Safe::add(keysize, 3)), languageTextMaxSize);
const unsigned int languageTextSize = static_cast<unsigned int>(languageText.size());
enforce(data.size_ >= Safe::add(static_cast<unsigned int>(Safe::add(keysize, 4)), languageTextSize),
Exiv2::kerCorruptedMetadata);
// translated keyword string after the language description // translated keyword string after the language description
std::string translatedKeyText((const char*)(data.pData_ + keysize + 3 + languageTextSize +1)); std::string translatedKeyText =
unsigned int translatedKeyTextSize = static_cast<unsigned int>(translatedKeyText.size()); string_from_unterminated((const char*)(data.pData_ + keysize + 3 + languageTextSize + 1),
data.size_ - (keysize + 3 + languageTextSize + 1));
const unsigned int translatedKeyTextSize = static_cast<unsigned int>(translatedKeyText.size());
if ( compressionFlag == 0x00 ) if ((compressionFlag == 0x00) || (compressionFlag == 0x01 && compressionMethod == 0x00)) {
{ enforce(Safe::add(static_cast<unsigned int>(keysize + 3 + languageTextSize + 1),
Safe::add(translatedKeyTextSize, 1u)) <= data.size_,
Exiv2::kerCorruptedMetadata);
const byte* text = data.pData_ + keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1;
const long textsize = data.size_ - (keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1);
if (compressionFlag == 0x00) {
// then it's an uncompressed iTXt chunk // then it's an uncompressed iTXt chunk
#ifdef DEBUG #ifdef DEBUG
std::cout << "Exiv2::PngChunk::parseTXTChunk: We found an uncompressed iTXt field\n"; std::cout << "Exiv2::PngChunk::parseTXTChunk: We found an uncompressed iTXt field\n";
#endif #endif
// 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);
arr.alloc(textsize); arr.alloc(textsize);
arr = DataBuf(text, textsize); arr = DataBuf(text, textsize);
} } else if (compressionFlag == 0x01 && compressionMethod == 0x00) {
else if ( compressionFlag == 0x01 && compressionMethod == 0x00 )
{
// then it's a zlib compressed iTXt chunk // then it's a zlib compressed iTXt chunk
#ifdef DEBUG #ifdef DEBUG
std::cout << "Exiv2::PngChunk::parseTXTChunk: We found a zlib compressed iTXt field\n"; std::cout << "Exiv2::PngChunk::parseTXTChunk: We found a zlib compressed iTXt field\n";
#endif #endif
// the compressed text comes after the translated keyword, but isn't null terminated // the compressed text comes after the translated keyword, but isn't null terminated
const byte* compressedText = data.pData_ + keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1; zlibUncompress(text, textsize, arr);
long compressedTextSize = data.size_ - (keysize + 3 + languageTextSize + 1 + translatedKeyTextSize + 1);
zlibUncompress(compressedText, compressedTextSize, arr);
} }
else } else {
{
// then it isn't zlib compressed and we are sunk // then it isn't zlib compressed and we are sunk
#ifdef DEBUG #ifdef DEBUG
std::cerr << "Exiv2::PngChunk::parseTXTChunk: Non-standard iTXt compression method.\n"; std::cerr << "Exiv2::PngChunk::parseTXTChunk: Non-standard iTXt compression method.\n";

Loading…
Cancel
Save