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
This commit is contained in:
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 )
|
||||
{
|
||||
// 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<unsigned int>(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);
|
||||
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
|
||||
|
||||
zlibUncompress(compressedText, compressedTextSize, arr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user