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 "error.hpp"
#include "enforce.hpp"
#include "helper_functions.hpp"
#include "safe_op.hpp"
// + standard includes
#include <sstream>
@ -133,6 +135,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
@ -149,11 +153,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
@ -164,6 +170,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);
@ -178,41 +185,45 @@ namespace Exiv2 {
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<unsigned int>(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<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
std::string translatedKeyText((const char*)(data.pData_ + keysize + 3 + languageTextSize +1));
unsigned int translatedKeyTextSize = static_cast<unsigned int>(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<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
#ifdef DEBUG
std::cout << "Exiv2::PngChunk::parseTXTChunk: We found an uncompressed iTXt field\n";
#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 = DataBuf(text, textsize);
}
else if ( compressionFlag == 0x01 && compressionMethod == 0x00 )
{
} 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
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);
zlibUncompress(text, textsize, arr);
}
else
{
} else {
// then it isn't zlib compressed and we are sunk
#ifdef DEBUG
std::cerr << "Exiv2::PngChunk::parseTXTChunk: Non-standard iTXt compression method.\n";

Loading…
Cancel
Save